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_;
199 if (cloud.height == 0 || cloud.width == 0) {
203 double sq_obstacle_max_range = obs.obstacle_max_range_ * obs.obstacle_max_range_;
204 double sq_obstacle_min_range = obs.obstacle_min_range_ * obs.obstacle_min_range_;
206 sensor_msgs::PointCloud2ConstIterator<float> iter_x(cloud,
"x");
207 sensor_msgs::PointCloud2ConstIterator<float> iter_y(cloud,
"y");
208 sensor_msgs::PointCloud2ConstIterator<float> iter_z(cloud,
"z");
210 for (; iter_x != iter_x.end(); ++iter_x, ++iter_y, ++iter_z) {
222 double sq_dist = (*iter_x - obs.origin_.x) * (*iter_x - obs.origin_.x) +
223 (*iter_y - obs.origin_.y) * (*iter_y - obs.origin_.y) +
224 (*iter_z - obs.origin_.z) * (*iter_z - obs.origin_.z);
227 if (sq_dist >= sq_obstacle_max_range) {
232 if (sq_dist < sq_obstacle_min_range) {
237 unsigned int mx, my, mz;
238 if (*iter_z < origin_z_) {
239 if (!
worldToMap3D(*iter_x, *iter_y, origin_z_, mx, my, mz)) {
242 }
else if (!
worldToMap3D(*iter_x, *iter_y, *iter_z, mx, my, mz)) {
247 if (voxel_grid_.markVoxelInMap(mx, my, mz, mark_threshold_)) {
248 unsigned int index =
getIndex(mx, my);
250 costmap_[index] = LETHAL_OBSTACLE;
252 static_cast<double>(*iter_x),
static_cast<double>(*iter_y),
253 min_x, min_y, max_x, max_y);
258 if (publish_voxel_) {
259 auto grid_msg = std::make_unique<nav2_msgs::msg::VoxelGrid>();
260 unsigned int size = voxel_grid_.sizeX() * voxel_grid_.sizeY();
261 grid_msg->size_x = voxel_grid_.sizeX();
262 grid_msg->size_y = voxel_grid_.sizeY();
263 grid_msg->size_z = voxel_grid_.sizeZ();
264 grid_msg->data.resize(size);
265 memcpy(&grid_msg->data[0], voxel_grid_.getData(), size *
sizeof(
unsigned int));
267 grid_msg->origin.x = origin_x_;
268 grid_msg->origin.y = origin_y_;
269 grid_msg->origin.z = origin_z_;
271 grid_msg->resolutions.x = resolution_;
272 grid_msg->resolutions.y = resolution_;
273 grid_msg->resolutions.z = z_resolution_;
275 grid_msg->header.stamp = clock_->now();
277 voxel_pub_->publish(std::move(grid_msg));
280 updateFootprint(robot_x, robot_y, robot_yaw, min_x, min_y, max_x, max_y);
284 const Observation & clearing_observation,
double * min_x,
289 auto clearing_endpoints_ = std::make_unique<sensor_msgs::msg::PointCloud2>();
291 if (clearing_observation.cloud_.height == 0 || clearing_observation.cloud_.width == 0) {
295 double sensor_x, sensor_y, sensor_z;
296 double ox = clearing_observation.origin_.x;
297 double oy = clearing_observation.origin_.y;
298 double oz = clearing_observation.origin_.z;
303 "Sensor origin at (%.2f, %.2f %.2f) is out of map bounds "
304 "(%.2f, %.2f, %.2f) to (%.2f, %.2f, %.2f). "
305 "The costmap cannot raytrace for it.",
307 origin_x_, origin_y_, origin_z_,
314 bool publish_clearing_points;
317 auto node = node_.lock();
319 throw std::runtime_error{
"Failed to lock node"};
321 publish_clearing_points = (node->count_subscribers(
"clearing_endpoints") > 0);
324 clearing_endpoints_->data.clear();
325 clearing_endpoints_->width = clearing_observation.cloud_.width;
326 clearing_endpoints_->height = clearing_observation.cloud_.height;
327 clearing_endpoints_->is_dense =
true;
328 clearing_endpoints_->is_bigendian =
false;
330 sensor_msgs::PointCloud2Modifier modifier(*clearing_endpoints_);
331 modifier.setPointCloud2Fields(
332 3,
"x", 1, sensor_msgs::msg::PointField::FLOAT32,
333 "y", 1, sensor_msgs::msg::PointField::FLOAT32,
334 "z", 1, sensor_msgs::msg::PointField::FLOAT32);
336 sensor_msgs::PointCloud2Iterator<float> clearing_endpoints_iter_x(*clearing_endpoints_,
"x");
337 sensor_msgs::PointCloud2Iterator<float> clearing_endpoints_iter_y(*clearing_endpoints_,
"y");
338 sensor_msgs::PointCloud2Iterator<float> clearing_endpoints_iter_z(*clearing_endpoints_,
"z");
345 sensor_msgs::PointCloud2ConstIterator<float> iter_x(clearing_observation.cloud_,
"x");
346 sensor_msgs::PointCloud2ConstIterator<float> iter_y(clearing_observation.cloud_,
"y");
347 sensor_msgs::PointCloud2ConstIterator<float> iter_z(clearing_observation.cloud_,
"z");
349 for (; iter_x != iter_x.end(); ++iter_x, ++iter_y, ++iter_z) {
350 double wpx = *iter_x;
351 double wpy = *iter_y;
352 double wpz = *iter_z;
354 double distance =
dist(ox, oy, oz, wpx, wpy, wpz);
355 double scaling_fact = 1.0;
356 scaling_fact = std::max(std::min(scaling_fact, (distance - 2 * resolution_) / distance), 0.0);
357 wpx = scaling_fact * (wpx - ox) + ox;
358 wpy = scaling_fact * (wpy - oy) + oy;
359 wpz = scaling_fact * (wpz - oz) + oz;
365 bool wp_outside =
false;
368 if (wpz > map_end_z) {
370 t = std::max(0.0, std::min(t, (map_end_z - 0.01 - oz) / c));
372 }
else if (wpz < origin_z_) {
375 t = std::min(t, (origin_z_ - oz) / c);
380 if (wpx < origin_x_) {
381 t = std::min(t, (origin_x_ - ox) / a);
384 if (wpy < origin_y_) {
385 t = std::min(t, (origin_y_ - oy) / b);
390 if (wpx > map_end_x) {
391 t = std::min(t, (map_end_x - ox) / a);
394 if (wpy > map_end_y) {
395 t = std::min(t, (map_end_y - oy) / b);
399 constexpr
double wp_epsilon = 1e-5;
403 }
else if (t < 0.0) {
412 double point_x, point_y, point_z;
414 unsigned int cell_raytrace_max_range =
cellDistance(clearing_observation.raytrace_max_range_);
415 unsigned int cell_raytrace_min_range =
cellDistance(clearing_observation.raytrace_min_range_);
419 voxel_grid_.clearVoxelLineInMap(
420 sensor_x, sensor_y, sensor_z, point_x, point_y, point_z,
422 unknown_threshold_, mark_threshold_, FREE_SPACE, NO_INFORMATION,
423 cell_raytrace_max_range, cell_raytrace_min_range);
426 ox, oy, wpx, wpy, clearing_observation.raytrace_max_range_,
427 clearing_observation.raytrace_min_range_, min_x, min_y,
431 if (publish_clearing_points) {
432 *clearing_endpoints_iter_x = wpx;
433 *clearing_endpoints_iter_y = wpy;
434 *clearing_endpoints_iter_z = wpz;
436 ++clearing_endpoints_iter_x;
437 ++clearing_endpoints_iter_y;
438 ++clearing_endpoints_iter_z;
443 if (publish_clearing_points) {
445 clearing_endpoints_->header.stamp = clearing_observation.cloud_.header.stamp;
447 clearing_endpoints_pub_->publish(std::move(clearing_endpoints_));
454 int cell_ox, cell_oy;
455 cell_ox =
static_cast<int>((new_origin_x - origin_x_) / resolution_);
456 cell_oy =
static_cast<int>((new_origin_y - origin_y_) / resolution_);
460 double new_grid_ox, new_grid_oy;
461 new_grid_ox = origin_x_ + cell_ox * resolution_;
462 new_grid_oy = origin_y_ + cell_oy * resolution_;
465 int size_x = size_x_;
466 int size_y = size_y_;
469 int lower_left_x, lower_left_y, upper_right_x, upper_right_y;
470 lower_left_x = std::min(std::max(cell_ox, 0), size_x);
471 lower_left_y = std::min(std::max(cell_oy, 0), size_y);
472 upper_right_x = std::min(std::max(cell_ox + size_x, 0), size_x);
473 upper_right_y = std::min(std::max(cell_oy + size_y, 0), size_y);
475 unsigned int cell_size_x = upper_right_x - lower_left_x;
476 unsigned int cell_size_y = upper_right_y - lower_left_y;
479 unsigned char * local_map =
new unsigned char[cell_size_x * cell_size_y];
480 unsigned int * local_voxel_map =
new unsigned int[cell_size_x * cell_size_y];
481 unsigned int * voxel_map = voxel_grid_.getData();
485 costmap_, lower_left_x, lower_left_y, size_x_, local_map, 0, 0, cell_size_x,
489 voxel_map, lower_left_x, lower_left_y, size_x_, local_voxel_map, 0, 0, cell_size_x,
497 origin_x_ = new_grid_ox;
498 origin_y_ = new_grid_oy;
501 int start_x = lower_left_x - cell_ox;
502 int start_y = lower_left_y - cell_oy;
506 local_map, 0, 0, cell_size_x, costmap_, start_x, start_y, size_x_, cell_size_x,
509 local_voxel_map, 0, 0, cell_size_x, voxel_map, start_x, start_y, size_x_,
515 delete[] local_voxel_map;
519 const std::vector<rclcpp::Parameter> & parameters)
521 rcl_interfaces::msg::SetParametersResult result;
522 result.successful =
true;
523 for (
const auto & parameter : parameters) {
524 const auto & param_type = parameter.get_type();
525 const auto & param_name = parameter.get_name();
526 if (param_name.find(name_ +
".") != 0) {
529 if (param_name == name_ +
"." +
"publish_voxel_map") {
531 logger_,
"publish voxel map is not a dynamic parameter "
532 "cannot be changed while running. Rejecting parameter update.");
533 result.successful =
false;
534 }
else if (param_type == ParameterType::PARAMETER_DOUBLE) {
535 if (parameter.as_double() < 0.0 && param_name == name_ +
"." +
"z_resolution") {
537 logger_,
"The value of parameter '%s' is incorrectly set to %f, "
538 "it should be >=0. Ignoring parameter update.",
539 param_name.c_str(), parameter.as_double());
540 result.successful =
false;
549 const std::vector<rclcpp::Parameter> & parameters)
551 std::lock_guard<Costmap2D::mutex_t> guard(*getMutex());
552 bool resize_map_needed =
false;
554 for (
const auto & parameter : parameters) {
555 const auto & param_type = parameter.get_type();
556 const auto & param_name = parameter.get_name();
557 if (param_name.find(name_ +
".") != 0) {
561 if (param_type == ParameterType::PARAMETER_DOUBLE) {
562 if (param_name == name_ +
"." +
"min_obstacle_height" &&
567 }
else if (param_name == name_ +
"." +
"max_obstacle_height" &&
572 }
else if (param_name == name_ +
"." +
"origin_z" &&
573 origin_z_ != parameter.as_double())
575 origin_z_ = parameter.as_double();
576 resize_map_needed =
true;
578 }
else if (param_name == name_ +
"." +
"z_resolution" &&
579 z_resolution_ != parameter.as_double())
581 z_resolution_ = parameter.as_double();
582 resize_map_needed =
true;
586 }
else if (param_type == ParameterType::PARAMETER_BOOL) {
587 if (param_name == name_ +
"." +
"enabled" &&
588 enabled_ != parameter.as_bool())
590 enabled_ = parameter.as_bool();
592 }
else if (param_name == name_ +
"." +
"footprint_clearing_enabled") {
593 footprint_clearing_enabled_ = parameter.as_bool();
596 }
else if (param_type == ParameterType::PARAMETER_INTEGER) {
597 if (param_name == name_ +
"." +
"z_voxels" &&
598 size_z_ != parameter.as_int())
600 size_z_ = parameter.as_int();
601 resize_map_needed =
true;
603 }
else if (param_name == name_ +
"." +
"unknown_threshold") {
604 unknown_threshold_ = parameter.as_int() + (VOXEL_BITS - size_z_);
606 }
else if (param_name == name_ +
"." +
"mark_threshold") {
607 mark_threshold_ = parameter.as_int();
609 }
else if (param_name == name_ +
"." +
"combination_method") {
615 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.