16 #ifndef NAV2_MPPI_CONTROLLER__TOOLS__UTILS_HPP_
17 #define NAV2_MPPI_CONTROLLER__TOOLS__UTILS_HPP_
26 #include <xtensor/xarray.hpp>
27 #include <xtensor/xnorm.hpp>
28 #include <xtensor/xmath.hpp>
29 #include <xtensor/xview.hpp>
31 #include "angles/angles.h"
33 #include "tf2/utils.h"
34 #include "tf2_geometry_msgs/tf2_geometry_msgs.hpp"
36 #include "geometry_msgs/msg/twist_stamped.hpp"
37 #include "nav_msgs/msg/path.hpp"
38 #include "visualization_msgs/msg/marker_array.hpp"
40 #include "rclcpp/rclcpp.hpp"
41 #include "rclcpp_lifecycle/lifecycle_node.hpp"
43 #include "nav2_util/node_utils.hpp"
44 #include "nav2_core/goal_checker.hpp"
46 #include "nav2_mppi_controller/models/optimizer_settings.hpp"
47 #include "nav2_mppi_controller/models/control_sequence.hpp"
48 #include "nav2_mppi_controller/models/path.hpp"
49 #include "builtin_interfaces/msg/time.hpp"
50 #include "nav2_mppi_controller/critic_data.hpp"
54 using xt::evaluation_strategy::immediate;
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;
180 inline models::Path toTensor(
const nav_msgs::msg::Path & path)
182 auto result = models::Path{};
183 result.
reset(path.poses.size());
185 for (
size_t i = 0; i < path.poses.size(); ++i) {
186 result.x(i) = path.poses[i].pose.position.x;
187 result.y(i) = path.poses[i].pose.position.y;
188 result.yaws(i) = tf2::getYaw(path.poses[i].pose.orientation);
201 inline bool withinPositionGoalTolerance(
203 const geometry_msgs::msg::Pose & robot,
204 const models::Path & path)
206 const auto goal_idx = path.x.shape(0) - 1;
207 const auto goal_x = path.x(goal_idx);
208 const auto goal_y = path.y(goal_idx);
211 geometry_msgs::msg::Pose pose_tolerance;
212 geometry_msgs::msg::Twist velocity_tolerance;
213 goal_checker->
getTolerances(pose_tolerance, velocity_tolerance);
215 const auto pose_tolerance_sq = pose_tolerance.position.x * pose_tolerance.position.x;
217 auto dx = robot.position.x - goal_x;
218 auto dy = robot.position.y - goal_y;
220 auto dist_sq = dx * dx + dy * dy;
222 if (dist_sq < pose_tolerance_sq) {
237 inline bool withinPositionGoalTolerance(
238 float pose_tolerance,
239 const geometry_msgs::msg::Pose & robot,
240 const models::Path & path)
242 const auto goal_idx = path.x.shape(0) - 1;
243 const auto goal_x = path.x(goal_idx);
244 const auto goal_y = path.y(goal_idx);
246 const auto pose_tolerance_sq = pose_tolerance * pose_tolerance;
248 auto dx = robot.position.x - goal_x;
249 auto dy = robot.position.y - goal_y;
251 auto dist_sq = dx * dx + dy * dy;
253 if (dist_sq < pose_tolerance_sq) {
268 auto normalize_angles(
const T & angles)
270 auto && theta = xt::eval(xt::fmod(angles + M_PI, 2.0 * M_PI));
271 return xt::eval(xt::where(theta <= 0.0, theta + M_PI, theta - M_PI));
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();
313 float cur_dist = 0.0f;
315 for (
size_t i = 0; i < dists.shape(0); i++) {
317 min_distance_by_path = std::numeric_limits<float>::max();
318 for (
size_t j = 0; j < dists.shape(1); j++) {
319 cur_dist = dists(i, j);
320 if (cur_dist < min_distance_by_path) {
321 min_distance_by_path = cur_dist;
325 max_id_by_trajectories = std::max(max_id_by_trajectories, min_id_by_path);
327 return max_id_by_trajectories;
336 inline size_t findPathTrajectoryInitialPoint(
const CriticData & data)
339 const auto dx = data.path.x - data.trajectories.x(0, 0);
340 const auto dy = data.path.y - data.trajectories.y(0, 0);
341 const auto dists = dx * dx + dy * dy;
343 float min_distance_by_path = std::numeric_limits<float>::max();
345 for (
size_t j = 0; j < dists.shape(0); j++) {
346 if (dists(j) < min_distance_by_path) {
347 min_distance_by_path = dists(j);
359 inline void setPathFurthestPointIfNotSet(CriticData & data)
361 if (!data.furthest_reached_path_point) {
362 data.furthest_reached_path_point = findPathFurthestReachedPoint(data);
370 inline void findPathCosts(
372 std::shared_ptr<nav2_costmap_2d::Costmap2DROS> costmap_ros)
374 auto * costmap = costmap_ros->getCostmap();
375 unsigned int map_x, map_y;
376 const size_t path_segments_count = data.path.x.shape(0) - 1;
377 data.path_pts_valid = std::vector<bool>(path_segments_count,
false);
378 for (
unsigned int idx = 0; idx < path_segments_count; idx++) {
379 const auto path_x = data.path.x(idx);
380 const auto path_y = data.path.y(idx);
381 if (!costmap->worldToMap(path_x, path_y, map_x, map_y)) {
382 (*data.path_pts_valid)[idx] =
false;
386 switch (costmap->getCost(map_x, map_y)) {
388 case (LETHAL_OBSTACLE):
389 (*data.path_pts_valid)[idx] =
false;
391 case (INSCRIBED_INFLATED_OBSTACLE):
392 (*data.path_pts_valid)[idx] =
false;
394 case (NO_INFORMATION):
395 const bool is_tracking_unknown =
396 costmap_ros->getLayeredCostmap()->isTrackingUnknown();
397 (*data.path_pts_valid)[idx] = is_tracking_unknown ?
true :
false;
401 (*data.path_pts_valid)[idx] =
true;
409 inline void setPathCostsIfNotSet(
411 std::shared_ptr<nav2_costmap_2d::Costmap2DROS> costmap_ros)
413 if (!data.path_pts_valid) {
414 findPathCosts(data, costmap_ros);
426 inline float posePointAngle(
427 const geometry_msgs::msg::Pose & pose,
double point_x,
double point_y,
bool forward_preference)
429 float pose_x = pose.position.x;
430 float pose_y = pose.position.y;
431 float pose_yaw = tf2::getYaw(pose.orientation);
433 float yaw = atan2f(point_y - pose_y, point_x - pose_x);
436 if (!forward_preference) {
438 fabs(angles::shortest_angular_distance(yaw, pose_yaw)),
439 fabs(angles::shortest_angular_distance(yaw, angles::normalize_angle(pose_yaw + M_PI))));
442 return fabs(angles::shortest_angular_distance(yaw, pose_yaw));
451 inline void savitskyGolayFilter(
452 models::ControlSequence & control_sequence,
453 std::array<mppi::models::Control, 4> & control_history,
454 const models::OptimizerSettings & settings)
457 xt::xarray<float> filter = {-21.0, 14.0, 39.0, 54.0, 59.0, 54.0, 39.0, 14.0, -21.0};
460 const unsigned int num_sequences = control_sequence.vx.shape(0) - 1;
463 if (num_sequences < 20) {
467 auto applyFilter = [&](
const xt::xarray<float> & data) ->
float {
468 return xt::sum(data * filter, {0}, immediate)();
471 auto applyFilterOverAxis =
472 [&](xt::xtensor<float, 1> & sequence,
473 const float hist_0,
const float hist_1,
const float hist_2,
const float hist_3) ->
void
475 unsigned int idx = 0;
476 sequence(idx) = applyFilter(
489 sequence(idx) = applyFilter(
502 sequence(idx) = applyFilter(
515 sequence(idx) = applyFilter(
527 for (idx = 4; idx != num_sequences - 4; idx++) {
528 sequence(idx) = applyFilter(
542 sequence(idx) = applyFilter(
555 sequence(idx) = applyFilter(
568 sequence(idx) = applyFilter(
581 sequence(idx) = applyFilter(
596 control_sequence.vx, control_history[0].vx,
597 control_history[1].vx, control_history[2].vx, control_history[3].vx);
599 control_sequence.vy, control_history[0].vy,
600 control_history[1].vy, control_history[2].vy, control_history[3].vy);
602 control_sequence.wz, control_history[0].wz,
603 control_history[1].wz, control_history[2].wz, control_history[3].wz);
606 unsigned int offset = settings.shift_control_sequence ? 1 : 0;
607 control_history[0] = control_history[1];
608 control_history[1] = control_history[2];
609 control_history[2] = control_history[3];
610 control_history[3] = {
611 control_sequence.vx(offset),
612 control_sequence.vy(offset),
613 control_sequence.wz(offset)};
621 inline unsigned int findFirstPathInversion(nav_msgs::msg::Path & path)
624 if (path.poses.size() < 3) {
625 return path.poses.size();
629 for (
unsigned int idx = 1; idx < path.poses.size() - 1; ++idx) {
631 float oa_x = path.poses[idx].pose.position.x -
632 path.poses[idx - 1].pose.position.x;
633 float oa_y = path.poses[idx].pose.position.y -
634 path.poses[idx - 1].pose.position.y;
635 float ab_x = path.poses[idx + 1].pose.position.x -
636 path.poses[idx].pose.position.x;
637 float ab_y = path.poses[idx + 1].pose.position.y -
638 path.poses[idx].pose.position.y;
641 float dot_product = (oa_x * ab_x) + (oa_y * ab_y);
642 if (dot_product < 0.0) {
647 return path.poses.size();
655 inline unsigned int removePosesAfterFirstInversion(nav_msgs::msg::Path & path)
657 nav_msgs::msg::Path cropped_path = path;
658 const unsigned int first_after_inversion = findFirstPathInversion(cropped_path);
659 if (first_after_inversion == path.poses.size()) {
663 cropped_path.poses.erase(
664 cropped_path.poses.begin() + first_after_inversion, cropped_path.poses.end());
666 return first_after_inversion;
674 inline size_t findClosestPathPt(
const std::vector<float> & vec,
float dist,
size_t init = 0)
676 auto iter = std::lower_bound(vec.begin() + init, vec.end(), dist);
677 if (iter == vec.begin() + init) {
680 if (dist - *(iter - 1) < *iter - dist) {
681 return iter - 1 - vec.begin();
683 return iter - vec.begin();
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.