16 #ifndef NAV2_MPPI_CONTROLLER__TOOLS__UTILS_HPP_
17 #define NAV2_MPPI_CONTROLLER__TOOLS__UTILS_HPP_
19 #include <Eigen/Dense>
28 #include "angles/angles.h"
30 #include "tf2/utils.hpp"
31 #include "tf2_geometry_msgs/tf2_geometry_msgs.hpp"
33 #include "geometry_msgs/msg/twist_stamped.hpp"
34 #include "nav_msgs/msg/path.hpp"
35 #include "nav_msgs/msg/trajectory.hpp"
36 #include "visualization_msgs/msg/marker_array.hpp"
38 #include "rclcpp/rclcpp.hpp"
39 #include "rclcpp_lifecycle/lifecycle_node.hpp"
41 #include "nav2_ros_common/node_utils.hpp"
42 #include "nav2_core/goal_checker.hpp"
44 #include "nav2_mppi_controller/models/optimizer_settings.hpp"
45 #include "nav2_mppi_controller/models/control_sequence.hpp"
46 #include "nav2_mppi_controller/models/path.hpp"
47 #include "builtin_interfaces/msg/time.hpp"
48 #include "nav2_mppi_controller/critic_data.hpp"
50 #define M_PIF 3.141592653589793238462643383279502884e+00F
51 #define M_PIF_2 1.5707963267948966e+00F
62 inline geometry_msgs::msg::Pose createPose(
double x,
double y,
double z)
64 geometry_msgs::msg::Pose pose;
68 pose.orientation.w = 1;
69 pose.orientation.x = 0;
70 pose.orientation.y = 0;
71 pose.orientation.z = 0;
82 inline geometry_msgs::msg::Vector3 createScale(
double x,
double y,
double z)
84 geometry_msgs::msg::Vector3 scale;
99 inline std_msgs::msg::ColorRGBA createColor(
float r,
float g,
float b,
float a)
101 std_msgs::msg::ColorRGBA color;
118 inline visualization_msgs::msg::Marker createMarker(
119 int id,
const geometry_msgs::msg::Pose & pose,
const geometry_msgs::msg::Vector3 & scale,
120 const std_msgs::msg::ColorRGBA & color,
const std::string & frame_id,
const std::string & ns)
122 using visualization_msgs::msg::Marker;
124 marker.header.frame_id = frame_id;
125 marker.header.stamp = rclcpp::Time(0, 0);
128 marker.type = Marker::SPHERE;
129 marker.action = Marker::ADD;
132 marker.scale = scale;
133 marker.color = color;
144 inline geometry_msgs::msg::TwistStamped toTwistStamped(
145 float vx,
float wz,
const builtin_interfaces::msg::Time & stamp,
const std::string & frame)
147 geometry_msgs::msg::TwistStamped twist;
148 twist.header.frame_id = frame;
149 twist.header.stamp = stamp;
150 twist.twist.linear.x = vx;
151 twist.twist.angular.z = wz;
164 inline geometry_msgs::msg::TwistStamped toTwistStamped(
165 float vx,
float vy,
float wz,
const builtin_interfaces::msg::Time & stamp,
166 const std::string & frame)
168 auto twist = toTwistStamped(vx, wz, stamp, frame);
169 twist.twist.linear.y = vy;
174 inline std::unique_ptr<nav_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)
180 auto trajectory_msg = std::make_unique<nav_msgs::msg::Trajectory>();
181 trajectory_msg->header = header;
182 trajectory_msg->points.resize(trajectory.rows());
184 for (
int i = 0; i < trajectory.rows(); ++i) {
185 auto & curr_pt = trajectory_msg->points[i];
186 curr_pt.header.frame_id = header.frame_id;
187 curr_pt.header.stamp = header.stamp + rclcpp::Duration::from_seconds(i * model_dt);
188 curr_pt.pose.position.x = trajectory(i, 0);
189 curr_pt.pose.position.y = trajectory(i, 1);
190 tf2::Quaternion quat;
191 quat.setRPY(0.0, 0.0, trajectory(i, 2));
192 curr_pt.pose.orientation = tf2::toMsg(quat);
193 curr_pt.velocity.linear.x = control_sequence.vx(i);
194 curr_pt.velocity.angular.z = control_sequence.wz(i);
195 if (control_sequence.vy.size() > 0) {
196 curr_pt.velocity.linear.y = control_sequence.vy(i);
200 return trajectory_msg;
208 inline models::Path toTensor(
const nav_msgs::msg::Path & path)
210 auto result = models::Path{};
211 result.
reset(path.poses.size());
213 for (
size_t i = 0; i < path.poses.size(); ++i) {
214 result.x(i) = path.poses[i].pose.position.x;
215 result.y(i) = path.poses[i].pose.position.y;
216 result.yaws(i) = tf2::getYaw(path.poses[i].pose.orientation);
227 inline geometry_msgs::msg::Pose getLastPathPose(
const models::Path & path)
229 const unsigned int path_last_idx = path.x.size() - 1;
231 auto last_orientation = path.yaws(path_last_idx);
233 tf2::Quaternion pose_orientation;
234 pose_orientation.setRPY(0.0, 0.0, last_orientation);
236 geometry_msgs::msg::Pose pathPose;
237 pathPose.position.x = path.x(path_last_idx);
238 pathPose.position.y = path.y(path_last_idx);
239 pathPose.orientation.x = pose_orientation.x();
240 pathPose.orientation.y = pose_orientation.y();
241 pathPose.orientation.z = pose_orientation.z();
242 pathPose.orientation.w = pose_orientation.w();
255 auto normalize_angles(
const T & angles)
257 return (angles + M_PIF).unaryExpr(
259 float remainder = std::fmod(x, 2.0f * M_PIF);
260 return remainder < 0.0f ? remainder + M_PIF : remainder - M_PIF;
277 template<
typename F,
typename T>
278 auto shortest_angular_distance(
282 return normalize_angles(to - from);
292 inline size_t findPathFurthestReachedPoint(
const CriticData & data)
294 const int traj_cols = data.trajectories.x.cols();
295 const int n_rows =
static_cast<int>(data.trajectories.x.rows());
296 const int n_cols =
static_cast<int>(data.path.x.size());
299 std::vector<float> path_integrated_dists(n_cols, 0.0f);
300 for (
int i = 1; i < n_cols; ++i) {
301 const float dx = data.path.x(i) - data.path.x(i - 1);
302 const float dy = data.path.y(i) - data.path.y(i - 1);
303 path_integrated_dists[i] = path_integrated_dists[i - 1] + sqrtf(dx * dx + dy * dy);
307 const Eigen::ArrayXf traj_integrated_dists =
308 ((data.trajectories.x.rightCols(traj_cols - 1) -
309 data.trajectories.x.leftCols(traj_cols - 1)).square() +
310 (data.trajectories.y.rightCols(traj_cols - 1) -
311 data.trajectories.y.leftCols(traj_cols - 1)).square())
312 .sqrt().rowwise().sum();
314 const auto & traj_x_end = data.trajectories.x.col(traj_cols - 1);
315 const auto & traj_y_end = data.trajectories.y.col(traj_cols - 1);
318 for (
int i = 0; i < n_rows; ++i) {
319 int max_reachable_idx =
static_cast<int>(
321 path_integrated_dists.begin(), path_integrated_dists.end(),
322 traj_integrated_dists(i)) - path_integrated_dists.begin());
323 max_reachable_idx = std::min(max_reachable_idx, n_cols - 1);
326 Eigen::Index eucl_idx;
327 ((data.path.x.head(max_reachable_idx + 1) - traj_x_end(i)).square() +
328 (data.path.y.head(max_reachable_idx + 1) - traj_y_end(i)).square()).minCoeff(&eucl_idx);
330 max_idx = std::max(max_idx,
static_cast<int>(eucl_idx));
333 if (max_idx == n_cols - 1) {
338 return static_cast<size_t>(max_idx);
345 inline void setPathFurthestPointIfNotSet(CriticData & data)
347 if (!data.furthest_reached_path_point) {
348 data.furthest_reached_path_point = findPathFurthestReachedPoint(data);
356 inline void findPathCosts(
358 std::shared_ptr<nav2_costmap_2d::Costmap2DROS> costmap_ros)
360 auto * costmap = costmap_ros->getCostmap();
361 unsigned int map_x, map_y;
362 const size_t path_segments_count = data.path.x.size() - 1;
363 data.path_pts_valid = std::vector<bool>(path_segments_count,
false);
364 const bool tracking_unknown = costmap_ros->getLayeredCostmap()->isTrackingUnknown();
365 for (
unsigned int idx = 0; idx < path_segments_count; idx++) {
366 if (!costmap->worldToMap(data.path.x(idx), data.path.y(idx), map_x, map_y)) {
367 (*data.path_pts_valid)[idx] =
false;
371 switch (costmap->getCost(map_x, map_y)) {
372 case (nav2_costmap_2d::LETHAL_OBSTACLE):
373 (*data.path_pts_valid)[idx] =
false;
375 case (nav2_costmap_2d::INSCRIBED_INFLATED_OBSTACLE):
376 (*data.path_pts_valid)[idx] =
false;
378 case (nav2_costmap_2d::NO_INFORMATION):
379 (*data.path_pts_valid)[idx] = tracking_unknown ?
true :
false;
383 (*data.path_pts_valid)[idx] =
true;
391 inline void setPathCostsIfNotSet(
393 std::shared_ptr<nav2_costmap_2d::Costmap2DROS> costmap_ros)
395 if (!data.path_pts_valid) {
396 findPathCosts(data, costmap_ros);
408 inline float posePointAngle(
409 const geometry_msgs::msg::Pose & pose,
double point_x,
double point_y,
bool forward_preference)
411 float pose_x = pose.position.x;
412 float pose_y = pose.position.y;
413 float pose_yaw = tf2::getYaw(pose.orientation);
415 float yaw = atan2f(point_y - pose_y, point_x - pose_x);
418 if (!forward_preference) {
420 fabs(angles::shortest_angular_distance(yaw, pose_yaw)),
421 fabs(angles::shortest_angular_distance(yaw, angles::normalize_angle(pose_yaw + M_PIF))));
424 return fabs(angles::shortest_angular_distance(yaw, pose_yaw));
435 inline float posePointAngle(
436 const geometry_msgs::msg::Pose & pose,
437 double point_x,
double point_y,
double point_yaw)
439 float pose_x =
static_cast<float>(pose.position.x);
440 float pose_y =
static_cast<float>(pose.position.y);
441 float pose_yaw =
static_cast<float>(tf2::getYaw(pose.orientation));
443 float yaw = atan2f(
static_cast<float>(point_y) - pose_y,
static_cast<float>(point_x) - pose_x);
445 if (fabs(angles::shortest_angular_distance(yaw,
static_cast<float>(point_yaw))) > M_PIF_2) {
446 yaw = angles::normalize_angle(yaw + M_PIF);
449 return fabs(angles::shortest_angular_distance(yaw, pose_yaw));
458 inline void savitskyGolayFilter(
459 models::ControlSequence & control_sequence,
460 std::array<mppi::models::Control, 4> & control_history,
461 const models::OptimizerSettings & settings)
464 Eigen::Array<float, 9, 1> filter;
465 if (settings.sgf_order == 1) {
467 filter = {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f};
471 filter = {-21.0f, 14.0f, 39.0f, 54.0f, 59.0f, 54.0f, 39.0f, 14.0f, -21.0f};
476 const unsigned int num_sequences = control_sequence.vx.size() - 1;
477 if (num_sequences < 20) {
481 auto applyFilter = [&](
const Eigen::Array<float, 9, 1> & data) ->
float {
482 return (data * filter).eval().sum();
485 auto applyFilterOverAxis =
486 [&](Eigen::ArrayXf & sequence,
const Eigen::ArrayXf & initial_sequence,
487 const float hist_0,
const float hist_1,
const float hist_2,
const float hist_3) ->
void
489 float pt_m4 = hist_0;
490 float pt_m3 = hist_1;
491 float pt_m2 = hist_2;
492 float pt_m1 = hist_3;
493 float pt = initial_sequence(0);
494 float pt_p1 = initial_sequence(1);
495 float pt_p2 = initial_sequence(2);
496 float pt_p3 = initial_sequence(3);
497 float pt_p4 = initial_sequence(4);
499 for (
unsigned int idx = 0; idx != num_sequences; idx++) {
500 sequence(idx) = applyFilter({pt_m4, pt_m3, pt_m2, pt_m1, pt, pt_p1, pt_p2, pt_p3, pt_p4});
510 if (idx + 5 < num_sequences) {
511 pt_p4 = initial_sequence(idx + 5);
514 pt_p4 = initial_sequence(num_sequences);
520 const models::ControlSequence initial_control_sequence = control_sequence;
522 control_sequence.vx, initial_control_sequence.vx, control_history[0].vx,
523 control_history[1].vx, control_history[2].vx, control_history[3].vx);
525 control_sequence.vy, initial_control_sequence.vy, control_history[0].vy,
526 control_history[1].vy, control_history[2].vy, control_history[3].vy);
528 control_sequence.wz, initial_control_sequence.wz, control_history[0].wz,
529 control_history[1].wz, control_history[2].wz, control_history[3].wz);
532 unsigned int offset = settings.shift_control_sequence ? 1 : 0;
533 control_history[0] = control_history[1];
534 control_history[1] = control_history[2];
535 control_history[2] = control_history[3];
536 control_history[3] = {
537 control_sequence.vx(offset),
538 control_sequence.vy(offset),
539 control_sequence.wz(offset)};
548 inline unsigned int findClosestPathPt(
549 const std::vector<float> & vec,
const float dist,
const unsigned int init = 0u)
551 float distim1 = init != 0u ? vec[init] : 0.0f;
553 const unsigned int size = vec.size();
554 for (
unsigned int i = init + 1; i != size; i++) {
557 if (i > 0 && dist - distim1 < disti - dist) {
580 inline void shiftColumnsByOnePlace(Eigen::Ref<Eigen::ArrayXXf> e,
int direction)
583 if (size == 1) {
return;}
584 if (abs(direction) != 1) {
585 throw std::logic_error(
"Invalid direction, only 1 and -1 are valid values.");
588 if ((e.cols() == 1 || e.rows() == 1) && size > 1) {
589 auto start_ptr = direction == 1 ? e.data() + size - 2 : e.data() + 1;
590 auto end_ptr = direction == 1 ? e.data() : e.data() + size - 1;
591 while (start_ptr != end_ptr) {
592 *(start_ptr + direction) = *start_ptr;
593 start_ptr -= direction;
595 *(start_ptr + direction) = *start_ptr;
597 auto start_ptr = direction == 1 ? e.data() + size - 2 * e.rows() : e.data() + e.rows();
598 auto end_ptr = direction == 1 ? e.data() : e.data() + size - e.rows();
599 auto span = e.rows();
600 while (start_ptr != end_ptr) {
601 std::copy(start_ptr, start_ptr + span, start_ptr + direction * span);
602 start_ptr -= (direction * span);
604 std::copy(start_ptr, start_ptr + span, start_ptr + direction * span);
615 inline auto normalize_yaws_between_points(
616 const Eigen::Ref<const Eigen::ArrayXf> & last_yaws,
617 const Eigen::Ref<const Eigen::ArrayXf> & yaw_between_points)
619 Eigen::ArrayXf yaws = utils::shortest_angular_distance(
620 last_yaws, yaw_between_points).abs();
621 int size = yaws.size();
622 Eigen::ArrayXf yaws_between_points_corrected(size);
623 for (
int i = 0; i != size; i++) {
624 const float & yaw_between_point = yaw_between_points[i];
625 yaws_between_points_corrected[i] = yaws[i] < M_PIF_2 ?
626 yaw_between_point : angles::normalize_angle(yaw_between_point + M_PIF);
628 return yaws_between_points_corrected;
637 inline auto normalize_yaws_between_points(
638 const float goal_yaw,
const Eigen::Ref<const Eigen::ArrayXf> & yaw_between_points)
640 int size = yaw_between_points.size();
641 Eigen::ArrayXf yaws_between_points_corrected(size);
642 for (
int i = 0; i != size; i++) {
643 const float & yaw_between_point = yaw_between_points[i];
644 yaws_between_points_corrected[i] = fabs(
645 angles::normalize_angle(yaw_between_point - goal_yaw)) < M_PIF_2 ?
646 yaw_between_point : angles::normalize_angle(yaw_between_point + M_PIF);
648 return yaws_between_points_corrected;
658 const float lower_bound,
const float upper_bound,
const float input)
660 return std::min(upper_bound, std::max(input, lower_bound));
672 inline float clampVelocityByAccel(
673 const float last_vel,
const float curr_vel,
674 const float min_delta,
const float max_delta)
677 return clamp(last_vel + min_delta, last_vel + max_delta, curr_vel);
679 return clamp(last_vel - max_delta, last_vel - min_delta, curr_vel);
void reset(unsigned int size)
Reset path data.