Nav2 Navigation Stack - lyrical  lyrical
ROS 2 Navigation Stack
range_sensor_layer.cpp
1 /*
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2018 David V. Lu!!
5  * Copyright (c) 2020, Bytes Robotics
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 the copyright holder 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 HOLDER 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 
36 #include <angles/angles.h>
37 #include <algorithm>
38 #include <list>
39 #include <limits>
40 #include <string>
41 #include <vector>
42 
43 #include "pluginlib/class_list_macros.hpp"
44 #include "geometry_msgs/msg/point_stamped.hpp"
45 #include "nav2_costmap_2d/range_sensor_layer.hpp"
46 
48 
49 using nav2_costmap_2d::LETHAL_OBSTACLE;
50 using nav2_costmap_2d::INSCRIBED_INFLATED_OBSTACLE;
51 using nav2_costmap_2d::NO_INFORMATION;
52 
53 using namespace std::literals::chrono_literals;
54 
55 namespace nav2_costmap_2d
56 {
57 
58 RangeSensorLayer::RangeSensorLayer() {}
59 
60 void RangeSensorLayer::onInitialize()
61 {
62  setCurrent(true);
63  was_reset_ = false;
64  buffered_readings_ = 0;
65  last_reading_time_ = clock_->now();
66  default_value_ = to_cost(0.5);
67 
68  matchSize();
69  resetRange();
70 
71  auto node = node_.lock();
72  if (!node) {
73  throw std::runtime_error{"Failed to lock node"};
74  }
75 
76  enabled_ = node->declare_or_get_parameter(name_ + "." + "enabled", true);
77  phi_v_ = node->declare_or_get_parameter(name_ + "." + "phi", 1.2);
78  inflate_cone_ = node->declare_or_get_parameter(name_ + "." + "inflate_cone", 1.0);
79  no_readings_timeout_ = node->declare_or_get_parameter(
80  name_ + "." + "no_readings_timeout", 0.0);
81  clear_threshold_ = node->declare_or_get_parameter(
82  name_ + "." + "clear_threshold", 0.2);
83  mark_threshold_ = node->declare_or_get_parameter(
84  name_ + "." + "mark_threshold", 0.8);
85  clear_on_max_reading_ = node->declare_or_get_parameter(
86  name_ + "." + "clear_on_max_reading", false);
87 
88  double temp_tf_tol = 0.0;
89  node->get_parameter("transform_tolerance", temp_tf_tol);
90  transform_tolerance_ = tf2::durationFromSec(temp_tf_tol);
91 
92  std::vector<std::string> topic_names = node->declare_or_get_parameter(
93  name_ + "." + "topics", std::vector<std::string>{});
94 
95  InputSensorType input_sensor_type = InputSensorType::ALL;
96  std::string sensor_type_name = node->declare_or_get_parameter(
97  name_ + "." + "input_sensor_type", std::string("ALL"));
98 
99  std::transform(
100  sensor_type_name.begin(), sensor_type_name.end(),
101  sensor_type_name.begin(), ::toupper);
102  RCLCPP_INFO(
103  logger_, "%s: %s as input_sensor_type given",
104  name_.c_str(), sensor_type_name.c_str());
105 
106  if (sensor_type_name == "VARIABLE") {
107  input_sensor_type = InputSensorType::VARIABLE;
108  } else if (sensor_type_name == "FIXED") {
109  input_sensor_type = InputSensorType::FIXED;
110  } else if (sensor_type_name == "ALL") {
111  input_sensor_type = InputSensorType::ALL;
112  } else {
113  RCLCPP_ERROR(
114  logger_, "%s: Invalid input sensor type: %s. Defaulting to ALL.",
115  name_.c_str(), sensor_type_name.c_str());
116  }
117 
118  // Validate topic names list: it must be a (normally non-empty) list of strings
119  if (topic_names.empty()) {
120  RCLCPP_FATAL(
121  logger_, "Invalid topic names list: it must"
122  "be a non-empty list of strings");
123  return;
124  }
125 
126  // Traverse the topic names list subscribing to all of them with the same callback method
127  for (auto & topic_name : topic_names) {
128  topic_name = joinWithParentNamespace(topic_name);
129  if (input_sensor_type == InputSensorType::VARIABLE) {
130  processRangeMessageFunc_ = std::bind(
131  &RangeSensorLayer::processVariableRangeMsg, this,
132  std::placeholders::_1);
133  } else if (input_sensor_type == InputSensorType::FIXED) {
134  processRangeMessageFunc_ = std::bind(
135  &RangeSensorLayer::processFixedRangeMsg, this,
136  std::placeholders::_1);
137  } else if (input_sensor_type == InputSensorType::ALL) {
138  processRangeMessageFunc_ = std::bind(
139  &RangeSensorLayer::processRangeMsg, this,
140  std::placeholders::_1);
141  } else {
142  RCLCPP_ERROR(
143  logger_,
144  "%s: Invalid input sensor type: %s. Did you make a new type"
145  "and forgot to choose the subscriber for it?",
146  name_.c_str(), sensor_type_name.c_str());
147  }
148  range_subs_.push_back(
149  node->create_subscription<sensor_msgs::msg::Range>(
150  topic_name,
151  std::bind(
152  &RangeSensorLayer::bufferIncomingRangeMsg, this,
153  std::placeholders::_1),
155 
156  RCLCPP_INFO(
157  logger_, "RangeSensorLayer: subscribed to "
158  "topic %s", range_subs_.back()->get_topic_name());
159  }
160  global_frame_ = layered_costmap_->getGlobalFrameID();
161 }
162 
163 
164 double RangeSensorLayer::gamma(double theta)
165 {
166  if (fabs(theta) > max_angle_) {
167  return 0.0;
168  } else {
169  return 1 - pow(theta / max_angle_, 2);
170  }
171 }
172 
173 double RangeSensorLayer::delta(double phi)
174 {
175  return 1 - (1 + tanh(2 * (phi - phi_v_))) / 2;
176 }
177 
178 void RangeSensorLayer::get_deltas(double angle, double * dx, double * dy)
179 {
180  double ta = tan(angle);
181  if (ta == 0) {
182  *dx = 0;
183  } else {
184  *dx = resolution_ / ta;
185  }
186 
187  *dx = copysign(*dx, cos(angle));
188  *dy = copysign(resolution_, sin(angle));
189 }
190 
191 double RangeSensorLayer::sensor_model(double r, double phi, double theta)
192 {
193  double lbda = delta(phi) * gamma(theta);
194 
195  double delta = resolution_;
196 
197  if (phi >= 0.0 && phi < r - 2 * delta * r) {
198  return (1 - lbda) * (0.5);
199  } else if (phi < r - delta * r) {
200  return lbda * 0.5 * pow((phi - (r - 2 * delta * r)) / (delta * r), 2) +
201  (1 - lbda) * .5;
202  } else if (phi < r + delta * r) {
203  double J = (r - phi) / (delta * r);
204  return lbda * ((1 - (0.5) * pow(J, 2)) - 0.5) + 0.5;
205  } else {
206  return 0.5;
207  }
208 }
209 
210 void RangeSensorLayer::bufferIncomingRangeMsg(
211  const sensor_msgs::msg::Range::ConstSharedPtr & range_message)
212 {
213  range_message_mutex_.lock();
214  range_msgs_buffer_.push_back(*range_message);
215  range_message_mutex_.unlock();
216 }
217 
218 void RangeSensorLayer::updateCostmap()
219 {
220  std::list<sensor_msgs::msg::Range> range_msgs_buffer_copy;
221 
222  range_message_mutex_.lock();
223  range_msgs_buffer_copy = std::list<sensor_msgs::msg::Range>(range_msgs_buffer_);
224  range_msgs_buffer_.clear();
225  range_message_mutex_.unlock();
226 
227  for (auto & range_msgs_it : range_msgs_buffer_copy) {
228  processRangeMessageFunc_(range_msgs_it);
229  }
230 }
231 
232 void RangeSensorLayer::processRangeMsg(sensor_msgs::msg::Range & range_message)
233 {
234  if (range_message.min_range == range_message.max_range) {
235  processFixedRangeMsg(range_message);
236  } else {
237  processVariableRangeMsg(range_message);
238  }
239 }
240 
241 void RangeSensorLayer::processFixedRangeMsg(sensor_msgs::msg::Range & range_message)
242 {
243  if (!std::isinf(range_message.range)) {
244  RCLCPP_ERROR(
245  logger_,
246  "Fixed distance ranger (min_range == max_range) in frame %s sent invalid value. "
247  "Only -Inf (== object detected) and Inf (== no object detected) are valid.",
248  range_message.header.frame_id.c_str());
249  return;
250  }
251 
252  bool clear_sensor_cone = false;
253 
254  if (range_message.range > 0) { // +inf
255  if (!clear_on_max_reading_) {
256  return; // no clearing at all
257  }
258  clear_sensor_cone = true;
259  }
260 
261  range_message.range = range_message.min_range;
262 
263  updateCostmap(range_message, clear_sensor_cone);
264 }
265 
266 void RangeSensorLayer::processVariableRangeMsg(sensor_msgs::msg::Range & range_message)
267 {
268  if (range_message.range < range_message.min_range || range_message.range >
269  range_message.max_range)
270  {
271  return;
272  }
273 
274  bool clear_sensor_cone = false;
275 
276  if (range_message.range >= range_message.max_range && clear_on_max_reading_) {
277  clear_sensor_cone = true;
278  }
279 
280  updateCostmap(range_message, clear_sensor_cone);
281 }
282 
283 void RangeSensorLayer::updateCostmap(
284  sensor_msgs::msg::Range & range_message,
285  bool clear_sensor_cone)
286 {
287  max_angle_ = range_message.field_of_view / 2;
288 
289  geometry_msgs::msg::PointStamped in, out;
290  in.header.stamp = range_message.header.stamp;
291  in.header.frame_id = range_message.header.frame_id;
292 
293  if (!tf_->canTransform(
294  in.header.frame_id, global_frame_,
295  tf2_ros::fromMsg(in.header.stamp),
296  tf2_ros::fromRclcpp(transform_tolerance_)))
297  {
298  RCLCPP_INFO(
299  logger_, "Range sensor layer can't transform from %s to %s",
300  global_frame_.c_str(), in.header.frame_id.c_str());
301  return;
302  }
303 
304  tf_->transform(in, out, global_frame_, transform_tolerance_);
305 
306  double ox = out.point.x, oy = out.point.y;
307 
308  in.point.x = range_message.range;
309 
310  tf_->transform(in, out, global_frame_, transform_tolerance_);
311 
312  double tx = out.point.x, ty = out.point.y;
313 
314  // calculate target props
315  double dx = tx - ox, dy = ty - oy, theta = atan2(dy, dx), d = sqrt(dx * dx + dy * dy);
316 
317  // Integer Bounds of Update
318  int bx0, by0, bx1, by1;
319 
320  // Triangle that will be really updated; the other cells within bounds are ignored
321  // This triangle is formed by the origin and left and right sides of sonar cone
322  int Ox, Oy, Ax, Ay, Bx, By;
323 
324  // Bounds includes the origin
325  worldToMapNoBounds(ox, oy, Ox, Oy);
326  bx1 = bx0 = Ox;
327  by1 = by0 = Oy;
328  touch(ox, oy, &min_x_, &min_y_, &max_x_, &max_y_);
329 
330  // Update Map with Target Point
331  unsigned int aa, ab;
332  if (worldToMap(tx, ty, aa, ab)) {
333  setCost(aa, ab, 233);
334  touch(tx, ty, &min_x_, &min_y_, &max_x_, &max_y_);
335  }
336 
337  double mx, my;
338 
339  // Update left side of sonar cone
340  mx = ox + cos(theta - max_angle_) * d * 1.2;
341  my = oy + sin(theta - max_angle_) * d * 1.2;
342  worldToMapNoBounds(mx, my, Ax, Ay);
343  bx0 = std::min(bx0, Ax);
344  bx1 = std::max(bx1, Ax);
345  by0 = std::min(by0, Ay);
346  by1 = std::max(by1, Ay);
347  touch(mx, my, &min_x_, &min_y_, &max_x_, &max_y_);
348 
349  // Update right side of sonar cone
350  mx = ox + cos(theta + max_angle_) * d * 1.2;
351  my = oy + sin(theta + max_angle_) * d * 1.2;
352 
353  worldToMapNoBounds(mx, my, Bx, By);
354  bx0 = std::min(bx0, Bx);
355  bx1 = std::max(bx1, Bx);
356  by0 = std::min(by0, By);
357  by1 = std::max(by1, By);
358  touch(mx, my, &min_x_, &min_y_, &max_x_, &max_y_);
359 
360  // Limit Bounds to Grid
361  bx0 = std::max(0, bx0);
362  by0 = std::max(0, by0);
363  bx1 = std::min(static_cast<int>(size_x_) - 1, bx1);
364  by1 = std::min(static_cast<int>(size_y_) - 1, by1);
365 
366  // Signed indices, as the bounds are empty if the cone lies wholly off the grid
367  for (int x = bx0; x <= bx1; x++) {
368  for (int y = by0; y <= by1; y++) {
369  bool update_xy_cell = true;
370 
371  // Unless inflate_cone_ is set to 100 %, we update cells only within the
372  // (partially inflated) sensor cone, projected on the costmap as a triangle.
373  // 0 % corresponds to just the triangle, but if your sensor fov is very
374  // narrow, the covered area can become zero due to cell discretization.
375  // See wiki description for more details
376  if (inflate_cone_ < 1.0) {
377  // Determine barycentric coordinates
378  int w0 = orient2d(Ax, Ay, Bx, By, x, y);
379  int w1 = orient2d(Bx, By, Ox, Oy, x, y);
380  int w2 = orient2d(Ox, Oy, Ax, Ay, x, y);
381 
382  // Barycentric coordinates inside area threshold; this is not mathematically
383  // sound at all, but it works!
384  float bcciath = -static_cast<float>(inflate_cone_) * area(Ax, Ay, Bx, By, Ox, Oy);
385  update_xy_cell = w0 >= bcciath && w1 >= bcciath && w2 >= bcciath;
386  }
387 
388  if (update_xy_cell) {
389  double wx, wy;
390  mapToWorld(x, y, wx, wy);
391  update_cell(ox, oy, theta, range_message.range, wx, wy, clear_sensor_cone);
392  }
393  }
394  }
395 
396  buffered_readings_++;
397  last_reading_time_ = clock_->now();
398 }
399 
400 void RangeSensorLayer::update_cell(
401  double ox, double oy, double ot, double r,
402  double nx, double ny, bool clear)
403 {
404  unsigned int x, y;
405  if (worldToMap(nx, ny, x, y)) {
406  double dx = nx - ox, dy = ny - oy;
407  double theta = atan2(dy, dx) - ot;
408  theta = angles::normalize_angle(theta);
409  double phi = sqrt(dx * dx + dy * dy);
410  double sensor = 0.0;
411  if (!clear) {
412  sensor = sensor_model(r, phi, theta);
413  }
414  double prior = to_prob(getCost(x, y));
415  double prob_occ = sensor * prior;
416  double prob_not = (1 - sensor) * (1 - prior);
417  double new_prob = prob_occ / (prob_occ + prob_not);
418 
419  RCLCPP_DEBUG(
420  logger_,
421  "%f %f | %f %f = %f", dx, dy, theta, phi, sensor);
422  RCLCPP_DEBUG(
423  logger_,
424  "%f | %f %f | %f", prior, prob_occ, prob_not, new_prob);
425  unsigned char c = to_cost(new_prob);
426  setCost(x, y, c);
427  }
428 }
429 
430 void RangeSensorLayer::resetRange()
431 {
432  min_x_ = min_y_ = std::numeric_limits<double>::max();
433  max_x_ = max_y_ = -std::numeric_limits<double>::max();
434 }
435 
436 void RangeSensorLayer::updateBounds(
437  double robot_x, double robot_y,
438  double robot_yaw, double * min_x, double * min_y,
439  double * max_x, double * max_y)
440 {
441  robot_yaw = 0 + robot_yaw; // Avoid error if variable not in use
442  if (layered_costmap_->isRolling()) {
443  updateOrigin(robot_x - getSizeInMetersX() / 2, robot_y - getSizeInMetersY() / 2);
444  }
445 
446  updateCostmap();
447 
448  *min_x = std::min(*min_x, min_x_);
449  *min_y = std::min(*min_y, min_y_);
450  *max_x = std::max(*max_x, max_x_);
451  *max_y = std::max(*max_y, max_y_);
452 
453  resetRange();
454 
455  if (!enabled_) {
456  setCurrent(true);
457  return;
458  }
459 
460  if (buffered_readings_ == 0) {
461  if (no_readings_timeout_ > 0.0 &&
462  (clock_->now() - last_reading_time_).seconds() >
463  no_readings_timeout_)
464  {
465  RCLCPP_WARN(
466  logger_,
467  "No range readings received for %.2f seconds, while expected at least every %.2f seconds.",
468  (clock_->now() - last_reading_time_).seconds(),
469  no_readings_timeout_);
470  setCurrent(false);
471  }
472  }
473 }
474 
475 void RangeSensorLayer::updateCosts(
476  nav2_costmap_2d::Costmap2D & master_grid,
477  int min_i, int min_j, int max_i, int max_j)
478 {
479  if (!enabled_) {
480  return;
481  }
482 
483  unsigned char * master_array = master_grid.getCharMap();
484  unsigned int span = master_grid.getSizeInCellsX();
485  unsigned char clear = to_cost(clear_threshold_), mark = to_cost(mark_threshold_);
486 
487  for (int j = min_j; j < max_j; j++) {
488  unsigned int it = j * span + min_i;
489  for (int i = min_i; i < max_i; i++) {
490  unsigned char prob = costmap_[it];
491  unsigned char current;
492  if (prob == nav2_costmap_2d::NO_INFORMATION) {
493  it++;
494  continue;
495  } else if (prob > mark) {
496  current = nav2_costmap_2d::LETHAL_OBSTACLE;
497  } else if (prob < clear) {
498  current = nav2_costmap_2d::FREE_SPACE;
499  } else {
500  it++;
501  continue;
502  }
503 
504  unsigned char old_cost = master_array[it];
505 
506  if (old_cost == NO_INFORMATION || old_cost < current) {
507  master_array[it] = current;
508  }
509  it++;
510  }
511  }
512 
513  buffered_readings_ = 0;
514 
515  // if not current due to reset, set current now after clearing
516  if (!isCurrent() && was_reset_) {
517  was_reset_ = false;
518  setCurrent(true);
519  }
520 }
521 
522 void RangeSensorLayer::reset()
523 {
524  RCLCPP_DEBUG(logger_, "Resetting range sensor layer...");
525  deactivate();
526  resetMaps();
527  was_reset_ = true;
528  activate();
529 }
530 
531 void RangeSensorLayer::deactivate()
532 {
533  range_msgs_buffer_.clear();
534 }
535 
536 void RangeSensorLayer::activate()
537 {
538  range_msgs_buffer_.clear();
539 }
540 
541 } // 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 char * getCharMap() const
Will return a pointer to the underlying unsigned char array used as the costmap.
Definition: costmap_2d.cpp:260
unsigned int getSizeInCellsX() const
Accessor for the x size of the costmap in cells.
Definition: costmap_2d.cpp:548
Abstract class for layered costmap plugin implementations.
Definition: layer.hpp:62
Takes in IR/Sonar/similar point measurement sensors and populates in costmap.