Nav2 Navigation Stack - lyrical  lyrical
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 "sensor_msgs/msg/point_cloud2.hpp"
46 #include "visualization_msgs/msg/marker_array.hpp"
47 #include "visualization_msgs/msg/marker.hpp"
48 
49 using std::max;
50 using std::string;
51 
52 namespace dwb_core
53 {
54 
55 DWBPublisher::DWBPublisher(
56  const nav2::LifecycleNode::WeakPtr & parent,
57  const std::string & plugin_name)
58 : node_(parent),
59  plugin_name_(plugin_name)
60 {
61  auto node = node_.lock();
62  clock_ = node->get_clock();
63 }
64 
65 nav2::CallbackReturn
66 DWBPublisher::on_configure()
67 {
68  auto node = node_.lock();
69  if (!node) {
70  throw std::runtime_error{"Failed to lock node"};
71  }
72 
73  publish_evaluation_ = node->declare_or_get_parameter(
74  plugin_name_ + ".publish_evaluation", true);
75  publish_local_plan_ = node->declare_or_get_parameter(
76  plugin_name_ + ".publish_local_plan", true);
77  publish_trajectories_ = node->declare_or_get_parameter(
78  plugin_name_ + ".publish_trajectories", true);
79  publish_cost_grid_pc_ = node->declare_or_get_parameter(
80  plugin_name_ + ".publish_cost_grid_pc", false);
81  double marker_lifetime = node->declare_or_get_parameter(
82  plugin_name_ + ".marker_lifetime", 0.1);
83  marker_lifetime_ = rclcpp::Duration::from_seconds(marker_lifetime);
84 
85  eval_pub_ = node->create_publisher<dwb_msgs::msg::LocalPlanEvaluation>("evaluation");
86  local_pub_ = node->create_publisher<nav_msgs::msg::Path>("local_plan");
87  marker_pub_ = node->create_publisher<visualization_msgs::msg::MarkerArray>("marker");
88  cost_grid_pc_pub_ = node->create_publisher<sensor_msgs::msg::PointCloud2>("cost_cloud");
89 
90  return nav2::CallbackReturn::SUCCESS;
91 }
92 
93 nav2::CallbackReturn
94 DWBPublisher::on_activate()
95 {
96  eval_pub_->on_activate();
97  local_pub_->on_activate();
98  marker_pub_->on_activate();
99  cost_grid_pc_pub_->on_activate();
100 
101  return nav2::CallbackReturn::SUCCESS;
102 }
103 
104 nav2::CallbackReturn
105 DWBPublisher::on_deactivate()
106 {
107  eval_pub_->on_deactivate();
108  local_pub_->on_deactivate();
109  marker_pub_->on_deactivate();
110  cost_grid_pc_pub_->on_deactivate();
111 
112  return nav2::CallbackReturn::SUCCESS;
113 }
114 
115 nav2::CallbackReturn
116 DWBPublisher::on_cleanup()
117 {
118  eval_pub_.reset();
119  local_pub_.reset();
120  marker_pub_.reset();
121  cost_grid_pc_pub_.reset();
122 
123  return nav2::CallbackReturn::SUCCESS;
124 }
125 
126 void
127 DWBPublisher::publishEvaluation(std::shared_ptr<dwb_msgs::msg::LocalPlanEvaluation> results)
128 {
129  if (results) {
130  if (publish_evaluation_ && eval_pub_->get_subscription_count() > 0) {
131  auto msg = std::make_unique<dwb_msgs::msg::LocalPlanEvaluation>(*results);
132  eval_pub_->publish(std::move(msg));
133  }
134  publishTrajectories(*results);
135  }
136 }
137 
138 void
139 DWBPublisher::publishTrajectories(const dwb_msgs::msg::LocalPlanEvaluation & results)
140 {
141  if (marker_pub_->get_subscription_count() < 1) {return;}
142 
143  if (!publish_trajectories_) {return;}
144  auto ma = std::make_unique<visualization_msgs::msg::MarkerArray>();
145  visualization_msgs::msg::Marker m;
146 
147  if (results.twists.size() == 0) {return;}
148 
149  geometry_msgs::msg::Point pt;
150 
151  m.header = results.header;
152  m.type = m.LINE_STRIP;
153  m.pose.orientation.w = 1;
154  m.scale.x = 0.002;
155  m.color.a = 1.0;
156  m.lifetime = marker_lifetime_;
157 
158  double best_cost = results.twists[results.best_index].total;
159  double worst_cost = results.twists[results.worst_index].total;
160  double denominator = worst_cost - best_cost;
161 
162  if (std::fabs(denominator) < 1e-9) {
163  denominator = 1.0;
164  }
165 
166  unsigned currentValidId = 0;
167  unsigned currentInvalidId = 0;
168  string validNamespace("ValidTrajectories");
169  string invalidNamespace("InvalidTrajectories");
170  for (unsigned int i = 0; i < results.twists.size(); i++) {
171  const dwb_msgs::msg::TrajectoryScore & twist = results.twists[i];
172  double displayLevel = (twist.total - best_cost) / denominator;
173  if (twist.total >= 0) {
174  m.color.r = displayLevel;
175  m.color.g = 1.0 - displayLevel;
176  m.color.b = 0;
177  m.color.a = 1.0;
178  m.ns = validNamespace;
179  m.id = currentValidId;
180  ++currentValidId;
181  } else {
182  m.color.r = 0;
183  m.color.g = 0;
184  m.color.b = 0;
185  m.color.a = 1.0;
186  m.ns = invalidNamespace;
187  m.id = currentInvalidId;
188  ++currentInvalidId;
189  }
190  m.points.clear();
191  for (unsigned int j = 0; j < twist.traj.poses.size(); ++j) {
192  pt.x = twist.traj.poses[j].position.x;
193  pt.y = twist.traj.poses[j].position.y;
194  pt.z = 0;
195  m.points.push_back(pt);
196  }
197  ma->markers.push_back(m);
198  }
199  marker_pub_->publish(std::move(ma));
200 }
201 
202 void
203 DWBPublisher::publishLocalPlan(
204  const std_msgs::msg::Header & header,
205  const dwb_msgs::msg::Trajectory2D & traj)
206 {
207  if (!publish_local_plan_) {return;}
208 
209  auto path =
210  std::make_unique<nav_msgs::msg::Path>(
211  nav_2d_utils::posesToPath(
212  traj.poses, header.frame_id,
213  header.stamp));
214 
215  if (local_pub_->get_subscription_count() > 0) {
216  local_pub_->publish(std::move(path));
217  }
218 }
219 
220 void
221 DWBPublisher::publishCostGrid(
222  const std::shared_ptr<nav2_costmap_2d::Costmap2DROS> costmap_ros,
223  const std::vector<TrajectoryCritic::Ptr> critics)
224 {
225  if (cost_grid_pc_pub_->get_subscription_count() < 1) {return;}
226 
227  if (!publish_cost_grid_pc_) {return;}
228 
229  auto cost_grid_pc = std::make_unique<sensor_msgs::msg::PointCloud2>();
230  cost_grid_pc->header.frame_id = costmap_ros->getGlobalFrameID();
231  cost_grid_pc->header.stamp = clock_->now();
232 
233  nav2_costmap_2d::Costmap2D * costmap = costmap_ros->getCostmap();
234  double x_coord, y_coord;
235  unsigned int size_x = costmap->getSizeInCellsX();
236  unsigned int size_y = costmap->getSizeInCellsY();
237 
238  std::vector<std::pair<std::string, std::vector<float>>> cost_channels;
239  std::vector<float> total_cost(size_x * size_y, 0.0);
240 
241  for (TrajectoryCritic::Ptr critic : critics) {
242  unsigned int channel_index = cost_channels.size();
243  critic->addCriticVisualization(cost_channels);
244  if (channel_index == cost_channels.size()) {
245  // No channels were added, so skip to next critic
246  continue;
247  }
248  double scale = critic->getScale();
249  for (unsigned int i = 0; i < size_x * size_y; i++) {
250  total_cost[i] += cost_channels[channel_index].second[i] * scale;
251  }
252  }
253 
254  cost_channels.push_back(std::make_pair("total_cost", total_cost));
255 
256  cost_grid_pc->width = size_x * size_y;
257  cost_grid_pc->height = 1;
258  cost_grid_pc->fields.resize(3 + cost_channels.size()); // x,y,z, + cost channels
259  cost_grid_pc->is_dense = true;
260  cost_grid_pc->is_bigendian = false;
261 
262  int offset = 0;
263  for (size_t i = 0; i < cost_grid_pc->fields.size(); ++i, offset += 4) {
264  cost_grid_pc->fields[i].offset = offset;
265  cost_grid_pc->fields[i].count = 1;
266  cost_grid_pc->fields[i].datatype = sensor_msgs::msg::PointField::FLOAT32;
267  if (i >= 3) {
268  cost_grid_pc->fields[i].name = cost_channels[i - 3].first;
269  }
270  }
271 
272  cost_grid_pc->fields[0].name = "x";
273  cost_grid_pc->fields[1].name = "y";
274  cost_grid_pc->fields[2].name = "z";
275 
276  cost_grid_pc->point_step = offset;
277  cost_grid_pc->row_step = cost_grid_pc->point_step * cost_grid_pc->width;
278  cost_grid_pc->data.resize(cost_grid_pc->row_step * cost_grid_pc->height);
279 
280  std::vector<sensor_msgs::PointCloud2Iterator<float>> cost_grid_pc_iter;
281 
282  for (size_t i = 0; i < cost_grid_pc->fields.size(); ++i) {
283  sensor_msgs::PointCloud2Iterator<float> iter(*cost_grid_pc, cost_grid_pc->fields[i].name);
284  cost_grid_pc_iter.push_back(iter);
285  }
286 
287  unsigned int j = 0;
288  for (unsigned int cy = 0; cy < size_y; cy++) {
289  for (unsigned int cx = 0; cx < size_x; cx++) {
290  costmap->mapToWorld(cx, cy, x_coord, y_coord);
291  *cost_grid_pc_iter[0] = x_coord;
292  *cost_grid_pc_iter[1] = y_coord;
293  *cost_grid_pc_iter[2] = 0.0; // z value
294 
295  for (size_t i = 3; i < cost_grid_pc_iter.size(); ++i) {
296  *cost_grid_pc_iter[i] = cost_channels[i - 3].second[j];
297  ++cost_grid_pc_iter[i];
298  }
299  ++cost_grid_pc_iter[0];
300  ++cost_grid_pc_iter[1];
301  ++cost_grid_pc_iter[2];
302  j++;
303  }
304  }
305 
306  cost_grid_pc_pub_->publish(std::move(cost_grid_pc));
307 }
308 
309 void
310 DWBPublisher::publishLocalPlan(const nav_msgs::msg::Path plan)
311 {
312  publishGenericPlan(plan, *local_pub_, publish_local_plan_);
313 }
314 
315 void
316 DWBPublisher::publishGenericPlan(
317  const nav_msgs::msg::Path plan,
318  rclcpp::Publisher<nav_msgs::msg::Path> & pub, bool flag) // nosemgrep
319 {
320  if (pub.get_subscription_count() < 1) {return;}
321  if (!flag) {return;}
322  auto path = std::make_unique<nav_msgs::msg::Path>(plan);
323  pub.publish(std::move(path));
324 }
325 
326 } // namespace dwb_core
A 2D costmap provides a mapping between points in the world and their associated "costs".
Definition: costmap_2d.hpp:69
void mapToWorld(unsigned int mx, unsigned int my, double &wx, double &wy) const
Convert from map coordinates to world coordinates.
Definition: costmap_2d.cpp:280
unsigned int getSizeInCellsX() const
Accessor for the x size of the costmap in cells.
Definition: costmap_2d.cpp:548
unsigned int getSizeInCellsY() const
Accessor for the y size of the costmap in cells.
Definition: costmap_2d.cpp:553