39 #include "nav2_costmap_2d/voxel_layer.hpp"
47 #include "pluginlib/class_list_macros.hpp"
48 #include "sensor_msgs/point_cloud2_iterator.hpp"
53 using nav2_costmap_2d::NO_INFORMATION;
54 using nav2_costmap_2d::LETHAL_OBSTACLE;
55 using nav2_costmap_2d::FREE_SPACE;
56 using rcl_interfaces::msg::ParameterType;
65 auto node = node_.lock();
67 throw std::runtime_error{
"Failed to lock node"};
70 enabled_ = node->declare_or_get_parameter(name_ +
"." +
"enabled",
true);
71 footprint_clearing_enabled_ = node->declare_or_get_parameter(
72 name_ +
"." +
"footprint_clearing_enabled",
true);
74 name_ +
"." +
"min_obstacle_height", 0.0);
76 name_ +
"." +
"max_obstacle_height", 2.0);
77 size_z_ = node->declare_or_get_parameter(name_ +
"." +
"z_voxels", 10);
78 origin_z_ = node->declare_or_get_parameter(name_ +
"." +
"origin_z", 0.0);
79 z_resolution_ = node->declare_or_get_parameter(name_ +
"." +
"z_resolution", 0.2);
80 unknown_threshold_ = node->declare_or_get_parameter(
81 name_ +
"." +
"unknown_threshold", 15);
82 mark_threshold_ = node->declare_or_get_parameter(name_ +
"." +
"mark_threshold", 0);
83 int combination_method_param = node->declare_or_get_parameter(
84 name_ +
"." +
"combination_method", 1);
85 publish_voxel_ = node->declare_or_get_parameter(
86 name_ +
"." +
"publish_voxel_map",
false);
90 voxel_pub_ = node->create_publisher<nav2_msgs::msg::VoxelGrid>(
92 voxel_pub_->on_activate();
95 clearing_endpoints_pub_ = node->create_publisher<sensor_msgs::msg::PointCloud2>(
97 clearing_endpoints_pub_->on_activate();
99 unknown_threshold_ += (VOXEL_BITS - size_z_);
106 auto node = node_.lock();
108 post_set_params_handler_ = node->add_post_set_parameters_callback(
111 this, std::placeholders::_1));
112 on_set_params_handler_ = node->add_on_set_parameters_callback(
115 this, std::placeholders::_1));
121 auto node = node_.lock();
122 if (post_set_params_handler_ && node) {
123 node->remove_post_set_parameters_callback(post_set_params_handler_.get());
125 post_set_params_handler_.reset();
126 if (on_set_params_handler_ && node) {
127 node->remove_on_set_parameters_callback(on_set_params_handler_.get());
129 on_set_params_handler_.reset();
138 std::lock_guard<Costmap2D::mutex_t> guard(*getMutex());
140 voxel_grid_.
resize(size_x_, size_y_, size_z_);
141 assert(voxel_grid_.sizeX() == size_x_ && voxel_grid_.sizeY() == size_y_);
162 double robot_x,
double robot_y,
double robot_yaw,
double * min_x,
163 double * min_y,
double * max_x,
double * max_y)
165 std::lock_guard<Costmap2D::mutex_t> guard(*getMutex());
167 if (rolling_window_) {
173 useExtraBounds(min_x, min_y, max_x, max_y);
176 std::vector<Observation::ConstSharedPtr> observations, clearing_observations;
188 for (
const auto & clearing_observation : clearing_observations) {
193 for (
const auto & observation : observations) {
196 const sensor_msgs::msg::PointCloud2 & cloud = obs.cloud_;
198 double sq_obstacle_max_range = obs.obstacle_max_range_ * obs.obstacle_max_range_;
199 double sq_obstacle_min_range = obs.obstacle_min_range_ * obs.obstacle_min_range_;
201 sensor_msgs::PointCloud2ConstIterator<float> iter_x(cloud,
"x");
202 sensor_msgs::PointCloud2ConstIterator<float> iter_y(cloud,
"y");
203 sensor_msgs::PointCloud2ConstIterator<float> iter_z(cloud,
"z");
205 for (; iter_x != iter_x.end(); ++iter_x, ++iter_y, ++iter_z) {
217 double sq_dist = (*iter_x - obs.origin_.x) * (*iter_x - obs.origin_.x) +
218 (*iter_y - obs.origin_.y) * (*iter_y - obs.origin_.y) +
219 (*iter_z - obs.origin_.z) * (*iter_z - obs.origin_.z);
222 if (sq_dist >= sq_obstacle_max_range) {
227 if (sq_dist < sq_obstacle_min_range) {
232 unsigned int mx, my, mz;
233 if (*iter_z < origin_z_) {
234 if (!
worldToMap3D(*iter_x, *iter_y, origin_z_, mx, my, mz)) {
237 }
else if (!
worldToMap3D(*iter_x, *iter_y, *iter_z, mx, my, mz)) {
242 if (voxel_grid_.markVoxelInMap(mx, my, mz, mark_threshold_)) {
243 unsigned int index =
getIndex(mx, my);
245 costmap_[index] = LETHAL_OBSTACLE;
247 static_cast<double>(*iter_x),
static_cast<double>(*iter_y),
248 min_x, min_y, max_x, max_y);
253 if (publish_voxel_) {
254 auto grid_msg = std::make_unique<nav2_msgs::msg::VoxelGrid>();
255 unsigned int size = voxel_grid_.sizeX() * voxel_grid_.sizeY();
256 grid_msg->size_x = voxel_grid_.sizeX();
257 grid_msg->size_y = voxel_grid_.sizeY();
258 grid_msg->size_z = voxel_grid_.sizeZ();
259 grid_msg->data.resize(size);
260 memcpy(&grid_msg->data[0], voxel_grid_.getData(), size *
sizeof(
unsigned int));
262 grid_msg->origin.x = origin_x_;
263 grid_msg->origin.y = origin_y_;
264 grid_msg->origin.z = origin_z_;
266 grid_msg->resolutions.x = resolution_;
267 grid_msg->resolutions.y = resolution_;
268 grid_msg->resolutions.z = z_resolution_;
270 grid_msg->header.stamp = clock_->now();
272 voxel_pub_->publish(std::move(grid_msg));
275 updateFootprint(robot_x, robot_y, robot_yaw, min_x, min_y, max_x, max_y);
279 const Observation & clearing_observation,
double * min_x,
284 auto clearing_endpoints_ = std::make_unique<sensor_msgs::msg::PointCloud2>();
286 if (clearing_observation.cloud_.height == 0 || clearing_observation.cloud_.width == 0) {
290 double sensor_x, sensor_y, sensor_z;
291 double ox = clearing_observation.origin_.x;
292 double oy = clearing_observation.origin_.y;
293 double oz = clearing_observation.origin_.z;
298 "Sensor origin at (%.2f, %.2f %.2f) is out of map bounds "
299 "(%.2f, %.2f, %.2f) to (%.2f, %.2f, %.2f). "
300 "The costmap cannot raytrace for it.",
302 origin_x_, origin_y_, origin_z_,
309 bool publish_clearing_points;
312 auto node = node_.lock();
314 throw std::runtime_error{
"Failed to lock node"};
316 publish_clearing_points = (node->count_subscribers(
"clearing_endpoints") > 0);
319 clearing_endpoints_->data.clear();
320 clearing_endpoints_->width = clearing_observation.cloud_.width;
321 clearing_endpoints_->height = clearing_observation.cloud_.height;
322 clearing_endpoints_->is_dense =
true;
323 clearing_endpoints_->is_bigendian =
false;
325 sensor_msgs::PointCloud2Modifier modifier(*clearing_endpoints_);
326 modifier.setPointCloud2Fields(
327 3,
"x", 1, sensor_msgs::msg::PointField::FLOAT32,
328 "y", 1, sensor_msgs::msg::PointField::FLOAT32,
329 "z", 1, sensor_msgs::msg::PointField::FLOAT32);
331 sensor_msgs::PointCloud2Iterator<float> clearing_endpoints_iter_x(*clearing_endpoints_,
"x");
332 sensor_msgs::PointCloud2Iterator<float> clearing_endpoints_iter_y(*clearing_endpoints_,
"y");
333 sensor_msgs::PointCloud2Iterator<float> clearing_endpoints_iter_z(*clearing_endpoints_,
"z");
340 sensor_msgs::PointCloud2ConstIterator<float> iter_x(clearing_observation.cloud_,
"x");
341 sensor_msgs::PointCloud2ConstIterator<float> iter_y(clearing_observation.cloud_,
"y");
342 sensor_msgs::PointCloud2ConstIterator<float> iter_z(clearing_observation.cloud_,
"z");
344 for (; iter_x != iter_x.end(); ++iter_x, ++iter_y, ++iter_z) {
345 double wpx = *iter_x;
346 double wpy = *iter_y;
347 double wpz = *iter_z;
349 double distance =
dist(ox, oy, oz, wpx, wpy, wpz);
350 double scaling_fact = 1.0;
351 scaling_fact = std::max(std::min(scaling_fact, (distance - 2 * resolution_) / distance), 0.0);
352 wpx = scaling_fact * (wpx - ox) + ox;
353 wpy = scaling_fact * (wpy - oy) + oy;
354 wpz = scaling_fact * (wpz - oz) + oz;
360 bool wp_outside =
false;
363 if (wpz > map_end_z) {
365 t = std::max(0.0, std::min(t, (map_end_z - 0.01 - oz) / c));
367 }
else if (wpz < origin_z_) {
370 t = std::min(t, (origin_z_ - oz) / c);
375 if (wpx < origin_x_) {
376 t = std::min(t, (origin_x_ - ox) / a);
379 if (wpy < origin_y_) {
380 t = std::min(t, (origin_y_ - oy) / b);
385 if (wpx > map_end_x) {
386 t = std::min(t, (map_end_x - ox) / a);
389 if (wpy > map_end_y) {
390 t = std::min(t, (map_end_y - oy) / b);
394 constexpr
double wp_epsilon = 1e-5;
398 }
else if (t < 0.0) {
407 double point_x, point_y, point_z;
409 unsigned int cell_raytrace_max_range =
cellDistance(clearing_observation.raytrace_max_range_);
410 unsigned int cell_raytrace_min_range =
cellDistance(clearing_observation.raytrace_min_range_);
414 voxel_grid_.clearVoxelLineInMap(
415 sensor_x, sensor_y, sensor_z, point_x, point_y, point_z,
417 unknown_threshold_, mark_threshold_, FREE_SPACE, NO_INFORMATION,
418 cell_raytrace_max_range, cell_raytrace_min_range);
421 ox, oy, wpx, wpy, clearing_observation.raytrace_max_range_,
422 clearing_observation.raytrace_min_range_, min_x, min_y,
426 if (publish_clearing_points) {
427 *clearing_endpoints_iter_x = wpx;
428 *clearing_endpoints_iter_y = wpy;
429 *clearing_endpoints_iter_z = wpz;
431 ++clearing_endpoints_iter_x;
432 ++clearing_endpoints_iter_y;
433 ++clearing_endpoints_iter_z;
438 if (publish_clearing_points) {
440 clearing_endpoints_->header.stamp = clearing_observation.cloud_.header.stamp;
442 clearing_endpoints_pub_->publish(std::move(clearing_endpoints_));
449 int cell_ox, cell_oy;
450 cell_ox =
static_cast<int>((new_origin_x - origin_x_) / resolution_);
451 cell_oy =
static_cast<int>((new_origin_y - origin_y_) / resolution_);
455 double new_grid_ox, new_grid_oy;
456 new_grid_ox = origin_x_ + cell_ox * resolution_;
457 new_grid_oy = origin_y_ + cell_oy * resolution_;
460 int size_x = size_x_;
461 int size_y = size_y_;
464 int lower_left_x, lower_left_y, upper_right_x, upper_right_y;
465 lower_left_x = std::min(std::max(cell_ox, 0), size_x);
466 lower_left_y = std::min(std::max(cell_oy, 0), size_y);
467 upper_right_x = std::min(std::max(cell_ox + size_x, 0), size_x);
468 upper_right_y = std::min(std::max(cell_oy + size_y, 0), size_y);
470 unsigned int cell_size_x = upper_right_x - lower_left_x;
471 unsigned int cell_size_y = upper_right_y - lower_left_y;
474 unsigned char * local_map =
new unsigned char[cell_size_x * cell_size_y];
475 unsigned int * local_voxel_map =
new unsigned int[cell_size_x * cell_size_y];
476 unsigned int * voxel_map = voxel_grid_.getData();
480 costmap_, lower_left_x, lower_left_y, size_x_, local_map, 0, 0, cell_size_x,
484 voxel_map, lower_left_x, lower_left_y, size_x_, local_voxel_map, 0, 0, cell_size_x,
492 origin_x_ = new_grid_ox;
493 origin_y_ = new_grid_oy;
496 int start_x = lower_left_x - cell_ox;
497 int start_y = lower_left_y - cell_oy;
501 local_map, 0, 0, cell_size_x, costmap_, start_x, start_y, size_x_, cell_size_x,
504 local_voxel_map, 0, 0, cell_size_x, voxel_map, start_x, start_y, size_x_,
510 delete[] local_voxel_map;
514 const std::vector<rclcpp::Parameter> & parameters)
516 rcl_interfaces::msg::SetParametersResult result;
517 result.successful =
true;
518 for (
const auto & parameter : parameters) {
519 const auto & param_type = parameter.get_type();
520 const auto & param_name = parameter.get_name();
521 if (param_name.find(name_ +
".") != 0) {
524 if (param_name == name_ +
"." +
"publish_voxel_map") {
526 logger_,
"publish voxel map is not a dynamic parameter "
527 "cannot be changed while running. Rejecting parameter update.");
528 result.successful =
false;
529 }
else if (param_type == ParameterType::PARAMETER_DOUBLE) {
530 if (parameter.as_double() < 0.0 && param_name == name_ +
"." +
"z_resolution") {
532 logger_,
"The value of parameter '%s' is incorrectly set to %f, "
533 "it should be >=0. Ignoring parameter update.",
534 param_name.c_str(), parameter.as_double());
535 result.successful =
false;
544 const std::vector<rclcpp::Parameter> & parameters)
546 std::lock_guard<Costmap2D::mutex_t> guard(*getMutex());
547 bool resize_map_needed =
false;
549 for (
const auto & parameter : parameters) {
550 const auto & param_type = parameter.get_type();
551 const auto & param_name = parameter.get_name();
552 if (param_name.find(name_ +
".") != 0) {
556 if (param_type == ParameterType::PARAMETER_DOUBLE) {
557 if (param_name == name_ +
"." +
"min_obstacle_height" &&
562 }
else if (param_name == name_ +
"." +
"max_obstacle_height" &&
567 }
else if (param_name == name_ +
"." +
"origin_z" &&
568 origin_z_ != parameter.as_double())
570 origin_z_ = parameter.as_double();
571 resize_map_needed =
true;
573 }
else if (param_name == name_ +
"." +
"z_resolution" &&
574 z_resolution_ != parameter.as_double())
576 z_resolution_ = parameter.as_double();
577 resize_map_needed =
true;
581 }
else if (param_type == ParameterType::PARAMETER_BOOL) {
582 if (param_name == name_ +
"." +
"enabled" &&
583 enabled_ != parameter.as_bool())
585 enabled_ = parameter.as_bool();
587 }
else if (param_name == name_ +
"." +
"footprint_clearing_enabled") {
588 footprint_clearing_enabled_ = parameter.as_bool();
591 }
else if (param_type == ParameterType::PARAMETER_INTEGER) {
592 if (param_name == name_ +
"." +
"z_voxels" &&
593 size_z_ != parameter.as_int())
595 size_z_ = parameter.as_int();
596 resize_map_needed =
true;
598 }
else if (param_name == name_ +
"." +
"unknown_threshold") {
599 unknown_threshold_ = parameter.as_int() + (VOXEL_BITS - size_z_);
601 }
else if (param_name == name_ +
"." +
"mark_threshold") {
602 mark_threshold_ = parameter.as_int();
604 }
else if (param_name == name_ +
"." +
"combination_method") {
610 if (resize_map_needed) {
A QoS profile for latched, reliable topics with a history of 1 messages.
unsigned int getIndex(unsigned int mx, unsigned int my) const
Given two map coordinates... compute the associated index.
void copyMapRegion(data_type *source_map, unsigned int sm_lower_left_x, unsigned int sm_lower_left_y, unsigned int sm_size_x, data_type *dest_map, unsigned int dm_lower_left_x, unsigned int dm_lower_left_y, unsigned int dm_size_x, unsigned int region_size_x, unsigned int region_size_y)
Copy a region of a source map into a destination map.
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.
virtual void resetMaps()
Resets the costmap and static_map to be unknown space.
unsigned int cellDistance(double world_dist)
Given distance in the world... convert it to cells.
void touch(double x, double y, double *min_x, double *min_y, double *max_x, double *max_y)
virtual void matchSize()
Match the size of the master costmap.
CombinationMethod combination_method_from_int(const int value)
Converts an integer to a CombinationMethod enum and logs on failure.
Abstract class for layered costmap plugin implementations.
void setCurrent(bool current)
Set whether the data in the layer is up to date.
Stores an observation in terms of a point cloud and the origin of the source.
virtual void activate()
Activate the layer.
std::string global_frame_
The global frame for the costmap.
void updateRaytraceBounds(double ox, double oy, double wx, double wy, double max_range, double min_range, double *min_x, double *min_y, double *max_x, double *max_y)
Process update costmap with raytracing the window bounds.
bool getMarkingObservations(std::vector< nav2_costmap_2d::Observation::ConstSharedPtr > &marking_observations) const
Get the observations used to mark space.
bool getClearingObservations(std::vector< nav2_costmap_2d::Observation::ConstSharedPtr > &clearing_observations) const
Get the observations used to clear space.
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 deactivate()
Deactivate the layer.
virtual void onInitialize()
Initialization process of layer on startup.
double min_obstacle_height_
Max Obstacle Height.
double max_obstacle_height_
Max Obstacle Height.
virtual void reset()
Reset this costmap.
Takes laser and pointcloud data to populate a 3D voxel representation of the environment.
virtual void onInitialize()
Initialization process of layer on startup.
virtual void activate()
Activate the layer.
virtual ~VoxelLayer()
Voxel Layer destructor.
double getSizeInMetersZ() const
Get the height of the voxel sizes in meters.
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.
void updateOrigin(double new_origin_x, double new_origin_y)
Update the layer's origin to a new pose, often when in a rolling costmap.
virtual void deactivate()
Deactivate the layer.
bool worldToMap3DFloat(double wx, double wy, double wz, double &mx, double &my, double &mz)
Convert world coordinates into map coordinates.
bool worldToMap3D(double wx, double wy, double wz, unsigned int &mx, unsigned int &my, unsigned int &mz)
Convert world coordinates into map coordinates.
virtual void resetMaps()
Reset internal maps.
virtual void matchSize()
Match the size of the master costmap.
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 updateParametersCallback(const std::vector< rclcpp::Parameter > ¶meters)
Apply parameter updates after validation This callback is executed when parameters have been successf...
virtual void reset()
Reset this costmap.
double dist(double x0, double y0, double z0, double x1, double y1, double z1)
Find L2 norm distance in 3D.
virtual void raytraceFreespace(const nav2_costmap_2d::Observation &clearing_observation, double *min_x, double *min_y, double *max_x, double *max_y)
Use raycasting between 2 points to clear freespace.
void resize(unsigned int size_x, unsigned int size_y, unsigned int size_z)
Resizes a voxel grid to the desired size.