39 #include "nav2_costmap_2d/obstacle_layer.hpp"
46 #include "pluginlib/class_list_macros.hpp"
47 #include "sensor_msgs/point_cloud2_iterator.hpp"
48 #include "nav2_costmap_2d/costmap_math.hpp"
49 #include "rclcpp/version.h"
53 using nav2_costmap_2d::NO_INFORMATION;
54 using nav2_costmap_2d::LETHAL_OBSTACLE;
55 using nav2_costmap_2d::FREE_SPACE;
59 using rcl_interfaces::msg::ParameterType;
66 auto node = node_.lock();
78 bool track_unknown_space;
79 double transform_tolerance;
82 std::string topics_string;
85 declareParameter(
"footprint_clearing_enabled", rclcpp::ParameterValue(
true));
89 declareParameter(
"observation_sources", rclcpp::ParameterValue(std::string(
"")));
91 auto node = node_.lock();
93 throw std::runtime_error{
"Failed to lock node"};
96 node->get_parameter(name_ +
"." +
"enabled", enabled_);
97 node->get_parameter(name_ +
"." +
"footprint_clearing_enabled", footprint_clearing_enabled_);
100 node->get_parameter(
"track_unknown_space", track_unknown_space);
101 node->get_parameter(
"transform_tolerance", transform_tolerance);
102 node->get_parameter(name_ +
"." +
"observation_sources", topics_string);
103 double tf_filter_tolerance = nav2::declare_or_get_parameter(node, name_ +
"." +
104 "tf_filter_tolerance", 0.05);
106 int combination_method_param{};
107 node->get_parameter(name_ +
"." +
"combination_method", combination_method_param);
114 std::placeholders::_1));
118 "Subscribed to Topics: %s", topics_string.c_str());
120 rolling_window_ = layered_costmap_->
isRolling();
122 if (track_unknown_space) {
123 default_value_ = NO_INFORMATION;
125 default_value_ = FREE_SPACE;
134 auto sub_opt = rclcpp::SubscriptionOptions();
135 sub_opt.callback_group = callback_group_;
138 std::stringstream ss(topics_string);
141 while (ss >> source) {
143 double observation_keep_time, expected_update_rate, min_obstacle_height, max_obstacle_height;
144 std::string topic, sensor_frame, data_type;
145 bool inf_is_valid, clearing, marking;
148 declareParameter(source +
"." +
"sensor_frame", rclcpp::ParameterValue(std::string(
"")));
149 declareParameter(source +
"." +
"observation_persistence", rclcpp::ParameterValue(0.0));
150 declareParameter(source +
"." +
"expected_update_rate", rclcpp::ParameterValue(0.0));
151 declareParameter(source +
"." +
"data_type", rclcpp::ParameterValue(std::string(
"LaserScan")));
152 declareParameter(source +
"." +
"min_obstacle_height", rclcpp::ParameterValue(0.0));
153 declareParameter(source +
"." +
"max_obstacle_height", rclcpp::ParameterValue(0.0));
154 declareParameter(source +
"." +
"inf_is_valid", rclcpp::ParameterValue(
false));
157 declareParameter(source +
"." +
"obstacle_max_range", rclcpp::ParameterValue(2.5));
158 declareParameter(source +
"." +
"obstacle_min_range", rclcpp::ParameterValue(0.0));
159 declareParameter(source +
"." +
"raytrace_max_range", rclcpp::ParameterValue(3.0));
160 declareParameter(source +
"." +
"raytrace_min_range", rclcpp::ParameterValue(0.0));
162 node->get_parameter(name_ +
"." + source +
"." +
"topic", topic);
163 node->get_parameter(name_ +
"." + source +
"." +
"sensor_frame", sensor_frame);
165 name_ +
"." + source +
"." +
"observation_persistence",
166 observation_keep_time);
168 name_ +
"." + source +
"." +
"expected_update_rate",
169 expected_update_rate);
170 node->get_parameter(name_ +
"." + source +
"." +
"data_type", data_type);
171 node->get_parameter(name_ +
"." + source +
"." +
"min_obstacle_height", min_obstacle_height);
172 node->get_parameter(name_ +
"." + source +
"." +
"max_obstacle_height", max_obstacle_height);
173 node->get_parameter(name_ +
"." + source +
"." +
"inf_is_valid", inf_is_valid);
174 node->get_parameter(name_ +
"." + source +
"." +
"marking", marking);
175 node->get_parameter(name_ +
"." + source +
"." +
"clearing", clearing);
177 if (!(data_type ==
"PointCloud2" || data_type ==
"LaserScan")) {
180 "Only topics that use point cloud2s or laser scans are currently supported");
181 throw std::runtime_error(
182 "Only topics that use point cloud2s or laser scans are currently supported");
186 double obstacle_max_range, obstacle_min_range;
187 node->get_parameter(name_ +
"." + source +
"." +
"obstacle_max_range", obstacle_max_range);
188 node->get_parameter(name_ +
"." + source +
"." +
"obstacle_min_range", obstacle_min_range);
191 double raytrace_max_range, raytrace_min_range;
192 node->get_parameter(name_ +
"." + source +
"." +
"raytrace_min_range", raytrace_min_range);
193 node->get_parameter(name_ +
"." + source +
"." +
"raytrace_max_range", raytrace_max_range);
199 "Creating an observation buffer for source %s, topic %s, frame %s",
200 source.c_str(), topic.c_str(),
201 sensor_frame.c_str());
208 node, topic, observation_keep_time, expected_update_rate,
210 max_obstacle_height, obstacle_max_range, obstacle_min_range, raytrace_max_range,
211 raytrace_min_range, *tf_,
213 sensor_frame, tf2::durationFromSec(transform_tolerance))));
227 "Created an observation buffer for source %s, topic %s, global frame: %s, "
228 "expected update rate: %.2f, observation persistence: %.2f",
229 source.c_str(), topic.c_str(),
230 global_frame_.c_str(), expected_update_rate, observation_keep_time);
235 if (data_type ==
"LaserScan") {
237 #if RCLCPP_VERSION_GTE(29, 6, 0)
238 std::shared_ptr<message_filters::Subscriber<sensor_msgs::msg::LaserScan>> sub;
240 std::shared_ptr<message_filters::Subscriber<sensor_msgs::msg::LaserScan,
241 rclcpp_lifecycle::LifecycleNode>> sub;
245 #if RCLCPP_VERSION_GTE(29, 6, 0)
246 sub = std::make_shared<message_filters::Subscriber<sensor_msgs::msg::LaserScan>>(
247 node, topic, custom_qos_profile, sub_opt);
249 #elif RCLCPP_VERSION_GTE(29, 0, 0)
250 sub = std::make_shared<message_filters::Subscriber<sensor_msgs::msg::LaserScan,
251 rclcpp_lifecycle::LifecycleNode>>(
252 std::static_pointer_cast<rclcpp_lifecycle::LifecycleNode>(node),
253 topic, custom_qos_profile, sub_opt);
256 sub = std::make_shared<message_filters::Subscriber<sensor_msgs::msg::LaserScan,
257 rclcpp_lifecycle::LifecycleNode>>(
258 std::static_pointer_cast<rclcpp_lifecycle::LifecycleNode>(node),
259 topic, custom_qos_profile.get_rmw_qos_profile(), sub_opt);
264 auto filter = std::make_shared<tf2_ros::MessageFilter<sensor_msgs::msg::LaserScan>>(
266 node->get_node_logging_interface(),
267 node->get_node_clock_interface(),
268 tf2::durationFromSec(transform_tolerance));
271 filter->registerCallback(
277 filter->registerCallback(
287 tf_filter_tolerance));
291 #if RCLCPP_VERSION_GTE(29, 6, 0)
292 std::shared_ptr<message_filters::Subscriber<sensor_msgs::msg::PointCloud2>> sub;
294 std::shared_ptr<message_filters::Subscriber<sensor_msgs::msg::PointCloud2,
295 rclcpp_lifecycle::LifecycleNode>> sub;
299 #if RCLCPP_VERSION_GTE(29, 6, 0)
300 sub = std::make_shared<message_filters::Subscriber<sensor_msgs::msg::PointCloud2>>(
301 node, topic, custom_qos_profile, sub_opt);
303 #elif RCLCPP_VERSION_GTE(29, 0, 0)
304 sub = std::make_shared<message_filters::Subscriber<sensor_msgs::msg::PointCloud2,
305 rclcpp_lifecycle::LifecycleNode>>(
306 std::static_pointer_cast<rclcpp_lifecycle::LifecycleNode>(node),
307 topic, custom_qos_profile, sub_opt);
310 sub = std::make_shared<message_filters::Subscriber<sensor_msgs::msg::PointCloud2,
311 rclcpp_lifecycle::LifecycleNode>>(
312 std::static_pointer_cast<rclcpp_lifecycle::LifecycleNode>(node),
313 topic, custom_qos_profile.get_rmw_qos_profile(), sub_opt);
321 "obstacle_layer: inf_is_valid option is not applicable to PointCloud observations.");
324 auto filter = std::make_shared<tf2_ros::MessageFilter<sensor_msgs::msg::PointCloud2>>(
326 node->get_node_logging_interface(),
327 node->get_node_clock_interface(),
328 tf2::durationFromSec(transform_tolerance));
330 filter->registerCallback(
339 if (sensor_frame !=
"") {
340 std::vector<std::string> target_frames;
342 target_frames.push_back(sensor_frame);
348 rcl_interfaces::msg::SetParametersResult
350 std::vector<rclcpp::Parameter> parameters)
352 std::lock_guard<Costmap2D::mutex_t> guard(*getMutex());
353 rcl_interfaces::msg::SetParametersResult result;
355 for (
auto parameter : parameters) {
356 const auto & param_type = parameter.get_type();
357 const auto & param_name = parameter.get_name();
358 if (param_name.find(name_ +
".") != 0) {
362 if (param_type == ParameterType::PARAMETER_DOUBLE) {
363 if (param_name == name_ +
"." +
"min_obstacle_height") {
365 }
else if (param_name == name_ +
"." +
"max_obstacle_height") {
368 }
else if (param_type == ParameterType::PARAMETER_BOOL) {
369 if (param_name == name_ +
"." +
"enabled" && enabled_ != parameter.as_bool()) {
370 enabled_ = parameter.as_bool();
374 }
else if (param_name == name_ +
"." +
"footprint_clearing_enabled") {
375 footprint_clearing_enabled_ = parameter.as_bool();
377 }
else if (param_type == ParameterType::PARAMETER_INTEGER) {
378 if (param_name == name_ +
"." +
"combination_method") {
384 result.successful =
true;
390 sensor_msgs::msg::LaserScan::ConstSharedPtr message,
391 const std::shared_ptr<nav2_costmap_2d::ObservationBuffer> & buffer)
394 sensor_msgs::msg::PointCloud2 cloud;
395 cloud.header = message->header;
399 projector_.transformLaserScanToPointCloud(message->header.frame_id, *message, cloud, *tf_);
400 }
catch (tf2::TransformException & ex) {
403 "High fidelity enabled, but TF returned a transform exception to frame %s: %s",
407 }
catch (std::runtime_error & ex) {
410 "transformLaserScanToPointCloud error, it seems the message from laser is malformed."
411 " Ignore this message. what(): %s",
418 buffer->bufferCloud(cloud);
424 sensor_msgs::msg::LaserScan::ConstSharedPtr raw_message,
425 const std::shared_ptr<nav2_costmap_2d::ObservationBuffer> & buffer)
428 float epsilon = 0.0001;
429 sensor_msgs::msg::LaserScan message = *raw_message;
430 for (
size_t i = 0; i < message.ranges.size(); i++) {
431 float range = message.ranges[i];
432 if (!std::isfinite(range) && range > 0) {
433 message.ranges[i] = message.range_max - epsilon;
438 sensor_msgs::msg::PointCloud2 cloud;
439 cloud.header = message.header;
443 projector_.transformLaserScanToPointCloud(message.header.frame_id, message, cloud, *tf_);
444 }
catch (tf2::TransformException & ex) {
447 "High fidelity enabled, but TF returned a transform exception to frame %s: %s",
450 }
catch (std::runtime_error & ex) {
453 "transformLaserScanToPointCloud error, it seems the message from laser is malformed."
454 " Ignore this message. what(): %s",
461 buffer->bufferCloud(cloud);
467 sensor_msgs::msg::PointCloud2::ConstSharedPtr message,
468 const std::shared_ptr<ObservationBuffer> & buffer)
472 buffer->bufferCloud(*message);
478 double robot_x,
double robot_y,
double robot_yaw,
double * min_x,
479 double * min_y,
double * max_x,
double * max_y)
481 std::lock_guard<Costmap2D::mutex_t> guard(*getMutex());
482 if (rolling_window_) {
488 useExtraBounds(min_x, min_y, max_x, max_y);
491 std::vector<Observation> observations, clearing_observations;
503 for (
unsigned int i = 0; i < clearing_observations.size(); ++i) {
508 for (std::vector<Observation>::const_iterator it = observations.begin();
509 it != observations.end(); ++it)
513 const sensor_msgs::msg::PointCloud2 & cloud = *(obs.cloud_);
515 double sq_obstacle_max_range = obs.obstacle_max_range_ * obs.obstacle_max_range_;
516 double sq_obstacle_min_range = obs.obstacle_min_range_ * obs.obstacle_min_range_;
518 sensor_msgs::PointCloud2ConstIterator<float> iter_x(cloud,
"x");
519 sensor_msgs::PointCloud2ConstIterator<float> iter_y(cloud,
"y");
520 sensor_msgs::PointCloud2ConstIterator<float> iter_z(cloud,
"z");
522 for (; iter_x != iter_x.end(); ++iter_x, ++iter_y, ++iter_z) {
523 double px = *iter_x, py = *iter_y, pz = *iter_z;
527 RCLCPP_DEBUG(logger_,
"The point is too low");
533 RCLCPP_DEBUG(logger_,
"The point is too high");
540 obs.origin_.x) * (px - obs.origin_.x) + (py - obs.origin_.y) * (py - obs.origin_.y) +
541 (pz - obs.origin_.z) * (pz - obs.origin_.z);
544 if (sq_dist >= sq_obstacle_max_range) {
545 RCLCPP_DEBUG(logger_,
"The point is too far away");
550 if (sq_dist < sq_obstacle_min_range) {
551 RCLCPP_DEBUG(logger_,
"The point is too close");
558 RCLCPP_DEBUG(logger_,
"Computing map coords failed");
562 unsigned int index =
getIndex(mx, my);
563 costmap_[index] = LETHAL_OBSTACLE;
564 touch(px, py, min_x, min_y, max_x, max_y);
568 updateFootprint(robot_x, robot_y, robot_yaw, min_x, min_y, max_x, max_y);
573 double robot_x,
double robot_y,
double robot_yaw,
574 double * min_x,
double * min_y,
578 if (!footprint_clearing_enabled_) {
return;}
581 for (
unsigned int i = 0; i < transformed_footprint_.size(); i++) {
582 touch(transformed_footprint_[i].x, transformed_footprint_[i].y, min_x, min_y, max_x, max_y);
592 std::lock_guard<Costmap2D::mutex_t> guard(*getMutex());
598 if (!current_ && was_reset_) {
603 if (footprint_clearing_enabled_) {
607 switch (combination_method_) {
609 updateWithOverwrite(master_grid, min_i, min_j, max_i, max_j);
612 updateWithMax(master_grid, min_i, min_j, max_i, max_j);
615 updateWithMaxWithoutUnknownOverwrite(master_grid, min_i, min_j, max_i, max_j);
623 ObstacleLayer::addStaticObservation(
625 bool marking,
bool clearing)
628 static_marking_observations_.push_back(obs);
631 static_clearing_observations_.push_back(obs);
636 ObstacleLayer::clearStaticObservations(
bool marking,
bool clearing)
639 static_marking_observations_.clear();
642 static_clearing_observations_.clear();
657 marking_observations.insert(
658 marking_observations.end(),
659 static_marking_observations_.begin(), static_marking_observations_.end());
674 clearing_observations.insert(
675 clearing_observations.end(),
676 static_clearing_observations_.begin(), static_clearing_observations_.end());
682 const Observation & clearing_observation,
double * min_x,
687 double ox = clearing_observation.origin_.x;
688 double oy = clearing_observation.origin_.y;
689 const sensor_msgs::msg::PointCloud2 & cloud = *(clearing_observation.cloud_);
696 "Sensor origin at (%.2f, %.2f) is out of map bounds (%.2f, %.2f) to (%.2f, %.2f). "
697 "The costmap cannot raytrace for it.",
699 origin_x_, origin_y_,
705 double origin_x = origin_x_, origin_y = origin_y_;
706 double map_end_x = origin_x + size_x_ * resolution_;
707 double map_end_y = origin_y + size_y_ * resolution_;
710 touch(ox, oy, min_x, min_y, max_x, max_y);
714 sensor_msgs::PointCloud2ConstIterator<float> iter_x(cloud,
"x");
715 sensor_msgs::PointCloud2ConstIterator<float> iter_y(cloud,
"y");
717 for (; iter_x != iter_x.end(); ++iter_x, ++iter_y) {
728 double t = (origin_x - ox) / a;
733 double t = (origin_y - oy) / b;
739 if (wx > map_end_x) {
740 double t = (map_end_x - ox) / a;
741 wx = map_end_x - .001;
744 if (wy > map_end_y) {
745 double t = (map_end_y - oy) / b;
747 wy = map_end_y - .001;
758 unsigned int cell_raytrace_max_range =
cellDistance(clearing_observation.raytrace_max_range_);
759 unsigned int cell_raytrace_min_range =
cellDistance(clearing_observation.raytrace_min_range_);
760 MarkCell marker(costmap_, FREE_SPACE);
762 raytraceLine(marker, x0, y0, x1, y1, cell_raytrace_max_range, cell_raytrace_min_range);
765 ox, oy, wx, wy, clearing_observation.raytrace_max_range_,
766 clearing_observation.raytrace_min_range_, min_x, min_y, max_x,
799 double ox,
double oy,
double wx,
double wy,
double max_range,
double min_range,
800 double * min_x,
double * min_y,
double * max_x,
double * max_y)
802 double dx = wx - ox, dy = wy - oy;
803 double full_distance = hypot(dx, dy);
804 if (full_distance < min_range) {
807 double scale = std::min(1.0, max_range / full_distance);
808 double ex = ox + dx * scale, ey = oy + dy * scale;
809 touch(ex, ey, min_x, min_y, max_x, max_y);
A QoS profile for best-effort sensor data with a history of 10 messages.
A 2D costmap provides a mapping between points in the world and their associated "costs".
unsigned int getIndex(unsigned int mx, unsigned int my) const
Given two map coordinates... compute the associated index.
void raytraceLine(ActionType at, unsigned int x0, unsigned int y0, unsigned int x1, unsigned int y1, unsigned int max_length=UINT_MAX, unsigned int min_length=0)
Raytrace a line and apply some action at each step.
bool worldToMap(double wx, double wy, unsigned int &mx, unsigned int &my) const
Convert from world coordinates to map coordinates.
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.
bool setConvexPolygonCost(const std::vector< geometry_msgs::msg::Point > &polygon, unsigned char cost_value)
Sets the cost of a convex polygon to a desired value.
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.
std::string joinWithParentNamespace(const std::string &topic)
void declareParameter(const std::string ¶m_name, const rclcpp::ParameterValue &value)
Convenience functions for declaring ROS parameters.
const std::vector< geometry_msgs::msg::Point > & getFootprint() const
Convenience function for layered_costmap_->getFootprint().
bool isRolling()
If this costmap is rolling or not.
Takes in point clouds from sensors, transforms them to the desired frame, and stores them.
Stores an observation in terms of a point cloud and the origin of the source.
Takes in laser and pointcloud data to populate into 2D costmap.
void pointCloud2Callback(sensor_msgs::msg::PointCloud2::ConstSharedPtr message, const std::shared_ptr< nav2_costmap_2d::ObservationBuffer > &buffer)
A callback to handle buffering PointCloud2 messages.
std::vector< std::shared_ptr< nav2_costmap_2d::ObservationBuffer > > marking_buffers_
Used to store observation buffers used for marking obstacles.
virtual void activate()
Activate the layer.
std::vector< std::shared_ptr< tf2_ros::MessageFilterBase > > observation_notifiers_
Used to make sure that transforms are available for each sensor.
bool getMarkingObservations(std::vector< nav2_costmap_2d::Observation > &marking_observations) const
Get the observations used to mark space.
std::string global_frame_
The global frame for the costmap.
bool getClearingObservations(std::vector< nav2_costmap_2d::Observation > &clearing_observations) const
Get the observations used to clear space.
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.
rclcpp::node_interfaces::OnSetParametersCallbackHandle::SharedPtr dyn_params_handler_
Dynamic parameters handler.
void laserScanCallback(sensor_msgs::msg::LaserScan::ConstSharedPtr message, const std::shared_ptr< nav2_costmap_2d::ObservationBuffer > &buffer)
A callback to handle buffering LaserScan messages.
void resetBuffersLastUpdated()
triggers the update of observations buffer
std::vector< std::shared_ptr< nav2_costmap_2d::ObservationBuffer > > clearing_buffers_
Used to store observation buffers used for clearing obstacles.
void laserScanValidInfCallback(sensor_msgs::msg::LaserScan::ConstSharedPtr message, const std::shared_ptr< nav2_costmap_2d::ObservationBuffer > &buffer)
A callback to handle buffering LaserScan messages which need filtering to turn Inf values into range_...
std::vector< std::shared_ptr< message_filters::SubscriberBase< rclcpp_lifecycle::LifecycleNode > > > observation_subscribers_
Used for the observation message filters.
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 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 dynamicParametersCallback(std::vector< rclcpp::Parameter > parameters)
Callback executed when a parameter change is detected.
virtual ~ObstacleLayer()
A destructor.
std::vector< std::shared_ptr< nav2_costmap_2d::ObservationBuffer > > observation_buffers_
Used to store observations from various sensors.
virtual void onInitialize()
Initialization process of layer on startup.
double min_obstacle_height_
Max Obstacle Height.
laser_geometry::LaserProjection projector_
Used to project laser scans into point clouds.
double max_obstacle_height_
Max Obstacle Height.
virtual void reset()
Reset this costmap.
virtual void raytraceFreespace(const nav2_costmap_2d::Observation &clearing_observation, double *min_x, double *min_y, double *max_x, double *max_y)
Clear freespace based on one observation.
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 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)
@ MaxWithoutUnknownOverwrite