Nav2 Navigation Stack - lyrical  lyrical
ROS 2 Navigation Stack
legacy_inflation_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  *********************************************************************/
38 #include "nav2_costmap_2d/legacy_inflation_layer.hpp"
39 
40 #include <limits>
41 #include <map>
42 #include <vector>
43 #include <algorithm>
44 #include <utility>
45 
46 #include "nav2_costmap_2d/costmap_math.hpp"
47 #include "nav2_costmap_2d/footprint.hpp"
48 #include "pluginlib/class_list_macros.hpp"
49 #include "rclcpp/parameter_events_filter.hpp"
50 
52 
53 using nav2_costmap_2d::LETHAL_OBSTACLE;
54 using nav2_costmap_2d::INSCRIBED_INFLATED_OBSTACLE;
55 using nav2_costmap_2d::NO_INFORMATION;
56 using rcl_interfaces::msg::ParameterType;
57 
58 namespace nav2_costmap_2d
59 {
60 
62 : inflation_radius_(0),
63  inscribed_radius_(0),
64  cost_scaling_factor_(0),
65  inflate_unknown_(false),
66  inflate_around_unknown_(false),
67  cell_inflation_radius_(0),
68  cached_cell_inflation_radius_(0),
69  resolution_(0),
70  cache_length_(0),
71  last_min_x_(std::numeric_limits<double>::lowest()),
72  last_min_y_(std::numeric_limits<double>::lowest()),
73  last_max_x_(std::numeric_limits<double>::max()),
74  last_max_y_(std::numeric_limits<double>::max())
75 {
76  access_ = new mutex_t();
77 }
78 
80 {
81  auto node = node_.lock();
82  if (dyn_params_handler_ && node) {
83  node->remove_on_set_parameters_callback(dyn_params_handler_.get());
84  }
85  dyn_params_handler_.reset();
86  delete access_;
87 }
88 
89 void
91 {
92  {
93  auto node = node_.lock();
94  if (!node) {
95  throw std::runtime_error{"Failed to lock node"};
96  }
97  enabled_ = node->declare_or_get_parameter(name_ + "." + "enabled", true);
98  inflation_radius_ = node->declare_or_get_parameter(name_ + "." + "inflation_radius", 0.55);
99  cost_scaling_factor_ = node->declare_or_get_parameter(
100  name_ + "." + "cost_scaling_factor", 10.0);
101  inflate_unknown_ = node->declare_or_get_parameter(name_ + "." + "inflate_unknown", false);
102  inflate_around_unknown_ = node->declare_or_get_parameter(
103  name_ + "." + "inflate_around_unknown", false);
104 
105  dyn_params_handler_ = node->add_on_set_parameters_callback(
106  std::bind(
108  this, std::placeholders::_1));
109  }
110 
111  current_ = true;
112  seen_.clear();
113  cached_distances_.clear();
114  cached_costs_.clear();
115  need_reinflation_ = false;
116  cell_inflation_radius_ = cellDistance(inflation_radius_);
117  matchSize();
118 }
119 
120 void
122 {
123  std::lock_guard<Costmap2D::mutex_t> guard(*getMutex());
124  nav2_costmap_2d::Costmap2D * costmap = layered_costmap_->getCostmap();
125  resolution_ = costmap->getResolution();
126  cell_inflation_radius_ = cellDistance(inflation_radius_);
127  computeCaches();
128  seen_ = std::vector<bool>(costmap->getSizeInCellsX() * costmap->getSizeInCellsY(), false);
129 }
130 
131 void
133  double /*robot_x*/, double /*robot_y*/, double /*robot_yaw*/, double * min_x,
134  double * min_y, double * max_x, double * max_y)
135 {
136  std::lock_guard<Costmap2D::mutex_t> guard(*getMutex());
137  if (need_reinflation_) {
138  // Reset last_* to "no expansion" values so the next cycle won't
139  // merge with these full-map bounds (avoids double full-map update after reset)
140  last_min_x_ = last_min_y_ = std::numeric_limits<double>::max();
141  last_max_x_ = last_max_y_ = std::numeric_limits<double>::lowest();
142 
143  *min_x = std::numeric_limits<double>::lowest();
144  *min_y = std::numeric_limits<double>::lowest();
145  *max_x = std::numeric_limits<double>::max();
146  *max_y = std::numeric_limits<double>::max();
147  need_reinflation_ = false;
148  } else {
149  double tmp_min_x = last_min_x_;
150  double tmp_min_y = last_min_y_;
151  double tmp_max_x = last_max_x_;
152  double tmp_max_y = last_max_y_;
153  last_min_x_ = *min_x;
154  last_min_y_ = *min_y;
155  last_max_x_ = *max_x;
156  last_max_y_ = *max_y;
157  *min_x = std::min(tmp_min_x, *min_x) - inflation_radius_;
158  *min_y = std::min(tmp_min_y, *min_y) - inflation_radius_;
159  *max_x = std::max(tmp_max_x, *max_x) + inflation_radius_;
160  *max_y = std::max(tmp_max_y, *max_y) + inflation_radius_;
161  }
162 }
163 
164 void
166 {
167  std::lock_guard<Costmap2D::mutex_t> guard(*getMutex());
168  inscribed_radius_ = layered_costmap_->getInscribedRadius();
169  cell_inflation_radius_ = cellDistance(inflation_radius_);
170  computeCaches();
171  need_reinflation_ = true;
172 
173  if (inflation_radius_ < inscribed_radius_) {
174  RCLCPP_ERROR(
175  logger_,
176  "The configured inflation radius (%.3f) is smaller than "
177  "the computed inscribed radius (%.3f) of your footprint, "
178  "it is highly recommended to set inflation radius to be at "
179  "least as big as the inscribed radius to avoid collisions",
180  inflation_radius_, inscribed_radius_);
181  }
182 
183  RCLCPP_DEBUG(
184  logger_, "LegacyInflationLayer::onFootprintChanged(): num footprint points: %zu,"
185  " inscribed_radius_ = %.3f, inflation_radius_ = %.3f",
186  layered_costmap_->getFootprint().size(), inscribed_radius_, inflation_radius_);
187 }
188 
189 void
191  nav2_costmap_2d::Costmap2D & master_grid, int min_i, int min_j,
192  int max_i,
193  int max_j)
194 {
195  std::lock_guard<Costmap2D::mutex_t> guard(*getMutex());
196  if (!enabled_ || (cell_inflation_radius_ == 0)) {
197  return;
198  }
199 
200  // make sure the inflation list is empty at the beginning of the cycle (should always be true)
201  for (auto & dist : inflation_cells_) {
202  RCLCPP_FATAL_EXPRESSION(
203  logger_,
204  !dist.empty(), "The inflation list must be empty at the beginning of inflation");
205  }
206 
207  unsigned char * master_array = master_grid.getCharMap();
208  unsigned int size_x = master_grid.getSizeInCellsX(), size_y = master_grid.getSizeInCellsY();
209 
210  if (seen_.size() != size_x * size_y) {
211  RCLCPP_WARN(
212  logger_, "LegacyInflationLayer::updateCosts(): seen_ vector size is wrong");
213  seen_ = std::vector<bool>(size_x * size_y, false);
214  }
215 
216  std::fill(begin(seen_), end(seen_), false);
217 
218  // We need to include in the inflation cells outside the bounding
219  // box min_i...max_j, by the amount cell_inflation_radius_. Cells
220  // up to that distance outside the box can still influence the costs
221  // stored in cells inside the box.
222  const int base_min_i = min_i;
223  const int base_min_j = min_j;
224  const int base_max_i = max_i;
225  const int base_max_j = max_j;
226  min_i -= static_cast<int>(cell_inflation_radius_);
227  min_j -= static_cast<int>(cell_inflation_radius_);
228  max_i += static_cast<int>(cell_inflation_radius_);
229  max_j += static_cast<int>(cell_inflation_radius_);
230 
231  min_i = std::max(0, min_i);
232  min_j = std::max(0, min_j);
233  max_i = std::min(static_cast<int>(size_x), max_i);
234  max_j = std::min(static_cast<int>(size_y), max_j);
235 
236  // Inflation list; we append cells to visit in a list associated with
237  // its distance to the nearest obstacle
238  // We use a map<distance, list> to emulate the priority queue used before,
239  // with a notable performance boost
240 
241  // Start with lethal obstacles: by definition distance is 0.0
242  auto & obs_bin = inflation_cells_[0];
243  obs_bin.reserve(200);
244  for (int j = min_j; j < max_j; j++) {
245  for (int i = min_i; i < max_i; i++) {
246  int index = static_cast<int>(master_grid.getIndex(i, j));
247  unsigned char cost = master_array[index];
248  if (cost == LETHAL_OBSTACLE || (inflate_around_unknown_ && cost == NO_INFORMATION)) {
249  obs_bin.emplace_back(i, j, i, j);
250  }
251  }
252  }
253 
254  // Process cells by increasing distance; new cells are appended to the
255  // corresponding distance bin, so they
256  // can overtake previously inserted but farther away cells
257  for (auto & dist_bin : inflation_cells_) {
258  dist_bin.reserve(200);
259  for (std::size_t i = 0; i < dist_bin.size(); ++i) {
260  // Do not use iterator or for-range based loops to
261  // iterate though dist_bin, since it's size might
262  // change when a new cell is enqueued, invalidating all iterators
263  const CellData & cell = dist_bin[i];
264  unsigned int mx = cell.x_;
265  unsigned int my = cell.y_;
266  unsigned int sx = cell.src_x_;
267  unsigned int sy = cell.src_y_;
268  unsigned int index = master_grid.getIndex(mx, my);
269 
270  // ignore if already visited
271  if (seen_[index]) {
272  continue;
273  }
274 
275  seen_[index] = true;
276 
277  // assign the cost associated with the distance from an obstacle to the cell
278  unsigned char cost = costLookup(mx, my, sx, sy);
279  unsigned char old_cost = master_array[index];
280  // In order to avoid artifacts appeared out of boundary areas
281  // when some layer is going after inflation_layer,
282  // we need to apply inflation_layer only to inside of given bounds
283  if (static_cast<int>(mx) >= base_min_i &&
284  static_cast<int>(my) >= base_min_j &&
285  static_cast<int>(mx) < base_max_i &&
286  static_cast<int>(my) < base_max_j)
287  {
288  if (old_cost == NO_INFORMATION &&
289  (inflate_unknown_ ? (cost > FREE_SPACE) : (cost >= INSCRIBED_INFLATED_OBSTACLE)))
290  {
291  master_array[index] = cost;
292  } else {
293  master_array[index] = std::max(old_cost, cost);
294  }
295  }
296 
297  // attempt to put the neighbors of the current cell onto the inflation list
298  if (mx > 0) {
299  enqueue(index - 1, mx - 1, my, sx, sy);
300  }
301  if (my > 0) {
302  enqueue(index - size_x, mx, my - 1, sx, sy);
303  }
304  if (mx < size_x - 1) {
305  enqueue(index + 1, mx + 1, my, sx, sy);
306  }
307  if (my < size_y - 1) {
308  enqueue(index + size_x, mx, my + 1, sx, sy);
309  }
310  }
311  // This level of inflation_cells_ is not needed anymore. We can free the memory
312  // Note that dist_bin.clear() is not enough, because it won't free the memory
313  dist_bin = std::vector<CellData>();
314  }
315 
316  current_ = true;
317 }
318 
328 void
330  unsigned int index, unsigned int mx, unsigned int my,
331  unsigned int src_x, unsigned int src_y)
332 {
333  if (!seen_[index]) {
334  // we compute our distance table one cell further than the
335  // inflation radius dictates so we can make the check below
336  double distance = distanceLookup(mx, my, src_x, src_y);
337 
338  // we only want to put the cell in the list if it is within
339  // the inflation radius of the obstacle point
340  if (distance > cell_inflation_radius_) {
341  return;
342  }
343 
344  const unsigned int r = cell_inflation_radius_ + 2;
345 
346  // push the cell data onto the inflation list and mark
347  const auto dist = distance_matrix_[mx - src_x + r][my - src_y + r];
348  inflation_cells_[dist].emplace_back(mx, my, src_x, src_y);
349  }
350 }
351 
352 void
354 {
355  std::lock_guard<Costmap2D::mutex_t> guard(*getMutex());
356  if (cell_inflation_radius_ == 0) {
357  return;
358  }
359 
360  cache_length_ = cell_inflation_radius_ + 2;
361 
362  // based on the inflation radius... compute distance and cost caches
363  if (cell_inflation_radius_ != cached_cell_inflation_radius_) {
364  cached_costs_.resize(cache_length_ * cache_length_);
365  cached_distances_.resize(cache_length_ * cache_length_);
366 
367  for (unsigned int i = 0; i < cache_length_; ++i) {
368  for (unsigned int j = 0; j < cache_length_; ++j) {
369  cached_distances_[i * cache_length_ + j] = hypot(i, j);
370  }
371  }
372 
373  cached_cell_inflation_radius_ = cell_inflation_radius_;
374  }
375 
376  for (unsigned int i = 0; i < cache_length_; ++i) {
377  for (unsigned int j = 0; j < cache_length_; ++j) {
378  cached_costs_[i * cache_length_ + j] = computeCost(cached_distances_[i * cache_length_ + j]);
379  }
380  }
381 
382  int max_dist = generateIntegerDistances();
383  inflation_cells_.clear();
384  inflation_cells_.resize(max_dist + 1);
385 }
386 
387 int
389 {
390  const int r = cell_inflation_radius_ + 2;
391  const int size = r * 2 + 1;
392 
393  std::vector<std::pair<int, int>> points;
394 
395  for (int y = -r; y <= r; y++) {
396  for (int x = -r; x <= r; x++) {
397  if (x * x + y * y <= r * r) {
398  points.emplace_back(x, y);
399  }
400  }
401  }
402 
403  std::sort(
404  points.begin(), points.end(),
405  [](const std::pair<int, int> & a, const std::pair<int, int> & b) -> bool {
406  return a.first * a.first + a.second * a.second < b.first * b.first + b.second * b.second;
407  }
408  );
409 
410  std::vector<std::vector<int>> distance_matrix(size, std::vector<int>(size, 0));
411  std::pair<int, int> last = {0, 0};
412  int level = 0;
413  for (auto const & p : points) {
414  if (p.first * p.first + p.second * p.second !=
415  last.first * last.first + last.second * last.second)
416  {
417  level++;
418  }
419  distance_matrix[p.first + r][p.second + r] = level;
420  last = p;
421  }
422 
423  distance_matrix_ = distance_matrix;
424  return level;
425 }
426 
431 rcl_interfaces::msg::SetParametersResult
433  std::vector<rclcpp::Parameter> parameters)
434 {
435  std::lock_guard<Costmap2D::mutex_t> guard(*getMutex());
436  rcl_interfaces::msg::SetParametersResult result;
437 
438  bool need_cache_recompute = false;
439 
440  for (auto parameter : parameters) {
441  const auto & param_type = parameter.get_type();
442  const auto & param_name = parameter.get_name();
443  if (param_name.find(name_ + ".") != 0) {
444  continue;
445  }
446 
447  if (param_type == ParameterType::PARAMETER_DOUBLE) {
448  if (param_name == name_ + "." + "inflation_radius" &&
449  inflation_radius_ != parameter.as_double())
450  {
451  inflation_radius_ = parameter.as_double();
452  need_reinflation_ = true;
453  need_cache_recompute = true;
454  } else if (param_name == name_ + "." + "cost_scaling_factor" && // NOLINT
455  getCostScalingFactor() != parameter.as_double())
456  {
457  cost_scaling_factor_ = parameter.as_double();
458  need_reinflation_ = true;
459  need_cache_recompute = true;
460  }
461  } else if (param_type == ParameterType::PARAMETER_BOOL) {
462  if (param_name == name_ + "." + "enabled" && enabled_ != parameter.as_bool()) {
463  enabled_ = parameter.as_bool();
464  need_reinflation_ = true;
465  current_ = false;
466  } else if (param_name == name_ + "." + "inflate_unknown" && // NOLINT
467  inflate_unknown_ != parameter.as_bool())
468  {
469  inflate_unknown_ = parameter.as_bool();
470  need_reinflation_ = true;
471  } else if (param_name == name_ + "." + "inflate_around_unknown" && // NOLINT
472  inflate_around_unknown_ != parameter.as_bool())
473  {
474  inflate_around_unknown_ = parameter.as_bool();
475  need_reinflation_ = true;
476  }
477  }
478  }
479 
480  if (need_cache_recompute) {
481  matchSize();
482  }
483 
484  result.successful = true;
485  return result;
486 }
487 
488 } // namespace nav2_costmap_2d
Storage for cell information used during obstacle inflation.
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
unsigned char * getCharMap() const
Will return a pointer to the underlying unsigned char array used as the costmap.
Definition: costmap_2d.cpp:260
double getResolution() const
Accessor for the resolution of the costmap.
Definition: costmap_2d.cpp:578
unsigned int getSizeInCellsX() const
Accessor for the x size of the costmap in cells.
Definition: costmap_2d.cpp:548
unsigned int getSizeInCellsY() const
Accessor for the y size of the costmap in cells.
Definition: costmap_2d.cpp:553
Abstract class for layered costmap plugin implementations.
Definition: layer.hpp:60
Costmap2D * getCostmap()
Get the costmap pointer to the master costmap.
double getInscribedRadius()
The radius of a circle centered at the origin of the robot which is just within all points and edges ...
const std::vector< geometry_msgs::msg::Point > & getFootprint()
Returns the latest footprint stored with setFootprint().
Layer to convolve costmap by robot's radius or footprint to prevent collisions and largely simply col...
void enqueue(unsigned int index, unsigned int mx, unsigned int my, unsigned int src_x, unsigned int src_y)
Enqueue new cells in cache distance update search.
unsigned char computeCost(double distance) const override
Given a distance, compute a cost.
void updateBounds(double robot_x, double robot_y, double robot_yaw, double *min_x, double *min_y, double *max_x, double *max_y) override
Update the bounds of the master costmap by this layer's update dimensions.
void matchSize() override
Match the size of the master costmap.
int generateIntegerDistances()
Compute cached dsitances.
void onInitialize() override
Initialization process of layer on startup.
rcl_interfaces::msg::SetParametersResult dynamicParametersCallback(std::vector< rclcpp::Parameter > parameters)
Callback executed when a parameter change is detected.
void computeCaches()
Compute cached dsitances.
unsigned int cellDistance(double world_dist)
Compute cached dsitances.
void updateCosts(nav2_costmap_2d::Costmap2D &master_grid, int min_i, int min_j, int max_i, int max_j) override
Update the costs in the master costmap in the window.
unsigned char costLookup(unsigned int mx, unsigned int my, unsigned int src_x, unsigned int src_y)
Lookup pre-computed costs.
void onFootprintChanged() override
Process updates on footprint changes to the inflation layer.
double distanceLookup(unsigned int mx, unsigned int my, unsigned int src_x, unsigned int src_y)
Lookup pre-computed distances.
mutex_t * getMutex() override
Get the mutex of the inflation information.
double getCostScalingFactor() override
Get the cost scaling factor.