15 #include "nav2_costmap_2d/inflation_layer.hpp"
28 #include "nav2_costmap_2d/costmap_math.hpp"
29 #include "nav2_costmap_2d/footprint.hpp"
30 #include "nav2_costmap_2d/distance_transform.hpp"
31 #include "pluginlib/class_list_macros.hpp"
32 #include "rclcpp/parameter_events_filter.hpp"
36 using nav2_costmap_2d::LETHAL_OBSTACLE;
37 using nav2_costmap_2d::INSCRIBED_INFLATED_OBSTACLE;
38 using nav2_costmap_2d::NO_INFORMATION;
39 using rcl_interfaces::msg::ParameterType;
45 : inflation_radius_(0),
47 custom_inscribed_radius_(-1.0),
48 cost_scaling_factor_(0),
49 inflate_unknown_(false),
50 inflate_around_unknown_(false),
51 cell_inflation_radius_(0),
54 last_min_x_(std::numeric_limits<double>::lowest()),
55 last_min_y_(std::numeric_limits<double>::lowest()),
56 last_max_x_(std::numeric_limits<double>::max()),
57 last_max_y_(std::numeric_limits<double>::max())
59 access_ =
new mutex_t();
70 if (custom_inscribed_radius_ >= 0.0) {
73 "POTENTIAL SAFETY ISSUE!!! Make sure you fully understand the consequences of changing the "
74 "inscribed radius! Inflation layer is set to use a custom inscribed radius of %.3f m instead "
75 "of the footprint's inscribed radius of %.3f m. This can have serious implications on the "
76 "robot's safety and is not recommended unless specifically needed. This practice is intended "
77 "only for controllers that are customized explicitly to feed on such data. It is NOT "
78 "intended for global path planners or setups that depend on the footprint inscribed radius!",
80 inscribed_radius_ = custom_inscribed_radius_;
90 auto node = node_.lock();
92 throw std::runtime_error{
"Failed to lock node"};
94 enabled_ = node->declare_or_get_parameter(name_ +
"." +
"enabled",
true);
95 inflation_radius_ = node->declare_or_get_parameter(name_ +
"." +
"inflation_radius", 0.55);
96 custom_inscribed_radius_ = node->declare_or_get_parameter(
97 name_ +
"." +
"custom_inscribed_radius", -1.0);
98 cost_scaling_factor_ = node->declare_or_get_parameter(
99 name_ +
"." +
"cost_scaling_factor", 10.0);
100 inflate_unknown_ = node->declare_or_get_parameter(name_ +
"." +
"inflate_unknown",
false);
101 inflate_around_unknown_ = node->declare_or_get_parameter(
102 name_ +
"." +
"inflate_around_unknown",
false);
103 num_threads_ = node->declare_or_get_parameter(
104 name_ +
"." +
"num_threads", -1);
108 need_reinflation_ =
false;
114 auto node = node_.lock();
115 post_set_params_handler_ = node->add_post_set_parameters_callback(
118 this, std::placeholders::_1));
119 on_set_params_handler_ = node->add_on_set_parameters_callback(
122 this, std::placeholders::_1));
127 auto node = node_.lock();
128 if (post_set_params_handler_ && node) {
129 node->remove_post_set_parameters_callback(post_set_params_handler_.get());
131 post_set_params_handler_.reset();
132 if (on_set_params_handler_ && node) {
133 node->remove_on_set_parameters_callback(on_set_params_handler_.get());
135 on_set_params_handler_.reset();
141 std::lock_guard<Costmap2D::mutex_t> guard(*
getMutex());
144 cell_inflation_radius_ =
cellDistance(inflation_radius_);
150 double ,
double ,
double ,
double * min_x,
151 double * min_y,
double * max_x,
double * max_y)
153 std::lock_guard<Costmap2D::mutex_t> guard(*
getMutex());
154 if (need_reinflation_) {
157 last_min_x_ = last_min_y_ = std::numeric_limits<double>::max();
158 last_max_x_ = last_max_y_ = std::numeric_limits<double>::lowest();
160 *min_x = std::numeric_limits<double>::lowest();
161 *min_y = std::numeric_limits<double>::lowest();
162 *max_x = std::numeric_limits<double>::max();
163 *max_y = std::numeric_limits<double>::max();
164 need_reinflation_ =
false;
166 double tmp_min_x = last_min_x_;
167 double tmp_min_y = last_min_y_;
168 double tmp_max_x = last_max_x_;
169 double tmp_max_y = last_max_y_;
170 last_min_x_ = *min_x;
171 last_min_y_ = *min_y;
172 last_max_x_ = *max_x;
173 last_max_y_ = *max_y;
174 *min_x = std::min(tmp_min_x, *min_x) - inflation_radius_;
175 *min_y = std::min(tmp_min_y, *min_y) - inflation_radius_;
176 *max_x = std::max(tmp_max_x, *max_x) + inflation_radius_;
177 *max_y = std::max(tmp_max_y, *max_y) + inflation_radius_;
186 if (num_threads_ > 0) {
189 "OpenMP: Using configured num_threads: %d",
197 int cpu_cores = omp_get_max_threads();
198 int optimal = std::max(1, cpu_cores / 2);
202 "OpenMP: %d cores available, using %d threads (auto)",
214 std::lock_guard<Costmap2D::mutex_t> guard(*
getMutex());
216 cell_inflation_radius_ =
cellDistance(inflation_radius_);
218 need_reinflation_ =
true;
220 if (inflation_radius_ < inscribed_radius_) {
223 "The configured inflation radius (%.3f) is smaller than "
224 "the computed/custom inscribed radius (%.3f) of your footprint, "
225 "it is highly recommended to set inflation radius to be at "
226 "least as big as the inscribed radius to avoid collisions",
227 inflation_radius_, inscribed_radius_);
231 logger_,
"InflationLayer::onFootprintChanged(): num footprint points: %zu,"
232 " inscribed_radius_ = %.3f, inflation_radius_ = %.3f",
233 layered_costmap_->
getFootprint().size(), inscribed_radius_, inflation_radius_);
238 unsigned char * master_array,
240 int min_i,
int min_j,
int max_i,
int max_j,
241 int roi_min_i,
int roi_min_j,
244 const float cell_inflation_radius_f =
static_cast<float>(cell_inflation_radius_);
245 const int lut_max =
static_cast<int>(cost_lut_.size() - 1);
246 const unsigned char * lut_data = cost_lut_.data();
247 const int lut_precision = COST_LUT_PRECISION;
248 const bool inflate_unk = inflate_unknown_;
252 #pragma omp parallel for num_threads(num_threads) schedule(dynamic, 16)
254 for (
int j = min_j; j < max_j; ++j) {
255 const int row_offset = j *
static_cast<int>(size_x);
256 const int dist_row = j - roi_min_j;
258 for (
int i = min_i; i < max_i; ++i) {
259 const float distance_cells = distance_map(dist_row, i - roi_min_i);
260 if (distance_cells > cell_inflation_radius_f) {
264 const unsigned int index = row_offset + i;
265 const unsigned char old_cost = master_array[index];
266 const unsigned int d_scaled = std::min(
267 static_cast<unsigned int>(lut_max),
268 static_cast<unsigned int>(distance_cells * lut_precision + 0.5f));
269 const unsigned char cost = lut_data[d_scaled];
271 if (old_cost == NO_INFORMATION &&
272 (inflate_unk ? (cost > FREE_SPACE) : (cost >= INSCRIBED_INFLATED_OBSTACLE)))
274 master_array[index] = cost;
276 master_array[index] = std::max(old_cost, cost);
288 std::lock_guard<Costmap2D::mutex_t> guard(*
getMutex());
289 if (!enabled_ || (cell_inflation_radius_ == 0)) {
293 unsigned char * master_array = master_grid.
getCharMap();
297 min_i = std::max(0, min_i);
298 min_j = std::max(0, min_j);
299 max_i = std::min(
static_cast<int>(size_x), max_i);
300 max_j = std::min(
static_cast<int>(size_y), max_j);
303 const int padding =
static_cast<int>(cell_inflation_radius_);
304 int roi_min_i = std::max(0, min_i - padding);
305 int roi_min_j = std::max(0, min_j - padding);
306 int roi_max_i = std::min(
static_cast<int>(size_x), max_i + padding);
307 int roi_max_j = std::min(
static_cast<int>(size_y), max_j + padding);
309 const int roi_width = roi_max_i - roi_min_i;
310 const int roi_height = roi_max_j - roi_min_j;
313 MatrixXfRM distance_map(roi_height, roi_width);
318 #pragma omp parallel for num_threads(num_threads) schedule(dynamic, 16)
320 for (
int y = 0; y < roi_height; y++) {
321 const int src_y = y + roi_min_j;
322 for (
int x = 0; x < roi_width; x++) {
323 const int src_x = x + roi_min_i;
324 const unsigned char cell = master_array[src_y * size_x + src_x];
326 if (inflate_around_unknown_) {
329 x) = (cell != LETHAL_OBSTACLE &&
343 master_array, distance_map,
344 min_i, min_j, max_i, max_j,
345 roi_min_i, roi_min_j, size_x);
354 std::lock_guard<Costmap2D::mutex_t> guard(*
getMutex());
355 if (cell_inflation_radius_ == 0) {
360 const unsigned int max_dist_scaled = cell_inflation_radius_ * COST_LUT_PRECISION + 1;
361 cost_lut_.resize(max_dist_scaled + 1);
362 for (
unsigned int d_scaled = 0; d_scaled <= max_dist_scaled; ++d_scaled) {
363 const double distance =
static_cast<double>(d_scaled) / COST_LUT_PRECISION;
370 const std::vector<rclcpp::Parameter> & parameters)
372 rcl_interfaces::msg::SetParametersResult result;
373 result.successful =
true;
374 for (
const auto & parameter : parameters) {
375 const auto & param_type = parameter.get_type();
376 const auto & param_name = parameter.get_name();
377 if (param_name.find(name_ +
".") != 0) {
380 if (param_type == ParameterType::PARAMETER_DOUBLE) {
381 if (param_name != name_ +
"." +
"custom_inscribed_radius" &&
382 parameter.as_double() < 0.0)
385 logger_,
"The value of parameter '%s' is incorrectly set to %f, "
386 "it should be >=0. Ignoring parameter update.",
387 param_name.c_str(), parameter.as_double());
388 result.successful =
false;
397 const std::vector<rclcpp::Parameter> & parameters)
399 std::lock_guard<Costmap2D::mutex_t> guard(*
getMutex());
401 bool need_cache_recompute =
false;
403 for (
const auto & parameter : parameters) {
404 const auto & param_type = parameter.get_type();
405 const auto & param_name = parameter.get_name();
406 if (param_name.find(name_ +
".") != 0) {
410 if (param_type == ParameterType::PARAMETER_DOUBLE) {
411 if (param_name == name_ +
"." +
"inflation_radius" &&
412 inflation_radius_ != parameter.as_double())
414 inflation_radius_ = parameter.as_double();
415 need_reinflation_ =
true;
416 need_cache_recompute =
true;
418 }
else if (param_name == name_ +
"." +
"custom_inscribed_radius" &&
419 custom_inscribed_radius_ != parameter.as_double())
421 custom_inscribed_radius_ = parameter.as_double();
423 need_reinflation_ =
true;
424 need_cache_recompute =
true;
425 }
else if (param_name == name_ +
"." +
"cost_scaling_factor" &&
428 cost_scaling_factor_ = parameter.as_double();
429 need_reinflation_ =
true;
430 need_cache_recompute =
true;
433 }
else if (param_type == ParameterType::PARAMETER_INTEGER) {
434 if (param_name == name_ +
"." +
"num_threads" &&
435 num_threads_ != parameter.as_int())
437 int new_value = parameter.as_int();
439 if (new_value < -1) {
442 "Invalid num_threads value %d, must be -1 (auto) or > 0. Ignoring.",
445 int available_cores = omp_get_max_threads();
446 if (new_value > available_cores) {
449 "num_threads=%d exceeds available cores (%d). Ignoring.",
450 new_value, available_cores);
452 num_threads_ = new_value;
455 "Updated num_threads to %d %s",
457 num_threads_ == -1 ?
"(auto)" :
"");
463 "num_threads parameter ignored - OpenMP support not available. "
464 "Inflation layer will use single thread.");
465 num_threads_ = new_value;
468 }
else if (param_type == ParameterType::PARAMETER_BOOL) {
469 if (param_name == name_ +
"." +
"enabled" && enabled_ != parameter.as_bool()) {
470 enabled_ = parameter.as_bool();
471 need_reinflation_ =
true;
473 }
else if (param_name == name_ +
"." +
"inflate_unknown" &&
474 inflate_unknown_ != parameter.as_bool())
476 inflate_unknown_ = parameter.as_bool();
477 need_reinflation_ =
true;
479 }
else if (param_name == name_ +
"." +
"inflate_around_unknown" &&
480 inflate_around_unknown_ != parameter.as_bool())
482 inflate_around_unknown_ = parameter.as_bool();
483 need_reinflation_ =
true;
489 if (need_cache_recompute) {
A 2D costmap provides a mapping between points in the world and their associated "costs".
unsigned char * getCharMap() const
Will return a pointer to the underlying unsigned char array used as the costmap.
double getResolution() const
Accessor for the resolution of the costmap.
unsigned int getSizeInCellsX() const
Accessor for the x size of the costmap in cells.
unsigned int getSizeInCellsY() const
Accessor for the y size of the costmap in cells.
Layer to convolve costmap by robot's radius or footprint using Eigen-based Felzenszwalb-Huttenlocher ...
double getCostScalingFactor() override
Get the cost scaling factor.
~InflationLayer()
A destructor.
void computeCaches()
Generate cost lookup table for distance to cost mapping.
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.
int getOptimalThreadCount()
Determine optimal thread count based on system resources.
unsigned int cellDistance(double world_dist)
Convert world distance to cell distance.
InflationLayer()
A constructor.
void deactivate() override
Deactivate the layer.
void onInitialize() override
Initialization process of layer on startup.
void matchSize() override
Match the size of the master costmap.
void onFootprintChanged() override
Process updates on footprint changes to the inflation layer.
unsigned char computeCost(double distance) const override
Given a distance, compute a cost.
void updateParametersCallback(const std::vector< rclcpp::Parameter > ¶meters)
Apply parameter updates after validation This callback is executed when parameters have been successf...
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.
rcl_interfaces::msg::SetParametersResult validateParameterUpdatesCallback(const std::vector< rclcpp::Parameter > ¶meters)
Validate incoming parameter updates before applying them. This callback is triggered when one or more...
void activate() override
Activate the layer.
void applyInflation(unsigned char *master_array, const MatrixXfRM &distance_map, int min_i, int min_j, int max_i, int max_j, int roi_min_i, int roi_min_j, unsigned int size_x)
Apply inflation costs from distance map to costmap.
void updateInscribedRadius()
Update inscribed_radius_ from custom_inscribed_radius_ or the footprint, logging a warning when the c...
mutex_t * getMutex() override
Get the mutex of the inflation information.
Abstract class for layered costmap plugin implementations.
void setCurrent(bool current)
Set whether the data in the layer is up to date.
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().
Eigen::Matrix< float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > MatrixXfRM
Row-major float matrix type for efficient row-wise access.