40 #include "nav2_costmap_2d/static_layer.hpp"
45 #include "pluginlib/class_list_macros.hpp"
46 #include "tf2/convert.hpp"
47 #include "tf2_geometry_msgs/tf2_geometry_msgs.hpp"
48 #include "nav2_ros_common/validate_messages.hpp"
54 using nav2_costmap_2d::NO_INFORMATION;
55 using nav2_costmap_2d::LETHAL_OBSTACLE;
56 using nav2_costmap_2d::INSCRIBED_INFLATED_OBSTACLE;
57 using nav2_costmap_2d::FREE_SPACE;
58 using rcl_interfaces::msg::ParameterType;
64 : map_buffer_(nullptr)
80 if (map_subscribe_transient_local_) {
86 "Subscribing to the map topic (%s) with %s durability",
88 map_subscribe_transient_local_ ?
"transient local" :
"volatile");
90 auto node = node_.lock();
92 throw std::runtime_error{
"Failed to lock node"};
95 map_sub_ = node->create_subscription<nav_msgs::msg::OccupancyGrid>(
100 if (subscribe_to_updates_) {
101 RCLCPP_INFO(logger_,
"Subscribing to updates");
102 map_update_sub_ = node->create_subscription<map_msgs::msg::OccupancyGridUpdate>(
103 map_topic_ +
"_updates",
111 auto node = node_.lock();
113 post_set_params_handler_ = node->add_post_set_parameters_callback(
116 this, std::placeholders::_1));
117 on_set_params_handler_ = node->add_on_set_parameters_callback(
120 this, std::placeholders::_1));
126 auto node = node_.lock();
127 if (post_set_params_handler_ && node) {
128 node->remove_post_set_parameters_callback(post_set_params_handler_.get());
130 post_set_params_handler_.reset();
131 if (on_set_params_handler_ && node) {
132 node->remove_on_set_parameters_callback(on_set_params_handler_.get());
134 on_set_params_handler_.reset();
147 int temp_lethal_threshold = 0;
148 double temp_tf_tol = 0.0;
150 auto node = node_.lock();
152 throw std::runtime_error{
"Failed to lock node"};
155 enabled_ = node->declare_or_get_parameter(name_ +
"." +
"enabled",
true);
156 subscribe_to_updates_ = node->declare_or_get_parameter(
157 name_ +
"." +
"subscribe_to_updates",
false);
158 footprint_clearing_enabled_ = node->declare_or_get_parameter(
159 name_ +
"." +
"footprint_clearing_enabled",
false);
160 restore_cleared_footprint_ = node->declare_or_get_parameter(
161 name_ +
"." +
"restore_cleared_footprint",
true);
162 map_topic_ = node->declare_or_get_parameter(
163 name_ +
"." +
"map_topic", std::string(
"map"));
165 map_subscribe_transient_local_ = node->declare_or_get_parameter(
166 name_ +
"." +
"map_subscribe_transient_local",
true);
167 node->get_parameter(
"track_unknown_space", track_unknown_space_);
168 node->get_parameter(
"use_maximum", use_maximum_);
169 node->get_parameter(
"lethal_cost_threshold", temp_lethal_threshold);
170 node->get_parameter(
"inscribed_obstacle_cost_value", inscribed_obstacle_cost_value_);
171 node->get_parameter(
"unknown_cost_value", unknown_cost_value_);
172 node->get_parameter(
"trinary_costmap", trinary_costmap_);
173 node->get_parameter(
"transform_tolerance", temp_tf_tol);
176 lethal_threshold_ = std::max(std::min(temp_lethal_threshold, 100), 0);
177 map_received_ =
false;
178 map_received_in_update_bounds_ =
false;
180 transform_tolerance_ = tf2::durationFromSec(temp_tf_tol);
186 RCLCPP_DEBUG(logger_,
"StaticLayer: Process map");
188 unsigned int size_x = new_map.info.width;
189 unsigned int size_y = new_map.info.height;
193 "StaticLayer: Received a %d X %d map at %f m/pix", size_x, size_y,
194 new_map.info.resolution);
208 "StaticLayer: Resizing costmap to %d X %d at %f m/pix", size_x, size_y,
209 new_map.info.resolution);
211 double fmod_x = std::fmod(new_map.info.origin.position.x, new_map.info.resolution);
212 double fmod_y = std::fmod(new_map.info.origin.position.y, new_map.info.resolution);
214 if (std::abs(fmod_x) > EPSILON || std::abs(fmod_y) > EPSILON) {
217 "StaticLayer: Costmap origin coordinates are not perfectly aligned with the resolution. "
218 "This may cause misalignment aliasing between rolling and non-rolling costmaps.\n"
219 "Map origin: (%.f, %.f) | Resolution: %.f",
220 new_map.info.origin.position.x, new_map.info.origin.position.y,
221 new_map.info.resolution);
225 size_x, size_y, new_map.info.resolution,
226 new_map.info.origin.position.x,
227 new_map.info.origin.position.y,
229 }
else if (size_x_ != size_x || size_y_ != size_y ||
230 !
isEqual(resolution_, new_map.info.resolution, EPSILON) ||
231 !
isEqual(origin_x_, new_map.info.origin.position.x, EPSILON) ||
232 !
isEqual(origin_y_, new_map.info.origin.position.y, EPSILON))
237 "StaticLayer: Resizing static layer to %d X %d at %f m/pix", size_x, size_y,
238 new_map.info.resolution);
240 size_x, size_y, new_map.info.resolution,
241 new_map.info.origin.position.x, new_map.info.origin.position.y);
244 unsigned int index = 0;
247 std::lock_guard<Costmap2D::mutex_t> guard(*getMutex());
250 for (
unsigned int i = 0; i < size_y; ++i) {
251 for (
unsigned int j = 0; j < size_x; ++j) {
252 unsigned char value = new_map.data[index];
258 map_frame_ = new_map.header.frame_id;
285 if (track_unknown_space_ && value == unknown_cost_value_) {
286 return NO_INFORMATION;
287 }
else if (!track_unknown_space_ && value == unknown_cost_value_) {
289 }
else if (value == inscribed_obstacle_cost_value_) {
290 return INSCRIBED_INFLATED_OBSTACLE;
291 }
else if (value >= lethal_threshold_) {
292 return LETHAL_OBSTACLE;
293 }
else if (trinary_costmap_) {
297 double scale =
static_cast<double>(value) / lethal_threshold_;
298 return scale * LETHAL_OBSTACLE;
304 if (!nav2::validateMsg(*new_map)) {
305 RCLCPP_ERROR(logger_,
"Received map message is malformed. Rejecting.");
308 if (!map_received_) {
310 map_received_ =
true;
313 std::lock_guard<Costmap2D::mutex_t> guard(*getMutex());
314 map_buffer_ = new_map;
321 if (!nav2::validateMsg(*update)) {
322 RCLCPP_ERROR(logger_,
"Received map update is malformed. Rejecting.");
326 std::lock_guard<Costmap2D::mutex_t> guard(*getMutex());
327 if (update->y <
static_cast<int32_t
>(y_) ||
328 y_ + height_ < update->y + update->height ||
329 update->x <
static_cast<int32_t
>(x_) ||
330 x_ + width_ < update->x + update->width)
334 "StaticLayer: Map update ignored. Exceeds bounds of static layer.\n"
335 "Static layer origin: %d, %d bounds: %d X %d\n"
336 "Update origin: %d, %d bounds: %d X %d",
337 x_, y_, width_, height_, update->x, update->y, update->width,
342 if (update->header.frame_id != map_frame_) {
345 "StaticLayer: Map update ignored. Current map is in frame %s "
346 "but update was in frame %s",
347 map_frame_.c_str(), update->header.frame_id.c_str());
352 for (
unsigned int y = 0; y < update->height; y++) {
353 unsigned int index_base = (update->y + y) * size_x_;
354 for (
unsigned int x = 0; x < update->width; x++) {
355 unsigned int index = index_base + x + update->x;
366 double robot_x,
double robot_y,
double robot_yaw,
double * min_x,
371 if (!map_received_) {
372 map_received_in_update_bounds_ =
false;
375 map_received_in_update_bounds_ =
true;
377 std::lock_guard<Costmap2D::mutex_t> guard(*getMutex());
382 map_buffer_ =
nullptr;
391 useExtraBounds(min_x, min_y, max_x, max_y);
403 *min_x = std::min(robot_x - half_w, *min_x);
404 *min_y = std::min(robot_y - half_h, *min_y);
405 *max_x = std::max(robot_x + half_w, *max_x);
406 *max_y = std::max(robot_y + half_h, *max_y);
411 *min_x = std::min(wx, *min_x);
412 *min_y = std::min(wy, *min_y);
414 mapToWorld(x_ + width_, y_ + height_, wx, wy);
415 *max_x = std::max(wx, *max_x);
416 *max_y = std::max(wy, *max_y);
421 updateFootprint(robot_x, robot_y, robot_yaw, min_x, min_y, max_x, max_y);
426 double robot_x,
double robot_y,
double robot_yaw,
427 double * min_x,
double * min_y,
431 if (!footprint_clearing_enabled_) {
return;}
435 for (
unsigned int i = 0; i < transformed_footprint_.size(); i++) {
436 touch(transformed_footprint_[i].x, transformed_footprint_[i].y, min_x, min_y, max_x, max_y);
443 int min_i,
int min_j,
int max_i,
int max_j)
445 std::lock_guard<Costmap2D::mutex_t> guard(*getMutex());
449 if (!map_received_in_update_bounds_) {
450 static int count = 0;
453 RCLCPP_WARN(logger_,
"Can't update static costmap layer, no map received");
459 std::vector<MapLocation> map_region_to_restore;
460 if (footprint_clearing_enabled_) {
461 map_region_to_restore.reserve(100);
469 updateWithTrueOverwrite(master_grid, min_i, min_j, max_i, max_j);
471 updateWithMax(master_grid, min_i, min_j, max_i, max_j);
478 geometry_msgs::msg::TransformStamped transform;
480 transform = tf_->lookupTransform(
482 transform_tolerance_);
483 }
catch (tf2::TransformException & ex) {
484 RCLCPP_ERROR(logger_,
"StaticLayer: %s", ex.what());
488 tf2::Transform tf2_transform;
489 tf2::fromMsg(transform.transform, tf2_transform);
491 for (
int i = min_i; i < max_i; ++i) {
492 for (
int j = min_j; j < max_j; ++j) {
496 tf2::Vector3 p(wx, wy, 0);
497 p = tf2_transform * p;
510 if (footprint_clearing_enabled_ && restore_cleared_footprint_) {
526 return std::abs(a - b) < epsilon;
530 const std::vector<rclcpp::Parameter> & parameters)
532 rcl_interfaces::msg::SetParametersResult result;
533 result.successful =
true;
534 for (
const auto & parameter : parameters) {
535 const auto & param_type = parameter.get_type();
536 const auto & param_name = parameter.get_name();
537 if (param_name.find(name_ +
".") != 0) {
541 if (param_name == name_ +
"." +
"map_subscribe_transient_local" ||
542 param_name == name_ +
"." +
"map_topic" ||
543 param_name == name_ +
"." +
"subscribe_to_updates")
546 logger_,
"%s is not a dynamic parameter "
547 "cannot be changed while running. Rejecting parameter update.", param_name.c_str());
548 }
else if (param_type == ParameterType::PARAMETER_BOOL &&
549 param_name == name_ +
"." +
"restore_cleared_footprint")
551 if (!footprint_clearing_enabled_) {
553 logger_,
"restore_cleared_footprint cannot be used "
554 "when footprint_clearing_enabled is False. Rejecting parameter update.");
555 result.successful =
false;
564 const std::vector<rclcpp::Parameter> & parameters)
566 std::lock_guard<Costmap2D::mutex_t> guard(*getMutex());
568 for (
const auto & parameter : parameters) {
569 const auto & param_type = parameter.get_type();
570 const auto & param_name = parameter.get_name();
571 if (param_name.find(name_ +
".") != 0) {
575 if (param_type == ParameterType::PARAMETER_BOOL) {
576 if (param_name == name_ +
"." +
"enabled" && enabled_ != parameter.as_bool()) {
577 enabled_ = parameter.as_bool();
584 }
else if (param_name == name_ +
"." +
"footprint_clearing_enabled") {
585 footprint_clearing_enabled_ = parameter.as_bool();
586 }
else if (param_name == name_ +
"." +
"restore_cleared_footprint") {
587 restore_cleared_footprint_ = parameter.as_bool();
A QoS profile for latched, reliable topics with a history of 10 messages.
A QoS profile for standard reliable topics with a history of 10 messages.
A 2D costmap provides a mapping between points in the world and their associated "costs".
void mapToWorld(unsigned int mx, unsigned int my, double &wx, double &wy) const
Convert from map coordinates to world coordinates.
void resizeMap(unsigned int size_x, unsigned int size_y, double resolution, double origin_x, double origin_y)
Resize the costmap.
unsigned char getCost(unsigned int mx, unsigned int my) const
Get the cost of a cell in the costmap.
bool worldToMap(double wx, double wy, unsigned int &mx, unsigned int &my) const
Convert from world coordinates to map coordinates.
double getResolution() const
Accessor for the resolution of the costmap.
double getSizeInMetersY() const
Accessor for the y size of the costmap in meters.
double getSizeInMetersX() const
Accessor for the x size of the costmap in meters.
void setMapRegionOccupiedByPolygon(const std::vector< MapLocation > &polygon_map_region, unsigned char new_cost_value)
Sets the given map region to desired value.
void restoreMapRegionOccupiedByPolygon(const std::vector< MapLocation > &polygon_map_region)
Restores the corresponding map region using given map region.
bool getMapRegionOccupiedByPolygon(const std::vector< geometry_msgs::msg::Point > &polygon, std::vector< MapLocation > &polygon_map_region)
Gets the map region occupied by polygon.
unsigned int getSizeInCellsX() const
Accessor for the x size of the costmap in cells.
double getOriginY() const
Accessor for the y origin of the costmap.
unsigned int getSizeInCellsY() const
Accessor for the y size of the costmap in cells.
double getOriginX() const
Accessor for the x origin of the costmap.
void setCost(unsigned int mx, unsigned int my, unsigned char cost)
Set the cost of a cell in the costmap.
void touch(double x, double y, double *min_x, double *min_y, double *max_x, double *max_y)
Abstract class for layered costmap plugin implementations.
std::string joinWithParentNamespace(const std::string &topic)
void setCurrent(bool current)
Set whether the data in the layer is up to date.
const std::vector< geometry_msgs::msg::Point > & getFootprint() const
Convenience function for layered_costmap_->getFootprint().
bool isRolling()
If this costmap is rolling or not.
Costmap2D * getCostmap()
Get the costmap pointer to the master costmap.
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 isSizeLocked()
Get if the size of the costmap is locked.
Takes in a map generated from SLAM to add costs to costmap.
void getParameters()
Get parameters of layer.
virtual ~StaticLayer()
Static Layer destructor.
virtual void deactivate()
Deactivate this layer.
bool has_updated_data_
frame that map is located in
void updateParametersCallback(const std::vector< rclcpp::Parameter > ¶meters)
Apply parameter updates after validation This callback is executed when parameters have been successf...
unsigned char interpretValue(unsigned char value)
Interpret the value in the static map given on the topic to convert into costs for the costmap to uti...
StaticLayer()
Static Layer constructor.
virtual void matchSize()
Match the size of the master costmap.
virtual void onInitialize()
Initialization process of layer on startup.
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.
virtual void activate()
Activate this layer.
void incomingUpdate(map_msgs::msg::OccupancyGridUpdate::ConstSharedPtr update)
Callback to update the costmap's map from the map_server (or SLAM) with an update in a particular are...
bool isEqual(double a, double b, double epsilon)
Check if two double values are equal within a given epsilon.
std::string global_frame_
The global frame for the costmap.
void processMap(const nav_msgs::msg::OccupancyGrid &new_map)
Process a new map coming from a topic.
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.
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 incomingMap(const nav_msgs::msg::OccupancyGrid::ConstSharedPtr &new_map)
Callback to update the costmap's map from the map_server.
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.
virtual void reset()
Reset this costmap.
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)