Skip to content

Conversation

mini-1235
Copy link
Contributor


Basic Info

Info Please fill out this column
Ticket(s) this addresses #4757 and supersedes #4763
Primary OS tested on Ubuntu
Robotic platform tested on gazebo sim with ackermann robot
Does this PR contain AI generated software? No
Was this PR description generated by AI software? Out of respect for maintainers, AI for human-to-human communications are banned

Description of contribution in a few bullet points

  • Like mppi, this PR added the feature for exact path following

Description of documentation updates required from your changes

New parameters are added

Description of how this change was tested

In gazebo sim, I will show the result below


Future work that may be required in bullet points

For graceful and DWB as well?

For Maintainers:

  • Check that any new parameters added are updated in docs.nav2.org
  • Check that any significant change is added to the migration guide
  • Check that any new features OR changes to existing behaviors are reflected in the tuning guide
  • Check that any new functions have Doxygen added
  • Check that any new features have test coverage
  • Check that any new plugins is added to the plugins page
  • If BT Node, Additionally: add to BT's XML index of nodes for groot, BT package's readme table, and BT library lists
  • Should this be backported to current distributions? If so, tag with backport-*.

Signed-off-by: mini-1235 <[email protected]>
Signed-off-by: mini-1235 <[email protected]>
@mini-1235
Copy link
Contributor Author

One thing I am not sure here: is allow_reversing == enforce_path_inversion?

I will show my results here

Before this PR:
https://github.com/user-attachments/assets/59abc163-20a0-45e3-93c8-173fd4e991d1

After this PR:
https://github.com/user-attachments/assets/fc94401d-f740-4676-85d4-20b024f14238

Copy link

codecov bot commented Aug 28, 2025

Codecov Report

❌ Patch coverage is 73.52941% with 18 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...lated_pure_pursuit_controller/src/path_handler.cpp 55.17% 13 Missing ⚠️
..._pure_pursuit_controller/src/parameter_handler.cpp 81.25% 3 Missing ⚠️
nav2_mppi_controller/src/path_handler.cpp 0.00% 2 Missing ⚠️
Files with missing lines Coverage Δ
...oller/include/nav2_mppi_controller/tools/utils.hpp 97.68% <ø> (-0.20%) ⬇️
...ated_pure_pursuit_controller/parameter_handler.hpp 100.00% <ø> (ø)
...regulated_pure_pursuit_controller/path_handler.hpp 100.00% <ø> (ø)
...ntroller/src/regulated_pure_pursuit_controller.cpp 88.06% <100.00%> (+2.43%) ⬆️
nav2_util/include/nav2_util/controller_utils.hpp 100.00% <100.00%> (ø)
nav2_mppi_controller/src/path_handler.cpp 83.82% <0.00%> (ø)
..._pure_pursuit_controller/src/parameter_handler.cpp 92.77% <81.25%> (-0.78%) ⬇️
...lated_pure_pursuit_controller/src/path_handler.cpp 74.57% <55.17%> (-20.02%) ⬇️

... and 3 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@SteveMacenski
Copy link
Member

@mini-1235 if we're talking about moving the path handler into the controller server in #5446, why is this action necessary? findFirstPathInversion and removePosesAfterFirstInversion would be moved into that central path handler so it doesn't need to be put into a generic utility in nav2_util

Copy link
Member

@SteveMacenski SteveMacenski left a comment

Choose a reason for hiding this comment

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

Otherwise, no issue other than the comment above

if (dist_to_cusp < lookahead_dist) {
lookahead_dist = dist_to_cusp;
}
if (dist_to_cusp < curv_lookahead_dist) {
Copy link
Member

Choose a reason for hiding this comment

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

I think this is a subtle point worth narrowing in on a little. The curvature lookahead is updated on approach to the goal that is otherwise left unchecked on L191 above. I think we should still check for this case or is that implicitly handled correctly now from your other updates?

Copy link
Contributor Author

@mini-1235 mini-1235 Sep 5, 2025

Choose a reason for hiding this comment

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

I remember that in my experiments, when I set enforce_path_inversion to true, both the condition here

if (dist_to_cusp < lookahead_dist) {
   lookahead_dist = dist_to_cusp;
}
if (dist_to_cusp < curv_lookahead_dist) {
  curv_lookahead_dist = dist_to_cusp;
}

is never true, I will double check again tomorrow

@mini-1235
Copy link
Contributor Author

mini-1235 commented Sep 5, 2025

@mini-1235 if we're talking about moving the path handler into the controller server in #5446, why is this action necessary? findFirstPathInversion and removePosesAfterFirstInversion would be moved into that central path handler so it doesn't need to be put into a generic utility in nav2_util

I agree, but I thought in #4763 (comment) we discussed that we are going to implement/verify the missing logic in RPP/Graceful/DWB 's path handler, and then finally moving the path_handler into controller server if possible in #5446, no?

IMO, we can just keep it temporarily in nav2 utils, and after we have tested in all controllers, we can remove them. If you are worrying about the code coverage, perhaps I can add something like smachybrid + RPP to the system tests(I guess what we have currently is Navfn + dwb/mppi?)

@mini-1235
Copy link
Contributor Author

mini-1235 commented Sep 6, 2025

@SteveMacenski, I have done more experiments, at the time I raised this PR, I have only tested with ackermann. Recently,I have tested with differential model as well. Sharing my results here:

  1. When enforce_path_inversion is enabled, findVelocitySignChange always return max, thus it never enters the following condition
if (dist_to_cusp < lookahead_dist) {
   lookahead_dist = dist_to_cusp;
}
if (dist_to_cusp < curv_lookahead_dist) {
  curv_lookahead_dist = dist_to_cusp;
}

I think this is reasonable, since we are pruning the path containing cusp points in the path handler

When setting with differential model, the above condition can be true even if enforce_path_inversion is true, I believe this is because we are not handling in place rotations in MPPI's findFirstPathInversion, I have also added this in the latest commit

Linking related tickets here: #5098, #3934, I have also gone through them and make sure it works fine after my changes in this PR

  1. While reading the code in smoother / planner, I don't understand why we don't check the last pose
double last_theta = tf2::getYaw(path.poses[idx - 1].pose.orientation);
    double dtheta = angles::shortest_angular_distance(last_theta, cur_theta);
    if (fabs(oa_x) < 1e-4 && fabs(oa_y) < 1e-4 && fabs(dtheta) > 1e-4) {
      curr_segment.end = idx;
      segments.push_back(curr_segment);
      curr_segment.start = idx;
    }

here

// Checking for the existence of a differential rotation in place.
double cur_theta = tf2::getYaw(path.poses[idx].pose.orientation);
double next_theta = tf2::getYaw(path.poses[idx + 1].pose.orientation);
double dtheta = angles::shortest_angular_distance(cur_theta, next_theta);
if (fabs(ab_x) < 1e-4 && fabs(ab_y) < 1e-4 && fabs(dtheta) > 1e-4) {
curr_segment.end = idx;
segments.push_back(curr_segment);
curr_segment.start = idx;

Is it because idx 0 (start) and idx 1 will always differ in position?

@SteveMacenski
Copy link
Member

SteveMacenski commented Sep 15, 2025

@mini-1235 I will admit that I'm starting to lose the thread on these PRs that are all touching similar sections of the code. I'd love to get some of these in 😆 . What's actually going on with #4763? Is that still relevant now with this PR and moving the path handler into the controller server?

I agree, but I thought in #4763 (comment) we discussed that we are going to implement/verify the missing logic in RPP/Graceful/DWB 's path handler, and then finally moving the path_handler into controller server if possible in #5446, no?

I think what I'm meaning in that comment is that we're updating the RPP logic to better handle these things. If we're doing that for RPP, should we also simultaneously update DWB/Graceful controllers so that they're all consistent (as MPPI already has it).

If we're moving the path handler to the controller server, then that does it implicitly. So I guess we can ignore that for now.

IMO, we can just keep it temporarily in nav2 utils, and after we have tested in all controllers, we can remove them.

OK - but also if we're moving the path handler globally into the controller server in the other one - do we need this PR? Though I do understand this work in RPP is going to be shared across them so its useful as a stepping stone to do it separately. Is that your understanding as well?

@SteveMacenski
Copy link
Member

SteveMacenski commented Sep 15, 2025

When enforce_path_inversion is enabled, findVelocitySignChange always return max, thus it never enters the following condition

Ah, this was my thinking:

I get that for dist_to_cusp < lookahead_dist, but what about dist_to_cusp < curv_lookahead_dist? This should still be done, I think, based off of lookahead_dist rather than dist_to_cusp now, right? Otherwise we never reduce the curvature lookahead distance.

But I'm realizing now the path is already pruned so even if we don't reduce this distance, it will only get up to the inversion anyway 👍


I grepped through the code and don't see that snippet you mention with last_theta. The other bit we don't check the last pose because we compare IDX with IDX + 1, there is no IDX+1 for the final path point.

@mini-1235
Copy link
Contributor Author

mini-1235 commented Sep 16, 2025

@mini-1235 I will admit that I'm starting to lose the thread on these PRs that are all touching similar sections of the code. I'd love to get some of these in 😆 . What's actually going on with #4763? Is that still relevant now with this PR and moving the path handler into the controller server?

Ok, I will try to conclude everything here:

OK - but also if we're moving the path handler globally into the controller server in the other one - do we need this PR? Though I do understand this work in RPP is going to be shared across them so its useful as a stepping stone to do it separately. Is that your understanding as well?

So, my original plan here is to update RPP, Graceful, and then DWB, some might only involve updating the path handler, while others might need to update its internal logic as well. If everything works properly after my PRs in DWB/Graceful/RPP/MPPI then I can continue to move the path handlers to controller server.

Personally, I would prefer to have this PR merged in, this helps me to test the logic when the other two controllers are paired with feasible planners. Also, it seems like we need to do a major update to DWB, I think it will be too much to do all of these in #5446, what do you think?

@mini-1235
Copy link
Contributor Author

mini-1235 commented Sep 16, 2025

I grepped through the code and don't see that snippet you mention with last_theta. The other bit we don't check the last pose because we compare IDX with IDX + 1, there is no IDX+1 for the final path point.

I mean this part

// Checking for the existence of a differential rotation in place.
double cur_theta = tf2::getYaw(path.poses[idx].pose.orientation);
double next_theta = tf2::getYaw(path.poses[idx + 1].pose.orientation);
double dtheta = angles::shortest_angular_distance(cur_theta, next_theta);
if (fabs(ab_x) < 1e-4 && fabs(ab_y) < 1e-4 && fabs(dtheta) > 1e-4) {
curr_segment.end = idx;
segments.push_back(curr_segment);
curr_segment.start = idx;
should be updated as

double last_theta = tf2::getYaw(path.poses[idx - 1].pose.orientation);
double dtheta = angles::shortest_angular_distance(last_theta, cur_theta);
if (fabs(oa_x) < 1e-4 && fabs(oa_y) < 1e-4 && fabs(dtheta) > 1e-4) {
  curr_segment.end = idx;
  segments.push_back(curr_segment);
  curr_segment.start = idx;
}
double cur_theta = tf2::getYaw(path.poses[idx].pose.orientation);
double next_theta = tf2::getYaw(path.poses[idx + 1].pose.orientation);
double dtheta = angles::shortest_angular_distance(cur_theta, next_theta);
if (fabs(ab_x) < 1e-4 && fabs(ab_y) < 1e-4 && fabs(dtheta) > 1e-4) {
  curr_segment.end = idx;
  segments.push_back(curr_segment);
  curr_segment.start = idx;
}

no?

@SteveMacenski
Copy link
Member

SteveMacenski commented Sep 17, 2025

Thanks!

#4763 (targeting humble) is trying to solve #4757, this PR(targeting main) supersedes it

Should this be closed since its been open for most of a year? If we're fixing in main and the humble user doesn't want to update that, I'm OK closing as long as its not a 'bug' as much as a feature not supported in Humble (yet).

#5446 is trying to move the path handler into the controller server, however there are a couple of things that are blocking this

MPPI sounds like that's all set. RPP it sounds like from the context of this PR that's been refactored and working, no? For graceful, if we just use the Controller Server-based Path Handler, doesn't that enable that feature into Graceful - We'd just need to test?

DWB doesn't handle path inversions really, it doesn't have the critics that consider the path-point's orientation. While that's notable to address, I don't think its critical to resolve before moving the path handler into the controller server as long as it works as well as it works now when given path inversions. If its not respecting them now and doesn't respect them with the Controller Server-based Path Handler, then we're no worse for wear.

So, my original plan here is to update RPP, Graceful, and then DWB, some might only involve updating the path handler, while others might need to update its internal logic as well. If everything works properly after my PRs in DWB/Graceful/RPP/MPPI then I can continue to move the path handlers to controller server.

Is your plan to incrementally add these features to each to test, then refactor to remove it from the controllers into the controller server? If so, #5446 would be a WIP draft. I'm actually OK with it all in #5446 except for vast DWB changes - as long as DWB still works as well as it works now. If it breaks, then I agree with a more piecemeal strategy. If a temporary API for DWB to 'skip' inversion consideration for a controller is possible, then that would be OK too to merge in and then remove that + fix DWB in a follow up PR would be fine.


On the smoother, I don't think your snippet compiles since it references variables not yet defined. I think I catch your drift, but is this trying to fix an actual issue or something notional? Note that if we did that for just the last path point, that would be a segment size of 1. We can't smooth a segment size of one - that's not meaningful.

@mini-1235
Copy link
Contributor Author

Should this be closed since its been open for most of a year? If we're fixing in main and the humble user doesn't want to update that, I'm OK closing as long as its not a 'bug' as much as a feature not supported in Humble (yet).

I guess we can close the PR, if the original author / others is interested in implementing this feature targeting humble, I can help reviewing as well

RPP it sounds like from the context of this PR that's been refactored and working, no?

Yes, I think RPP is ready to go

DWB doesn't handle path inversions really, it doesn't have the critics that consider the path-point's orientation. While that's notable to address, I don't think its critical to resolve before moving the path handler into the controller server as long as it works as well as it works now when given path inversions. If its not respecting them now and doesn't respect them with the Controller Server-based Path Handler, then we're no worse for wear.

Ahhh, Ok, I thought handling path inversions in DWB is a must before moving on in #5446. Then I think I can just close this PR and move everything here to #5446, right?

On the smoother, I don't think your snippet compiles since it references variables not yet defined. I think I catch your drift, but is this trying to fix an actual issue or something notional? Note that if we did that for just the last path point, that would be a segment size of 1. We can't smooth a segment size of one - that's not meaningful.

It was just something I am trying to understand when reading planner's

// Iterating through the path to determine the position of the cusp
for (unsigned int idx = 1; idx < path.poses.size() - 1; ++idx) {
// We have two vectors for the dot product OA and AB. Determining the vectors.
double oa_x = path.poses[idx].pose.position.x -
path.poses[idx - 1].pose.position.x;
double oa_y = path.poses[idx].pose.position.y -
path.poses[idx - 1].pose.position.y;
double ab_x = path.poses[idx + 1].pose.position.x -
path.poses[idx].pose.position.x;
double ab_y = path.poses[idx + 1].pose.position.y -
path.poses[idx].pose.position.y;
// Checking for the existence of cusp, in the path, using the dot product.
double dot_product = (oa_x * ab_x) + (oa_y * ab_y);
if (dot_product < 0.0) {
curr_segment.end = idx;
segments.push_back(curr_segment);
curr_segment.start = idx;
}
// Checking for the existence of a differential rotation in place.
double cur_theta = tf2::getYaw(path.poses[idx].pose.orientation);
double next_theta = tf2::getYaw(path.poses[idx + 1].pose.orientation);
double dtheta = angles::shortest_angular_distance(cur_theta, next_theta);
if (fabs(ab_x) < 1e-4 && fabs(ab_y) < 1e-4 && fabs(dtheta) > 1e-4) {
curr_segment.end = idx;
segments.push_back(curr_segment);
curr_segment.start = idx;
}
}

Smoother's

inline std::vector<PathSegment> findDirectionalPathSegments(
const nav_msgs::msg::Path & path)
{
std::vector<PathSegment> segments;
PathSegment curr_segment;
curr_segment.start = 0;
// Iterating through the path to determine the position of the cusp
for (unsigned int idx = 1; idx < path.poses.size() - 1; ++idx) {
// We have two vectors for the dot product OA and AB. Determining the vectors.
double oa_x = path.poses[idx].pose.position.x -
path.poses[idx - 1].pose.position.x;
double oa_y = path.poses[idx].pose.position.y -
path.poses[idx - 1].pose.position.y;
double ab_x = path.poses[idx + 1].pose.position.x -
path.poses[idx].pose.position.x;
double ab_y = path.poses[idx + 1].pose.position.y -
path.poses[idx].pose.position.y;
// Checking for the existence of cusp, in the path, using the dot product.
double dot_product = (oa_x * ab_x) + (oa_y * ab_y);
if (dot_product < 0.0) {
curr_segment.end = idx;
segments.push_back(curr_segment);
curr_segment.start = idx;
}
// Checking for the existence of a differential rotation in place.
double cur_theta = tf2::getYaw(path.poses[idx].pose.orientation);
double next_theta = tf2::getYaw(path.poses[idx + 1].pose.orientation);
double dtheta = angles::shortest_angular_distance(cur_theta, next_theta);
if (fabs(ab_x) < 1e-4 && fabs(ab_y) < 1e-4 && fabs(dtheta) > 1e-4) {
curr_segment.end = idx;
segments.push_back(curr_segment);
curr_segment.start = idx;
}
}
curr_segment.end = path.poses.size() - 1;
segments.push_back(curr_segment);
return segments;
}

vs

RPP's

double RegulatedPurePursuitController::findVelocitySignChange(
const nav_msgs::msg::Path & transformed_plan)
{
// Iterating through the transformed global path to determine the position of the cusp
for (unsigned int pose_id = 1; pose_id < transformed_plan.poses.size() - 1; ++pose_id) {
// We have two vectors for the dot product OA and AB. Determining the vectors.
double oa_x = transformed_plan.poses[pose_id].pose.position.x -
transformed_plan.poses[pose_id - 1].pose.position.x;
double oa_y = transformed_plan.poses[pose_id].pose.position.y -
transformed_plan.poses[pose_id - 1].pose.position.y;
double ab_x = transformed_plan.poses[pose_id + 1].pose.position.x -
transformed_plan.poses[pose_id].pose.position.x;
double ab_y = transformed_plan.poses[pose_id + 1].pose.position.y -
transformed_plan.poses[pose_id].pose.position.y;
/* Checking for the existence of cusp, in the path, using the dot product
and determine it's distance from the robot. If there is no cusp in the path,
then just determine the distance to the goal location. */
const double dot_prod = (oa_x * ab_x) + (oa_y * ab_y);
if (dot_prod < 0.0) {
// returning the distance if there is a cusp
// The transformed path is in the robots frame, so robot is at the origin
return hypot(
transformed_plan.poses[pose_id].pose.position.x,
transformed_plan.poses[pose_id].pose.position.y);
}
if (
(hypot(oa_x, oa_y) == 0.0 &&
transformed_plan.poses[pose_id - 1].pose.orientation !=
transformed_plan.poses[pose_id].pose.orientation)
||
(hypot(ab_x, ab_y) == 0.0 &&
transformed_plan.poses[pose_id].pose.orientation !=
transformed_plan.poses[pose_id + 1].pose.orientation))
{
// returning the distance since the points overlap
// but are not simply duplicate points (e.g. in place rotation)
return hypot(
transformed_plan.poses[pose_id].pose.position.x,
transformed_plan.poses[pose_id].pose.position.y);
}
}
return std::numeric_limits<double>::max();
}

@SteveMacenski
Copy link
Member

I guess we can close the PR

OK! Can you link the ticket #4757 as solved by your PR then so that it autocloses on merge?

Ahhh, Ok, I thought handling path inversions in DWB is a must before moving on in #5446. Then I think I can just close this PR and move everything here to #5446, right?

As long as (a) DWB is not handling it correctly now and (b) the behavior is consistent afterwards in the way that its still not working. A follow up PR would be great to make DWB work, but if its not working right now, its not breaking anything to have it still not work then 😆

Just make sure to either have the defaults set to a working state for DWB or have DWB explicitly disable that feature by setting the parameter itself to disable if it doesn't work (and print a warning)

If that's all good, then we can move it over and you're welcome to close this.


RegulatedPurePursuitController is the same as the other two, but just exits at the first dot_prod < 0.0 and doesn't care about the angle.

The Smac Smoother and smoother_utils appear to be exactly the same to me, except the Smac has a callout for if the planner is holonomic so we can just skip all checks. There's actually a ticket open for consolidating these since it is duplication #5425 - however the implementation hasn't been followed up and may have gone too far #5435. If you wanted to implement that ticket, I would appreciate it and gets another thing off the plate with a stalled PR

@mini-1235
Copy link
Contributor Author

RegulatedPurePursuitController is the same as the other two, but just exits at the first dot_prod < 0.0 and doesn't care about the angle.

I think you are referring to MPPI's

inline unsigned int findFirstPathInversion(nav_msgs::msg::Path & path)
{
// At least 3 poses for a possible inversion
if (path.poses.size() < 3) {
return path.poses.size();
}
// Iterating through the path to determine the position of the path inversion
for (unsigned int idx = 1; idx < path.poses.size() - 1; ++idx) {
// We have two vectors for the dot product OA and AB. Determining the vectors.
float oa_x = path.poses[idx].pose.position.x -
path.poses[idx - 1].pose.position.x;
float oa_y = path.poses[idx].pose.position.y -
path.poses[idx - 1].pose.position.y;
float ab_x = path.poses[idx + 1].pose.position.x -
path.poses[idx].pose.position.x;
float ab_y = path.poses[idx + 1].pose.position.y -
path.poses[idx].pose.position.y;
// Checking for the existence of cusp, in the path, using the dot product.
float dot_product = (oa_x * ab_x) + (oa_y * ab_y);
if (dot_product < 0.0f) {
return idx + 1;
}
}
return path.poses.size();
}

We actually cares about angle in RPP because we want to handle in place rotation, no?

@SteveMacenski
Copy link
Member

SteveMacenski commented Sep 18, 2025

RPP too, we just return a distance:

if (dot_prod < 0.0) {
// returning the distance if there is a cusp
// The transformed path is in the robots frame, so robot is at the origin
return hypot(
transformed_plan.poses[pose_id].pose.position.x,
transformed_plan.poses[pose_id].pose.position.y);
}

@mini-1235
Copy link
Contributor Author

Sorry if I wasn't clear earlier, what I wanted to discuss is actually this part about in place rotation

if (
(hypot(oa_x, oa_y) == 0.0 &&
transformed_plan.poses[pose_id - 1].pose.orientation !=
transformed_plan.poses[pose_id].pose.orientation)
||
(hypot(ab_x, ab_y) == 0.0 &&
transformed_plan.poses[pose_id].pose.orientation !=
transformed_plan.poses[pose_id + 1].pose.orientation))
{
// returning the distance since the points overlap
// but are not simply duplicate points (e.g. in place rotation)
return hypot(
transformed_plan.poses[pose_id].pose.position.x,
transformed_plan.poses[pose_id].pose.position.y);
}
}

RPP evaluates both last_orientation → current_orientation and current_orientation → next_orientation, whereas the planner and smoother only evaluate current_orientation → next_orientation, I feel like this is not exactly the same for me

@SteveMacenski
Copy link
Member

SteveMacenski commented Sep 18, 2025

Ah.

The Smoother Utils is used by the Simple / SG Smoothers which do not specifically model or care about in place rotations. So we don't consider that in those.

The Smac Planner doesn't check the last point because we enforce boundary conditions elsewhere in the smoother (I believe) so we maintain that orientation after smoothing for any possible in-place rotation of the final goal angle. So while not modeled as a new path segment, it is maintained after a smoothing process either way -- whether including an in-place rotation or not.

Both of these are generating the list of path segments to smooth individually, whereas the RPP controller is looking for when they happen, which could/often will happen in the very first starting path point to rotate to a new orientation before starting to track a path. So we check idx - 1 against idx to identify that case. RPP is a controller so it does care about in place rotations, motivated by #3089.

MPPI might be a gap - though I don't think anything in MPPI right now will actually listen to a planned request for an in place rotation explicitly. We could detect the cusp to stop, but will then start from that point which may not exactly include an in place rotation (as much as generally then going in that new orientation in the more MPPI-flexible-behavior fashion). MPPI isn't as rigid as RPP, but we could make it listen to it for cusp detection for pruning and then let the critics decide how to handle it! Might be good as parameterization though.

@mini-1235
Copy link
Contributor Author

Ah.

The Smoother Utils is used by the Simple / SG Smoothers which do not specifically model or care about in place rotations. So we don't consider that in those.

The Smac Planner doesn't check the last point because we enforce boundary conditions elsewhere in the smoother (I believe) so we maintain that orientation after smoothing for any possible in-place rotation of the final goal angle. So while not modeled as a new path segment, it is maintained after a smoothing process either way -- whether including an in-place rotation or not.

Both of these are generating the list of path segments to smooth individually, whereas the RPP controller is looking for when they happen, which could/often will happen in the very first starting path point to rotate to a new orientation before starting to track a path. So we check idx - 1 against idx to identify that case. RPP is a controller so it does care about in place rotations, motivated by #3089.

MPPI might be a gap - though I don't think anything in MPPI right now will actually listen to a planned request for an in place rotation explicitly. We could detect the cusp to stop, but will then start from that point which may not exactly include an in place rotation (as much as generally then going in that new orientation in the more MPPI-flexible-behavior fashion). MPPI isn't as rigid as RPP, but we could make it listen to it for cusp detection for pruning and then let the critics decide how to handle it! Might be good as parameterization though.

Thanks for the explanation!

@SteveMacenski
Copy link
Member

Review what I said as being sensible / accurate. I wouldn't be surprised if there were gaps

@mini-1235
Copy link
Contributor Author

Review what I said as being sensible / accurate. I wouldn't be surprised if there were gaps

I believe what we discussed here is related to #2683, I will revisit the discussions here when I have time to go through the ticket

@mini-1235
Copy link
Contributor Author

#5446 supersedes

@mini-1235 mini-1235 closed this Sep 27, 2025
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.

2 participants