Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Bundle and BundleTangent types #175

Closed
wants to merge 5 commits into from
Closed

Conversation

pettni
Copy link
Contributor

@pettni pettni commented Oct 18, 2020

I felt compelled to take a stab at #84 and this is the result.

I believe it is functional as it is, though it could for sure use more testing. Wanted to see what you think before proceeding.

Some comments:

  • Both Composite and CompositeTangent are templated on the underlying lie group type, i.e. CompositeTangent<double, SE3, R2> (and not CompositeTangent<double, SE3Tangent, R2Tangent>) is the tangent of Composite<double, SE3, R2>.
  • Both types have a member function template<size_t I> get() that allows access to the individual parts via a Map. I would have preferred to inherit from std::tuple to get std::get for free but it seems like it is assumed throughout the library that all types store the underlying data as a single continuous array.
  • I first wrote it for C++17 and had to work quite hard to convert it to C++11. As a result there is a replacement for C++14 std::integer_sequence and the previous C++17 fold expressions are now using this C++11 initializer_list trick.
  • Some library parts are a bit unnatural for the composite type, such as the LieAlg and Transformation types. They are there so that the full interface is implemented and the tests run
  • Jacobian and Transformation are block-diagonal and could potentially be represented with sparse instead of dense matrices.

@pettni pettni changed the title Add Composite lie group and associated CompositeTangent Add Composite Lie group and associated CompositeTangent Oct 18, 2020
@pettni pettni force-pushed the devel branch 3 times, most recently from 2166ec0 to d8c0ca3 Compare October 18, 2020 20:15
@artivis
Copy link
Owner

artivis commented Oct 19, 2020

Hi @pettni and thanks for this PR!

It is quite some work, there is a lot to review and go through. First of all, I essentially agree with your first 3 points. I know it can be painful to write this kind of code in C++11 but I believe that this low requirement is also one of the nice feature of manif.
About the fifth point, such a change would have to be reflected throughout manif and I'm not sure the benefits are worth the effort.

Altho it is not reflected in #84, we decided a while back to name this class Bundle rather than Composite to avoid any confusion. Let me ask @joansola for a confirmation before doing any renaming.

Would you however add some more composite types to the test suites using the test macros? Make the types diverse just to make sure that virtually any combination is doable (e.g. MANIF_TEST(Composite<double, SE2, R7, SO3, R2, R2>) and such).

Regarding the MSVC failure, the first error it complains about is Error C2947 around this line. I believe it can be resolved with something along the lines,

template<size_t Idx>
  using PartType = typename std::tuple_element<
    Idx, std::tuple<typename _T<_Scalar>::template Tangent ...>
    >::type;                                ^^^

@joansola
Copy link
Collaborator

joansola commented Oct 19, 2020

Hi Petter, thanks for this very interesting input!

Responding to Jeremie-s question, regarding the name bundle vs. composite, there are pros and cons:

  • Composite is the name appearing in the paper. "Bundle" appears as a clarification.
  • Bundle is shorter and good for coding, so I like it in the library.

After these two considerations, I really have no preference for one or the other. In my talks and other discussions about this topic, I use "composite" and not "bundle", but these talks are about math and not about code.

@joansola
Copy link
Collaborator

  • Both types have a member function template<size_t I> get() that allows access to the individual parts via a Map. I would have preferred to inherit from std::tuple to get std::get for free but it seems like it is assumed throughout the library that all types store the underlying data as a single continuous array.

Interesting. What is the key in this map to access the composite's components?

@artivis
Copy link
Owner

artivis commented Oct 19, 2020

Interesting. What is the key in this map to access the composite's components?

If I'm not mistaken, given Composite<double, SO3, R3> my_composite then my_composite.get<0>() return a Map to the first element, that is SO3. Similarly my_composite.get<1>() return a Map<R3> etc.

@pettni
Copy link
Contributor Author

pettni commented Oct 19, 2020

Interesting. What is the key in this map to access the composite's components?

If I'm not mistaken, given Composite<double, SO3, R3> my_composite then my_composite.get<1>() return a Map to the first element, that is SO3. Similarly my_composite.get<2>() return a Map<R3> etc.

Yes this is exactly how it works (except that it is zero-indexed); I was referring to an Eigen::Map<SO3>, not a key: value map such as std::unordered_map.

I have not been successful in fixing the issue on MSVC. I think that it can be solved by iterating various forms of the problematic template, but as I don't have access to the compiler it is tedious to debug.

After adding the additional tests the CI job with Ceres also fails, seemingly due to the compiler running out of memory.

@artivis
Copy link
Owner

artivis commented Oct 19, 2020

Yes this is exactly how it works (except that it is zero-indexed);

Right, I fixed my comment.

I have not been successful in fixing the issue on MSVC. I think that it can be solved by iterating various forms of the problematic template, but as I don't have access to the compiler it is tedious to debug.

I don't have access to a machine with MSVC at the moment either. Altho I might try and give it a shot against the CI to help out.

After adding the additional tests the CI job with Ceres also fails, seemingly due to the compiler running out of memory.

The CI is actually running out of time rather than memory. It is hitting a default limit on Travis so that if the compilation does not produce any output for more than 10min, it aborts. I had similar a issue with Rn and the coverage job (see e.g. here). I might add another similar compilation flag here.

@joansola
Copy link
Collaborator

joansola commented Oct 19, 2020

Interesting. What is the key in this map to access the composite's components?

If I'm not mistaken, given Composite<double, SO3, R3> my_composite then my_composite.get<1>() return a Map to the first element, that is SO3. Similarly my_composite.get<2>() return a Map<R3> etc.

Yes this is exactly how it works (except that it is zero-indexed); I was referring to an Eigen::Map<SO3>, not a key: value map such as std::unordered_map.

Ah OK I see. Thanks for the clarification.

BTW a question comes to my mind: is it possible with this implementation to know which type of Lie group a certain block is? For example, to know which type is my_composite<2> at runtime?

@artivis artivis added the enhancement New feature or request label Oct 19, 2020
@artivis artivis self-assigned this Oct 19, 2020
@joansola
Copy link
Collaborator

  • Jacobian and Transformation are block-diagonal and could potentially be represented with sparse instead of dense matrices.

Jacobian should be either a full matrix or a doubly-indexed set of block matrices, but not a block-diagonal:
e.g. imagine a function f from composite <SO3, R3> to itself:

X = <R,T> \in <SO3,  R3>
p \in R3
f : <SO3,  R3> --> <SO3,  R3> , f(R,T) = <Ro, To> = <R.tr, R.tr*p + T>
J = df/dX = [ dRo/dR  dRo/dT ; dTo/dR dTo/dT ]

where only the block dRo/dT is zero, but this is because of f, not by definition.

How to represent the Jacobian J I don't know, but either indexed doubly with the input and output indices of the composites involved in f(), or as a full matrix.

See eq. 89 of the paper.

@pettni
Copy link
Contributor Author

pettni commented Oct 20, 2020

I don't have access to a machine with MSVC at the moment either. Altho I might try and give it a shot against the CI to help out.

The CI is actually running out of time rather than memory. It is hitting a default limit on Travis so that if the compilation does not produce any output for more than 10min, it aborts. I had similar a issue with Rn and the coverage job (see e.g. here). I might add another similar compilation flag here.

Some assistance with those issues would be great!

BTW a question comes to my mind: is it possible with this implementation to know which type of Lie group a certain block is? For example, to know which type is my_composite<2> at runtime?

The parts are known at compile time so it's fine to write e.g.

using group2 = decltype(my_composite.get<2>())::LieGroup;

Jacobian should be either a full matrix or a doubly-indexed set of block matrices, but not a block-diagonal:

Ah yes that seems correct. What I had in mind was that (I believe) all the Jacobians that are provided by the library become block-diagonal, since the associated mappings (log, exp, inverse, act, compose, etc) are perfectly decomposed, something like

  <x1, x2, ... xN>  |-->  <f1(x1), f2(x2), ... f3(x3)>    or     (<x1, x2, ... xN>, <y1, y2, ... yN>)  |-->  <f1(x1, y1), f2(x2, y2), ... fN(xN, yN)>

However, I guess a user would multiply those block-diagonal jacobians with other derivatives that are not block-diagonal to obtain jacobians for more general mappings such as the one you proposed, so it seems like the best representation is as a full matrix. I admit I haven't used the jacobian functionality in manif myself since autodiff is very convenient :)

To facilitate working with full-size jacobians it's an option to expose the BegDoF (start of block) and LenDoF (length of block) index lists that are currently used internally. With those users could retrieve the block start indices and dimensions without manually keeping track of dimensions. For example, <SO3, R2> has tangent space dimensions 3 and 2, so in this case BegDoF = [0, 3] and LenDoF = [3, 2] and the (0, 1) block starts at BegDoF[0] x BegDoF[1] and has size LenDoF[0] x LenDoF[1].

@pettni pettni force-pushed the devel branch 3 times, most recently from 1e6fed3 to ac91687 Compare December 24, 2020 01:58
@codecov
Copy link

codecov bot commented Dec 24, 2020

Codecov Report

Merging #175 (81e9498) into devel (54013cf) will increase coverage by 0.16%.
The diff coverage is 100.00%.

@@            Coverage Diff             @@
##            devel     #175      +/-   ##
==========================================
+ Coverage   98.40%   98.57%   +0.16%     
==========================================
  Files          51       57       +6     
  Lines        1506     1679     +173     
==========================================
+ Hits         1482     1655     +173     
  Misses         24       24              

@pettni pettni force-pushed the devel branch 2 times, most recently from d96244f to 650398e Compare December 24, 2020 02:33
@pettni
Copy link
Contributor Author

pettni commented Dec 24, 2020

Was able to make MSVC happy after some experimentation.

Some changes I did outside the bundle type:

  • Had an issue with no copy constructor for maps, so I added one in the macros.
  • Had problems with outdated package URLs in the CI so I added an apt update step there.

Let me know if there are further things you'd like to see to accept the PR.

Copy link
Owner

@artivis artivis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is a first round of review, please add a reference to the Bundle object in the readme as well.
I'll give a more thorough look after the festivities.

//

/**
* @brief Represents a Bundle element.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a little more documentation, especially references to Chap. IV and Example 7 of the paper.

/**
* @brief Get the inverse of this.
* @param[out] -optional- J_minv_m Jacobian of the inverse wrt this.
* @note r^-1 = -r
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this note.

/**
* @brief intseq: std::integer_sequence-equivalent
*/
template<int ... _I>
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move all of the meta functions and helpers to the traits.h. Moreover, move all of it under the manif::internal namespace and remove the *::bundle namespace.

* @tparam _Idx part index
*/
template<int _Idx>
Eigen::Map<PartType<_Idx>> get();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function get is too generic, we need a better name - e.g. group/tangent where appropriate or bundled. @joansola Any suggestion?

Copy link
Collaborator

@joansola joansola Dec 24, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the paper we speak of blocks, but a function block() does not appear too explicit for me either.

I suppose from BundleTangent::get<Idx>() we obtain the Idx-th tangent element?

And similarly that from Bundle::get<Idx>() we obtain the Idx-th group element?

What about element(), block(), part()?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK I think I see it clearer now.

If the syntax BundleTangent::get<Idx>() is correct, then I'd use block<Idx>()

Same for Bundle::block<Idx>()

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whatever the decision, I think it's good to be consistent. So, if one part of the code uses PartType, then this should be part<Idx>(). Otherwise, use BlockType and block<Idx>().

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Block as a meaning in Eigen so I'd rather use element to avoid confusion.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, the meaning of block in Eigen is quite similar than in our case, if that helps.

element is OK too.

Would it be ElementType too, instead of PartType?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be ElementType too, instead of PartType?

I suppose so. Note that PartType<Idx> really only is a place holder for whatever underlying manif type (SO2, RnTangent etc...).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK I just said it in case Part had the same semantic meaning as element, otherwise it's not good to use the same word of course!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW it would be interesting to see Examples V.B and V.C in the paper implemented with the new bundle!

Copy link
Contributor Author

@pettni pettni Dec 24, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestions, I went with block<Idx>() here.

Edit: Ops, I didn't see your new comments when writing this. I don't have a strong opinion on this matter so if you prefer element I'll change to that

@pettni
Copy link
Contributor Author

pettni commented Dec 24, 2020

Thanks for the suggestions, I went ahead and took care of those for now.

I realized that Bundle_properties.hpp ended up not being used, and while I was trying to understand what the properties files were doing for other groups I realized that Rn_properties.hpp also could be deleted. I can add them back if there is another reason to keep them there.

@pettni pettni changed the title Add Composite Lie group and associated CompositeTangent Add Bundle and BundleTangent types Dec 24, 2020
@joansola
Copy link
Collaborator

What's the status of this PR?

@artivis
Copy link
Owner

artivis commented Feb 27, 2021

What's the status of this PR?

This PR has some conflicts now and could probably use a rebasing. I'll take care of that.
There are still a couple things I'm not convinced by... And it misses an example.

I would suggest to move this Bundle implementation behind some 'flag' as to let the user know to use it with caution. What I have in mind is simply to move it to an experimental subfolder and not include it by default in the main manif.h include file. To use it, one would have to explicitly include it e.g.

#include <manif/experimental/bundle.h>

The main motivation is the 'hackyness' of the implementation, especially the cpp11 unfolding trick. (no offense @pettni, it is fairly difficult to use this level of meta-programming with only cpp11). So if we are to merge it as is, I would strongly suggest the experimental subfolder.

@pettni
Copy link
Contributor Author

pettni commented Feb 27, 2021

The main motivation is the 'hackyness' of the implementation, especially the cpp11 unfolding trick. (no offense @pettni, it is fairly difficult to use this level of meta-programming with only cpp11). So if we are to merge it as is, I would strongly suggest the experimental subfolder.

If it's only the fold trick you don't like we could probably do something different. I'm not exactly a fan either but decided to prioritize conciseness (and was coming from cpp17).

Here's an alternative for_each approach with compile-time recursion that could serve the same purpose.

template<typename F, typename ... Seqs>
void for_each(F && f, Seqs ...)
{
  f.template func<intseq_element<0, Seqs>::value ...>();
  for_each(std::forward<F>(f), typename intseq_tail<1, Seqs>::type{}...);
}

template<typename F, int ... Is>
void for_each(F && f, intseq<Is> ...)
{
  f.template func<Is...>();
}

struct MyFunctor
{
  template<int I, int J, int K>
  static void func()
  {
    std::cout << I << " " << J << " " << K << std::endl;
  }
};

int main()
{
  auto a = intseq<0, 1, 2, 3, 4>{};
  auto b = intseq<4, 3, 2, 1, 0>{};
  auto c = intseq<1, 2, 1, 2, 1>{};

  for_each(MyFunctor{}, a, b, c);

  return 0;
}

It compiles with g++ --std=c++11 with the intseq in this PR with this additional intseq_tail:

template<std::size_t _Idx, typename _Seq>
struct intseq_tail;

template<std::size_t _Idx, template<int ...> class _IntSeq, int Head, int ... Tail>
struct intseq_tail<_Idx, _IntSeq<Head, Tail...>>
  : public intseq_tail<_Idx - 1, _IntSeq<Tail ...>>
{};

template<template<int...> class _IntSeq, int Head, int ... Tail>
struct intseq_tail<0, _IntSeq<Head, Tail...>>
{
  using type = _IntSeq<Head, Tail...>;
};

@artivis
Copy link
Owner

artivis commented Feb 27, 2021

@pettni thanks for the quick feedback.
I do prefer this for_each approach better, but this requires to define a bunch of functors (a la MyFunctor), which is pretty much the purpose of #150 - however this PR is far from ready to roll out.
I'd propose we stick with the unfolding trick and move this PR behind an experimental wall as mentioned earlier in order to get it merged asap and revisit it after #150 lands to eventually bring Bundle fully into manif.
What's your opinion about this plan?

@pettni
Copy link
Contributor Author

pettni commented Feb 27, 2021

@pettni thanks for the quick feedback.
I do prefer this for_each approach better, but this requires to define a bunch of functors (a la MyFunctor), which is pretty much the purpose of #150 - however this PR is far from ready to roll out.
I'd propose we stick with the unfolding trick and move this PR behind an experimental wall as mentioned earlier in order to get it merged asap and revisit it after #150 lands to eventually bring Bundle fully into manif.
What's your opinion about this plan?

I'll leave it to the maintainer to decide what's experimental and not :). To me for_each vs fold is mostly a matter of taste.

If you think for_each is preferable I can probably find some time to implement it this weekend or next. The functors would replace the _impl methods that are currently in the PR, so the total amount of code would be comparable (unless I am missing something). I don't understand exactly what #150 does, but the functors needed here would be fairly specific so I'm not sure there is much overlap.

@artivis
Copy link
Owner

artivis commented Feb 28, 2021

If you think for_each is preferable I can probably find some time to implement it this weekend or next.

I do think it is preferable but hold on implementing it 'til we discuss the following.

The functors would replace the _impl methods that are currently in the PR, so the total amount of code would be comparable (unless I am missing something). I don't understand exactly what #150 does, but the functors needed here would be fairly specific so I'm not sure there is much overlap.

What #150 does is that it implements (struct) functors for each of the functions that requires per-group specialization (e.g. inverse, log, exp etc...), something along the lines,

template <typename LieGroup> struct ExpFunctor;
template <> struct ExpFunctor<SO2> { static void run(Coeffs& c) { ... } };

which are then called directly from the base (e.g. LieGroupBase). This change will ease partial specialization at the function level.

The difference between such functors (ExpFunctor) and those necessary here (MyFunctor) seem pretty thin if not null. Thus I'd like to avoid duplicates.

If you have time, would you consider implementing an example as suggested by @joansola?? These are great from a library documentation point of view.

* Rename get() to block()
* README entry
* Some fixes to in-code docstrings
* Move traits to traits.h
@pettni
Copy link
Contributor Author

pettni commented Feb 28, 2021

If you have time, would you consider implementing an example as suggested by @joansola?? These are great from a library documentation point of view.

I ported the example in V.B to the bundle type, but in my opinion it's not a great use case since the problem size must be known at compile time. One could envision a "run-time bundle" that supports dynamically adding element etc, but that's a whole other project, and the compile-time version is more useful for the problems I encounter. The new example does however map well between the paper and the bundle type, so it might make sense to have it in the library. The state update reduces to this which is pretty nice:

X += BundleT::Tangent(dX);

I also modified the se3_localization example to work on both SE(3) and <R3, SO3> which was quite easy to do with the bundle type. To me this represents a more natural use case, where the bundle is the robot state rather than a trajectory.

@pettni
Copy link
Contributor Author

pettni commented Feb 28, 2021

The difference between such functors (ExpFunctor) and those necessary here (MyFunctor) seem pretty thin if not null. Thus I'd like to avoid duplicates.

I'm not sure we could write functors that work for both #150 and this PR, although there may be benefits from writing them at the same time in terms of keeping the code cohesive. What I had in mind here are functors along these lines that can be looped over with for_each:

struct Functor
{
Functor(M & m, Jac & J)
: m_(m), J_(J) {}

template<std::size_t Element, int Beg, int Len>
void func()
{
  // do some operation on m_.element<Element>() and insert derivative into J_.block<Len, Len>(Beg, Beg)
}

private:
  M & m_;
  Jac & J_;
}; 

@artivis
Copy link
Owner

artivis commented Feb 28, 2021

I ported the example in V.B to the bundle type, but in my opinion it's not a great use case since the problem size must be known at compile time

Thanks for the example. Indeed V.B is not a great fit for the Bundle class since it is static whereas the problem is dynamic, but that's not the main use case we have in mind for it. We rather consider V.C where it will be better employed to represent systems/sensors in time-varying self-calibration scenario - e.g. Bundle<double, SE2, R2> -> differential drive base with wheels calibration or Bundle<double, SE_2_3, R3, R3> -> IMU with bias estimation).

One could envision a "run-time bundle"

One could indeed, but I think that's outside the scope of manif. And one would probably be better of using e.g. a SLAM library and make use of manif where appropriate (that's the very point of the Map<manif::*> classes).

<R3, SO3> [...]To me this represents a more natural use case, where the bundle is the robot state rather than a trajectory

I can only assume you know that SE3 and <R3, SO3> are radically different since you implemented the Bundle class.
For future readers tho: SE3 performs translation and rotation simultaneously as a continuum while in <R3, SO3> the rotation and translation do not interact at all. See part IV of the paper and more specifically Example 7.

I'm not sure we could write functors that work for both #150 and this PR

See e.g. the inverse functor (nevermind the comments). That looks too similar to your code sample not to foresee duplicates. I'm not claiming they are one to one replacements, simply that there is some forward thinking to do.

@pettni
Copy link
Contributor Author

pettni commented Mar 1, 2021

We rather consider V.C where it will be better employed to represent systems/sensors in time-varying self-calibration scenario - e.g. Bundle<double, SE2, R2> -> differential drive base with wheels calibration or Bundle<double, SE_2_3, R3, R3> -> IMU with bias estimation).

Ah yeah this is exactly the type of problem I have used it for in the past and why I'm interested in getting the bundle into manif :). I didn't make the connection when looking through the paper since the bundle in V.C is defined similarly to V.B. Since it's harder to write a new example than to port an existing one, and a fair amount of documentation would be warranted given that there is no corresponding example in the paper, I'm not sure I have the time right now to write such an example so I'd prefer leaving it for the future.

<R3, SO3> [...]To me this represents a more natural use case, where the bundle is the robot state rather than a trajectory

I can only assume you know that SE3 and <R3, SO3> are radically different since you implemented the Bundle class.
For future readers tho: SE3 performs translation and rotation simultaneously as a continuum while in <R3, SO3> the rotation and translation do not interact at all. See part IV of the paper and more specifically Example 7.

Yeah, they are very different from a dynamics perspective, but, in my understanding, as representations of orientation plus translation they are more or less equivalent and in some applications it can be better to use <R3, SO3> since computations are lighter. I think this is true for problems like pose graph estimation and sensor-to-sensor calibration, but probably not for filtering which is closely intertwined with dynamics. I considered changing se3_localization to simulate dynamics on SE(3) but run the filter on <R3, SO3> so that the same problem is solved but with different parameterizations, but it ended up being a bit too complicated/contrived (requires implementing rplus of SE(3) on <R3, SO3>) and likely more confusing than helpful. I'll revert my changes to se3_localization.

@artivis
Copy link
Owner

artivis commented Jun 1, 2021

Hi @pettni,

Sorry for the long wait. I have finally been able to 'play' around with the Bundle class in a larger project. I find it a bit cumbersome, but that has nothing to do with your implementation, it is the way it is being all static, template, generic and whatnot. Great job anyway 👍.

I've made a few modifications that I've temporarily pushed on the branch pettni/devel. The first one being to expose the inner groups through an Element member variable using an stl tuple. Doing so, it is much easier to retrieve each group DoF/Dim and whatnot both as a Bundle user or dev - e.g. MyBundle::Element<0> give you the element 0 type and MyBundle::Element<0>::DoF its DoF. It also remove the need for some of the intseqs (Len*) and recursions. I've also replaced the Beg* intseqs with constexpr arrays to rely on standard bits as much as possible (e.g. std::get). All put together, this greatly simplifies the internal API, e.g.

from

template<int ... _Idx, int ... _BegDoF, int ... _LenDoF>
LieGroup inverse_impl(OptJacobianRef, intseq<_Idx...>, intseq<_BegDoF...>, intseq<_LenDoF...>) const;

to

template <int ... _Idx>
LieGroup inverse_impl(OptJacobianRef, internal::intseq<_Idx...>) const;

I'm not quite done yet, there are still a corner or two I'd like to smooth but I'd love to hear your thoughts on these modifications. Anyway I feel like I'm having a good grip on it and I'm feeling more confident to merge it soon.

In case you're interested, I'm using the Bundle class in a Kalman filter for the self-calibration of a diff-drive motion model (see here - @joansola you're likely going to be interested as well). Note that this is WIP as well...

@pettni
Copy link
Contributor Author

pettni commented Jun 8, 2021

Cool, I think that's a good improvement. Let me know if there's anything else I can do on my end.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants