Nav2 Navigation Stack - lyrical  lyrical
ROS 2 Navigation Stack
trajectory_visualizer.cpp
1 // Copyright (c) 2022 Samsung Research America, @artofnothingness Alexey Budyakov
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <memory>
16 #include <algorithm>
17 #include <cmath>
18 #include <limits>
19 #include "nav2_mppi_controller/tools/trajectory_visualizer.hpp"
20 
21 namespace mppi
22 {
23 
25  nav2::LifecycleNode::WeakPtr parent, const std::string & name,
26  const std::string & frame_id, ParametersHandler * parameters_handler)
27 {
28  auto node = parent.lock();
29  logger_ = node->get_logger();
30  frame_id_ = frame_id;
31  trajectories_publisher_ =
32  node->create_publisher<visualization_msgs::msg::MarkerArray>("~/candidate_trajectories");
33  optimal_path_pub_ = node->create_publisher<nav_msgs::msg::Path>("~/optimal_path");
34  parameters_handler_ = parameters_handler;
35 
36  auto getParam = parameters_handler->getParamGetter(name + ".TrajectoryVisualizer");
37 
38  getParam(trajectory_step_, "trajectory_step", 5);
39  getParam(time_step_, "time_step", 3);
40 
41  reset();
42 }
43 
45 {
46  trajectories_publisher_.reset();
47  optimal_path_pub_.reset();
48 }
49 
51 {
52  trajectories_publisher_->on_activate();
53  optimal_path_pub_->on_activate();
54 }
55 
57 {
58  trajectories_publisher_->on_deactivate();
59  optimal_path_pub_->on_deactivate();
60 }
61 
63  const Eigen::ArrayXXf & trajectory,
64  const std::string & marker_namespace,
65  const builtin_interfaces::msg::Time & cmd_stamp)
66 {
67  if (optimal_path_pub_->get_subscription_count() == 0 &&
68  trajectories_publisher_->get_subscription_count() == 0)
69  {
70  return;
71  }
72 
73  size_t size = trajectory.rows();
74  if (!size) {
75  return;
76  }
77 
78  auto add_marker = [&](auto i) {
79  float component = static_cast<float>(i) / static_cast<float>(size);
80 
81  auto pose = utils::createPose(trajectory(i, 0), trajectory(i, 1), 0.06);
82  auto scale =
83  i != size - 1 ?
84  utils::createScale(0.03, 0.03, 0.07) :
85  utils::createScale(0.07, 0.07, 0.09);
86  auto color = utils::createColor(0, component, component, 1);
87  auto marker = utils::createMarker(
88  marker_id_++, pose, scale, color, frame_id_, marker_namespace);
89  points_->markers.push_back(marker);
90 
91  // populate optimal path
92  geometry_msgs::msg::PoseStamped pose_stamped;
93  pose_stamped.header.frame_id = frame_id_;
94  pose_stamped.pose = pose;
95 
96  tf2::Quaternion quaternion_tf2;
97  quaternion_tf2.setRPY(0., 0., trajectory(i, 2));
98  pose_stamped.pose.orientation = tf2::toMsg(quaternion_tf2);
99 
100  optimal_path_->poses.push_back(pose_stamped);
101  };
102 
103  optimal_path_->header.stamp = cmd_stamp;
104  optimal_path_->header.frame_id = frame_id_;
105  for (size_t i = 0; i < size; i++) {
106  add_marker(i);
107  }
108 }
109 
111  const models::Trajectories & trajectories,
112  const Eigen::ArrayXf & costs,
113  const std::vector<bool> & collisions,
114  const builtin_interfaces::msg::Time & stamp)
115 {
116  if (trajectories_publisher_->get_subscription_count() == 0) {
117  return;
118  }
119 
120  const size_t n_rows = trajectories.x.rows();
121  if (n_rows == 0 || costs.size() == 0 ||
122  static_cast<size_t>(costs.size()) < n_rows)
123  {
124  return;
125  }
126 
127  // Normalize costs excluding collision trajectories for better gradient resolution.
128  float min_val = std::numeric_limits<float>::max();
129  float max_val = std::numeric_limits<float>::lowest();
130  for (Eigen::Index k = 0; k < costs.size(); ++k) {
131  if (!collisions.empty() && static_cast<size_t>(k) < collisions.size() && collisions[k]) {
132  continue;
133  }
134  auto curr_cost = costs(k);
135  if (curr_cost < min_val) {min_val = curr_cost;}
136  if (curr_cost > max_val) {max_val = curr_cost;}
137  }
138  if (max_val < min_val) {
139  min_val = costs.minCoeff();
140  max_val = costs.maxCoeff();
141  }
142  float range = max_val - min_val;
143 
144  for (size_t i = 0; i < n_rows; i += trajectory_step_) {
145  float norm = (range > 0.0f) ?
146  (costs(i) - min_val) / range : 0.0f;
147  bool in_collision =
148  !collisions.empty() && i < collisions.size() && collisions[i];
149  addCostColoredTrajectory(i, trajectories, norm, in_collision, stamp);
150  }
151 }
152 
154  size_t trajectory_idx,
155  const models::Trajectories & trajectories,
156  float normalized_cost,
157  bool in_collision,
158  const builtin_interfaces::msg::Time & stamp)
159 {
160  using visualization_msgs::msg::Marker;
161  const size_t n_cols = trajectories.x.cols();
162 
163  Marker marker;
164  marker.header.frame_id = frame_id_;
165  marker.header.stamp = stamp;
166  marker.ns = "Candidate Trajectories";
167  marker.id = marker_id_++;
168  marker.type = Marker::LINE_STRIP;
169  marker.action = Marker::ADD;
170  marker.pose.orientation.w = 1.0;
171  marker.scale.x = 0.01; // line width
172  marker.color = in_collision ?
173  utils::createColor(1.0f, 0.0f, 1.0f, 0.6f) : // magenta for collisions
174  costToColor(normalized_cost);
175 
176  marker.points.reserve(n_cols / time_step_ + 1);
177  for (size_t j = 0; j < n_cols; j += time_step_) {
178  geometry_msgs::msg::Point pt;
179  pt.x = trajectories.x(trajectory_idx, j);
180  pt.y = trajectories.y(trajectory_idx, j);
181  pt.z = 0.03;
182  marker.points.push_back(pt);
183  }
184 
185  points_->markers.push_back(std::move(marker));
186 }
187 
188 std_msgs::msg::ColorRGBA TrajectoryVisualizer::costToColor(float normalized)
189 {
190  // Green (0) -> Yellow (0.5) -> Red (1.0)
191  normalized = std::clamp(normalized, 0.0f, 1.0f);
192  float r, g;
193  if (normalized < 0.5f) {
194  r = 2.0f * normalized;
195  g = 1.0f;
196  } else {
197  r = 1.0f;
198  g = 2.0f * (1.0f - normalized);
199  }
200  return utils::createColor(r, g, 0.0f, 0.8f);
201 }
202 
204 {
205  marker_id_ = 0;
206  points_ = std::make_unique<visualization_msgs::msg::MarkerArray>();
207  optimal_path_ = std::make_unique<nav_msgs::msg::Path>();
208 }
209 
211 {
212  if (trajectories_publisher_->get_subscription_count() > 0) {
213  trajectories_publisher_->publish(std::move(points_));
214  }
215 
216  if (optimal_path_pub_->get_subscription_count() > 0) {
217  optimal_path_pub_->publish(std::move(optimal_path_));
218  }
219 
220  reset();
221 }
222 
223 } // namespace mppi
Handles getting parameters and dynamic parameter changes.
auto getParamGetter(const std::string &ns)
Get an object to retrieve parameters.
void add(const Eigen::ArrayXXf &trajectory, const std::string &marker_namespace, const builtin_interfaces::msg::Time &cmd_stamp)
Add an optimal trajectory to visualize.
void on_deactivate()
Deactivate object.
void on_configure(nav2::LifecycleNode::WeakPtr parent, const std::string &name, const std::string &frame_id, ParametersHandler *parameters_handler)
Configure trajectory visualizer.
void addCostColoredTrajectory(size_t trajectory_idx, const models::Trajectories &trajectories, float normalized_cost, bool in_collision, const builtin_interfaces::msg::Time &stamp)
Create a LINE_STRIP marker for a single trajectory colored by normalized cost.
void visualize()
Visualize the plan.
static std_msgs::msg::ColorRGBA costToColor(float normalized)
Convert a normalized cost [0,1] to a green->yellow->red color.
void on_activate()
Activate object.
void on_cleanup()
Cleanup object on shutdown.
Candidate Trajectories.