Skip to content

Commit 9a29fb5

Browse files
SteveMacenskistevedanomodolor
authored andcommitted
Publishing a full time-series MPPI trajectory if desirable (ros-navigation#5057)
* Publishing a full MPPI trajectory if desirable Signed-off-by: Steve Macenski <[email protected]> * only create publisher when needed Signed-off-by: Steve Macenski <[email protected]> * adjust topics Signed-off-by: Steve Macenski <[email protected]> * Moving for lint Signed-off-by: Steve Macenski <[email protected]> * updating topics in tests Signed-off-by: Steve Macenski <[email protected]> --------- Signed-off-by: Steve Macenski <[email protected]> Signed-off-by: stevedanomodolor <[email protected]>
1 parent 9695250 commit 9a29fb5

File tree

16 files changed

+208
-11
lines changed

16 files changed

+208
-11
lines changed

nav2_bringup/params/nav2_params.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ controller_server:
131131
gamma: 0.015
132132
motion_model: "DiffDrive"
133133
visualize: true
134+
publish_optimal_trajectory: true
134135
regenerate_noises: true
135136
TrajectoryVisualizer:
136137
trajectory_step: 5

nav2_mppi_controller/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ This process is then repeated a number of times and returns a converged solution
5858
| visualize | bool | Default: false. Publish visualization of trajectories, which can slow down the controller significantly. Use only for debugging. |
5959
| retry_attempt_limit | int | Default 1. Number of attempts to find feasible trajectory on failure for soft-resets before reporting failure. |
6060
| regenerate_noises | bool | Default false. Whether to regenerate noises each iteration or use single noise distribution computed on initialization and reset. Practically, this is found to work fine since the trajectories are being sampled stochastically from a normal distribution and reduces compute jittering at run-time due to thread wake-ups to resample normal distribution. |
61+
| publish_optimal_trajectory | bool | Publishes the full optimal trajectory sequence each control iteration for downstream control systems, collision checkers, etc to have context beyond the next timestep. |
62+
6163

6264
#### Trajectory Visualizer
6365
| Parameter | Type | Definition |

nav2_mppi_controller/include/nav2_mppi_controller/controller.hpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,23 +105,28 @@ class MPPIController : public nav2_core::Controller
105105
/**
106106
* @brief Visualize trajectories
107107
* @param transformed_plan Transformed input plan
108+
* @param cmd_stamp Command stamp
109+
* @param optimal_trajectory Optimal trajectory, if already computed
108110
*/
109111
void visualize(
110112
nav_msgs::msg::Path transformed_plan,
111-
const builtin_interfaces::msg::Time & cmd_stamp);
113+
const builtin_interfaces::msg::Time & cmd_stamp,
114+
const Eigen::ArrayXXf & optimal_trajectory);
112115

113116
std::string name_;
114117
rclcpp_lifecycle::LifecycleNode::WeakPtr parent_;
115118
rclcpp::Logger logger_{rclcpp::get_logger("MPPIController")};
116119
std::shared_ptr<nav2_costmap_2d::Costmap2DROS> costmap_ros_;
117120
std::shared_ptr<tf2_ros::Buffer> tf_buffer_;
121+
rclcpp_lifecycle::LifecyclePublisher<nav2_msgs::msg::Trajectory>::SharedPtr opt_traj_pub_;
118122

119123
std::unique_ptr<ParametersHandler> parameters_handler_;
120124
Optimizer optimizer_;
121125
PathHandler path_handler_;
122126
TrajectoryVisualizer trajectory_visualizer_;
123127

124128
bool visualize_;
129+
bool publish_optimal_trajectory_;
125130
};
126131

127132
} // namespace nav2_mppi_controller

nav2_mppi_controller/include/nav2_mppi_controller/motion_models.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// Copyright (c) 2022 Samsung Research America, @artofnothingness Alexey Budyakov
2+
// Copyright (c) 2025 Open Navigation LLC
23
//
34
// Licensed under the Apache License, Version 2.0 (the "License");
45
// you may not use this file except in compliance with the License.

nav2_mppi_controller/include/nav2_mppi_controller/optimizer.hpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,12 @@ class Optimizer
105105
*/
106106
Eigen::ArrayXXf getOptimizedTrajectory();
107107

108+
/**
109+
* @brief Get the optimal control sequence for a cycle for visualization
110+
* @return Optimal control sequence
111+
*/
112+
const models::ControlSequence & getOptimalControlSequence();
113+
108114
/**
109115
* @brief Set the maximum speed based on the speed limits callback
110116
* @param speed_limit Limit of the speed for use
@@ -117,6 +123,15 @@ class Optimizer
117123
*/
118124
void reset();
119125

126+
/**
127+
* @brief Get the motion model time step
128+
* @return Time step of the model
129+
*/
130+
const models::OptimizerSettings & getSettings() const
131+
{
132+
return settings_;
133+
}
134+
120135
protected:
121136
/**
122137
* @brief Main function to generate, score, and return trajectories

nav2_mppi_controller/include/nav2_mppi_controller/tools/utils.hpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232

3333
#include "geometry_msgs/msg/twist_stamped.hpp"
3434
#include "nav_msgs/msg/path.hpp"
35+
#include "nav2_msgs/msg/trajectory.hpp"
3536
#include "visualization_msgs/msg/marker_array.hpp"
3637

3738
#include "rclcpp/rclcpp.hpp"
@@ -170,6 +171,34 @@ inline geometry_msgs::msg::TwistStamped toTwistStamped(
170171
return twist;
171172
}
172173

174+
inline std::unique_ptr<nav2_msgs::msg::Trajectory> toTrajectoryMsg(
175+
const Eigen::ArrayXXf & trajectory,
176+
const models::ControlSequence & control_sequence,
177+
const double & model_dt,
178+
const std_msgs::msg::Header & header)
179+
{
180+
auto trajectory_msg = std::make_unique<nav2_msgs::msg::Trajectory>();
181+
trajectory_msg->header = header;
182+
trajectory_msg->points.resize(trajectory.rows());
183+
184+
for (int i = 0; i < trajectory.rows(); ++i) {
185+
auto & curr_pt = trajectory_msg->points[i];
186+
curr_pt.time_from_start = rclcpp::Duration::from_seconds(i * model_dt);
187+
curr_pt.pose.position.x = trajectory(i, 0);
188+
curr_pt.pose.position.y = trajectory(i, 1);
189+
tf2::Quaternion quat;
190+
quat.setRPY(0.0, 0.0, trajectory(i, 2));
191+
curr_pt.pose.orientation = tf2::toMsg(quat);
192+
curr_pt.velocity.linear.x = control_sequence.vx(i);
193+
curr_pt.velocity.angular.z = control_sequence.wz(i);
194+
if (control_sequence.vy.size() > 0) {
195+
curr_pt.velocity.linear.y = control_sequence.vy(i);
196+
}
197+
}
198+
199+
return trajectory_msg;
200+
}
201+
173202
/**
174203
* @brief Convert path to a tensor
175204
* @param path Path to convert

nav2_mppi_controller/src/controller.cpp

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,20 @@ void MPPIController::configure(
3838
auto getParam = parameters_handler_->getParamGetter(name_);
3939
getParam(visualize_, "visualize", false);
4040

41+
getParam(publish_optimal_trajectory_, "publish_optimal_trajectory", false);
42+
4143
// Configure composed objects
4244
optimizer_.initialize(parent_, name_, costmap_ros_, parameters_handler_.get());
4345
path_handler_.initialize(parent_, name_, costmap_ros_, tf_buffer_, parameters_handler_.get());
4446
trajectory_visualizer_.on_configure(
4547
parent_, name_,
4648
costmap_ros_->getGlobalFrameID(), parameters_handler_.get());
4749

50+
if (publish_optimal_trajectory_) {
51+
opt_traj_pub_ = node->create_publisher<nav2_msgs::msg::Trajectory>(
52+
"~/optimal_trajectory", rclcpp::SystemDefaultsQoS());
53+
}
54+
4855
RCLCPP_INFO(logger_, "Configured MPPI Controller: %s", name_.c_str());
4956
}
5057

@@ -53,19 +60,27 @@ void MPPIController::cleanup()
5360
optimizer_.shutdown();
5461
trajectory_visualizer_.on_cleanup();
5562
parameters_handler_.reset();
63+
opt_traj_pub_.reset();
5664
RCLCPP_INFO(logger_, "Cleaned up MPPI Controller: %s", name_.c_str());
5765
}
5866

5967
void MPPIController::activate()
6068
{
69+
auto node = parent_.lock();
6170
trajectory_visualizer_.on_activate();
6271
parameters_handler_->start();
72+
if (opt_traj_pub_) {
73+
opt_traj_pub_->on_activate();
74+
}
6375
RCLCPP_INFO(logger_, "Activated MPPI Controller: %s", name_.c_str());
6476
}
6577

6678
void MPPIController::deactivate()
6779
{
6880
trajectory_visualizer_.on_deactivate();
81+
if (opt_traj_pub_) {
82+
opt_traj_pub_->on_deactivate();
83+
}
6984
RCLCPP_INFO(logger_, "Deactivated MPPI Controller: %s", name_.c_str());
7085
}
7186

@@ -100,19 +115,36 @@ geometry_msgs::msg::TwistStamped MPPIController::computeVelocityCommands(
100115
RCLCPP_INFO(logger_, "Control loop execution time: %ld [ms]", duration);
101116
#endif
102117

118+
Eigen::ArrayXXf optimal_trajectory;
119+
if (publish_optimal_trajectory_ && opt_traj_pub_->get_subscription_count() > 0) {
120+
optimal_trajectory = optimizer_.getOptimizedTrajectory();
121+
auto trajectory_msg = utils::toTrajectoryMsg(
122+
optimal_trajectory,
123+
optimizer_.getOptimalControlSequence(),
124+
optimizer_.getSettings().model_dt,
125+
cmd.header);
126+
opt_traj_pub_->publish(std::move(trajectory_msg));
127+
}
128+
103129
if (visualize_) {
104-
visualize(std::move(transformed_plan), cmd.header.stamp);
130+
visualize(std::move(transformed_plan), cmd.header.stamp, optimal_trajectory);
105131
}
106132

107133
return cmd;
108134
}
109135

110136
void MPPIController::visualize(
111137
nav_msgs::msg::Path transformed_plan,
112-
const builtin_interfaces::msg::Time & cmd_stamp)
138+
const builtin_interfaces::msg::Time & cmd_stamp,
139+
const Eigen::ArrayXXf & optimal_trajectory)
113140
{
114141
trajectory_visualizer_.add(optimizer_.getGeneratedTrajectories(), "Candidate Trajectories");
115-
trajectory_visualizer_.add(optimizer_.getOptimizedTrajectory(), "Optimal Trajectory", cmd_stamp);
142+
if (optimal_trajectory.size() > 0) {
143+
trajectory_visualizer_.add(optimal_trajectory, "Optimal Trajectory", cmd_stamp);
144+
} else {
145+
trajectory_visualizer_.add(
146+
optimizer_.getOptimizedTrajectory(), "Optimal Trajectory", cmd_stamp);
147+
}
116148
trajectory_visualizer_.visualize(std::move(transformed_plan));
117149
}
118150

nav2_mppi_controller/src/optimizer.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// Copyright (c) 2022 Samsung Research America, @artofnothingness Alexey Budyakov
2+
// Copyright (c) 2025 Open Navigation LLC
23
//
34
// Licensed under the Apache License, Version 2.0 (the "License");
45
// you may not use this file except in compliance with the License.
@@ -426,6 +427,11 @@ Eigen::ArrayXXf Optimizer::getOptimizedTrajectory()
426427
return trajectories;
427428
}
428429

430+
const models::ControlSequence & Optimizer::getOptimalControlSequence()
431+
{
432+
return control_sequence_;
433+
}
434+
429435
void Optimizer::updateControlSequence()
430436
{
431437
const bool is_holo = isHolonomic();

nav2_mppi_controller/src/trajectory_visualizer.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,10 @@ void TrajectoryVisualizer::on_configure(
2626
logger_ = node->get_logger();
2727
frame_id_ = frame_id;
2828
trajectories_publisher_ =
29-
node->create_publisher<visualization_msgs::msg::MarkerArray>("trajectories", 1);
30-
transformed_path_pub_ = node->create_publisher<nav_msgs::msg::Path>("transformed_global_plan", 1);
31-
optimal_path_pub_ = node->create_publisher<nav_msgs::msg::Path>("optimal_trajectory", 1);
29+
node->create_publisher<visualization_msgs::msg::MarkerArray>("~/candidate_trajectories", 1);
30+
transformed_path_pub_ = node->create_publisher<nav_msgs::msg::Path>(
31+
"~/transformed_global_plan", 1);
32+
optimal_path_pub_ = node->create_publisher<nav_msgs::msg::Path>("~/optimal_path", 1);
3233
parameters_handler_ = parameters_handler;
3334

3435
auto getParam = parameters_handler->getParamGetter(name + ".TrajectoryVisualizer");

nav2_mppi_controller/test/controller_state_transition_test.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ TEST(ControllerStateTransitionTest, ControllerNotFail)
3838
options.parameter_overrides(params);
3939

4040
auto node = getDummyNode(options);
41+
node->declare_parameter("publish_optimal_trajectory", true);
4142
auto tf_buffer = std::make_shared<tf2_ros::Buffer>(node->get_clock());
4243
auto costmap_ros = getDummyCostmapRos(costmap_settings);
4344
costmap_ros->setRobotFootprint(getDummySquareFootprint(0.01));

0 commit comments

Comments
 (0)