Nav2 Navigation Stack - lyrical  lyrical
ROS 2 Navigation Stack
obstacle_layer.cpp
1 /*********************************************************************
2  *
3  * Software License Agreement (BSD License)
4  *
5  * Copyright (c) 2008, 2013, Willow Garage, Inc.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * * Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  * * Redistributions in binary form must reproduce the above
15  * copyright notice, this list of conditions and the following
16  * disclaimer in the documentation and/or other materials provided
17  * with the distribution.
18  * * Neither the name of Willow Garage, Inc. nor the names of its
19  * contributors may be used to endorse or promote products derived
20  * from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33  * POSSIBILITY OF SUCH DAMAGE.
34  *
35  * Author: Eitan Marder-Eppstein
36  * David V. Lu!!
37  * Steve Macenski
38  *********************************************************************/
39 #include "nav2_costmap_2d/obstacle_layer.hpp"
40 
41 #include <algorithm>
42 #include <memory>
43 #include <string>
44 #include <vector>
45 
46 #include "pluginlib/class_list_macros.hpp"
47 #include "sensor_msgs/point_cloud2_iterator.hpp"
48 #include "nav2_util/raytrace_line_2d.hpp"
49 #include "nav2_costmap_2d/costmap_math.hpp"
50 #include "nav2_ros_common/node_utils.hpp"
51 #include "nav2_ros_common/interface_factories.hpp"
52 #include "rclcpp/version.h"
53 
55 
56 using nav2_costmap_2d::NO_INFORMATION;
57 using nav2_costmap_2d::LETHAL_OBSTACLE;
58 using nav2_costmap_2d::FREE_SPACE;
59 
62 using rcl_interfaces::msg::ParameterType;
63 
64 namespace nav2_costmap_2d
65 {
66 
68 {
69  for (auto & notifier : observation_notifiers_) {
70  notifier.reset();
71  }
72 }
73 
75 {
76  bool track_unknown_space = false;
77  double transform_tolerance = 0.1;
78 
79  // The topics that we'll subscribe to from the parameter server
80  std::string topics_string;
81 
82  auto node = node_.lock();
83  if (!node) {
84  throw std::runtime_error{"Failed to lock node"};
85  }
86 
87  allow_parameter_qos_overrides_ = nav2::declare_or_get_parameter(node,
88  "allow_parameter_qos_overrides", true);
89  enabled_ = node->declare_or_get_parameter(name_ + "." + "enabled", true);
90  footprint_clearing_enabled_ = node->declare_or_get_parameter(
91  name_ + "." + "footprint_clearing_enabled", true);
92  min_obstacle_height_ = node->declare_or_get_parameter(
93  name_ + "." + "min_obstacle_height", 0.0);
94  max_obstacle_height_ = node->declare_or_get_parameter(
95  name_ + "." + "max_obstacle_height", 2.0);
96  int combination_method_param = node->declare_or_get_parameter(
97  name_ + "." + "combination_method", 1);
98  topics_string = node->declare_or_get_parameter(
99  name_ + "." + "observation_sources", std::string(""));
100  node->get_parameter("track_unknown_space", track_unknown_space);
101  node->get_parameter("transform_tolerance", transform_tolerance);
102  double tf_filter_tolerance = nav2::declare_or_get_parameter(
103  node, name_ + "." +
104  "tf_filter_tolerance", 0.05);
105  combination_method_ = combination_method_from_int(combination_method_param);
106 
107  RCLCPP_INFO(
108  logger_,
109  "Subscribed to Topics: %s", topics_string.c_str());
110 
111  rolling_window_ = layered_costmap_->isRolling();
112 
113  if (track_unknown_space) {
114  default_value_ = NO_INFORMATION;
115  } else {
116  default_value_ = FREE_SPACE;
117  }
118 
120  setCurrent(true);
121  was_reset_ = false;
122 
123  global_frame_ = layered_costmap_->getGlobalFrameID();
124 
125  // now we need to split the topics based on whitespace which we can use a stringstream for
126  std::stringstream ss(topics_string);
127 
128  std::string source;
129  while (ss >> source) {
130  // get the parameters for the specific topic
131  double observation_keep_time, expected_update_rate, min_obstacle_height, max_obstacle_height;
132  std::string topic, sensor_frame, data_type, transport_type;
133  bool inf_is_valid, clearing, marking;
134 
135  topic = node->declare_or_get_parameter(
136  name_ + "." + source + "." + "topic", source);
137  sensor_frame = node->declare_or_get_parameter(
138  name_ + "." + source + "." + "sensor_frame", std::string(""));
139  observation_keep_time = node->declare_or_get_parameter(
140  name_ + "." + source + "." + "observation_persistence", 0.0);
141  expected_update_rate = node->declare_or_get_parameter(
142  name_ + "." + source + "." + "expected_update_rate", 0.0);
143  data_type = node->declare_or_get_parameter(
144  name_ + "." + source + "." + "data_type", std::string("LaserScan"));
145  min_obstacle_height = node->declare_or_get_parameter(
146  name_ + "." + source + "." + "min_obstacle_height", 0.0);
147  max_obstacle_height = node->declare_or_get_parameter(
148  name_ + "." + source + "." + "max_obstacle_height", 0.0);
149  inf_is_valid = node->declare_or_get_parameter(
150  name_ + "." + source + "." + "inf_is_valid", false);
151  marking = node->declare_or_get_parameter(
152  name_ + "." + source + "." + "marking", true);
153  clearing = node->declare_or_get_parameter(
154  name_ + "." + source + "." + "clearing", false);
155  transport_type = node->declare_or_get_parameter(
156  name_ + "." + source + "." + "transport_type", std::string("raw"));
157 
158  if (!(data_type == "PointCloud2" || data_type == "LaserScan")) {
159  RCLCPP_FATAL(
160  logger_,
161  "Only topics that use point cloud2s or laser scans are currently supported");
162  throw std::runtime_error(
163  "Only topics that use point cloud2s or laser scans are currently supported");
164  }
165 
166  // get the obstacle range for the sensor
167  double obstacle_max_range = node->declare_or_get_parameter(
168  name_ + "." + source + "." + "obstacle_max_range", 2.5);
169  double obstacle_min_range = node->declare_or_get_parameter(
170  name_ + "." + source + "." + "obstacle_min_range", 0.0);
171 
172  // get the raytrace ranges for the sensor
173  double raytrace_max_range = node->declare_or_get_parameter(
174  name_ + "." + source + "." + "raytrace_max_range", 3.0);
175  double raytrace_min_range = node->declare_or_get_parameter(
176  name_ + "." + source + "." + "raytrace_min_range", 0.0);
177 
178  topic = joinWithParentNamespace(topic);
179 
180  RCLCPP_DEBUG(
181  logger_,
182  "Creating an observation buffer for source %s, topic %s, frame %s",
183  source.c_str(), topic.c_str(),
184  sensor_frame.c_str());
185 
186  // create an observation buffer
187  observation_buffers_.push_back(
188  std::make_shared<ObservationBuffer>(node, topic, observation_keep_time,
189  expected_update_rate,
190  min_obstacle_height,
191  max_obstacle_height, obstacle_max_range, obstacle_min_range, raytrace_max_range,
192  raytrace_min_range, *tf_,
194  sensor_frame, tf2::durationFromSec(transform_tolerance)));
195 
196  // check if we'll add this buffer to our marking observation buffers
197  if (marking) {
198  marking_buffers_.push_back(observation_buffers_.back());
199  }
200 
201  // check if we'll also add this buffer to our clearing observation buffers
202  if (clearing) {
203  clearing_buffers_.push_back(observation_buffers_.back());
204  }
205 
206  RCLCPP_DEBUG(
207  logger_,
208  "Created an observation buffer for source %s, topic %s, global frame: %s, "
209  "expected update rate: %.2f, observation persistence: %.2f",
210  source.c_str(), topic.c_str(),
211  global_frame_.c_str(), expected_update_rate, observation_keep_time);
212 
213  const auto custom_qos_profile = nav2::qos::SensorDataQoS(50);
214 
215  // create a callback for the topic
216  if (data_type == "LaserScan") {
217  auto sub_opt = nav2::interfaces::createSubscriptionOptions(
218  topic, allow_parameter_qos_overrides_, callback_group_);
219 
220  // For Kilted and Older Support from Message Filters API change
221  #if RCLCPP_VERSION_GTE(29, 6, 0)
222  std::shared_ptr<message_filters::Subscriber<sensor_msgs::msg::LaserScan>> sub;
223  #else
224  std::shared_ptr<message_filters::Subscriber<sensor_msgs::msg::LaserScan,
225  rclcpp_lifecycle::LifecycleNode>> sub;
226  #endif
227 
228  // For Kilted compatibility in Message Filters API change
229  #if RCLCPP_VERSION_GTE(29, 6, 0)
230  sub = std::make_shared<message_filters::Subscriber<sensor_msgs::msg::LaserScan>>(
231  node, topic, custom_qos_profile, sub_opt);
232  // For Jazzy compatibility in Message Filters API change
233  #elif RCLCPP_VERSION_GTE(29, 0, 0)
234  sub = std::make_shared<message_filters::Subscriber<sensor_msgs::msg::LaserScan,
235  rclcpp_lifecycle::LifecycleNode>>(
236  std::static_pointer_cast<rclcpp_lifecycle::LifecycleNode>(node),
237  topic, custom_qos_profile, sub_opt);
238  // For Humble and Older compatibility in Message Filters API change
239  #else
240  sub = std::make_shared<message_filters::Subscriber<sensor_msgs::msg::LaserScan,
241  rclcpp_lifecycle::LifecycleNode>>(
242  std::static_pointer_cast<rclcpp_lifecycle::LifecycleNode>(node),
243  topic, custom_qos_profile.get_rmw_qos_profile(), sub_opt);
244  #endif
245 
246  sub->unsubscribe();
247 
248  auto filter = nav2::create_message_filter<sensor_msgs::msg::LaserScan>(
249  *sub, *tf_, global_frame_, 50,
250  node, tf2::durationFromSec(transform_tolerance));
251 
252  if (inf_is_valid) {
253  filter->registerCallback(
254  std::bind(
255  &ObstacleLayer::laserScanValidInfCallback, this, std::placeholders::_1,
256  observation_buffers_.back()));
257 
258  } else {
259  filter->registerCallback(
260  std::bind(
261  &ObstacleLayer::laserScanCallback, this, std::placeholders::_1,
262  observation_buffers_.back()));
263  }
264 
265  observation_subscribers_.push_back(sub);
266 
267  observation_notifiers_.push_back(filter);
268  observation_notifiers_.back()->setTolerance(
269  rclcpp::Duration::from_seconds(
270  tf_filter_tolerance));
271 
272  } else {
273  auto sub_opt = nav2::interfaces::createSubscriptionOptions(
274  topic, allow_parameter_qos_overrides_, callback_group_);
275 
276  // For Rolling and Newer Support from PointCloudTransport API change
277  #if RCLCPP_VERSION_GTE(30, 0, 0)
278  std::shared_ptr<point_cloud_transport::SubscriberFilter> sub;
279  // For Kilted and Older Support from Message Filters API change
280  #elif RCLCPP_VERSION_GTE(29, 6, 0)
281  std::shared_ptr<message_filters::Subscriber<sensor_msgs::msg::PointCloud2>> sub;
282  #else
283  std::shared_ptr<message_filters::Subscriber<sensor_msgs::msg::PointCloud2,
284  rclcpp_lifecycle::LifecycleNode>> sub;
285  #endif
286 
287  // For Rolling compatibility in PointCloudTransport API change
288  #if RCLCPP_VERSION_GTE(30, 0, 0)
289  sub = std::make_shared<point_cloud_transport::SubscriberFilter>(
290  *node, topic, transport_type, custom_qos_profile, sub_opt);
291  // For Kilted compatibility in Message Filters API change
292  #elif RCLCPP_VERSION_GTE(29, 6, 0)
293  sub = std::make_shared<message_filters::Subscriber<sensor_msgs::msg::PointCloud2>>(
294  node, topic, custom_qos_profile, sub_opt);
295  // For Jazzy compatibility in Message Filters API change
296  #elif RCLCPP_VERSION_GTE(29, 0, 0)
297  sub = std::make_shared<message_filters::Subscriber<sensor_msgs::msg::PointCloud2,
298  rclcpp_lifecycle::LifecycleNode>>(
299  std::static_pointer_cast<rclcpp_lifecycle::LifecycleNode>(node),
300  topic, custom_qos_profile, sub_opt);
301  // For Humble and Older compatibility in Message Filters API change
302  #else
303  sub = std::make_shared<message_filters::Subscriber<sensor_msgs::msg::PointCloud2,
304  rclcpp_lifecycle::LifecycleNode>>(
305  std::static_pointer_cast<rclcpp_lifecycle::LifecycleNode>(node),
306  topic, custom_qos_profile.get_rmw_qos_profile(), sub_opt);
307  #endif
308 
309  sub->unsubscribe();
310 
311  if (inf_is_valid) {
312  RCLCPP_WARN(
313  logger_,
314  "obstacle_layer: inf_is_valid option is not applicable to PointCloud observations.");
315  }
316 
317  auto filter = nav2::create_message_filter<sensor_msgs::msg::PointCloud2>(
318  *sub, *tf_, global_frame_, 50,
319  node, tf2::durationFromSec(transform_tolerance));
320 
321  filter->registerCallback(
322  std::bind(
323  &ObstacleLayer::pointCloud2Callback, this, std::placeholders::_1,
324  observation_buffers_.back()));
325 
326  observation_subscribers_.push_back(sub);
327  observation_notifiers_.push_back(filter);
328  }
329 
330  if (sensor_frame != "") {
331  std::vector<std::string> target_frames;
332  target_frames.push_back(global_frame_);
333  target_frames.push_back(sensor_frame);
334  observation_notifiers_.back()->setTargetFrames(target_frames);
335  }
336  }
337 }
338 
339 rcl_interfaces::msg::SetParametersResult ObstacleLayer::validateParameterUpdatesCallback(
340  const std::vector<rclcpp::Parameter> & /*parameters*/)
341 {
342  rcl_interfaces::msg::SetParametersResult result;
343  result.successful = true;
344  return result;
345 }
346 
347 void
349  const std::vector<rclcpp::Parameter> & parameters)
350 {
351  std::lock_guard<Costmap2D::mutex_t> guard(*getMutex());
352 
353  for (const auto & parameter : parameters) {
354  const auto & param_type = parameter.get_type();
355  const auto & param_name = parameter.get_name();
356  if (param_name.find(name_ + ".") != 0) {
357  continue;
358  }
359 
360  if (param_type == ParameterType::PARAMETER_DOUBLE) {
361  if (param_name == name_ + "." + "min_obstacle_height" &&
362  min_obstacle_height_ != parameter.as_double())
363  {
364  min_obstacle_height_ = parameter.as_double();
365  setCurrent(false);
366  } else if (param_name == name_ + "." + "max_obstacle_height" && // NOLINT(readability/braces)
367  max_obstacle_height_ != parameter.as_double())
368  {
369  max_obstacle_height_ = parameter.as_double();
370  setCurrent(false);
371  }
372  } else if (param_type == ParameterType::PARAMETER_BOOL) {
373  if (param_name == name_ + "." + "enabled" && enabled_ != parameter.as_bool()) {
374  enabled_ = parameter.as_bool();
375  setCurrent(false);
376  } else if (param_name == name_ + "." + "footprint_clearing_enabled") {
377  footprint_clearing_enabled_ = parameter.as_bool();
378  }
379  } else if (param_type == ParameterType::PARAMETER_INTEGER) {
380  if (param_name == name_ + "." + "combination_method") {
381  combination_method_ = combination_method_from_int(parameter.as_int());
382  }
383  }
384  }
385 }
386 
387 void
389  sensor_msgs::msg::LaserScan::ConstSharedPtr message,
390  const std::shared_ptr<ObservationBuffer> & buffer)
391 {
392  // project the laser into a point cloud
393  sensor_msgs::msg::PointCloud2 cloud;
394  cloud.header = message->header;
395 
396  // project the scan into a point cloud
397  try {
398  projector_.transformLaserScanToPointCloud(message->header.frame_id, *message, cloud, *tf_);
399  } catch (tf2::TransformException & ex) {
400  RCLCPP_WARN(
401  logger_,
402  "High fidelity enabled, but TF returned a transform exception to frame %s: %s",
403  global_frame_.c_str(),
404  ex.what());
405  projector_.projectLaser(*message, cloud);
406  } catch (std::runtime_error & ex) {
407  RCLCPP_WARN(
408  logger_,
409  "transformLaserScanToPointCloud error, it seems the message from laser is malformed."
410  " Ignore this message. what(): %s",
411  ex.what());
412  return;
413  }
414 
415  // buffer the point cloud
416  buffer->lock();
417  buffer->bufferCloud(cloud);
418  buffer->unlock();
419 }
420 
421 void
423  sensor_msgs::msg::LaserScan::ConstSharedPtr raw_message,
424  const std::shared_ptr<ObservationBuffer> & buffer)
425 {
426  // Filter positive infinities ("Inf"s) to max_range.
427  float epsilon = 0.0001; // a tenth of a millimeter
428  sensor_msgs::msg::LaserScan message = *raw_message;
429  for (size_t i = 0; i < message.ranges.size(); i++) {
430  float range = message.ranges[i];
431  if (!std::isfinite(range) && range > 0) {
432  message.ranges[i] = message.range_max - epsilon;
433  }
434  }
435 
436  // project the laser into a point cloud
437  sensor_msgs::msg::PointCloud2 cloud;
438  cloud.header = message.header;
439 
440  // project the scan into a point cloud
441  try {
442  projector_.transformLaserScanToPointCloud(message.header.frame_id, message, cloud, *tf_);
443  } catch (tf2::TransformException & ex) {
444  RCLCPP_WARN(
445  logger_,
446  "High fidelity enabled, but TF returned a transform exception to frame %s: %s",
447  global_frame_.c_str(), ex.what());
448  projector_.projectLaser(message, cloud);
449  } catch (std::runtime_error & ex) {
450  RCLCPP_WARN(
451  logger_,
452  "transformLaserScanToPointCloud error, it seems the message from laser is malformed."
453  " Ignore this message. what(): %s",
454  ex.what());
455  return;
456  }
457 
458  // buffer the point cloud
459  buffer->lock();
460  buffer->bufferCloud(cloud);
461  buffer->unlock();
462 }
463 
464 void
466  sensor_msgs::msg::PointCloud2::ConstSharedPtr message,
467  const std::shared_ptr<ObservationBuffer> & buffer)
468 {
469  // buffer the point cloud
470  buffer->lock();
471  buffer->bufferCloud(*message);
472  buffer->unlock();
473 }
474 
475 void
477  double robot_x, double robot_y, double robot_yaw, double * min_x,
478  double * min_y, double * max_x, double * max_y)
479 {
480  std::lock_guard<Costmap2D::mutex_t> guard(*getMutex());
481  if (rolling_window_) {
482  updateOrigin(robot_x - getSizeInMetersX() / 2, robot_y - getSizeInMetersY() / 2);
483  }
484  if (!enabled_) {
485  return;
486  }
487  useExtraBounds(min_x, min_y, max_x, max_y);
488 
489  bool current = true;
490  std::vector<Observation::ConstSharedPtr> observations, clearing_observations;
491 
492  // get the marking observations
493  current = current && getMarkingObservations(observations);
494 
495  // get the clearing observations
496  current = current && getClearingObservations(clearing_observations);
497 
498  // update the global current status
499  setCurrent(current);
500 
501  // raytrace freespace
502  for (const auto & clearing_observation : clearing_observations) {
503  raytraceFreespace(*clearing_observation, min_x, min_y, max_x, max_y);
504  }
505 
506  // place the new obstacles into a priority queue... each with a priority of zero to begin with
507  for (const auto & observation : observations) {
508  const Observation & obs = *observation;
509 
510  const sensor_msgs::msg::PointCloud2 & cloud = obs.cloud_;
511 
512  const unsigned int max_range_cells = cellDistance(obs.obstacle_max_range_);
513  const unsigned int min_range_cells = cellDistance(obs.obstacle_min_range_);
514 
515  unsigned int x0, y0;
516  if (!worldToMap(obs.origin_.x, obs.origin_.y, x0, y0)) {
517  RCLCPP_DEBUG(logger_, "Sensor origin is out of map bounds");
518  continue;
519  }
520 
521  sensor_msgs::PointCloud2ConstIterator<float> iter_x(cloud, "x");
522  sensor_msgs::PointCloud2ConstIterator<float> iter_y(cloud, "y");
523  sensor_msgs::PointCloud2ConstIterator<float> iter_z(cloud, "z");
524 
525  for (; iter_x != iter_x.end(); ++iter_x, ++iter_y, ++iter_z) {
526  double px = *iter_x, py = *iter_y, pz = *iter_z;
527 
528  // if the obstacle is too low, we won't add it
529  if (pz < min_obstacle_height_) {
530  RCLCPP_DEBUG(logger_, "The point is too low");
531  continue;
532  }
533 
534  // if the obstacle is too high or too far away from the robot we won't add it
535  if (pz > max_obstacle_height_) {
536  RCLCPP_DEBUG(logger_, "The point is too high");
537  continue;
538  }
539 
540  // now we need to compute the map coordinates for the observation
541  unsigned int mx, my;
542  if (!worldToMap(px, py, mx, my)) {
543  RCLCPP_DEBUG(logger_, "Computing map coords failed");
544  continue;
545  }
546 
547  // Pre-filter by world distance to avoid cell discretization boundary
548  // effects where hypot(dx,dy) truncation makes far points appear in range
549  const double wdx = px - obs.origin_.x;
550  const double wdy = py - obs.origin_.y;
551  const double world_dist_sq = wdx * wdx + wdy * wdy;
552  if (world_dist_sq > obs.obstacle_max_range_ * obs.obstacle_max_range_) {
553  continue;
554  }
555  if (world_dist_sq < obs.obstacle_min_range_ * obs.obstacle_min_range_) {
556  continue;
557  }
558 
559  // compute the distance from the hitpoint to the pointcloud's origin
560  // Calculate the distance in cell space to match the ray trace algorithm
561  // used for clearing obstacles (see Costmap2D::raytraceLine).
562  const int dx = static_cast<int>(mx) - static_cast<int>(x0);
563  const int dy = static_cast<int>(my) - static_cast<int>(y0);
564  const unsigned int dist = static_cast<unsigned int>(
565  std::hypot(static_cast<double>(dx), static_cast<double>(dy)));
566 
567  // if the point is far enough away... we won't consider it
568  if (dist > max_range_cells) {
569  RCLCPP_DEBUG(logger_, "The point is too far away");
570  continue;
571  }
572 
573  // if the point is too close, do not consider it
574  if (dist < min_range_cells) {
575  RCLCPP_DEBUG(logger_, "The point is too close");
576  continue;
577  }
578 
579  unsigned int index = getIndex(mx, my);
580  costmap_[index] = LETHAL_OBSTACLE;
581  touch(px, py, min_x, min_y, max_x, max_y);
582  }
583  }
584 
585  updateFootprint(robot_x, robot_y, robot_yaw, min_x, min_y, max_x, max_y);
586 }
587 
588 void
590  double robot_x, double robot_y, double robot_yaw,
591  double * min_x, double * min_y,
592  double * max_x,
593  double * max_y)
594 {
595  if (!footprint_clearing_enabled_) {return;}
596  transformFootprint(robot_x, robot_y, robot_yaw, getFootprint(), transformed_footprint_);
597 
598  for (unsigned int i = 0; i < transformed_footprint_.size(); i++) {
599  touch(transformed_footprint_[i].x, transformed_footprint_[i].y, min_x, min_y, max_x, max_y);
600  }
601 }
602 
603 void
605  nav2_costmap_2d::Costmap2D & master_grid, int min_i, int min_j,
606  int max_i,
607  int max_j)
608 {
609  std::lock_guard<Costmap2D::mutex_t> guard(*getMutex());
610  if (!enabled_) {
611  return;
612  }
613 
614  // if not current due to reset, set current now after clearing
615  if (!isCurrent() && was_reset_) {
616  was_reset_ = false;
617  setCurrent(true);
618  }
619 
620  if (footprint_clearing_enabled_) {
621  setConvexPolygonCost(transformed_footprint_, nav2_costmap_2d::FREE_SPACE);
622  }
623 
624  switch (combination_method_) {
626  updateWithOverwrite(master_grid, min_i, min_j, max_i, max_j);
627  break;
629  updateWithMax(master_grid, min_i, min_j, max_i, max_j);
630  break;
632  updateWithMaxWithoutUnknownOverwrite(master_grid, min_i, min_j, max_i, max_j);
633  break;
634  default: // Nothing
635  break;
636  }
637 }
638 
639 void
640 ObstacleLayer::addStaticObservation(
642  bool marking, bool clearing)
643 {
644  const auto observation = Observation::make_shared(std::move(obs));
645  if (marking) {
646  static_marking_observations_.push_back(observation);
647  }
648  if (clearing) {
649  static_clearing_observations_.push_back(observation);
650  }
651 }
652 
653 void
654 ObstacleLayer::clearStaticObservations(bool marking, bool clearing)
655 {
656  if (marking) {
657  static_marking_observations_.clear();
658  }
659  if (clearing) {
660  static_clearing_observations_.clear();
661  }
662 }
663 
664 bool
666  std::vector<Observation::ConstSharedPtr> & marking_observations) const
667 {
668  bool current = true;
669  // get the marking observations
670  for (const auto & marking_buffer : marking_buffers_) {
671  if (marking_buffer) {
672  marking_buffer->lock();
673  marking_buffer->getObservations(marking_observations);
674  current = marking_buffer->isCurrent() && current;
675  marking_buffer->unlock();
676  }
677  }
678  marking_observations.insert(
679  marking_observations.end(),
680  static_marking_observations_.begin(), static_marking_observations_.end());
681  return current;
682 }
683 
684 bool
686  std::vector<Observation::ConstSharedPtr> & clearing_observations) const
687 {
688  bool current = true;
689  // get the clearing observations
690  for (const auto & clearing_buffer : clearing_buffers_) {
691  if (clearing_buffer) {
692  clearing_buffer->lock();
693  clearing_buffer->getObservations(clearing_observations);
694  current = clearing_buffer->isCurrent() && current;
695  clearing_buffer->unlock();
696  }
697  }
698  clearing_observations.insert(
699  clearing_observations.end(),
700  static_clearing_observations_.begin(), static_clearing_observations_.end());
701  return current;
702 }
703 
704 void
706  const Observation & clearing_observation, double * min_x,
707  double * min_y,
708  double * max_x,
709  double * max_y)
710 {
711  double ox = clearing_observation.origin_.x;
712  double oy = clearing_observation.origin_.y;
713  const sensor_msgs::msg::PointCloud2 & cloud = clearing_observation.cloud_;
714 
715  // get the map coordinates of the origin of the sensor
716  unsigned int x0, y0;
717  if (!worldToMap(ox, oy, x0, y0)) {
718  RCLCPP_WARN(
719  logger_,
720  "Sensor origin at (%.2f, %.2f) is out of map bounds (%.2f, %.2f) to (%.2f, %.2f). "
721  "The costmap cannot raytrace for it.",
722  ox, oy,
723  origin_x_, origin_y_,
724  origin_x_ + getSizeInMetersX(), origin_y_ + getSizeInMetersY());
725  return;
726  }
727 
728  // we can pre-compute the endpoints of the map outside of the inner loop... we'll need these later
729  double origin_x = origin_x_, origin_y = origin_y_;
730  double map_end_x = origin_x + size_x_ * resolution_;
731  double map_end_y = origin_y + size_y_ * resolution_;
732 
733 
734  touch(ox, oy, min_x, min_y, max_x, max_y);
735 
736  // for each point in the cloud, we want to trace a line from the origin
737  // and clear obstacles along it
738  sensor_msgs::PointCloud2ConstIterator<float> iter_x(cloud, "x");
739  sensor_msgs::PointCloud2ConstIterator<float> iter_y(cloud, "y");
740 
741  for (; iter_x != iter_x.end(); ++iter_x, ++iter_y) {
742  double wx = *iter_x;
743  double wy = *iter_y;
744 
745  // now we also need to make sure that the endpoint we're raytracing
746  // to isn't off the costmap and scale if necessary
747  double a = wx - ox;
748  double b = wy - oy;
749 
750  // the minimum value to raytrace from is the origin
751  if (wx < origin_x) {
752  double t = (origin_x - ox) / a;
753  wx = origin_x;
754  wy = oy + b * t;
755  }
756  if (wy < origin_y) {
757  double t = (origin_y - oy) / b;
758  wx = ox + a * t;
759  wy = origin_y;
760  }
761 
762  // the maximum value to raytrace to is the end of the map
763  if (wx > map_end_x) {
764  double t = (map_end_x - ox) / a;
765  wx = map_end_x - .001;
766  wy = oy + b * t;
767  }
768  if (wy > map_end_y) {
769  double t = (map_end_y - oy) / b;
770  wx = ox + a * t;
771  wy = map_end_y - .001;
772  }
773 
774  // now that the vector is scaled correctly... we'll get the map coordinates of its endpoint
775  unsigned int x1, y1;
776 
777  // check for legality just in case
778  if (!worldToMap(wx, wy, x1, y1)) {
779  continue;
780  }
781 
782  unsigned int cell_raytrace_max_range = cellDistance(clearing_observation.raytrace_max_range_);
783  unsigned int cell_raytrace_min_range = cellDistance(clearing_observation.raytrace_min_range_);
784  MarkCell marker(costmap_, FREE_SPACE);
785  // and finally... we can execute our trace to clear obstacles along that line
786  nav2_util::raytraceLine(
787  marker, x0, y0, x1, y1, size_x_, cell_raytrace_max_range, cell_raytrace_min_range);
788 
790  ox, oy, wx, wy, clearing_observation.raytrace_max_range_,
791  clearing_observation.raytrace_min_range_, min_x, min_y, max_x,
792  max_y);
793  }
794 }
795 
796 void
798 {
799  auto node = node_.lock();
800  // Add callback for dynamic parameters
801  post_set_params_handler_ = node->add_post_set_parameters_callback(
802  std::bind(
804  this, std::placeholders::_1));
805  on_set_params_handler_ = node->add_on_set_parameters_callback(
806  std::bind(
808  this, std::placeholders::_1));
809  for (auto & notifier : observation_notifiers_) {
810  notifier->clear();
811  }
812 
813  // if we're stopped we need to re-subscribe to topics
814  for (unsigned int i = 0; i < observation_subscribers_.size(); ++i) {
815  if (observation_subscribers_[i] != NULL) {
816  observation_subscribers_[i]->subscribe();
817  }
818  }
820 }
821 
822 void
824 {
825  auto node = node_.lock();
826  if (post_set_params_handler_ && node) {
827  node->remove_post_set_parameters_callback(post_set_params_handler_.get());
828  }
829  post_set_params_handler_.reset();
830  if (on_set_params_handler_ && node) {
831  node->remove_on_set_parameters_callback(on_set_params_handler_.get());
832  }
833  on_set_params_handler_.reset();
834 
835  for (unsigned int i = 0; i < observation_subscribers_.size(); ++i) {
836  if (observation_subscribers_[i] != NULL) {
837  observation_subscribers_[i]->unsubscribe();
838  }
839  }
840 }
841 
842 void
844  double ox, double oy, double wx, double wy, double max_range, double min_range,
845  double * min_x, double * min_y, double * max_x, double * max_y)
846 {
847  double dx = wx - ox, dy = wy - oy;
848  double full_distance = hypot(dx, dy);
849  if (full_distance < min_range) {
850  return;
851  }
852  double scale = std::min(1.0, max_range / full_distance);
853  double ex = ox + dx * scale, ey = oy + dy * scale;
854  touch(ex, ey, min_x, min_y, max_x, max_y);
855 }
856 
857 void
859 {
860  resetMaps();
862  setCurrent(false);
863  was_reset_ = true;
864 }
865 
866 void
868 {
869  for (const auto & observation_buffer : observation_buffers_) {
870  if (observation_buffer) {
871  observation_buffer->resetLastUpdated();
872  }
873  }
874 }
875 
876 } // namespace nav2_costmap_2d
A QoS profile for best-effort sensor data with a history of 10 messages.
A 2D costmap provides a mapping between points in the world and their associated "costs".
Definition: costmap_2d.hpp:69
unsigned int getIndex(unsigned int mx, unsigned int my) const
Given two map coordinates... compute the associated index.
Definition: costmap_2d.hpp:231
bool worldToMap(double wx, double wy, unsigned int &mx, unsigned int &my) const
Convert from world coordinates to map coordinates.
Definition: costmap_2d.cpp:292
virtual void updateOrigin(double new_origin_x, double new_origin_y)
Move the origin of the costmap to a new location.... keeping data when it can.
Definition: costmap_2d.cpp:350
bool setConvexPolygonCost(const std::vector< geometry_msgs::msg::Point > &polygon, unsigned char cost_value)
Sets the cost of a convex polygon to a desired value.
Definition: costmap_2d.cpp:406
double getSizeInMetersY() const
Accessor for the y size of the costmap in meters.
Definition: costmap_2d.cpp:563
double getSizeInMetersX() const
Accessor for the x size of the costmap in meters.
Definition: costmap_2d.cpp:558
virtual void resetMaps()
Resets the costmap and static_map to be unknown space.
Definition: costmap_2d.cpp:125
unsigned int cellDistance(double world_dist)
Given distance in the world... convert it to cells.
Definition: costmap_2d.cpp:254
void touch(double x, double y, double *min_x, double *min_y, double *max_x, double *max_y)
virtual void matchSize()
Match the size of the master costmap.
CombinationMethod combination_method_from_int(const int value)
Converts an integer to a CombinationMethod enum and logs on failure.
Abstract class for layered costmap plugin implementations.
Definition: layer.hpp:60
std::string joinWithParentNamespace(const std::string &topic)
Definition: layer.cpp:83
void setCurrent(bool current)
Set whether the data in the layer is up to date.
Definition: layer.hpp:147
bool isCurrent() const
Check to make sure all the data in the layer is up to date. If the layer is not up to date,...
Definition: layer.hpp:138
const std::vector< geometry_msgs::msg::Point > & getFootprint() const
Convenience function for layered_costmap_->getFootprint().
Definition: layer.cpp:70
bool isRolling()
If this costmap is rolling or not.
Takes in point clouds from sensors, transforms them to the desired frame, and stores them.
Stores an observation in terms of a point cloud and the origin of the source.
Definition: observation.hpp:47
Takes in laser and pointcloud data to populate into 2D costmap.
void pointCloud2Callback(sensor_msgs::msg::PointCloud2::ConstSharedPtr message, const std::shared_ptr< nav2_costmap_2d::ObservationBuffer > &buffer)
A callback to handle buffering PointCloud2 messages.
std::vector< std::shared_ptr< nav2_costmap_2d::ObservationBuffer > > marking_buffers_
Used to store observation buffers used for marking obstacles.
virtual void activate()
Activate the layer.
std::vector< std::shared_ptr< tf2_ros::MessageFilterBase > > observation_notifiers_
Used to make sure that transforms are available for each sensor.
std::string global_frame_
The global frame for the costmap.
void updateRaytraceBounds(double ox, double oy, double wx, double wy, double max_range, double min_range, double *min_x, double *min_y, double *max_x, double *max_y)
Process update costmap with raytracing the window bounds.
void laserScanCallback(sensor_msgs::msg::LaserScan::ConstSharedPtr message, const std::shared_ptr< nav2_costmap_2d::ObservationBuffer > &buffer)
A callback to handle buffering LaserScan messages.
bool getMarkingObservations(std::vector< nav2_costmap_2d::Observation::ConstSharedPtr > &marking_observations) const
Get the observations used to mark space.
void resetBuffersLastUpdated()
triggers the update of observations buffer
bool getClearingObservations(std::vector< nav2_costmap_2d::Observation::ConstSharedPtr > &clearing_observations) const
Get the observations used to clear space.
rclcpp::node_interfaces::PostSetParametersCallbackHandle::SharedPtr post_set_params_handler_
Dynamic parameters handler.
std::vector< std::shared_ptr< nav2_costmap_2d::ObservationBuffer > > clearing_buffers_
Used to store observation buffers used for clearing obstacles.
void laserScanValidInfCallback(sensor_msgs::msg::LaserScan::ConstSharedPtr message, const std::shared_ptr< nav2_costmap_2d::ObservationBuffer > &buffer)
A callback to handle buffering LaserScan messages which need filtering to turn Inf values into range_...
std::vector< std::shared_ptr< message_filters::SubscriberBase< rclcpp_lifecycle::LifecycleNode > > > observation_subscribers_
Used for the observation message filters.
void updateFootprint(double robot_x, double robot_y, double robot_yaw, double *min_x, double *min_y, double *max_x, double *max_y)
Clear costmap layer info below the robot's footprint.
rcl_interfaces::msg::SetParametersResult validateParameterUpdatesCallback(const std::vector< rclcpp::Parameter > &parameters)
Validate incoming parameter updates before applying them. This callback is triggered when one or more...
virtual void deactivate()
Deactivate the layer.
virtual void updateCosts(nav2_costmap_2d::Costmap2D &master_grid, int min_i, int min_j, int max_i, int max_j)
Update the costs in the master costmap in the window.
virtual ~ObstacleLayer()
A destructor.
std::vector< std::shared_ptr< nav2_costmap_2d::ObservationBuffer > > observation_buffers_
Used to store observations from various sensors.
virtual void onInitialize()
Initialization process of layer on startup.
double min_obstacle_height_
Max Obstacle Height.
laser_geometry::LaserProjection projector_
Used to project laser scans into point clouds.
double max_obstacle_height_
Max Obstacle Height.
virtual void reset()
Reset this costmap.
virtual void raytraceFreespace(const nav2_costmap_2d::Observation &clearing_observation, double *min_x, double *min_y, double *max_x, double *max_y)
Clear freespace based on one observation.
void updateParametersCallback(const std::vector< rclcpp::Parameter > &parameters)
Apply parameter updates after validation This callback is executed when parameters have been successf...
virtual void updateBounds(double robot_x, double robot_y, double robot_yaw, double *min_x, double *min_y, double *max_x, double *max_y)
Update the bounds of the master costmap by this layer's update dimensions.
void transformFootprint(double x, double y, double theta, const std::vector< geometry_msgs::msg::Point > &footprint_spec, std::vector< geometry_msgs::msg::Point > &oriented_footprint)
Given a pose and base footprint, build the oriented footprint of the robot (list of Points)
Definition: footprint.cpp:112