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  // the height filter of the observation buffer can leave a cloud without points
513  if (cloud.height == 0 || cloud.width == 0) {
514  continue;
515  }
516 
517  const unsigned int max_range_cells = cellDistance(obs.obstacle_max_range_);
518  const unsigned int min_range_cells = cellDistance(obs.obstacle_min_range_);
519 
520  unsigned int x0, y0;
521  if (!worldToMap(obs.origin_.x, obs.origin_.y, x0, y0)) {
522  RCLCPP_DEBUG(logger_, "Sensor origin is out of map bounds");
523  continue;
524  }
525 
526  sensor_msgs::PointCloud2ConstIterator<float> iter_x(cloud, "x");
527  sensor_msgs::PointCloud2ConstIterator<float> iter_y(cloud, "y");
528  sensor_msgs::PointCloud2ConstIterator<float> iter_z(cloud, "z");
529 
530  for (; iter_x != iter_x.end(); ++iter_x, ++iter_y, ++iter_z) {
531  double px = *iter_x, py = *iter_y, pz = *iter_z;
532 
533  // if the obstacle is too low, we won't add it
534  if (pz < min_obstacle_height_) {
535  RCLCPP_DEBUG(logger_, "The point is too low");
536  continue;
537  }
538 
539  // if the obstacle is too high or too far away from the robot we won't add it
540  if (pz > max_obstacle_height_) {
541  RCLCPP_DEBUG(logger_, "The point is too high");
542  continue;
543  }
544 
545  // now we need to compute the map coordinates for the observation
546  unsigned int mx, my;
547  if (!worldToMap(px, py, mx, my)) {
548  RCLCPP_DEBUG(logger_, "Computing map coords failed");
549  continue;
550  }
551 
552  // Pre-filter by world distance to avoid cell discretization boundary
553  // effects where hypot(dx,dy) truncation makes far points appear in range
554  const double wdx = px - obs.origin_.x;
555  const double wdy = py - obs.origin_.y;
556  const double world_dist_sq = wdx * wdx + wdy * wdy;
557  if (world_dist_sq > obs.obstacle_max_range_ * obs.obstacle_max_range_) {
558  continue;
559  }
560  if (world_dist_sq < obs.obstacle_min_range_ * obs.obstacle_min_range_) {
561  continue;
562  }
563 
564  // compute the distance from the hitpoint to the pointcloud's origin
565  // Calculate the distance in cell space to match the ray trace algorithm
566  // used for clearing obstacles (see Costmap2D::raytraceLine).
567  const int dx = static_cast<int>(mx) - static_cast<int>(x0);
568  const int dy = static_cast<int>(my) - static_cast<int>(y0);
569  const unsigned int dist = static_cast<unsigned int>(
570  std::hypot(static_cast<double>(dx), static_cast<double>(dy)));
571 
572  // if the point is far enough away... we won't consider it
573  if (dist > max_range_cells) {
574  RCLCPP_DEBUG(logger_, "The point is too far away");
575  continue;
576  }
577 
578  // if the point is too close, do not consider it
579  if (dist < min_range_cells) {
580  RCLCPP_DEBUG(logger_, "The point is too close");
581  continue;
582  }
583 
584  unsigned int index = getIndex(mx, my);
585  costmap_[index] = LETHAL_OBSTACLE;
586  touch(px, py, min_x, min_y, max_x, max_y);
587  }
588  }
589 
590  updateFootprint(robot_x, robot_y, robot_yaw, min_x, min_y, max_x, max_y);
591 }
592 
593 void
595  double robot_x, double robot_y, double robot_yaw,
596  double * min_x, double * min_y,
597  double * max_x,
598  double * max_y)
599 {
600  if (!footprint_clearing_enabled_) {return;}
601  transformFootprint(robot_x, robot_y, robot_yaw, getFootprint(), transformed_footprint_);
602 
603  for (unsigned int i = 0; i < transformed_footprint_.size(); i++) {
604  touch(transformed_footprint_[i].x, transformed_footprint_[i].y, min_x, min_y, max_x, max_y);
605  }
606 }
607 
608 void
610  nav2_costmap_2d::Costmap2D & master_grid, int min_i, int min_j,
611  int max_i,
612  int max_j)
613 {
614  std::lock_guard<Costmap2D::mutex_t> guard(*getMutex());
615  if (!enabled_) {
616  return;
617  }
618 
619  // if not current due to reset, set current now after clearing
620  if (!isCurrent() && was_reset_) {
621  was_reset_ = false;
622  setCurrent(true);
623  }
624 
625  if (footprint_clearing_enabled_) {
626  setConvexPolygonCost(transformed_footprint_, nav2_costmap_2d::FREE_SPACE);
627  }
628 
629  switch (combination_method_) {
631  updateWithOverwrite(master_grid, min_i, min_j, max_i, max_j);
632  break;
634  updateWithMax(master_grid, min_i, min_j, max_i, max_j);
635  break;
637  updateWithMaxWithoutUnknownOverwrite(master_grid, min_i, min_j, max_i, max_j);
638  break;
639  default: // Nothing
640  break;
641  }
642 }
643 
644 void
645 ObstacleLayer::addStaticObservation(
647  bool marking, bool clearing)
648 {
649  const auto observation = Observation::make_shared(std::move(obs));
650  if (marking) {
651  static_marking_observations_.push_back(observation);
652  }
653  if (clearing) {
654  static_clearing_observations_.push_back(observation);
655  }
656 }
657 
658 void
659 ObstacleLayer::clearStaticObservations(bool marking, bool clearing)
660 {
661  if (marking) {
662  static_marking_observations_.clear();
663  }
664  if (clearing) {
665  static_clearing_observations_.clear();
666  }
667 }
668 
669 bool
671  std::vector<Observation::ConstSharedPtr> & marking_observations) const
672 {
673  bool current = true;
674  // get the marking observations
675  for (const auto & marking_buffer : marking_buffers_) {
676  if (marking_buffer) {
677  marking_buffer->lock();
678  marking_buffer->getObservations(marking_observations);
679  current = marking_buffer->isCurrent() && current;
680  marking_buffer->unlock();
681  }
682  }
683  marking_observations.insert(
684  marking_observations.end(),
685  static_marking_observations_.begin(), static_marking_observations_.end());
686  return current;
687 }
688 
689 bool
691  std::vector<Observation::ConstSharedPtr> & clearing_observations) const
692 {
693  bool current = true;
694  // get the clearing observations
695  for (const auto & clearing_buffer : clearing_buffers_) {
696  if (clearing_buffer) {
697  clearing_buffer->lock();
698  clearing_buffer->getObservations(clearing_observations);
699  current = clearing_buffer->isCurrent() && current;
700  clearing_buffer->unlock();
701  }
702  }
703  clearing_observations.insert(
704  clearing_observations.end(),
705  static_clearing_observations_.begin(), static_clearing_observations_.end());
706  return current;
707 }
708 
709 void
711  const Observation & clearing_observation, double * min_x,
712  double * min_y,
713  double * max_x,
714  double * max_y)
715 {
716  double ox = clearing_observation.origin_.x;
717  double oy = clearing_observation.origin_.y;
718  const sensor_msgs::msg::PointCloud2 & cloud = clearing_observation.cloud_;
719 
720  // get the map coordinates of the origin of the sensor
721  unsigned int x0, y0;
722  if (!worldToMap(ox, oy, x0, y0)) {
723  RCLCPP_WARN(
724  logger_,
725  "Sensor origin at (%.2f, %.2f) is out of map bounds (%.2f, %.2f) to (%.2f, %.2f). "
726  "The costmap cannot raytrace for it.",
727  ox, oy,
728  origin_x_, origin_y_,
729  origin_x_ + getSizeInMetersX(), origin_y_ + getSizeInMetersY());
730  return;
731  }
732 
733  // we can pre-compute the endpoints of the map outside of the inner loop... we'll need these later
734  double origin_x = origin_x_, origin_y = origin_y_;
735  double map_end_x = origin_x + size_x_ * resolution_;
736  double map_end_y = origin_y + size_y_ * resolution_;
737 
738 
739  touch(ox, oy, min_x, min_y, max_x, max_y);
740 
741  // the height filter of the observation buffer can leave a cloud without points
742  if (cloud.height == 0 || cloud.width == 0) {
743  return;
744  }
745 
746  // for each point in the cloud, we want to trace a line from the origin
747  // and clear obstacles along it
748  sensor_msgs::PointCloud2ConstIterator<float> iter_x(cloud, "x");
749  sensor_msgs::PointCloud2ConstIterator<float> iter_y(cloud, "y");
750 
751  for (; iter_x != iter_x.end(); ++iter_x, ++iter_y) {
752  double wx = *iter_x;
753  double wy = *iter_y;
754 
755  // now we also need to make sure that the endpoint we're raytracing
756  // to isn't off the costmap and scale if necessary
757  double a = wx - ox;
758  double b = wy - oy;
759 
760  // the minimum value to raytrace from is the origin
761  if (wx < origin_x) {
762  double t = (origin_x - ox) / a;
763  wx = origin_x;
764  wy = oy + b * t;
765  }
766  if (wy < origin_y) {
767  double t = (origin_y - oy) / b;
768  wx = ox + a * t;
769  wy = origin_y;
770  }
771 
772  // the maximum value to raytrace to is the end of the map
773  if (wx > map_end_x) {
774  double t = (map_end_x - ox) / a;
775  wx = map_end_x - .001;
776  wy = oy + b * t;
777  }
778  if (wy > map_end_y) {
779  double t = (map_end_y - oy) / b;
780  wx = ox + a * t;
781  wy = map_end_y - .001;
782  }
783 
784  // now that the vector is scaled correctly... we'll get the map coordinates of its endpoint
785  unsigned int x1, y1;
786 
787  // check for legality just in case
788  if (!worldToMap(wx, wy, x1, y1)) {
789  continue;
790  }
791 
792  unsigned int cell_raytrace_max_range = cellDistance(clearing_observation.raytrace_max_range_);
793  unsigned int cell_raytrace_min_range = cellDistance(clearing_observation.raytrace_min_range_);
794  MarkCell marker(costmap_, FREE_SPACE);
795  // and finally... we can execute our trace to clear obstacles along that line
796  nav2_util::raytraceLine(
797  marker, x0, y0, x1, y1, size_x_, cell_raytrace_max_range, cell_raytrace_min_range);
798 
800  ox, oy, wx, wy, clearing_observation.raytrace_max_range_,
801  clearing_observation.raytrace_min_range_, min_x, min_y, max_x,
802  max_y);
803  }
804 }
805 
806 void
808 {
809  auto node = node_.lock();
810  // Add callback for dynamic parameters
811  post_set_params_handler_ = node->add_post_set_parameters_callback(
812  std::bind(
814  this, std::placeholders::_1));
815  on_set_params_handler_ = node->add_on_set_parameters_callback(
816  std::bind(
818  this, std::placeholders::_1));
819  for (auto & notifier : observation_notifiers_) {
820  notifier->clear();
821  }
822 
823  // if we're stopped we need to re-subscribe to topics
824  for (unsigned int i = 0; i < observation_subscribers_.size(); ++i) {
825  if (observation_subscribers_[i] != NULL) {
826  observation_subscribers_[i]->subscribe();
827  }
828  }
830 }
831 
832 void
834 {
835  auto node = node_.lock();
836  if (post_set_params_handler_ && node) {
837  node->remove_post_set_parameters_callback(post_set_params_handler_.get());
838  }
839  post_set_params_handler_.reset();
840  if (on_set_params_handler_ && node) {
841  node->remove_on_set_parameters_callback(on_set_params_handler_.get());
842  }
843  on_set_params_handler_.reset();
844 
845  for (unsigned int i = 0; i < observation_subscribers_.size(); ++i) {
846  if (observation_subscribers_[i] != NULL) {
847  observation_subscribers_[i]->unsubscribe();
848  }
849  }
850 }
851 
852 void
854  double ox, double oy, double wx, double wy, double max_range, double min_range,
855  double * min_x, double * min_y, double * max_x, double * max_y)
856 {
857  double dx = wx - ox, dy = wy - oy;
858  double full_distance = hypot(dx, dy);
859  if (full_distance < min_range) {
860  return;
861  }
862  double scale = std::min(1.0, max_range / full_distance);
863  double ex = ox + dx * scale, ey = oy + dy * scale;
864  touch(ex, ey, min_x, min_y, max_x, max_y);
865 }
866 
867 void
869 {
870  resetMaps();
872  setCurrent(false);
873  was_reset_ = true;
874 }
875 
876 void
878 {
879  for (const auto & observation_buffer : observation_buffers_) {
880  if (observation_buffer) {
881  observation_buffer->resetLastUpdated();
882  }
883  }
884 }
885 
886 } // 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:62
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:149
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:140
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