Nav2 Navigation Stack - jazzy  jazzy
ROS 2 Navigation Stack
publisher.cpp
1 /*
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2017, Locus Robotics
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above
14  * copyright notice, this list of conditions and the following
15  * disclaimer in the documentation and/or other materials provided
16  * with the distribution.
17  * * Neither the name of the copyright holder nor the names of its
18  * contributors may be used to endorse or promote products derived
19  * from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  */
34 
35 #include "dwb_core/publisher.hpp"
36 
37 #include <algorithm>
38 #include <memory>
39 #include <string>
40 #include <vector>
41 #include <utility>
42 
43 #include "sensor_msgs/point_cloud2_iterator.hpp"
44 #include "nav_2d_utils/conversions.hpp"
45 #include "nav2_util/node_utils.hpp"
46 #include "sensor_msgs/msg/point_cloud2.hpp"
47 #include "visualization_msgs/msg/marker_array.hpp"
48 #include "visualization_msgs/msg/marker.hpp"
49 
50 using std::max;
51 using std::string;
52 using nav2_util::declare_parameter_if_not_declared;
53 
54 namespace dwb_core
55 {
56 
57 DWBPublisher::DWBPublisher(
58  const rclcpp_lifecycle::LifecycleNode::WeakPtr & parent,
59  const std::string & plugin_name)
60 : node_(parent),
61  plugin_name_(plugin_name)
62 {
63  auto node = node_.lock();
64  clock_ = node->get_clock();
65 }
66 
67 nav2_util::CallbackReturn
68 DWBPublisher::on_configure()
69 {
70  auto node = node_.lock();
71  if (!node) {
72  throw std::runtime_error{"Failed to lock node"};
73  }
74 
75  declare_parameter_if_not_declared(
76  node, plugin_name_ + ".publish_evaluation",
77  rclcpp::ParameterValue(true));
78  declare_parameter_if_not_declared(
79  node, plugin_name_ + ".publish_global_plan",
80  rclcpp::ParameterValue(true));
81  declare_parameter_if_not_declared(
82  node, plugin_name_ + ".publish_transformed_plan",
83  rclcpp::ParameterValue(true));
84  declare_parameter_if_not_declared(
85  node, plugin_name_ + ".publish_local_plan",
86  rclcpp::ParameterValue(true));
87  declare_parameter_if_not_declared(
88  node, plugin_name_ + ".publish_trajectories",
89  rclcpp::ParameterValue(true));
90  declare_parameter_if_not_declared(
91  node, plugin_name_ + ".publish_cost_grid_pc",
92  rclcpp::ParameterValue(false));
93  declare_parameter_if_not_declared(
94  node, plugin_name_ + ".marker_lifetime",
95  rclcpp::ParameterValue(0.1));
96 
97  node->get_parameter(plugin_name_ + ".publish_evaluation", publish_evaluation_);
98  node->get_parameter(plugin_name_ + ".publish_global_plan", publish_global_plan_);
99  node->get_parameter(plugin_name_ + ".publish_transformed_plan", publish_transformed_);
100  node->get_parameter(plugin_name_ + ".publish_local_plan", publish_local_plan_);
101  node->get_parameter(plugin_name_ + ".publish_trajectories", publish_trajectories_);
102  node->get_parameter(plugin_name_ + ".publish_cost_grid_pc", publish_cost_grid_pc_);
103 
104  eval_pub_ = node->create_publisher<dwb_msgs::msg::LocalPlanEvaluation>("evaluation", 1);
105  global_pub_ = node->create_publisher<nav_msgs::msg::Path>("received_global_plan", 1);
106  transformed_pub_ = node->create_publisher<nav_msgs::msg::Path>("transformed_global_plan", 1);
107  local_pub_ = node->create_publisher<nav_msgs::msg::Path>("local_plan", 1);
108  marker_pub_ = node->create_publisher<visualization_msgs::msg::MarkerArray>("marker", 1);
109  cost_grid_pc_pub_ = node->create_publisher<sensor_msgs::msg::PointCloud2>("cost_cloud", 1);
110 
111  double marker_lifetime = 0.0;
112  node->get_parameter(plugin_name_ + ".marker_lifetime", marker_lifetime);
113  marker_lifetime_ = rclcpp::Duration::from_seconds(marker_lifetime);
114 
115  return nav2_util::CallbackReturn::SUCCESS;
116 }
117 
118 nav2_util::CallbackReturn
119 DWBPublisher::on_activate()
120 {
121  eval_pub_->on_activate();
122  global_pub_->on_activate();
123  transformed_pub_->on_activate();
124  local_pub_->on_activate();
125  marker_pub_->on_activate();
126  cost_grid_pc_pub_->on_activate();
127 
128  return nav2_util::CallbackReturn::SUCCESS;
129 }
130 
131 nav2_util::CallbackReturn
132 DWBPublisher::on_deactivate()
133 {
134  eval_pub_->on_deactivate();
135  global_pub_->on_deactivate();
136  transformed_pub_->on_deactivate();
137  local_pub_->on_deactivate();
138  marker_pub_->on_deactivate();
139  cost_grid_pc_pub_->on_deactivate();
140 
141  return nav2_util::CallbackReturn::SUCCESS;
142 }
143 
144 nav2_util::CallbackReturn
145 DWBPublisher::on_cleanup()
146 {
147  eval_pub_.reset();
148  global_pub_.reset();
149  transformed_pub_.reset();
150  local_pub_.reset();
151  marker_pub_.reset();
152  cost_grid_pc_pub_.reset();
153 
154  return nav2_util::CallbackReturn::SUCCESS;
155 }
156 
157 void
158 DWBPublisher::publishEvaluation(std::shared_ptr<dwb_msgs::msg::LocalPlanEvaluation> results)
159 {
160  if (results) {
161  if (publish_evaluation_ && eval_pub_->get_subscription_count() > 0) {
162  auto msg = std::make_unique<dwb_msgs::msg::LocalPlanEvaluation>(*results);
163  eval_pub_->publish(std::move(msg));
164  }
165  publishTrajectories(*results);
166  }
167 }
168 
169 void
170 DWBPublisher::publishTrajectories(const dwb_msgs::msg::LocalPlanEvaluation & results)
171 {
172  if (marker_pub_->get_subscription_count() < 1) {return;}
173 
174  if (!publish_trajectories_) {return;}
175  auto ma = std::make_unique<visualization_msgs::msg::MarkerArray>();
176  visualization_msgs::msg::Marker m;
177 
178  if (results.twists.size() == 0) {return;}
179 
180  geometry_msgs::msg::Point pt;
181 
182  m.header = results.header;
183  m.type = m.LINE_STRIP;
184  m.pose.orientation.w = 1;
185  m.scale.x = 0.002;
186  m.color.a = 1.0;
187  m.lifetime = marker_lifetime_;
188 
189  double best_cost = results.twists[results.best_index].total;
190  double worst_cost = results.twists[results.worst_index].total;
191  double denominator = worst_cost - best_cost;
192 
193  if (std::fabs(denominator) < 1e-9) {
194  denominator = 1.0;
195  }
196 
197  unsigned currentValidId = 0;
198  unsigned currentInvalidId = 0;
199  string validNamespace("ValidTrajectories");
200  string invalidNamespace("InvalidTrajectories");
201  for (unsigned int i = 0; i < results.twists.size(); i++) {
202  const dwb_msgs::msg::TrajectoryScore & twist = results.twists[i];
203  double displayLevel = (twist.total - best_cost) / denominator;
204  if (twist.total >= 0) {
205  m.color.r = displayLevel;
206  m.color.g = 1.0 - displayLevel;
207  m.color.b = 0;
208  m.color.a = 1.0;
209  m.ns = validNamespace;
210  m.id = currentValidId;
211  ++currentValidId;
212  } else {
213  m.color.r = 0;
214  m.color.g = 0;
215  m.color.b = 0;
216  m.color.a = 1.0;
217  m.ns = invalidNamespace;
218  m.id = currentInvalidId;
219  ++currentInvalidId;
220  }
221  m.points.clear();
222  for (unsigned int j = 0; j < twist.traj.poses.size(); ++j) {
223  pt.x = twist.traj.poses[j].x;
224  pt.y = twist.traj.poses[j].y;
225  pt.z = 0;
226  m.points.push_back(pt);
227  }
228  ma->markers.push_back(m);
229  }
230  marker_pub_->publish(std::move(ma));
231 }
232 
233 void
234 DWBPublisher::publishLocalPlan(
235  const std_msgs::msg::Header & header,
236  const dwb_msgs::msg::Trajectory2D & traj)
237 {
238  if (!publish_local_plan_) {return;}
239 
240  auto path =
241  std::make_unique<nav_msgs::msg::Path>(
242  nav_2d_utils::poses2DToPath(
243  traj.poses, header.frame_id,
244  header.stamp));
245 
246  if (local_pub_->get_subscription_count() > 0) {
247  local_pub_->publish(std::move(path));
248  }
249 }
250 
251 void
252 DWBPublisher::publishCostGrid(
253  const std::shared_ptr<nav2_costmap_2d::Costmap2DROS> costmap_ros,
254  const std::vector<TrajectoryCritic::Ptr> critics)
255 {
256  if (cost_grid_pc_pub_->get_subscription_count() < 1) {return;}
257 
258  if (!publish_cost_grid_pc_) {return;}
259 
260  auto cost_grid_pc = std::make_unique<sensor_msgs::msg::PointCloud2>();
261  cost_grid_pc->header.frame_id = costmap_ros->getGlobalFrameID();
262  cost_grid_pc->header.stamp = clock_->now();
263 
264  nav2_costmap_2d::Costmap2D * costmap = costmap_ros->getCostmap();
265  double x_coord, y_coord;
266  unsigned int size_x = costmap->getSizeInCellsX();
267  unsigned int size_y = costmap->getSizeInCellsY();
268 
269  std::vector<std::pair<std::string, std::vector<float>>> cost_channels;
270  std::vector<float> total_cost(size_x * size_y, 0.0);
271 
272  for (TrajectoryCritic::Ptr critic : critics) {
273  unsigned int channel_index = cost_channels.size();
274  critic->addCriticVisualization(cost_channels);
275  if (channel_index == cost_channels.size()) {
276  // No channels were added, so skip to next critic
277  continue;
278  }
279  double scale = critic->getScale();
280  for (unsigned int i = 0; i < size_x * size_y; i++) {
281  total_cost[i] += cost_channels[channel_index].second[i] * scale;
282  }
283  }
284 
285  cost_channels.push_back(std::make_pair("total_cost", total_cost));
286 
287  cost_grid_pc->width = size_x * size_y;
288  cost_grid_pc->height = 1;
289  cost_grid_pc->fields.resize(3 + cost_channels.size()); // x,y,z, + cost channels
290  cost_grid_pc->is_dense = true;
291  cost_grid_pc->is_bigendian = false;
292 
293  int offset = 0;
294  for (size_t i = 0; i < cost_grid_pc->fields.size(); ++i, offset += 4) {
295  cost_grid_pc->fields[i].offset = offset;
296  cost_grid_pc->fields[i].count = 1;
297  cost_grid_pc->fields[i].datatype = sensor_msgs::msg::PointField::FLOAT32;
298  if (i >= 3) {
299  cost_grid_pc->fields[i].name = cost_channels[i - 3].first;
300  }
301  }
302 
303  cost_grid_pc->fields[0].name = "x";
304  cost_grid_pc->fields[1].name = "y";
305  cost_grid_pc->fields[2].name = "z";
306 
307  cost_grid_pc->point_step = offset;
308  cost_grid_pc->row_step = cost_grid_pc->point_step * cost_grid_pc->width;
309  cost_grid_pc->data.resize(cost_grid_pc->row_step * cost_grid_pc->height);
310 
311  std::vector<sensor_msgs::PointCloud2Iterator<float>> cost_grid_pc_iter;
312 
313  for (size_t i = 0; i < cost_grid_pc->fields.size(); ++i) {
314  sensor_msgs::PointCloud2Iterator<float> iter(*cost_grid_pc, cost_grid_pc->fields[i].name);
315  cost_grid_pc_iter.push_back(iter);
316  }
317 
318  unsigned int j = 0;
319  for (unsigned int cy = 0; cy < size_y; cy++) {
320  for (unsigned int cx = 0; cx < size_x; cx++) {
321  costmap->mapToWorld(cx, cy, x_coord, y_coord);
322  *cost_grid_pc_iter[0] = x_coord;
323  *cost_grid_pc_iter[1] = y_coord;
324  *cost_grid_pc_iter[2] = 0.0; // z value
325 
326  for (size_t i = 3; i < cost_grid_pc_iter.size(); ++i) {
327  *cost_grid_pc_iter[i] = cost_channels[i - 3].second[j];
328  ++cost_grid_pc_iter[i];
329  }
330  ++cost_grid_pc_iter[0];
331  ++cost_grid_pc_iter[1];
332  ++cost_grid_pc_iter[2];
333  j++;
334  }
335  }
336 
337  cost_grid_pc_pub_->publish(std::move(cost_grid_pc));
338 }
339 
340 void
341 DWBPublisher::publishGlobalPlan(const nav_2d_msgs::msg::Path2D plan)
342 {
343  publishGenericPlan(plan, *global_pub_, publish_global_plan_);
344 }
345 
346 void
347 DWBPublisher::publishTransformedPlan(const nav_2d_msgs::msg::Path2D plan)
348 {
349  publishGenericPlan(plan, *transformed_pub_, publish_transformed_);
350 }
351 
352 void
353 DWBPublisher::publishLocalPlan(const nav_2d_msgs::msg::Path2D plan)
354 {
355  publishGenericPlan(plan, *local_pub_, publish_local_plan_);
356 }
357 
358 void
359 DWBPublisher::publishGenericPlan(
360  const nav_2d_msgs::msg::Path2D plan,
361  rclcpp::Publisher<nav_msgs::msg::Path> & pub, bool flag)
362 {
363  if (pub.get_subscription_count() < 1) {return;}
364  if (!flag) {return;}
365  auto path = std::make_unique<nav_msgs::msg::Path>(nav_2d_utils::pathToPath(plan));
366  pub.publish(std::move(path));
367 }
368 
369 } // namespace dwb_core
A 2D costmap provides a mapping between points in the world and their associated "costs".
Definition: costmap_2d.hpp:68
void mapToWorld(unsigned int mx, unsigned int my, double &wx, double &wy) const
Convert from map coordinates to world coordinates.
Definition: costmap_2d.cpp:279
unsigned int getSizeInCellsX() const
Accessor for the x size of the costmap in cells.
Definition: costmap_2d.cpp:514
unsigned int getSizeInCellsY() const
Accessor for the y size of the costmap in cells.
Definition: costmap_2d.cpp:519