Nav2 Navigation Stack - jazzy  jazzy
ROS 2 Navigation Stack
layered_costmap.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/layered_costmap.hpp"
39 
40 #include <algorithm>
41 #include <cstdio>
42 #include <memory>
43 #include <string>
44 #include <vector>
45 #include <limits>
46 
47 #include <rclcpp/clock.hpp>
48 
49 #include "nav2_costmap_2d/footprint.hpp"
50 
51 
52 using std::vector;
53 
54 namespace nav2_costmap_2d
55 {
56 
57 LayeredCostmap::LayeredCostmap(std::string global_frame, bool rolling_window, bool track_unknown)
58 : primary_costmap_(), combined_costmap_(),
59  global_frame_(global_frame),
60  rolling_window_(rolling_window),
61  current_(false),
62  minx_(0.0),
63  miny_(0.0),
64  maxx_(0.0),
65  maxy_(0.0),
66  bx0_(0),
67  bxn_(0),
68  by0_(0),
69  byn_(0),
70  initialized_(false),
71  size_locked_(false),
72  circumscribed_radius_(1.0),
73  inscribed_radius_(0.1),
74  footprint_(std::make_shared<std::vector<geometry_msgs::msg::Point>>())
75 {
76  if (track_unknown) {
77  primary_costmap_.setDefaultValue(255);
78  combined_costmap_.setDefaultValue(255);
79  } else {
80  primary_costmap_.setDefaultValue(0);
81  combined_costmap_.setDefaultValue(0);
82  }
83 }
84 
86 {
87  while (plugins_.size() > 0) {
88  plugins_.pop_back();
89  }
90  while (filters_.size() > 0) {
91  filters_.pop_back();
92  }
93 }
94 
95 void LayeredCostmap::addPlugin(std::shared_ptr<Layer> plugin)
96 {
97  std::unique_lock<Costmap2D::mutex_t> lock(*(combined_costmap_.getMutex()));
98  plugins_.push_back(plugin);
99 }
100 
101 void LayeredCostmap::addFilter(std::shared_ptr<Layer> filter)
102 {
103  std::unique_lock<Costmap2D::mutex_t> lock(*(combined_costmap_.getMutex()));
104  filters_.push_back(filter);
105 }
106 
108  unsigned int size_x, unsigned int size_y, double resolution,
109  double origin_x,
110  double origin_y,
111  bool size_locked)
112 {
113  std::unique_lock<Costmap2D::mutex_t> lock(*(combined_costmap_.getMutex()));
114  size_locked_ = size_locked;
115  primary_costmap_.resizeMap(size_x, size_y, resolution, origin_x, origin_y);
116  combined_costmap_.resizeMap(size_x, size_y, resolution, origin_x, origin_y);
117  for (vector<std::shared_ptr<Layer>>::iterator plugin = plugins_.begin();
118  plugin != plugins_.end(); ++plugin)
119  {
120  if (*plugin) {
121  (*plugin)->matchSize();
122  }
123  }
124  for (vector<std::shared_ptr<Layer>>::iterator filter = filters_.begin();
125  filter != filters_.end(); ++filter)
126  {
127  (*filter)->matchSize();
128  }
129 }
130 
131 bool LayeredCostmap::isOutofBounds(double robot_x, double robot_y)
132 {
133  unsigned int mx, my;
134  return !combined_costmap_.worldToMap(robot_x, robot_y, mx, my);
135 }
136 
137 void LayeredCostmap::updateMap(double robot_x, double robot_y, double robot_yaw)
138 {
139  // Lock for the remainder of this function, some plugins (e.g. VoxelLayer)
140  // implement thread unsafe updateBounds() functions.
141  std::unique_lock<Costmap2D::mutex_t> lock(*(combined_costmap_.getMutex()));
142 
143  // if we're using a rolling buffer costmap...
144  // we need to update the origin using the robot's position
145  if (rolling_window_) {
146  double new_origin_x = robot_x - combined_costmap_.getSizeInMetersX() / 2;
147  double new_origin_y = robot_y - combined_costmap_.getSizeInMetersY() / 2;
148  primary_costmap_.updateOrigin(new_origin_x, new_origin_y);
149  combined_costmap_.updateOrigin(new_origin_x, new_origin_y);
150  }
151 
152  if (isOutofBounds(robot_x, robot_y)) {
153  rclcpp::Clock clock{RCL_ROS_TIME};
154  RCLCPP_WARN_THROTTLE(
155  rclcpp::get_logger("nav2_costmap_2d"),
156  clock, 5000,
157  "Robot is out of bounds of the costmap");
158  }
159 
160  if (plugins_.size() == 0 && filters_.size() == 0) {
161  return;
162  }
163 
164  minx_ = miny_ = std::numeric_limits<double>::max();
165  maxx_ = maxy_ = std::numeric_limits<double>::lowest();
166 
167  for (vector<std::shared_ptr<Layer>>::iterator plugin = plugins_.begin();
168  plugin != plugins_.end(); ++plugin)
169  {
170  double prev_minx = minx_;
171  double prev_miny = miny_;
172  double prev_maxx = maxx_;
173  double prev_maxy = maxy_;
174  (*plugin)->updateBounds(robot_x, robot_y, robot_yaw, &minx_, &miny_, &maxx_, &maxy_);
175  if (minx_ > prev_minx || miny_ > prev_miny || maxx_ < prev_maxx || maxy_ < prev_maxy) {
176  RCLCPP_WARN(
177  rclcpp::get_logger(
178  "nav2_costmap_2d"), "Illegal bounds change, was [tl: (%f, %f), br: (%f, %f)], but "
179  "is now [tl: (%f, %f), br: (%f, %f)]. The offending layer is %s",
180  prev_minx, prev_miny, prev_maxx, prev_maxy,
181  minx_, miny_, maxx_, maxy_,
182  (*plugin)->getName().c_str());
183  }
184  }
185  for (vector<std::shared_ptr<Layer>>::iterator filter = filters_.begin();
186  filter != filters_.end(); ++filter)
187  {
188  double prev_minx = minx_;
189  double prev_miny = miny_;
190  double prev_maxx = maxx_;
191  double prev_maxy = maxy_;
192  (*filter)->updateBounds(robot_x, robot_y, robot_yaw, &minx_, &miny_, &maxx_, &maxy_);
193  if (minx_ > prev_minx || miny_ > prev_miny || maxx_ < prev_maxx || maxy_ < prev_maxy) {
194  RCLCPP_WARN(
195  rclcpp::get_logger(
196  "nav2_costmap_2d"), "Illegal bounds change, was [tl: (%f, %f), br: (%f, %f)], but "
197  "is now [tl: (%f, %f), br: (%f, %f)]. The offending filter is %s",
198  prev_minx, prev_miny, prev_maxx, prev_maxy,
199  minx_, miny_, maxx_, maxy_,
200  (*filter)->getName().c_str());
201  }
202  }
203 
204  int x0, xn, y0, yn;
205  combined_costmap_.worldToMapEnforceBounds(minx_, miny_, x0, y0);
206  combined_costmap_.worldToMapEnforceBounds(maxx_, maxy_, xn, yn);
207 
208  x0 = std::max(0, x0);
209  xn = std::min(static_cast<int>(combined_costmap_.getSizeInCellsX()), xn + 1);
210  y0 = std::max(0, y0);
211  yn = std::min(static_cast<int>(combined_costmap_.getSizeInCellsY()), yn + 1);
212 
213  RCLCPP_DEBUG(
214  rclcpp::get_logger(
215  "nav2_costmap_2d"), "Updating area x: [%d, %d] y: [%d, %d]", x0, xn, y0, yn);
216 
217  if (xn < x0 || yn < y0) {
218  return;
219  }
220 
221  if (filters_.size() == 0) {
222  // If there are no filters enabled just update costmap sequentially by each plugin
223  combined_costmap_.resetMap(x0, y0, xn, yn);
224  for (vector<std::shared_ptr<Layer>>::iterator plugin = plugins_.begin();
225  plugin != plugins_.end(); ++plugin)
226  {
227  (*plugin)->updateCosts(combined_costmap_, x0, y0, xn, yn);
228  }
229  } else {
230  // Costmap Filters enabled
231  // 1. Update costmap by plugins
232  primary_costmap_.resetMap(x0, y0, xn, yn);
233  for (vector<std::shared_ptr<Layer>>::iterator plugin = plugins_.begin();
234  plugin != plugins_.end(); ++plugin)
235  {
236  (*plugin)->updateCosts(primary_costmap_, x0, y0, xn, yn);
237  }
238 
239  // 2. Copy processed costmap window to a final costmap.
240  // primary_costmap_ remain to be untouched for further usage by plugins.
241  if (!combined_costmap_.copyWindow(primary_costmap_, x0, y0, xn, yn, x0, y0)) {
242  RCLCPP_ERROR(
243  rclcpp::get_logger("nav2_costmap_2d"),
244  "Can not copy costmap (%i,%i)..(%i,%i) window",
245  x0, y0, xn, yn);
246  throw std::runtime_error{"Can not copy costmap"};
247  }
248 
249  // 3. Apply filters over the plugins in order to make filters' work
250  // not being considered by plugins on next updateMap() calls
251  for (vector<std::shared_ptr<Layer>>::iterator filter = filters_.begin();
252  filter != filters_.end(); ++filter)
253  {
254  (*filter)->updateCosts(combined_costmap_, x0, y0, xn, yn);
255  }
256  }
257 
258  bx0_ = x0;
259  bxn_ = xn;
260  by0_ = y0;
261  byn_ = yn;
262 
263  initialized_ = true;
264 }
265 
267 {
268  current_ = true;
269  for (vector<std::shared_ptr<Layer>>::iterator plugin = plugins_.begin();
270  plugin != plugins_.end(); ++plugin)
271  {
272  current_ = current_ && ((*plugin)->isCurrent() || !(*plugin)->isEnabled());
273  }
274  for (vector<std::shared_ptr<Layer>>::iterator filter = filters_.begin();
275  filter != filters_.end(); ++filter)
276  {
277  current_ = current_ && ((*filter)->isCurrent() || !(*filter)->isEnabled());
278  }
279  return current_;
280 }
281 
282 void LayeredCostmap::setFootprint(const std::vector<geometry_msgs::msg::Point> & footprint_spec)
283 {
284  std::pair<double, double> inside_outside = nav2_costmap_2d::calculateMinAndMaxDistances(
285  footprint_spec);
286  // use atomic store here since footprint is used by various planners/controllers
287  // and not otherwise locked
288  std::atomic_store(
289  &footprint_,
290  std::make_shared<std::vector<geometry_msgs::msg::Point>>(footprint_spec));
291  inscribed_radius_.store(std::get<0>(inside_outside));
292  circumscribed_radius_.store(std::get<1>(inside_outside));
293 
294  for (vector<std::shared_ptr<Layer>>::iterator plugin = plugins_.begin();
295  plugin != plugins_.end();
296  ++plugin)
297  {
298  (*plugin)->onFootprintChanged();
299  }
300  for (vector<std::shared_ptr<Layer>>::iterator filter = filters_.begin();
301  filter != filters_.end();
302  ++filter)
303  {
304  (*filter)->onFootprintChanged();
305  }
306 }
307 
308 } // namespace nav2_costmap_2d
void resetMap(unsigned int x0, unsigned int y0, unsigned int xn, unsigned int yn)
Reset the costmap in bounds.
Definition: costmap_2d.cpp:130
void resizeMap(unsigned int size_x, unsigned int size_y, double resolution, double origin_x, double origin_y)
Resize the costmap.
Definition: costmap_2d.cpp:110
bool copyWindow(const Costmap2D &source, unsigned int sx0, unsigned int sy0, unsigned int sxn, unsigned int syn, unsigned int dx0, unsigned int dy0)
Copies the (x0,y0)..(xn,yn) window from source costmap into a current costmap.
Definition: costmap_2d.cpp:184
void worldToMapEnforceBounds(double wx, double wy, int &mx, int &my) const
Convert from world coordinates to map coordinates, constraining results to legal bounds.
Definition: costmap_2d.cpp:321
bool worldToMap(double wx, double wy, unsigned int &mx, unsigned int &my) const
Convert from world coordinates to map coordinates.
Definition: costmap_2d.cpp:285
void setDefaultValue(unsigned char c)
Set the default background value of the costmap.
Definition: costmap_2d.hpp:290
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:343
double getSizeInMetersY() const
Accessor for the y size of the costmap in meters.
Definition: costmap_2d.cpp:529
double getSizeInMetersX() const
Accessor for the x size of the costmap in meters.
Definition: costmap_2d.cpp:524
unsigned int getSizeInCellsX() const
Accessor for the x size of the costmap in cells.
Definition: costmap_2d.cpp:514
unsigned int getSizeInCellsY() const
Accessor for the y size of the costmap in cells.
Definition: costmap_2d.cpp:519
void addPlugin(std::shared_ptr< Layer > plugin)
Add a new plugin to the plugins vector to process.
void addFilter(std::shared_ptr< Layer > filter)
Add a new costmap filter plugin to the filters vector to process.
void resizeMap(unsigned int size_x, unsigned int size_y, double resolution, double origin_x, double origin_y, bool size_locked=false)
Resize the map to a new size, resolution, or origin.
bool isOutofBounds(double robot_x, double robot_y)
Checks if the robot is outside the bounds of its costmap in the case of poorly configured setups.
void updateMap(double robot_x, double robot_y, double robot_yaw)
Update the underlying costmap with new data. If you want to update the map outside of the update loop...
LayeredCostmap(std::string global_frame, bool rolling_window, bool track_unknown)
Constructor for a costmap.
void setFootprint(const std::vector< geometry_msgs::msg::Point > &footprint_spec)
Updates the stored footprint, updates the circumscribed and inscribed radii, and calls onFootprintCha...
bool isCurrent()
If the costmap is current, e.g. are all the layers processing recent data and not stale information f...
std::pair< double, double > calculateMinAndMaxDistances(const std::vector< geometry_msgs::msg::Point > &footprint)
Calculate the extreme distances for the footprint.
Definition: footprint.cpp:43