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/duration.hpp"
39 #include "rclcpp/time.hpp"
40 #include "rclcpp_lifecycle/lifecycle_node.hpp"
42 #include "nav2_ros_common/node_utils.hpp"
43 #include "nav2_core/goal_checker.hpp"
45 #include "nav2_mppi_controller/models/optimizer_settings.hpp"
46 #include "nav2_mppi_controller/models/control_sequence.hpp"
47 #include "nav2_mppi_controller/models/path.hpp"
48 #include "builtin_interfaces/msg/time.hpp"
49 #include "nav2_mppi_controller/critic_data.hpp"
51 #define M_PIF 3.141592653589793238462643383279502884e+00F
52 #define M_PIF_2 1.5707963267948966e+00F
63 inline geometry_msgs::msg::Pose createPose(
double x,
double y,
double z)
65 geometry_msgs::msg::Pose pose;
69 pose.orientation.w = 1;
70 pose.orientation.x = 0;
71 pose.orientation.y = 0;
72 pose.orientation.z = 0;
83 inline geometry_msgs::msg::Vector3 createScale(
double x,
double y,
double z)
85 geometry_msgs::msg::Vector3 scale;
100 inline std_msgs::msg::ColorRGBA createColor(
float r,
float g,
float b,
float a)
102 std_msgs::msg::ColorRGBA color;
119 inline visualization_msgs::msg::Marker createMarker(
120 int id,
const geometry_msgs::msg::Pose & pose,
const geometry_msgs::msg::Vector3 & scale,
121 const std_msgs::msg::ColorRGBA & color,
const std::string & frame_id,
const std::string & ns)
123 using visualization_msgs::msg::Marker;
125 marker.header.frame_id = frame_id;
126 marker.header.stamp = rclcpp::Time(0, 0);
129 marker.type = Marker::SPHERE;
130 marker.action = Marker::ADD;
133 marker.scale = scale;
134 marker.color = color;
145 inline geometry_msgs::msg::TwistStamped toTwistStamped(
146 float vx,
float wz,
const builtin_interfaces::msg::Time & stamp,
const std::string & frame)
148 geometry_msgs::msg::TwistStamped twist;
149 twist.header.frame_id = frame;
150 twist.header.stamp = stamp;
151 twist.twist.linear.x = vx;
152 twist.twist.angular.z = wz;
165 inline geometry_msgs::msg::TwistStamped toTwistStamped(
166 float vx,
float vy,
float wz,
const builtin_interfaces::msg::Time & stamp,
167 const std::string & frame)
169 auto twist = toTwistStamped(vx, wz, stamp, frame);
170 twist.twist.linear.y = vy;
175 inline std::unique_ptr<nav_msgs::msg::Trajectory> toTrajectoryMsg(
176 const Eigen::ArrayXXf & trajectory,
177 const models::ControlSequence & control_sequence,
178 const double & model_dt,
179 const std_msgs::msg::Header & header)
181 auto trajectory_msg = std::make_unique<nav_msgs::msg::Trajectory>();
182 trajectory_msg->header = header;
183 trajectory_msg->points.resize(trajectory.rows());
185 for (
int i = 0; i < trajectory.rows(); ++i) {
186 auto & curr_pt = trajectory_msg->points[i];
187 curr_pt.header.frame_id = header.frame_id;
188 curr_pt.header.stamp = header.stamp + rclcpp::Duration::from_seconds(i * model_dt);
189 curr_pt.pose.position.x = trajectory(i, 0);
190 curr_pt.pose.position.y = trajectory(i, 1);
191 tf2::Quaternion quat;
192 quat.setRPY(0.0, 0.0, trajectory(i, 2));
193 curr_pt.pose.orientation = tf2::toMsg(quat);
194 curr_pt.velocity.linear.x = control_sequence.vx(i);
195 curr_pt.velocity.angular.z = control_sequence.wz(i);
196 if (control_sequence.vy.size() > 0) {
197 curr_pt.velocity.linear.y = control_sequence.vy(i);
201 return trajectory_msg;
209 inline models::Path toTensor(
const nav_msgs::msg::Path & path)
211 auto result = models::Path{};
212 result.
reset(path.poses.size());
214 for (
size_t i = 0; i < path.poses.size(); ++i) {
215 result.x(i) = path.poses[i].pose.position.x;
216 result.y(i) = path.poses[i].pose.position.y;
217 result.yaws(i) = tf2::getYaw(path.poses[i].pose.orientation);
228 inline geometry_msgs::msg::Pose getLastPathPose(
const models::Path & path)
230 const unsigned int path_last_idx = path.x.size() - 1;
232 auto last_orientation = path.yaws(path_last_idx);
234 tf2::Quaternion pose_orientation;
235 pose_orientation.setRPY(0.0, 0.0, last_orientation);
237 geometry_msgs::msg::Pose pathPose;
238 pathPose.position.x = path.x(path_last_idx);
239 pathPose.position.y = path.y(path_last_idx);
240 pathPose.orientation.x = pose_orientation.x();
241 pathPose.orientation.y = pose_orientation.y();
242 pathPose.orientation.z = pose_orientation.z();
243 pathPose.orientation.w = pose_orientation.w();
256 auto normalize_angles(
const T & angles)
258 return (angles + M_PIF).unaryExpr(
260 float remainder = std::fmod(x, 2.0f * M_PIF);
261 return remainder < 0.0f ? remainder + M_PIF : remainder - M_PIF;
278 template<
typename F,
typename T>
279 auto shortest_angular_distance(
283 return normalize_angles(to - from);
293 inline size_t findPathFurthestReachedPoint(
const CriticData & data)
295 const int traj_cols = data.trajectories.x.cols();
296 const int n_rows =
static_cast<int>(data.trajectories.x.rows());
297 const int n_cols =
static_cast<int>(data.path.x.size());
300 std::vector<float> path_integrated_dists(n_cols, 0.0f);
301 for (
int i = 1; i < n_cols; ++i) {
302 const float dx = data.path.x(i) - data.path.x(i - 1);
303 const float dy = data.path.y(i) - data.path.y(i - 1);
304 path_integrated_dists[i] = path_integrated_dists[i - 1] + sqrtf(dx * dx + dy * dy);
308 const Eigen::ArrayXf traj_integrated_dists =
309 ((data.trajectories.x.rightCols(traj_cols - 1) -
310 data.trajectories.x.leftCols(traj_cols - 1)).square() +
311 (data.trajectories.y.rightCols(traj_cols - 1) -
312 data.trajectories.y.leftCols(traj_cols - 1)).square())
313 .sqrt().rowwise().sum();
315 const auto & traj_x_end = data.trajectories.x.col(traj_cols - 1);
316 const auto & traj_y_end = data.trajectories.y.col(traj_cols - 1);
319 for (
int i = 0; i < n_rows; ++i) {
320 int max_reachable_idx =
static_cast<int>(
322 path_integrated_dists.begin(), path_integrated_dists.end(),
323 traj_integrated_dists(i)) - path_integrated_dists.begin());
324 max_reachable_idx = std::min(max_reachable_idx, n_cols - 1);
326 if (max_reachable_idx < 0) {
continue;}
329 Eigen::Index eucl_idx;
330 ((data.path.x.head(max_reachable_idx + 1) - traj_x_end(i)).square() +
331 (data.path.y.head(max_reachable_idx + 1) - traj_y_end(i)).square()).minCoeff(&eucl_idx);
333 max_idx = std::max(max_idx,
static_cast<int>(eucl_idx));
336 if (max_idx == n_cols - 1) {
341 return static_cast<size_t>(max_idx);
348 inline void setPathFurthestPointIfNotSet(CriticData & data)
350 if (!data.furthest_reached_path_point) {
351 data.furthest_reached_path_point = findPathFurthestReachedPoint(data);
359 inline void findPathCosts(
361 std::shared_ptr<nav2_costmap_2d::Costmap2DROS> costmap_ros)
363 auto * costmap = costmap_ros->getCostmap();
364 unsigned int map_x, map_y;
365 const size_t path_segments_count = data.path.x.size() - 1;
366 data.path_pts_valid = std::vector<bool>(path_segments_count,
false);
367 const bool tracking_unknown = costmap_ros->getLayeredCostmap()->isTrackingUnknown();
368 for (
unsigned int idx = 0; idx < path_segments_count; idx++) {
369 if (!costmap->worldToMap(data.path.x(idx), data.path.y(idx), map_x, map_y)) {
370 (*data.path_pts_valid)[idx] =
false;
374 switch (costmap->getCost(map_x, map_y)) {
375 case (nav2_costmap_2d::LETHAL_OBSTACLE):
376 (*data.path_pts_valid)[idx] =
false;
378 case (nav2_costmap_2d::INSCRIBED_INFLATED_OBSTACLE):
379 (*data.path_pts_valid)[idx] =
false;
381 case (nav2_costmap_2d::NO_INFORMATION):
382 (*data.path_pts_valid)[idx] = tracking_unknown ?
true :
false;
386 (*data.path_pts_valid)[idx] =
true;
394 inline void setPathCostsIfNotSet(
396 std::shared_ptr<nav2_costmap_2d::Costmap2DROS> costmap_ros)
398 if (!data.path_pts_valid) {
399 findPathCosts(data, costmap_ros);
411 inline float posePointAngle(
412 const geometry_msgs::msg::Pose & pose,
double point_x,
double point_y,
bool forward_preference)
414 float pose_x = pose.position.x;
415 float pose_y = pose.position.y;
416 float pose_yaw = tf2::getYaw(pose.orientation);
418 float yaw = atan2f(point_y - pose_y, point_x - pose_x);
421 if (!forward_preference) {
423 fabs(angles::shortest_angular_distance(yaw, pose_yaw)),
424 fabs(angles::shortest_angular_distance(yaw, angles::normalize_angle(pose_yaw + M_PIF))));
427 return fabs(angles::shortest_angular_distance(yaw, pose_yaw));
438 inline float posePointAngle(
439 const geometry_msgs::msg::Pose & pose,
440 double point_x,
double point_y,
double point_yaw)
442 float pose_x =
static_cast<float>(pose.position.x);
443 float pose_y =
static_cast<float>(pose.position.y);
444 float pose_yaw =
static_cast<float>(tf2::getYaw(pose.orientation));
446 float yaw = atan2f(
static_cast<float>(point_y) - pose_y,
static_cast<float>(point_x) - pose_x);
448 if (fabs(angles::shortest_angular_distance(yaw,
static_cast<float>(point_yaw))) > M_PIF_2) {
449 yaw = angles::normalize_angle(yaw + M_PIF);
452 return fabs(angles::shortest_angular_distance(yaw, pose_yaw));
461 inline void savitskyGolayFilter(
462 models::ControlSequence & control_sequence,
463 std::array<mppi::models::Control, 4> & control_history,
464 const models::OptimizerSettings & settings)
467 Eigen::Array<float, 9, 1> filter;
468 if (settings.sgf_order == 1) {
470 filter = {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f};
474 filter = {-21.0f, 14.0f, 39.0f, 54.0f, 59.0f, 54.0f, 39.0f, 14.0f, -21.0f};
479 const unsigned int num_sequences = control_sequence.vx.size() - 1;
480 if (num_sequences < 20) {
484 auto applyFilter = [&](
const Eigen::Array<float, 9, 1> & data) ->
float {
485 return (data * filter).eval().sum();
488 auto applyFilterOverAxis =
489 [&](Eigen::ArrayXf & sequence,
const Eigen::ArrayXf & initial_sequence,
490 const float hist_0,
const float hist_1,
const float hist_2,
const float hist_3) ->
void
492 float pt_m4 = hist_0;
493 float pt_m3 = hist_1;
494 float pt_m2 = hist_2;
495 float pt_m1 = hist_3;
496 float pt = initial_sequence(0);
497 float pt_p1 = initial_sequence(1);
498 float pt_p2 = initial_sequence(2);
499 float pt_p3 = initial_sequence(3);
500 float pt_p4 = initial_sequence(4);
502 for (
unsigned int idx = 0; idx != num_sequences; idx++) {
503 sequence(idx) = applyFilter({pt_m4, pt_m3, pt_m2, pt_m1, pt, pt_p1, pt_p2, pt_p3, pt_p4});
513 if (idx + 5 < num_sequences) {
514 pt_p4 = initial_sequence(idx + 5);
517 pt_p4 = initial_sequence(num_sequences);
523 const models::ControlSequence initial_control_sequence = control_sequence;
525 control_sequence.vx, initial_control_sequence.vx, control_history[0].vx,
526 control_history[1].vx, control_history[2].vx, control_history[3].vx);
528 control_sequence.vy, initial_control_sequence.vy, control_history[0].vy,
529 control_history[1].vy, control_history[2].vy, control_history[3].vy);
531 control_sequence.wz, initial_control_sequence.wz, control_history[0].wz,
532 control_history[1].wz, control_history[2].wz, control_history[3].wz);
535 unsigned int offset = settings.shift_control_sequence ? 1 : 0;
536 control_history[0] = control_history[1];
537 control_history[1] = control_history[2];
538 control_history[2] = control_history[3];
539 control_history[3] = {
540 control_sequence.vx(offset),
541 control_sequence.vy(offset),
542 control_sequence.wz(offset)};
551 inline unsigned int findClosestPathPt(
552 const std::vector<float> & vec,
const float dist,
const unsigned int init = 0u)
554 float distim1 = init != 0u ? vec[init] : 0.0f;
556 const unsigned int size = vec.size();
557 for (
unsigned int i = init + 1; i != size; i++) {
560 if (i > 0 && dist - distim1 < disti - dist) {
583 inline void shiftColumnsByOnePlace(Eigen::Ref<Eigen::ArrayXXf> e,
int direction)
586 if (size == 1) {
return;}
587 if (abs(direction) != 1) {
588 throw std::logic_error(
"Invalid direction, only 1 and -1 are valid values.");
591 if ((e.cols() == 1 || e.rows() == 1) && size > 1) {
592 auto start_ptr = direction == 1 ? e.data() + size - 2 : e.data() + 1;
593 auto end_ptr = direction == 1 ? e.data() : e.data() + size - 1;
594 while (start_ptr != end_ptr) {
595 *(start_ptr + direction) = *start_ptr;
596 start_ptr -= direction;
598 *(start_ptr + direction) = *start_ptr;
600 auto start_ptr = direction == 1 ? e.data() + size - 2 * e.rows() : e.data() + e.rows();
601 auto end_ptr = direction == 1 ? e.data() : e.data() + size - e.rows();
602 auto span = e.rows();
603 while (start_ptr != end_ptr) {
604 std::copy(start_ptr, start_ptr + span, start_ptr + direction * span);
605 start_ptr -= (direction * span);
607 std::copy(start_ptr, start_ptr + span, start_ptr + direction * span);
618 inline auto normalize_yaws_between_points(
619 const Eigen::Ref<const Eigen::ArrayXf> & last_yaws,
620 const Eigen::Ref<const Eigen::ArrayXf> & yaw_between_points)
622 Eigen::ArrayXf yaws = utils::shortest_angular_distance(
623 last_yaws, yaw_between_points).abs();
624 int size = yaws.size();
625 Eigen::ArrayXf yaws_between_points_corrected(size);
626 for (
int i = 0; i != size; i++) {
627 const float & yaw_between_point = yaw_between_points[i];
628 yaws_between_points_corrected[i] = yaws[i] < M_PIF_2 ?
629 yaw_between_point : angles::normalize_angle(yaw_between_point + M_PIF);
631 return yaws_between_points_corrected;
640 inline auto normalize_yaws_between_points(
641 const float goal_yaw,
const Eigen::Ref<const Eigen::ArrayXf> & yaw_between_points)
643 int size = yaw_between_points.size();
644 Eigen::ArrayXf yaws_between_points_corrected(size);
645 for (
int i = 0; i != size; i++) {
646 const float & yaw_between_point = yaw_between_points[i];
647 yaws_between_points_corrected[i] = fabs(
648 angles::normalize_angle(yaw_between_point - goal_yaw)) < M_PIF_2 ?
649 yaw_between_point : angles::normalize_angle(yaw_between_point + M_PIF);
651 return yaws_between_points_corrected;
661 const float lower_bound,
const float upper_bound,
const float input)
663 return std::min(upper_bound, std::max(input, lower_bound));
675 inline float clampVelocityByAccel(
676 const float last_vel,
const float curr_vel,
677 const float min_delta,
const float max_delta)
680 return clamp(last_vel + min_delta, last_vel + max_delta, curr_vel);
682 return clamp(last_vel - max_delta, last_vel - min_delta, curr_vel);
void reset(unsigned int size)
Reset path data.