Skip to content

Conversation

@chanhhoang99
Copy link
Contributor


Basic Info

Info Please fill out this column
Ticket(s) this addresses (add tickets here #5214)
Primary OS tested on (Ubuntu)
Robotic platform tested on (Personal real platform)
Does this PR contain AI generated software? (No)
Was this PR description generated by AI software? No

Description of contribution in a few bullet points

  • It is unavoidable that it takes time for motors to reach a specific setpoint. Espescially, in case where acceleration and model_dt are small. The velocity steps (example 0.0 -> 0.025 -> 0.05 -> 0.075 ...) are also tight. Hence, torque generated by motor is very little and it's hard to move the motor. For instance, MPPI is running with model_dt=0.05, this means MPPI will get odometry every 50ms .But at 50ms, the motor has not yet reach it setpoint (at that time the torque generated my motor is not strong enough).
  • When close loop mode is used, MPPI will get the current odometry and add small velocity step for next setpoint every control iteration. Because of that, motor still could not generate enough torque.
  • When open_loop is used, MPPI uses last command velocity instead of odometry, this helps small velocity steps accumulate every control iteration and at some point, it will be enough to populate the motor.

Description of documentation updates required from your changes

Description of how this change was tested

With

      ax_max: 0.1
      ax_min: -0.1
      az_max: 0.1

Before the change

Screencast.from.2025-10-16.11-15-26.mp4

After the change
https://www.youtube.com/watch?v=HbLMPBcHRgI


Future work that may be required in bullet points

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: Chanh Hoang <chanhhoang999x@gmail.com>
state.wz.col(0) = static_cast<float>(state.speed.angular.z);
const bool open = settings_.open_loop;

const float vx0 = open ? control_sequence_.vx(0)
Copy link
Member

Choose a reason for hiding this comment

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

Is this correct considering we pop the initial one when we send it?

Copy link
Contributor Author

@chanhhoang99 chanhhoang99 Oct 17, 2025

Choose a reason for hiding this comment

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

These lines of code, the purpose is to take the last command velocity sent to the motor, give it to the MPPI predict() steps.
I think that control_sequence_.vx(0) is the last cmd_vel that sent to the motor.

Copy link
Member

Choose a reason for hiding this comment

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

But control_sequence_.vx(0) is not the last command sent to the motors. After we send a command to the motors, the trajectory is slid over to initialize for the next iteration

void Optimizer::shiftControlSequence()
{
auto size = control_sequence_.vx.size();
utils::shiftColumnsByOnePlace(control_sequence_.vx, -1);
utils::shiftColumnsByOnePlace(control_sequence_.wz, -1);
control_sequence_.vx(size - 1) = control_sequence_.vx(size - 2);
control_sequence_.wz(size - 1) = control_sequence_.wz(size - 2);
if (isHolonomic()) {
utils::shiftColumnsByOnePlace(control_sequence_.vy, -1);
control_sequence_.vy(size - 1) = control_sequence_.vy(size - 2);
}
}

Copy link
Contributor

Choose a reason for hiding this comment

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

I think control_sequence_.vx(0) is now control_sequence_.vx(1) from the last iteration, since it is shifted in https://github.com/enwaytech/navigation2/blob/main/nav2_mppi_controller/src/optimizer.cpp#L219

Copy link
Member

Choose a reason for hiding this comment

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

But we don't always use 1st point, we sometimes use 0th depending on the setting of the control frequency and timestep https://github.com/enwaytech/navigation2/blob/main/nav2_mppi_controller/src/optimizer.cpp#L443

I think you need to use the last control velocity as the state initial for open loop. If you stored that and set it in the open loop setting, that seems to me to be the easiest way.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah okay, forgot that setup where we dont always shift control sequence.
So now we will create member variable in Optimizer, store the last_cmd_vel then use it next iteration.

Copy link
Contributor Author

@chanhhoang99 chanhhoang99 Oct 21, 2025

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

So now we will create member variable in Optimizer, store the last_cmd_vel then use it next iteration.

Sounds good! I thnk this can be a member of the Optimizer class, since its not regarding the current trajectory state

Fix linting

Signed-off-by: Chanh Hoang <chanhhoang999x@gmail.com>
@codecov
Copy link

codecov bot commented Oct 17, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.

Files with missing lines Coverage Δ
...nav2_mppi_controller/models/optimizer_settings.hpp 100.00% <ø> (ø)
...troller/include/nav2_mppi_controller/optimizer.hpp 100.00% <ø> (ø)
nav2_mppi_controller/src/optimizer.cpp 96.96% <100.00%> (+0.04%) ⬆️

... and 11 files with indirect coverage changes

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

state.wz.col(0) = static_cast<float>(state.speed.angular.z);
const bool open = settings_.open_loop;

const float vx0 = open ? control_sequence_.vx(0)
Copy link
Member

Choose a reason for hiding this comment

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

But control_sequence_.vx(0) is not the last command sent to the motors. After we send a command to the motors, the trajectory is slid over to initialize for the next iteration

void Optimizer::shiftControlSequence()
{
auto size = control_sequence_.vx.size();
utils::shiftColumnsByOnePlace(control_sequence_.vx, -1);
utils::shiftColumnsByOnePlace(control_sequence_.wz, -1);
control_sequence_.vx(size - 1) = control_sequence_.vx(size - 2);
control_sequence_.wz(size - 1) = control_sequence_.wz(size - 2);
if (isHolonomic()) {
utils::shiftColumnsByOnePlace(control_sequence_.vy, -1);
control_sequence_.vy(size - 1) = control_sequence_.vy(size - 2);
}
}

Signed-off-by: Chanh Hoang <chanhhoang999x@gmail.com>
Signed-off-by: Chanh Hoang <chanhhoang999x@gmail.com>
Signed-off-by: Chanh Hoang <chanhhoang999x@gmail.com>
Signed-off-by: Chanh Hoang <chanhhoang999x@gmail.com>
Signed-off-by: Chanh Hoang <chanhhoang999x@gmail.com>
Signed-off-by: Chanh Hoang <chanhhoang999x@gmail.com>
@SteveMacenski
Copy link
Member

How as this tested and would it be useful to setup a new unit test for the optimizer that sets the open loop to true and checks that it initializes exactly the first value? That would be good I think and cover the missing lines

@chanhhoang99
Copy link
Contributor Author

chanhhoang99 commented Oct 22, 2025

How as this tested and would it be useful to setup a new unit test for the optimizer that sets the open loop to true and checks that it initializes exactly the first value? That would be good I think and cover the missing lines

Do you mean https://github.com/ros-navigation/navigation2/pull/5617/files#diff-6d2fd58ab98a8266e2217a3d534624786349d5f40e08547ec5125ff64ecae5a5R238 ?
How should I do to cover below lines ?

last_command_vel_ = geometry_msgs::msg::Twist();
// and 
last_command_vel_ = control.twist;

is it should be a test case where

EXPECT_FLOAT_EQ(opt.last_command_vel_.linear.x, 0.0f);

Signed-off-by: Chanh Hoang <chanhhoang999x@gmail.com>
Signed-off-by: Chanh Hoang <chanhhoang999x@gmail.com>
Signed-off-by: Chanh Hoang <chanhhoang999x@gmail.com>
Signed-off-by: Chanh Hoang <chanhhoang999x@gmail.com>
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.

How should I do to cover below lines ?

Like so 😉 Just check that they are populated correctly.

Or, make a unit test where you set the open loop parameter to true in a node before constructing the MPPI optimizer. Then send it some info with an odometry velocity twist of something ridiculous. Check that the trajectory is reasonable for the first and second time the velocity is requested (first for initialization conditions; second for normal conditions)

Comment on lines 221 to 223
if (settings_.open_loop) {
last_command_vel_ = control.twist;
}
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if (settings_.open_loop) {
last_command_vel_ = control.twist;
}
last_command_vel_ = control.twist;

Comment on lines +162 to +164
if (settings_.open_loop) {
last_command_vel_ = geometry_msgs::msg::Twist();
}
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if (settings_.open_loop) {
last_command_vel_ = geometry_msgs::msg::Twist();
}
last_command_vel_ = geometry_msgs::msg::Twist();

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.

Should you also update state_.speed = robot_speed;? Or, actually set the state_.speed to be the last command. Then the lines you changed for

 state.vx.col(0) = settings_.open_loop ? last_command_vel_.linear.x :
    static_cast<float>(state.speed.linear.x);

Can be reverted. That way anything that may use state (such as the critics) are all using the same consistent thing. You'd then move hte open_loop setting check for what to set in the prepare() method. Right now, the state object is inconsistent in the information its storing, so I think this is actually a better idea.

Signed-off-by: Chanh Hoang <chanhhoang999x@gmail.com>
Signed-off-by: Chanh Hoang <chanhhoang999x@gmail.com>
Signed-off-by: Chanh Hoang <chanhhoang999x@gmail.com>
Signed-off-by: Chanh Hoang <chanhhoang999x@gmail.com>
Signed-off-by: Chanh Hoang <chanhhoang999x@gmail.com>
@chanhhoang99
Copy link
Contributor Author

Hi @adivardi ,
Could you test this latest change on main branch ? Currently, I do not have the set up on main branch to test.(mine is kilted)
Thanks alot.

@adivardi
Copy link
Contributor

Hi @adivardi , Could you test this latest change on main branch ? Currently, I do not have the set up on main branch to test.(mine is kilted) Thanks alot.

Hi, sure, but I am traveling for ROSCon. So it will only be in 2 weeks.

@SteveMacenski
Copy link
Member

SteveMacenski commented Oct 23, 2025

@chanhhoang99 can you verify this work well for you on Kilted? The main branch compiles with Kilted 😉 If so, then there's no reason the middleware would change behavior and we can merge before I leave tomorrow for Singapore for ROSCon

P.S. If either or both of you are at ROSCon, come find me. I have some merch for Nav2 contributors 😉

@chanhhoang99
Copy link
Contributor Author

@SteveMacenski ,
For kilted, I was testing this all day yesterday but didn't find any problem.
This included testing in a narrow space requiring rotating in place, free space and dense place with tables, chairs and people.

@SteveMacenski SteveMacenski merged commit 8dbc929 into ros-navigation:main Oct 24, 2025
16 checks passed
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.

3 participants