16 #ifndef NAV2_MPPI_CONTROLLER__TOOLS__UTILS_HPP_
17 #define NAV2_MPPI_CONTROLLER__TOOLS__UTILS_HPP_
27 #pragma GCC diagnostic push
28 #pragma GCC diagnostic ignored "-Warray-bounds"
29 #pragma GCC diagnostic ignored "-Wstringop-overflow"
30 #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
31 #include <xtensor/xarray.hpp>
32 #include <xtensor/xnorm.hpp>
33 #include <xtensor/xmath.hpp>
34 #include <xtensor/xview.hpp>
35 #pragma GCC diagnostic pop
37 #include "angles/angles.h"
39 #include "tf2/utils.h"
40 #include "tf2_geometry_msgs/tf2_geometry_msgs.hpp"
42 #include "geometry_msgs/msg/twist_stamped.hpp"
43 #include "nav_msgs/msg/path.hpp"
44 #include "visualization_msgs/msg/marker_array.hpp"
46 #include "rclcpp/rclcpp.hpp"
47 #include "rclcpp_lifecycle/lifecycle_node.hpp"
49 #include "nav2_util/node_utils.hpp"
50 #include "nav2_core/goal_checker.hpp"
52 #include "nav2_mppi_controller/models/optimizer_settings.hpp"
53 #include "nav2_mppi_controller/models/control_sequence.hpp"
54 #include "nav2_mppi_controller/models/path.hpp"
55 #include "builtin_interfaces/msg/time.hpp"
56 #include "nav2_mppi_controller/critic_data.hpp"
58 #define M_PIF 3.141592653589793238462643383279502884e+00F
59 #define M_PIF_2 1.5707963267948966e+00F
63 using xt::evaluation_strategy::immediate;
72 inline geometry_msgs::msg::Pose createPose(
double x,
double y,
double z)
74 geometry_msgs::msg::Pose pose;
78 pose.orientation.w = 1;
79 pose.orientation.x = 0;
80 pose.orientation.y = 0;
81 pose.orientation.z = 0;
92 inline geometry_msgs::msg::Vector3 createScale(
double x,
double y,
double z)
94 geometry_msgs::msg::Vector3 scale;
109 inline std_msgs::msg::ColorRGBA createColor(
float r,
float g,
float b,
float a)
111 std_msgs::msg::ColorRGBA color;
128 inline visualization_msgs::msg::Marker createMarker(
129 int id,
const geometry_msgs::msg::Pose & pose,
const geometry_msgs::msg::Vector3 & scale,
130 const std_msgs::msg::ColorRGBA & color,
const std::string & frame_id,
const std::string & ns)
132 using visualization_msgs::msg::Marker;
134 marker.header.frame_id = frame_id;
135 marker.header.stamp = rclcpp::Time(0, 0);
138 marker.type = Marker::SPHERE;
139 marker.action = Marker::ADD;
142 marker.scale = scale;
143 marker.color = color;
154 inline geometry_msgs::msg::TwistStamped toTwistStamped(
155 float vx,
float wz,
const builtin_interfaces::msg::Time & stamp,
const std::string & frame)
157 geometry_msgs::msg::TwistStamped twist;
158 twist.header.frame_id = frame;
159 twist.header.stamp = stamp;
160 twist.twist.linear.x = vx;
161 twist.twist.angular.z = wz;
174 inline geometry_msgs::msg::TwistStamped toTwistStamped(
175 float vx,
float vy,
float wz,
const builtin_interfaces::msg::Time & stamp,
176 const std::string & frame)
178 auto twist = toTwistStamped(vx, wz, stamp, frame);
179 twist.twist.linear.y = vy;
189 inline models::Path toTensor(
const nav_msgs::msg::Path & path)
191 auto result = models::Path{};
192 result.
reset(path.poses.size());
194 for (
size_t i = 0; i < path.poses.size(); ++i) {
195 result.x(i) = path.poses[i].pose.position.x;
196 result.y(i) = path.poses[i].pose.position.y;
197 result.yaws(i) = tf2::getYaw(path.poses[i].pose.orientation);
210 inline bool withinPositionGoalTolerance(
212 const geometry_msgs::msg::Pose & robot,
213 const geometry_msgs::msg::Pose & goal)
216 geometry_msgs::msg::Pose pose_tolerance;
217 geometry_msgs::msg::Twist velocity_tolerance;
218 goal_checker->
getTolerances(pose_tolerance, velocity_tolerance);
220 const auto pose_tolerance_sq = pose_tolerance.position.x * pose_tolerance.position.x;
222 auto dx = robot.position.x - goal.position.x;
223 auto dy = robot.position.y - goal.position.y;
225 auto dist_sq = dx * dx + dy * dy;
227 if (dist_sq < pose_tolerance_sq) {
242 inline bool withinPositionGoalTolerance(
243 float pose_tolerance,
244 const geometry_msgs::msg::Pose & robot,
245 const geometry_msgs::msg::Pose & goal)
247 const double & dist_sq =
248 std::pow(goal.position.x - robot.position.x, 2) +
249 std::pow(goal.position.y - robot.position.y, 2);
251 const float pose_tolerance_sq = pose_tolerance * pose_tolerance;
253 if (dist_sq < pose_tolerance_sq) {
268 auto normalize_angles(
const T & angles)
270 auto theta = xt::eval(xt::fmod(angles + M_PIF, 2.0f * M_PIF));
271 return xt::eval(xt::where(theta < 0.0f, theta + M_PIF, theta - M_PIF));
287 template<
typename F,
typename T>
288 auto shortest_angular_distance(
292 return normalize_angles(to - from);
301 inline size_t findPathFurthestReachedPoint(
const CriticData & data)
303 const auto traj_x = xt::view(data.trajectories.x, xt::all(), -1, xt::newaxis());
304 const auto traj_y = xt::view(data.trajectories.y, xt::all(), -1, xt::newaxis());
306 const auto dx = data.path.x - traj_x;
307 const auto dy = data.path.y - traj_y;
309 const auto dists = dx * dx + dy * dy;
311 size_t max_id_by_trajectories = 0, min_id_by_path = 0;
312 float min_distance_by_path = std::numeric_limits<float>::max();
314 for (
size_t i = 0; i < dists.shape(0); i++) {
316 min_distance_by_path = std::numeric_limits<float>::max();
317 for (
size_t j = max_id_by_trajectories; j < dists.shape(1); j++) {
318 const float cur_dist = dists(i, j);
319 if (cur_dist < min_distance_by_path) {
320 min_distance_by_path = cur_dist;
324 max_id_by_trajectories = std::max(max_id_by_trajectories, min_id_by_path);
326 return max_id_by_trajectories;
333 inline void setPathFurthestPointIfNotSet(CriticData & data)
335 if (!data.furthest_reached_path_point) {
336 data.furthest_reached_path_point = findPathFurthestReachedPoint(data);
344 inline void findPathCosts(
346 std::shared_ptr<nav2_costmap_2d::Costmap2DROS> costmap_ros)
348 auto * costmap = costmap_ros->getCostmap();
349 unsigned int map_x, map_y;
350 const size_t path_segments_count = data.path.x.shape(0) - 1;
351 data.path_pts_valid = std::vector<bool>(path_segments_count,
false);
352 const bool tracking_unknown = costmap_ros->getLayeredCostmap()->isTrackingUnknown();
353 for (
unsigned int idx = 0; idx < path_segments_count; idx++) {
354 if (!costmap->worldToMap(data.path.x(idx), data.path.y(idx), map_x, map_y)) {
355 (*data.path_pts_valid)[idx] =
false;
359 switch (costmap->getCost(map_x, map_y)) {
360 case (nav2_costmap_2d::LETHAL_OBSTACLE):
361 (*data.path_pts_valid)[idx] =
false;
363 case (nav2_costmap_2d::INSCRIBED_INFLATED_OBSTACLE):
364 (*data.path_pts_valid)[idx] =
false;
366 case (nav2_costmap_2d::NO_INFORMATION):
367 (*data.path_pts_valid)[idx] = tracking_unknown ?
true :
false;
371 (*data.path_pts_valid)[idx] =
true;
379 inline void setPathCostsIfNotSet(
381 std::shared_ptr<nav2_costmap_2d::Costmap2DROS> costmap_ros)
383 if (!data.path_pts_valid) {
384 findPathCosts(data, costmap_ros);
396 inline float posePointAngle(
397 const geometry_msgs::msg::Pose & pose,
double point_x,
double point_y,
bool forward_preference)
399 float pose_x = pose.position.x;
400 float pose_y = pose.position.y;
401 float pose_yaw = tf2::getYaw(pose.orientation);
403 float yaw = atan2f(point_y - pose_y, point_x - pose_x);
406 if (!forward_preference) {
408 fabs(angles::shortest_angular_distance(yaw, pose_yaw)),
409 fabs(angles::shortest_angular_distance(yaw, angles::normalize_angle(pose_yaw + M_PIF))));
412 return fabs(angles::shortest_angular_distance(yaw, pose_yaw));
423 inline float posePointAngle(
424 const geometry_msgs::msg::Pose & pose,
425 double point_x,
double point_y,
double point_yaw)
427 float pose_x =
static_cast<float>(pose.position.x);
428 float pose_y =
static_cast<float>(pose.position.y);
429 float pose_yaw =
static_cast<float>(tf2::getYaw(pose.orientation));
431 float yaw = atan2f(
static_cast<float>(point_y) - pose_y,
static_cast<float>(point_x) - pose_x);
433 if (fabs(angles::shortest_angular_distance(yaw,
static_cast<float>(point_yaw))) > M_PIF_2) {
434 yaw = angles::normalize_angle(yaw + M_PIF);
437 return fabs(angles::shortest_angular_distance(yaw, pose_yaw));
446 inline void savitskyGolayFilter(
447 models::ControlSequence & control_sequence,
448 std::array<mppi::models::Control, 4> & control_history,
449 const models::OptimizerSettings & settings)
452 xt::xarray<float> filter = {-21.0, 14.0, 39.0, 54.0, 59.0, 54.0, 39.0, 14.0, -21.0};
456 const unsigned int num_sequences = control_sequence.vx.shape(0) - 1;
457 if (num_sequences < 20) {
461 auto applyFilter = [&](
const xt::xarray<float> & data) ->
float {
462 return xt::sum(data * filter, {0}, immediate)();
465 auto applyFilterOverAxis =
466 [&](xt::xtensor<float, 1> & sequence,
const xt::xtensor<float, 1> & initial_sequence,
467 const float hist_0,
const float hist_1,
const float hist_2,
const float hist_3) ->
void
469 float pt_m4 = hist_0;
470 float pt_m3 = hist_1;
471 float pt_m2 = hist_2;
472 float pt_m1 = hist_3;
473 float pt = initial_sequence(0);
474 float pt_p1 = initial_sequence(1);
475 float pt_p2 = initial_sequence(2);
476 float pt_p3 = initial_sequence(3);
477 float pt_p4 = initial_sequence(4);
479 for (
unsigned int idx = 0; idx != num_sequences; idx++) {
480 sequence(idx) = applyFilter({pt_m4, pt_m3, pt_m2, pt_m1, pt, pt_p1, pt_p2, pt_p3, pt_p4});
490 if (idx + 5 < num_sequences) {
491 pt_p4 = initial_sequence(idx + 5);
494 pt_p4 = initial_sequence(num_sequences);
500 const models::ControlSequence initial_control_sequence = control_sequence;
502 control_sequence.vx, initial_control_sequence.vx, control_history[0].vx,
503 control_history[1].vx, control_history[2].vx, control_history[3].vx);
505 control_sequence.vy, initial_control_sequence.vy, control_history[0].vy,
506 control_history[1].vy, control_history[2].vy, control_history[3].vy);
508 control_sequence.wz, initial_control_sequence.wz, control_history[0].wz,
509 control_history[1].wz, control_history[2].wz, control_history[3].wz);
512 unsigned int offset = settings.shift_control_sequence ? 1 : 0;
513 control_history[0] = control_history[1];
514 control_history[1] = control_history[2];
515 control_history[2] = control_history[3];
516 control_history[3] = {
517 control_sequence.vx(offset),
518 control_sequence.vy(offset),
519 control_sequence.wz(offset)};
527 inline unsigned int findFirstPathInversion(nav_msgs::msg::Path & path)
530 if (path.poses.size() < 3) {
531 return path.poses.size();
535 for (
unsigned int idx = 1; idx < path.poses.size() - 1; ++idx) {
537 float oa_x = path.poses[idx].pose.position.x -
538 path.poses[idx - 1].pose.position.x;
539 float oa_y = path.poses[idx].pose.position.y -
540 path.poses[idx - 1].pose.position.y;
541 float ab_x = path.poses[idx + 1].pose.position.x -
542 path.poses[idx].pose.position.x;
543 float ab_y = path.poses[idx + 1].pose.position.y -
544 path.poses[idx].pose.position.y;
547 float dot_product = (oa_x * ab_x) + (oa_y * ab_y);
548 if (dot_product < 0.0f) {
553 return path.poses.size();
561 inline unsigned int removePosesAfterFirstInversion(nav_msgs::msg::Path & path)
563 nav_msgs::msg::Path cropped_path = path;
564 const unsigned int first_after_inversion = findFirstPathInversion(cropped_path);
565 if (first_after_inversion == path.poses.size()) {
569 cropped_path.poses.erase(
570 cropped_path.poses.begin() + first_after_inversion, cropped_path.poses.end());
572 return first_after_inversion;
581 inline unsigned int findClosestPathPt(
582 const std::vector<float> & vec,
const float dist,
const unsigned int init = 0u)
584 float distim1 = init != 0u ? vec[init] : 0.0f;
586 const unsigned int size = vec.size();
587 for (
unsigned int i = init + 1; i != size; i++) {
590 if (i > 0 && dist - distim1 < disti - dist) {
Function-object for checking whether a goal has been reached.
virtual bool getTolerances(geometry_msgs::msg::Pose &pose_tolerance, geometry_msgs::msg::Twist &vel_tolerance)=0
Get the maximum possible tolerances used for goal checking in the major types. Any field without a va...
void reset(unsigned int size)
Reset path data.