Skip to content

Commit 7daea04

Browse files
feat(mppi): fix for gradual drift
Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 30e3ccd commit 7daea04

8 files changed

Lines changed: 106 additions & 25 deletions

File tree

planning/autoware_mppi_optimizer/README.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ Various features can be disabled by changing the following parameters set in `mp
6969
ignore_obstacles: true
7070
ignore_drivable_area: true
7171
force_cold_start_each_step: true
72+
use_last_control_as_nominal: true
7273
```
7374
7475
Then rebuild / restart the diffusion planner and compare live MPPI to offline retune.
@@ -79,12 +80,14 @@ Notes:
7980
- `ignore_drivable_area` is retained as an ablation flag; on this stack boundary crash is already
8081
disabled in the cost (`isEgoOutsideDrivableArea` always false).
8182
- `force_cold_start_each_step` only resets tracking counters / arc-length (control is already
82-
re-seeded from the reference via `updateImportanceSampler(u_nom)` each cycle).
83+
re-seeded via `updateImportanceSampler(u_nom)` each cycle).
84+
- `use_last_control_as_nominal` warm-starts `u_nom` from the shifted previous optimized control
85+
sequence when available; otherwise (and on cold start) reseeds from the diffusion reference.
8386

8487
### Replay only
8588

8689
```bash
87-
ros2 run autoware_mppi_optimizer mppi_debug_visualizer.py -- \
90+
ros2 run autoware_diffusion_planner mppi_debug_visualizer.py -- \
8891
--log-dir "$HOME/.cache/autoware/mppi_debug_log"
8992
```
9093

@@ -106,7 +109,7 @@ with diffusion reference (cyan), logged MPPI (red), and retuned MPPI (green):
106109

107110
```bash
108111
# Option A — visualizer with retune panel
109-
ros2 run autoware_mppi_optimizer mppi_debug_visualizer.py -- \
112+
ros2 run autoware_diffusion_planner mppi_debug_visualizer.py -- \
110113
--log-dir "$HOME/.cache/autoware/mppi_debug_log" \
111114
--enable-retune \
112115
--params-yaml $(ros2 pkg prefix autoware_mppi_optimizer)/share/autoware_mppi_optimizer/config/mppi_optimizer.param.yaml

planning/autoware_mppi_optimizer/config/mppi_optimizer.param.yaml

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,21 @@
22
ros__parameters:
33
# Vehicle actuation dynamics are loaded from $(vehicle_model)_description/config/simulator_model.param.yaml
44

5-
# MPPI controller / FirstOrderDubinsBicycleCostParams
6-
# (see first_order_dubins_mppi_interface.cu, first_order_dubins_bicycle_cost.cuh)
7-
lambda: 3000.0
5+
# FirstOrderDubinsBicycleCostParams (see first_order_dubins_bicycle_cost.cuh)
6+
lambda: 14000.0
87
desired_speed: 2.5
98
speed_coeff: 500.0
10-
track_coeff: 1000.0
11-
heading_coeff: 500.0
9+
track_coeff: 3000.0
10+
heading_coeff: 1000.0
1211
crash_coeff: 100000.0
1312
boundary_threshold: 0.8
1413
boundary_threshold_left: -1.0
1514
boundary_threshold_right: -1.0
1615
lateral_acceleration_coeff: 500.0
17-
lateral_jerk_coeff: 3000.0
18-
longitudinal_jerk_coeff: 1000.0
19-
accel_cmd_coeff: 200.0
20-
steer_cmd_coeff: 1000.0
21-
steer_rate_coeff: 5000.0
16+
lateral_jerk_coeff: 1000.0
17+
longitudinal_jerk_coeff: 10.0
18+
accel_cmd_coeff: 0.0
19+
steer_cmd_coeff: 10.0
2220
goal_pos_coeff: 1000.0
2321
goal_speed_coeff: 200.0
2422
goal_yaw_coeff: 500.0
@@ -34,3 +32,6 @@
3432
ignore_obstacles: false
3533
ignore_drivable_area: true
3634
force_cold_start_each_step: false
35+
36+
# Warm-start u_nom from shifted previous optimized controls (else reseed from DP each cycle).
37+
use_last_control_as_nominal: true

planning/autoware_mppi_optimizer/include/autoware/mppi_optimizer/first_order_dubins_mppi_cost_params.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ struct FirstOrderDubinsMppiCostParams
3434
float boundary_threshold_right{-1.0F};
3535
float accel_cmd_coeff{0.0F};
3636
float steer_cmd_coeff{0.0F};
37+
3738
float steer_rate_coeff{0.0F};
3839
float lateral_acceleration_coeff{300.0F};
3940
float lateral_jerk_coeff{300.0F};

planning/autoware_mppi_optimizer/include/autoware/mppi_optimizer/first_order_dubins_mppi_interface.hpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,13 @@ class FirstOrderDubinsMppiInterface
112112

113113
/**
114114
* @brief Ablation options to mirror mppi_offline_retune conditions in online sim.
115+
* @param use_last_control_as_nominal When true and a previous optimized control sequence
116+
* exists, seed u_nom by shifting that sequence (warm start) instead of reseeding
117+
* from the diffusion reference every cycle.
115118
*/
116119
void setAblationOptions(
117-
bool ignore_obstacles, bool ignore_drivable_area, bool force_cold_start_each_step);
120+
bool ignore_obstacles, bool ignore_drivable_area, bool force_cold_start_each_step,
121+
bool use_last_control_as_nominal = false);
118122

119123
/**
120124
* @brief Copy per-rollout raw costs and normalized importance weights from the last
@@ -137,7 +141,8 @@ class FirstOrderDubinsMppiInterface
137141
* @brief Track a diffusion-planner reference (poses + velocities) with one MPPI step.
138142
*
139143
* Uses the diffusion trajectory directly as the MPPI reference horizon (x, y, yaw, v),
140-
* seeds u_nom from the reference trajectory controls each cycle, and returns the MPPI-predicted
144+
* seeds u_nom from the previous optimized controls when use_last_control_as_nominal is set
145+
* (otherwise from the reference trajectory), and returns the MPPI-predicted
141146
* feasible state rollout that best tracks that reference.
142147
*
143148
* @param input Reference trajectory from the diffusion planner (map frame).

planning/autoware_mppi_optimizer/include/autoware/mppi_optimizer/first_order_dubins_mppi_runtime_options.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ struct FirstOrderDubinsMppiRuntimeOptions
2929
bool ignore_obstacles{false};
3030
bool ignore_drivable_area{false};
3131
bool force_cold_start_each_step{false};
32+
/** Warm-start u_nom from shifted previous optimized controls (else reseed from DP each cycle). */
33+
bool use_last_control_as_nominal{false};
3234
};
3335

3436
} // namespace autoware::mppi_optimizer

planning/autoware_mppi_optimizer/src/first_order_dubins/first_order_dubins_mppi_interface.cu

Lines changed: 74 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,9 @@ namespace
5353
constexpr int kMppiHorizon = 80;
5454
constexpr int kRefHorizon = kMppiHorizon;
5555
constexpr float kDt = 0.1F;
56+
constexpr size_t kMaxIter = 10;
5657
constexpr int kNumRollouts = 32 * 1024;
58+
constexpr float kLambda = 1500.0F;
5759
constexpr float kInitArcLength = 1.5F;
5860
constexpr size_t kTrackingIndexResetThreshold = 15U;
5961
// constexpr int kMaxVizRollouts = 200; // rollout viz disabled
@@ -338,6 +340,39 @@ void buildRolloutVisualization(
338340
}
339341
#endif
340342

343+
/// @brief check if the given trajectory needs to be optimized
344+
[[nodiscard]] bool is_optimization_required(const autoware::mppi_optimizer::Trajectory & trajectory)
345+
{
346+
const auto is_stopping = std::find_if(
347+
trajectory.points.begin(), trajectory.points.end(),
348+
[&](const autoware_planning_msgs::msg::TrajectoryPoint & p) {
349+
return p.longitudinal_velocity_mps < 0.02;
350+
}) != trajectory.points.end();
351+
auto length = 0.0;
352+
for (auto i = 0UL; i + 1 < trajectory.points.size(); ++i) {
353+
length += std::hypot(
354+
trajectory.points[i].pose.position.x - trajectory.points[i + 1].pose.position.x,
355+
trajectory.points[i].pose.position.y - trajectory.points[i + 1].pose.position.y);
356+
}
357+
const auto is_short = length < 4.0;
358+
return !is_stopping || !is_short;
359+
}
360+
361+
/// @brief override initial 0 velocities with engage velocities
362+
void set_initial_engage_velocity(autoware::mppi_optimizer::Trajectory & trajectory)
363+
{
364+
constexpr auto engage_velocity = 0.25;
365+
if (trajectory.points.size() < 3) return;
366+
const auto wants_to_move = std::find_if(
367+
trajectory.points.begin(), trajectory.points.end(),
368+
[&](const autoware_planning_msgs::msg::TrajectoryPoint & p) {
369+
return p.longitudinal_velocity_mps > engage_velocity;
370+
}) != trajectory.points.end();
371+
if (wants_to_move && trajectory.points[0].longitudinal_velocity_mps < 0.05) {
372+
trajectory.points[0].longitudinal_velocity_mps = engage_velocity;
373+
trajectory.points[1].longitudinal_velocity_mps = engage_velocity;
374+
}
375+
}
341376
} // namespace
342377

343378
struct FirstOrderDubinsMppiInterface::Impl
@@ -375,6 +410,8 @@ struct FirstOrderDubinsMppiInterface::Impl
375410
bool ignore_obstacles{false};
376411
bool ignore_drivable_area{false};
377412
bool force_cold_start_each_step{false};
413+
/** Warm-start u_nom from shifted previous u_opt when available. */
414+
bool use_last_control_as_nominal{false};
378415

379416
Impl() : feedback(&model, kDt), sampler(SAMPLER::SAMPLING_PARAMS_T{}) {}
380417

@@ -432,8 +469,7 @@ struct FirstOrderDubinsMppiInterface::Impl
432469
sampler = SAMPLER(sp);
433470

434471
controller = std::make_unique<Mppi>(
435-
&model, &cost, &feedback, &sampler, kDt, 1, user_cost_params_.lambda, 0.0F, kMppiHorizon,
436-
u_nom);
472+
&model, &cost, &feedback, &sampler, kDt, kMaxIter, kLambda, 0.0F, kMppiHorizon, u_nom);
437473
auto cp = controller->getParams();
438474
cp.dynamics_rollout_dim_ = dim3(32, 2, 1);
439475
cp.cost_rollout_dim_ = dim3(32, 2, 1);
@@ -471,6 +507,15 @@ struct FirstOrderDubinsMppiInterface::Impl
471507
sim_time = 0.0F;
472508
}
473509

510+
void seedNominalControlFromLastOptimized()
511+
{
512+
// Drop the control already applied at the previous cycle; hold the terminal command.
513+
for (int t = 0; t < kMppiHorizon - 1; ++t) {
514+
u_nom.col(t) = u_opt.col(t + 1);
515+
}
516+
u_nom.col(kMppiHorizon - 1) = u_opt.col(kMppiHorizon - 1);
517+
}
518+
474519
void seedNominalControlFromDiffusionReference(
475520
const Trajectory & reference, const size_t start_idx)
476521
{
@@ -509,6 +554,16 @@ struct FirstOrderDubinsMppiInterface::Impl
509554
}
510555
}
511556

557+
void seedNominalControl(const Trajectory & reference, const size_t start_idx)
558+
{
559+
// After a tracking reset, step_count is 0 and u_opt was cleared — fall back to DP seed.
560+
if (use_last_control_as_nominal && step_count > 0) {
561+
seedNominalControlFromLastOptimized();
562+
return;
563+
}
564+
seedNominalControlFromDiffusionReference(reference, start_idx);
565+
}
566+
512567
void updateDiffusionReference(
513568
const Trajectory & reference, const Odometry & odometry,
514569
const std::optional<geometry_msgs::msg::AccelWithCovarianceStamped> & acceleration,
@@ -539,7 +594,7 @@ struct FirstOrderDubinsMppiInterface::Impl
539594
resetTrackingState();
540595
}
541596
tracking_start_idx = new_start_idx;
542-
seedNominalControlFromDiffusionReference(reference, tracking_start_idx);
597+
seedNominalControl(reference, tracking_start_idx);
543598

544599
const float ego_yaw = yawFromOdometry(odometry);
545600
const float ego_v = static_cast<float>(odometry.twist.twist.linear.x);
@@ -708,9 +763,9 @@ void FirstOrderDubinsMppiInterface::setRuntimeOptions(
708763
setDebugTrajectoryLogging(
709764
options.enable_debug_trajectory_log, options.debug_trajectory_log_directory);
710765
setAblationOptions(
711-
options.ignore_obstacles, options.ignore_drivable_area, options.force_cold_start_each_step);
766+
options.ignore_obstacles, options.ignore_drivable_area, options.force_cold_start_each_step,
767+
options.use_last_control_as_nominal);
712768
}
713-
714769
void FirstOrderDubinsMppiInterface::setDebugTrajectoryLogging(
715770
const bool enable, const std::string & directory)
716771
{
@@ -723,20 +778,22 @@ void FirstOrderDubinsMppiInterface::setDebugTrajectoryLogging(
723778

724779
void FirstOrderDubinsMppiInterface::setAblationOptions(
725780
const bool ignore_obstacles, const bool ignore_drivable_area,
726-
const bool force_cold_start_each_step)
781+
const bool force_cold_start_each_step, const bool use_last_control_as_nominal)
727782
{
728783
if (!impl_) {
729784
throw std::runtime_error("FirstOrderDubinsMppiInterface implementation is missing");
730785
}
731786
impl_->ignore_obstacles = ignore_obstacles;
732787
impl_->ignore_drivable_area = ignore_drivable_area;
733788
impl_->force_cold_start_each_step = force_cold_start_each_step;
789+
impl_->use_last_control_as_nominal = use_last_control_as_nominal;
734790
RCLCPP_INFO(
735791
mppiLogger(),
736792
"MPPI ablation options: ignore_obstacles=%s ignore_drivable_area=%s "
737-
"force_cold_start_each_step=%s",
793+
"force_cold_start_each_step=%s use_last_control_as_nominal=%s",
738794
ignore_obstacles ? "true" : "false", ignore_drivable_area ? "true" : "false",
739-
force_cold_start_each_step ? "true" : "false");
795+
force_cold_start_each_step ? "true" : "false",
796+
use_last_control_as_nominal ? "true" : "false");
740797
}
741798

742799
bool FirstOrderDubinsMppiInterface::copySampleCostDistribution(
@@ -796,9 +853,14 @@ FirstOrderDubinsMppiOptimizationResult FirstOrderDubinsMppiInterface::optimizeTr
796853
throw std::runtime_error("FirstOrderDubinsMppiInterface implementation is missing");
797854
}
798855
FirstOrderDubinsMppiOptimizationResult result;
799-
if (input.points.size() < 2U) {
856+
const auto not_enough_input_points = input.points.size() < 2U;
857+
const auto optimization_required = is_optimization_required(input);
858+
if (not_enough_input_points || !optimization_required) {
800859
RCLCPP_WARN(
801-
mppiLogger(), "MPPI skipped: trajectory has %zu points (need >= 2)", input.points.size());
860+
mppiLogger(), "MPPI skipped: %s",
861+
not_enough_input_points ? "trajectory has fewer than 2 points"
862+
: "trajectory does not require optimization");
863+
802864
result.trajectory = input;
803865
result.debug.reference_trajectory = input;
804866
result.debug.optimized_trajectory = input;
@@ -889,6 +951,8 @@ FirstOrderDubinsMppiOptimizationResult FirstOrderDubinsMppiInterface::optimizeTr
889951
std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - start_time)
890952
.count();
891953

954+
set_initial_engage_velocity(output);
955+
892956
result.trajectory = output;
893957
result.debug.reference_trajectory = input;
894958
result.debug.optimized_trajectory = output;

planning/autoware_mppi_optimizer/src/first_order_dubins_mppi_runtime_options_ros.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ void declare_first_order_dubins_mppi_runtime_options(
4040
node.declare_parameter(param_name(prefix, "ignore_drivable_area"), defaults.ignore_drivable_area);
4141
node.declare_parameter(
4242
param_name(prefix, "force_cold_start_each_step"), defaults.force_cold_start_each_step);
43+
node.declare_parameter(
44+
param_name(prefix, "use_last_control_as_nominal"), defaults.use_last_control_as_nominal);
4345
}
4446

4547
FirstOrderDubinsMppiRuntimeOptions get_first_order_dubins_mppi_runtime_options(
@@ -55,6 +57,8 @@ FirstOrderDubinsMppiRuntimeOptions get_first_order_dubins_mppi_runtime_options(
5557
node.get_parameter(param_name(prefix, "ignore_drivable_area")).as_bool();
5658
options.force_cold_start_each_step =
5759
node.get_parameter(param_name(prefix, "force_cold_start_each_step")).as_bool();
60+
options.use_last_control_as_nominal =
61+
node.get_parameter(param_name(prefix, "use_last_control_as_nominal")).as_bool();
5862
return options;
5963
}
6064

planning/autoware_mppi_optimizer/src/mppi_optimizer.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include <memory>
2727
#include <optional>
2828
#include <stdexcept>
29+
#include <string>
2930

3031
namespace autoware::mppi_optimizer
3132
{

0 commit comments

Comments
 (0)