15 #include "nav2_mppi_controller/optimizer.hpp"
23 #include <xtensor/xmath.hpp>
24 #include <xtensor/xrandom.hpp>
25 #include <xtensor/xnoalias.hpp>
27 #include "nav2_core/controller_exceptions.hpp"
28 #include "nav2_costmap_2d/costmap_filters/filter_values.hpp"
33 using namespace xt::placeholders;
34 using xt::evaluation_strategy::immediate;
37 rclcpp_lifecycle::LifecycleNode::WeakPtr parent,
const std::string & name,
38 std::shared_ptr<nav2_costmap_2d::Costmap2DROS> costmap_ros,
43 costmap_ros_ = costmap_ros;
44 costmap_ = costmap_ros_->getCostmap();
45 parameters_handler_ = param_handler;
47 auto node = parent_.lock();
48 logger_ = node->get_logger();
52 critic_manager_.on_configure(parent_, name_, costmap_ros_, parameters_handler_);
53 noise_generator_.initialize(settings_, isHolonomic(), name_, parameters_handler_);
60 noise_generator_.shutdown();
65 std::string motion_model_name;
68 auto getParam = parameters_handler_->getParamGetter(name_);
69 auto getParentParam = parameters_handler_->getParamGetter(
"");
70 getParam(s.model_dt,
"model_dt", 0.05f);
71 getParam(s.time_steps,
"time_steps", 56);
72 getParam(s.batch_size,
"batch_size", 1000);
73 getParam(s.iteration_count,
"iteration_count", 1);
74 getParam(s.temperature,
"temperature", 0.3f);
75 getParam(s.gamma,
"gamma", 0.015f);
76 getParam(s.base_constraints.vx_max,
"vx_max", 0.5f);
77 getParam(s.base_constraints.vx_min,
"vx_min", -0.35f);
78 getParam(s.base_constraints.vy,
"vy_max", 0.5f);
79 getParam(s.base_constraints.wz,
"wz_max", 1.9f);
80 getParam(s.base_constraints.ax_max,
"ax_max", 3.0f);
81 getParam(s.base_constraints.ax_min,
"ax_min", -3.0f);
82 getParam(s.base_constraints.ay_max,
"ay_max", 3.0f);
83 getParam(s.base_constraints.ay_min,
"ay_min", -3.0f);
84 getParam(s.base_constraints.az_max,
"az_max", 3.5f);
85 getParam(s.sampling_std.vx,
"vx_std", 0.2f);
86 getParam(s.sampling_std.vy,
"vy_std", 0.2f);
87 getParam(s.sampling_std.wz,
"wz_std", 0.4f);
88 getParam(s.retry_attempt_limit,
"retry_attempt_limit", 1);
89 getParam(s.open_loop,
"open_loop",
false);
91 s.base_constraints.ax_max = std::abs(s.base_constraints.ax_max);
92 if (s.base_constraints.ax_min > 0.0) {
93 s.base_constraints.ax_min = -1.0 * s.base_constraints.ax_min;
96 "Sign of the parameter ax_min is incorrect, consider setting it negative.");
99 if (s.base_constraints.ay_min > 0.0) {
100 s.base_constraints.ay_min = -1.0 * s.base_constraints.ay_min;
103 "Sign of the parameter ay_min is incorrect, consider setting it negative.");
106 getParam(motion_model_name,
"motion_model", std::string(
"DiffDrive"));
108 s.constraints = s.base_constraints;
109 setMotionModel(motion_model_name);
110 parameters_handler_->addPostCallback([
this]() {reset();});
112 double controller_frequency;
113 getParentParam(controller_frequency,
"controller_frequency", 0.0, ParameterType::Static);
114 setOffset(controller_frequency);
119 const double controller_period = 1.0 / controller_frequency;
120 constexpr
double eps = 1e-6;
122 if ((controller_period + eps) < settings_.model_dt) {
125 "Controller period is less then model dt, consider setting it equal");
126 }
else if (abs(controller_period - settings_.model_dt) < eps) {
129 "Controller period is equal to model dt. Control sequence "
131 settings_.shift_control_sequence =
true;
134 "Controller period more then model dt, set it equal to model dt");
140 state_.reset(settings_.batch_size, settings_.time_steps);
141 control_sequence_.reset(settings_.time_steps);
142 control_history_[0] = {0.0f, 0.0f, 0.0f};
143 control_history_[1] = {0.0f, 0.0f, 0.0f};
144 control_history_[2] = {0.0f, 0.0f, 0.0f};
145 control_history_[3] = {0.0f, 0.0f, 0.0f};
147 if (settings_.open_loop) {
148 last_command_vel_ = geometry_msgs::msg::Twist();
151 if (reset_dynamic_speed_limits) {
152 settings_.constraints = settings_.base_constraints;
155 costs_ = xt::zeros<float>({settings_.batch_size});
156 generated_trajectories_.reset(settings_.batch_size, settings_.time_steps);
158 noise_generator_.reset(settings_, isHolonomic());
159 motion_model_->initialize(settings_.constraints, settings_.model_dt);
161 RCLCPP_INFO(logger_,
"Optimizer reset");
166 return motion_model_->isHolonomic();
170 const geometry_msgs::msg::PoseStamped & robot_pose,
171 const geometry_msgs::msg::Twist & robot_speed,
172 const nav_msgs::msg::Path & plan,
173 const geometry_msgs::msg::Pose & goal,
176 prepare(robot_pose, robot_speed, plan, goal, goal_checker);
180 }
while (fallback(critics_data_.fail_flag));
182 utils::savitskyGolayFilter(control_sequence_, control_history_, settings_);
183 auto control = getControlFromSequenceAsTwist(plan.header.stamp);
185 last_command_vel_ = control.twist;
187 if (settings_.shift_control_sequence) {
188 shiftControlSequence();
196 for (
size_t i = 0; i < settings_.iteration_count; ++i) {
197 generateNoisedTrajectories();
198 critic_manager_.evalTrajectoriesScores(critics_data_);
199 updateControlSequence();
205 static size_t counter = 0;
214 if (++counter > settings_.retry_attempt_limit) {
223 const geometry_msgs::msg::PoseStamped & robot_pose,
224 const geometry_msgs::msg::Twist & robot_speed,
225 const nav_msgs::msg::Path & plan,
226 const geometry_msgs::msg::Pose & goal,
229 state_.pose = robot_pose;
230 state_.speed = settings_.open_loop ? last_command_vel_ : robot_speed;
231 path_ = utils::toTensor(plan);
235 critics_data_.fail_flag =
false;
236 critics_data_.goal_checker = goal_checker;
237 critics_data_.motion_model = motion_model_;
238 critics_data_.furthest_reached_path_point.reset();
239 critics_data_.path_pts_valid.reset();
244 using namespace xt::placeholders;
245 control_sequence_.vx = xt::roll(control_sequence_.vx, -1);
246 control_sequence_.wz = xt::roll(control_sequence_.wz, -1);
249 xt::view(control_sequence_.vx, -1) =
250 xt::view(control_sequence_.vx, -2);
252 xt::view(control_sequence_.wz, -1) =
253 xt::view(control_sequence_.wz, -2);
257 control_sequence_.vy = xt::roll(control_sequence_.vy, -1);
258 xt::view(control_sequence_.vy, -1) =
259 xt::view(control_sequence_.vy, -2);
265 noise_generator_.setNoisedControls(state_, control_sequence_);
266 noise_generator_.generateNextNoises();
267 updateStateVelocities(state_);
268 integrateStateVelocities(generated_trajectories_, state_);
273 auto & s = settings_;
276 control_sequence_.vy = xt::clip(control_sequence_.vy, -s.constraints.vy, s.constraints.vy);
279 control_sequence_.vx = xt::clip(control_sequence_.vx, s.constraints.vx_min, s.constraints.vx_max);
280 control_sequence_.wz = xt::clip(control_sequence_.wz, -s.constraints.wz, s.constraints.wz);
282 float max_delta_vx = s.model_dt * s.constraints.ax_max;
283 float min_delta_vx = s.model_dt * s.constraints.ax_min;
284 float max_delta_vy = s.model_dt * s.constraints.ay_max;
285 float min_delta_vy = s.model_dt * s.constraints.ay_min;
286 float max_delta_wz = s.model_dt * s.constraints.az_max;
287 float vx_last = control_sequence_.vx(0);
288 float vy_last = control_sequence_.vy(0);
289 float wz_last = control_sequence_.wz(0);
290 for (
unsigned int i = 1; i != control_sequence_.vx.shape(0); i++) {
291 float & vx_curr = control_sequence_.vx(i);
293 vx_curr = std::clamp(vx_curr, vx_last + min_delta_vx, vx_last + max_delta_vx);
295 vx_curr = std::clamp(vx_curr, vx_last - max_delta_vx, vx_last - min_delta_vx);
299 float & wz_curr = control_sequence_.wz(i);
300 wz_curr = std::clamp(wz_curr, wz_last - max_delta_wz, wz_last + max_delta_wz);
304 float & vy_curr = control_sequence_.vy(i);
306 vy_curr = std::clamp(vy_curr, vy_last + min_delta_vy, vy_last + max_delta_vy);
308 vy_curr = std::clamp(vy_curr, vy_last - max_delta_vy, vy_last - min_delta_vy);
314 motion_model_->applyConstraints(control_sequence_);
320 updateInitialStateVelocities(state);
321 propagateStateVelocitiesFromInitials(state);
327 xt::noalias(xt::view(state.vx, xt::all(), 0)) =
static_cast<float>(state.speed.linear.x);
328 xt::noalias(xt::view(state.wz, xt::all(), 0)) =
static_cast<float>(state.speed.angular.z);
331 xt::noalias(xt::view(state.vy, xt::all(), 0)) =
static_cast<float>(state.speed.linear.y);
338 motion_model_->predict(state);
342 xt::xtensor<float, 2> & trajectory,
343 const xt::xtensor<float, 2> & sequence)
const
345 float initial_yaw =
static_cast<float>(tf2::getYaw(state_.pose.pose.orientation));
347 const auto vx = xt::view(sequence, xt::all(), 0);
348 const auto wz = xt::view(sequence, xt::all(), 1);
350 auto traj_x = xt::view(trajectory, xt::all(), 0);
351 auto traj_y = xt::view(trajectory, xt::all(), 1);
352 auto traj_yaws = xt::view(trajectory, xt::all(), 2);
354 xt::noalias(traj_yaws) = xt::cumsum(wz * settings_.model_dt, 0) + initial_yaw;
356 auto yaw_cos = xt::roll(xt::eval(xt::cos(traj_yaws)), 1);
357 auto yaw_sin = xt::roll(xt::eval(xt::sin(traj_yaws)), 1);
358 xt::view(yaw_cos, 0) = cosf(initial_yaw);
359 xt::view(yaw_sin, 0) = sinf(initial_yaw);
361 auto && dx = xt::eval(vx * yaw_cos);
362 auto && dy = xt::eval(vx * yaw_sin);
365 const auto vy = xt::view(sequence, xt::all(), 2);
366 dx = dx - vy * yaw_sin;
367 dy = dy + vy * yaw_cos;
370 xt::noalias(traj_x) = state_.pose.pose.position.x + xt::cumsum(dx * settings_.model_dt, 0);
371 xt::noalias(traj_y) = state_.pose.pose.position.y + xt::cumsum(dy * settings_.model_dt, 0);
378 const float initial_yaw =
static_cast<float>(tf2::getYaw(state.pose.pose.orientation));
380 xt::noalias(trajectories.yaws) =
381 xt::cumsum(state.wz * settings_.model_dt, {1}) + initial_yaw;
383 auto yaw_cos = xt::roll(xt::eval(xt::cos(trajectories.yaws)), 1, 1);
384 auto yaw_sin = xt::roll(xt::eval(xt::sin(trajectories.yaws)), 1, 1);
385 xt::view(yaw_cos, xt::all(), 0) = cosf(initial_yaw);
386 xt::view(yaw_sin, xt::all(), 0) = sinf(initial_yaw);
388 auto && dx = xt::eval(state.vx * yaw_cos);
389 auto && dy = xt::eval(state.vx * yaw_sin);
392 dx = dx - state.vy * yaw_sin;
393 dy = dy + state.vy * yaw_cos;
396 xt::noalias(trajectories.x) = state.pose.pose.position.x +
397 xt::cumsum(dx * settings_.model_dt, {1});
398 xt::noalias(trajectories.y) = state.pose.pose.position.y +
399 xt::cumsum(dy * settings_.model_dt, {1});
404 const bool is_holo = isHolonomic();
406 xt::xtensor<float, 2>::from_shape({settings_.time_steps, is_holo ? 3u : 2u});
407 auto && trajectories = xt::xtensor<float, 2>::from_shape({settings_.time_steps, 3});
409 xt::noalias(xt::view(sequence, xt::all(), 0)) = control_sequence_.vx;
410 xt::noalias(xt::view(sequence, xt::all(), 1)) = control_sequence_.wz;
413 xt::noalias(xt::view(sequence, xt::all(), 2)) = control_sequence_.vy;
416 integrateStateVelocities(trajectories, sequence);
417 return std::move(trajectories);
422 const bool is_holo = isHolonomic();
423 auto & s = settings_;
424 auto bounded_noises_vx = state_.cvx - control_sequence_.vx;
425 auto bounded_noises_wz = state_.cwz - control_sequence_.wz;
426 xt::noalias(costs_) +=
427 s.gamma / powf(s.sampling_std.vx, 2) * xt::sum(
428 xt::view(control_sequence_.vx, xt::newaxis(), xt::all()) * bounded_noises_vx, 1, immediate);
429 xt::noalias(costs_) +=
430 s.gamma / powf(s.sampling_std.wz, 2) * xt::sum(
431 xt::view(control_sequence_.wz, xt::newaxis(), xt::all()) * bounded_noises_wz, 1, immediate);
434 auto bounded_noises_vy = state_.cvy - control_sequence_.vy;
435 xt::noalias(costs_) +=
436 s.gamma / powf(s.sampling_std.vy, 2) * xt::sum(
437 xt::view(control_sequence_.vy, xt::newaxis(), xt::all()) * bounded_noises_vy,
441 auto && costs_normalized = costs_ - xt::amin(costs_, immediate);
442 auto && exponents = xt::eval(xt::exp(-1 / settings_.temperature * costs_normalized));
443 auto && softmaxes = xt::eval(exponents / xt::sum(exponents, immediate));
444 auto && softmaxes_extened = xt::eval(xt::view(softmaxes, xt::all(), xt::newaxis()));
446 xt::noalias(control_sequence_.vx) = xt::sum(state_.cvx * softmaxes_extened, 0, immediate);
447 xt::noalias(control_sequence_.wz) = xt::sum(state_.cwz * softmaxes_extened, 0, immediate);
449 xt::noalias(control_sequence_.vy) = xt::sum(state_.cvy * softmaxes_extened, 0, immediate);
452 applyControlSequenceConstraints();
456 const builtin_interfaces::msg::Time & stamp)
458 unsigned int offset = settings_.shift_control_sequence ? 1 : 0;
460 auto vx = control_sequence_.vx(offset);
461 auto wz = control_sequence_.wz(offset);
464 auto vy = control_sequence_.vy(offset);
465 return utils::toTwistStamped(vx, vy, wz, stamp, costmap_ros_->getBaseFrameID());
468 return utils::toTwistStamped(vx, wz, stamp, costmap_ros_->getBaseFrameID());
473 if (model ==
"DiffDrive") {
474 motion_model_ = std::make_shared<DiffDriveMotionModel>();
475 }
else if (model ==
"Omni") {
476 motion_model_ = std::make_shared<OmniMotionModel>();
477 }
else if (model ==
"Ackermann") {
478 motion_model_ = std::make_shared<AckermannMotionModel>(parameters_handler_, name_);
482 "Model " + model +
" is not valid! Valid options are DiffDrive, Omni, "
485 motion_model_->initialize(settings_.constraints, settings_.model_dt);
490 auto & s = settings_;
491 if (speed_limit == nav2_costmap_2d::NO_SPEED_LIMIT) {
492 s.constraints.vx_max = s.base_constraints.vx_max;
493 s.constraints.vx_min = s.base_constraints.vx_min;
494 s.constraints.vy = s.base_constraints.vy;
495 s.constraints.wz = s.base_constraints.wz;
499 double ratio = speed_limit / 100.0;
500 s.constraints.vx_max = s.base_constraints.vx_max * ratio;
501 s.constraints.vx_min = s.base_constraints.vx_min * ratio;
502 s.constraints.vy = s.base_constraints.vy * ratio;
503 s.constraints.wz = s.base_constraints.wz * ratio;
506 double ratio = speed_limit / s.base_constraints.vx_max;
507 s.constraints.vx_max = s.base_constraints.vx_max * ratio;
508 s.constraints.vx_min = s.base_constraints.vx_min * ratio;
509 s.constraints.vy = s.base_constraints.vy * ratio;
510 s.constraints.wz = s.base_constraints.wz * ratio;
513 motion_model_->initialize(settings_.constraints, settings_.model_dt);
518 return generated_trajectories_;
geometry_msgs::msg::TwistStamped evalControl(const geometry_msgs::msg::PoseStamped &robot_pose, const geometry_msgs::msg::Twist &robot_speed, const nav_msgs::msg::Path &plan, const geometry_msgs::msg::Pose &goal, nav2_core::GoalChecker *goal_checker)
Compute control using MPPI algorithm.
void updateStateVelocities(models::State &state) const
Update velocities in state.
void setMotionModel(const std::string &model)
Set the motion model of the vehicle platform.
void setOffset(double controller_frequency)
Using control frequence and time step size, determine if trajectory offset should be used to populate...
void reset(bool reset_dynamic_speed_limits=true)
Reset the optimization problem to initial conditions.
void prepare(const geometry_msgs::msg::PoseStamped &robot_pose, const geometry_msgs::msg::Twist &robot_speed, const nav_msgs::msg::Path &plan, const geometry_msgs::msg::Pose &goal, nav2_core::GoalChecker *goal_checker)
Prepare state information on new request for trajectory rollouts.
void integrateStateVelocities(models::Trajectories &trajectories, const models::State &state) const
Rollout velocities in state to poses.
void updateControlSequence()
Update control sequence with state controls weighted by costs using softmax function.
void generateNoisedTrajectories()
updates generated trajectories with noised trajectories from the last cycle's optimal control
bool fallback(bool fail)
Perform fallback behavior to try to recover from a set of trajectories in collision.
bool isHolonomic() const
Whether the motion model is holonomic.
models::Trajectories & getGeneratedTrajectories()
Get the trajectories generated in a cycle for visualization.
void updateInitialStateVelocities(models::State &state) const
Update initial velocity in state.
xt::xtensor< float, 2 > getOptimizedTrajectory()
Get the optimal trajectory for a cycle for visualization.
void shutdown()
Shutdown for optimizer at process end.
void optimize()
Main function to generate, score, and return trajectories.
void setSpeedLimit(double speed_limit, bool percentage)
Set the maximum speed based on the speed limits callback.
void shiftControlSequence()
Shift the optimal control sequence after processing for next iterations initial conditions after exec...
void applyControlSequenceConstraints()
Apply hard vehicle constraints on control sequence.
void getParams()
Obtain the main controller's parameters.
void propagateStateVelocitiesFromInitials(models::State &state) const
predict velocities in state using model for time horizon equal to timesteps
void initialize(rclcpp_lifecycle::LifecycleNode::WeakPtr parent, const std::string &name, std::shared_ptr< nav2_costmap_2d::Costmap2DROS > costmap_ros, ParametersHandler *dynamic_parameters_handler)
Initializes optimizer on startup.
geometry_msgs::msg::TwistStamped getControlFromSequenceAsTwist(const builtin_interfaces::msg::Time &stamp)
Convert control sequence to a twist commant.
Handles getting parameters and dynamic parmaeter changes.
Function-object for checking whether a goal has been reached.
State information: velocities, controls, poses, speed.