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

Clean up NDT implementation and examples #451

Closed
wants to merge 2 commits into from

Conversation

nahueespinosa
Copy link
Member

@nahueespinosa nahueespinosa commented Oct 27, 2024

Proposed changes

Simplifies the NDT localization nodes by reducing the number of supported features, specifically the available motion models and the recovery strategy configuration. This change eliminates the need for static dispatch and std::visit, easing some pressure on the compiler. Additionally, it cleans up header file inclusions.

Closes #424, since it removes the recovery strategy from the core AMCL pipeline.
Related to #385, since it reduces memory usage during compilation.

Type of change

  • 🐛 Bugfix (change which fixes an issue)
  • 🚀 Feature (change which adds functionality)
  • 📚 Documentation (change which fixes or extends documentation)

Checklist

  • Lint and unit tests (if any) pass locally with my changes
  • I have added tests that prove my fix is effective or that my feature works
  • I have added necessary documentation (if appropriate)
  • All commits have been signed for DCO

@nahueespinosa nahueespinosa force-pushed the nahuel/cleanup-ndt-nodes branch 2 times, most recently from ab224f6 to 8caa53c Compare October 27, 2024 23:53
@nahueespinosa nahueespinosa self-assigned this Oct 27, 2024
@nahueespinosa nahueespinosa force-pushed the nahuel/cleanup-ndt-nodes branch 2 times, most recently from 1b9f23c to 439e192 Compare October 28, 2024 02:08
@nahueespinosa nahueespinosa changed the title Clean up NDT base implementation and examples Clean up NDT implementation and examples Oct 28, 2024
@nahueespinosa nahueespinosa force-pushed the nahuel/cleanup-ndt-nodes branch 3 times, most recently from 2ad2dc4 to c70d782 Compare October 28, 2024 13:38
@nahueespinosa
Copy link
Member Author

Code coverage reports show -0.01%, but that seems like a numerical bug:
image

Copy link
Collaborator

@hidmic hidmic left a comment

Choose a reason for hiding this comment

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

First pass

const auto name = get_parameter("robot_model_type").as_string();
if (name == kDifferentialModelName) {
if (name == kDifferentialModelName || name == kNav2DifferentialModelName) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

@nahueespinosa meta: why adding kNav2DifferentialModelName here? The AMCL node needs it to ensure parity with Nav2 AMCL, but the NDT node does not have such constraint.

Copy link
Member Author

Choose a reason for hiding this comment

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

We use nav2_amcl::DifferentialMotionModel in the default_ndt.ros2.yaml file. Let me change that.

typename WeightT = beluga::Weight,
class ParticleType = std::tuple<typename SensorModel::state_type, WeightT>,
class ExecutionPolicy = std::execution::sequenced_policy>
class Particle = std::tuple<typename SensorModel::state_type, beluga::Weight>>
class Amcl {
Copy link
Collaborator

Choose a reason for hiding this comment

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

@nahueespinosa meta: there is redundancy between beluga::Amcl and beluga_ros::Amcl. I recall we did not get as far as to deduplicate them, but that'd be the logical path. In which case, beluga_ros::Amcl is doing a lot more than it should, and with this change, beluga::Amcl would be doing less. Why remove the execution policy? Why remove (as opposed to generalize, if applicable) state jitter to avoid distribution collapse?

Copy link
Member Author

@nahueespinosa nahueespinosa Oct 28, 2024

Choose a reason for hiding this comment

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

I don't think that's the kind of generalization that leads to maintainable code.

State jitter does not apply to NDT models. IIUC, the 2D case was sampling from the particle set, which has no effect; and the 3D case was adding zero-initialized states, which is simply wrong. We never created a map distribution compatible with the NDT map representation.

In any case, I think we should treat the NDT examples as a nice proof of concept, and avoid adding unnecessary features.

If we do want those features (e.g. configurable execution policies), we should do more thinking before generalizing things.

Copy link
Member Author

@nahueespinosa nahueespinosa Oct 29, 2024

Choose a reason for hiding this comment

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

Also, having this amount of std::visit calls and templates of templates makes it really hard to play with explicit template instantiation. I really don't think that we should keep things as they are today.

Copy link
Member Author

@nahueespinosa nahueespinosa Oct 29, 2024

Choose a reason for hiding this comment

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

The range pipeline refactor was meant to provide a way to leave behind (overly complicated) class templates and the mixin pattern. We are kind of going back to that if we try to provide an Amcl class that's general enough for any use case.

There are other approaches to composition that I think are worth exploring. I reckon I haven't had enough time to do so this year.

Copy link
Collaborator

@hidmic hidmic Oct 29, 2024

Choose a reason for hiding this comment

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

There's a lot to unpack there.

State jitter does not apply to NDT models.

But it does for AMCL, at least for this instantiation of it. Whether NDT filters should use this AMCL variant or a different MCL variant is a completely valid yet separate discussion IMO.

having this amount of std::visit calls and templates of templates makes it really hard to play with explicit template instantiation

That is a very good point. We ended up using std::visit to enable runtime configurations and to keep execution paths transparent to the compiler. We can push it to the edges, but I don't think we'll get to remove it. Can we?

If we do want those features (e.g. configurable execution policies), we should do more thinking before generalizing things.

I really don't think that we should keep things as they are today.

We are kind of going back to that if we try to provide an Amcl class that's general enough for any use case.

There are other approaches to composition that I think are worth exploring.

Hmm, all of that is valid, but that alternative design that is better than this doesn't exist yet. I give you that these AMCL classes are far from complete solutions, but walking back on them with nothing to show for in exchange seems like a regression to me.

Copy link
Member Author

Choose a reason for hiding this comment

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

But it does for AMCL, at least for this instantiation of it. Whether NDT filters should use this AMCL variant or a different MCL variant is a completely valid yet separate discussion IMO.

On second read, I didn't remove state jitter to avoid distribution collapse. That's taken care of by the motion model. I removed the recovery strategy based on injecting random particles from the map (which none of the NDT variants were actually doing).

"Adaptive" in AMCL originally stands for the fact that the sample size can change based on the KLD metric. The recovery strategy was not mentioned in the original paper, it comes from Prob Rob.

Can we?

Yes, with carefully implemented type erasure and benchmarking. I don't know how much benefit we get from inlining the sensor and motion models, we should measure that. I've also seen really interesting techniques to make type-erasure really efficient. The only drawback is not being able to inline, which might be a big deal in some scenarios. We should investigate.

Range pipelines on the other hand will suffer greatly without inlining. But that's not what we are trying to customize at runtime.

Hmm, all of that is valid, but that alternative design that is better than this doesn't exist yet.

Fair point.

I give you that these AMCL classes are far from complete solutions, but walking back on them with nothing to show for in exchange seems like a regression to me.

I understand your point, but it seems to me that we took a lot of shortcuts to get to this point and this is only one small step back. I'm not removing vital features, the two classes still implement (a version of) AMCL, and this unblocks experimentation to reduce memory usage at build time.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I removed the recovery strategy based on injecting random particles from the map

FWIW that still counts as roughening to cope with (particle depletion and) distribution collapse. Artificially increasing the variance of other distributions helps too but it is a different approach.

I've also seen really interesting techniques to make type-erasure really efficient. The only drawback is not being able to inline, which might be a big deal in some scenarios.

Hmm, the only path I see that avoids explicit combinatorial explosion (as std::visit already explodes but we don't see it) is type erasing within loops. Performance will take a hit, though I guess we could argue that you can't have it both ways: it's either performance or flexibility. I'm OK with that so long as top performance is still achievable.

it seems to me that we took a lot of shortcuts to get to this point

That's true.

this unblocks experimentation to reduce memory usage at build time.

Hmm, what's preventing explicit instantiation of the beluga::Amcl class in its current form? 🤔

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

Successfully merging this pull request may close these issues.

Add option to remove the adaptiveness to the core filter pipeline
2 participants