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_util/raytrace_line_2d.hpp"
49 #include "nav2_costmap_2d/costmap_math.hpp"
50 #include "nav2_ros_common/node_utils.hpp"
51 #include "rclcpp/version.h"
55 using nav2_costmap_2d::NO_INFORMATION;
56 using nav2_costmap_2d::LETHAL_OBSTACLE;
57 using nav2_costmap_2d::FREE_SPACE;
61 using rcl_interfaces::msg::ParameterType;
68 auto node = node_.lock();
80 bool track_unknown_space;
81 double transform_tolerance;
84 std::string topics_string;
87 declareParameter(
"footprint_clearing_enabled", rclcpp::ParameterValue(
true));
91 declareParameter(
"observation_sources", rclcpp::ParameterValue(std::string(
"")));
93 auto node = node_.lock();
95 throw std::runtime_error{
"Failed to lock node"};
98 node->get_parameter(name_ +
"." +
"enabled", enabled_);
99 node->get_parameter(name_ +
"." +
"footprint_clearing_enabled", footprint_clearing_enabled_);
102 node->get_parameter(
"track_unknown_space", track_unknown_space);
103 node->get_parameter(
"transform_tolerance", transform_tolerance);
104 node->get_parameter(name_ +
"." +
"observation_sources", topics_string);
105 double tf_filter_tolerance = nav2::declare_or_get_parameter(node, name_ +
"." +
106 "tf_filter_tolerance", 0.05);
108 int combination_method_param{};
109 node->get_parameter(name_ +
"." +
"combination_method", combination_method_param);
116 std::placeholders::_1));
120 "Subscribed to Topics: %s", topics_string.c_str());
122 rolling_window_ = layered_costmap_->
isRolling();
124 if (track_unknown_space) {
125 default_value_ = NO_INFORMATION;
127 default_value_ = FREE_SPACE;
136 auto sub_opt = rclcpp::SubscriptionOptions();
137 sub_opt.callback_group = callback_group_;
140 std::stringstream ss(topics_string);
143 while (ss >> source) {
145 double observation_keep_time, expected_update_rate, min_obstacle_height, max_obstacle_height;
146 std::string topic, sensor_frame, data_type, transport_type;
147 bool inf_is_valid, clearing, marking;
150 declareParameter(source +
"." +
"sensor_frame", rclcpp::ParameterValue(std::string(
"")));
151 declareParameter(source +
"." +
"observation_persistence", rclcpp::ParameterValue(0.0));
152 declareParameter(source +
"." +
"expected_update_rate", rclcpp::ParameterValue(0.0));
153 declareParameter(source +
"." +
"data_type", rclcpp::ParameterValue(std::string(
"LaserScan")));
154 declareParameter(source +
"." +
"min_obstacle_height", rclcpp::ParameterValue(0.0));
155 declareParameter(source +
"." +
"max_obstacle_height", rclcpp::ParameterValue(0.0));
156 declareParameter(source +
"." +
"inf_is_valid", rclcpp::ParameterValue(
false));
159 declareParameter(source +
"." +
"obstacle_max_range", rclcpp::ParameterValue(2.5));
160 declareParameter(source +
"." +
"obstacle_min_range", rclcpp::ParameterValue(0.0));
161 declareParameter(source +
"." +
"raytrace_max_range", rclcpp::ParameterValue(3.0));
162 declareParameter(source +
"." +
"raytrace_min_range", rclcpp::ParameterValue(0.0));
163 declareParameter(source +
"." +
"transport_type", rclcpp::ParameterValue(std::string(
"raw")));
165 node->get_parameter(name_ +
"." + source +
"." +
"topic", topic);
166 node->get_parameter(name_ +
"." + source +
"." +
"sensor_frame", sensor_frame);
168 name_ +
"." + source +
"." +
"observation_persistence",
169 observation_keep_time);
171 name_ +
"." + source +
"." +
"expected_update_rate",
172 expected_update_rate);
173 node->get_parameter(name_ +
"." + source +
"." +
"data_type", data_type);
174 node->get_parameter(name_ +
"." + source +
"." +
"min_obstacle_height", min_obstacle_height);
175 node->get_parameter(name_ +
"." + source +
"." +
"max_obstacle_height", max_obstacle_height);
176 node->get_parameter(name_ +
"." + source +
"." +
"inf_is_valid", inf_is_valid);
177 node->get_parameter(name_ +
"." + source +
"." +
"marking", marking);
178 node->get_parameter(name_ +
"." + source +
"." +
"clearing", clearing);
179 node->get_parameter(name_ +
"." + source +
"." +
"transport_type", transport_type);
181 if (!(data_type ==
"PointCloud2" || data_type ==
"LaserScan")) {
184 "Only topics that use point cloud2s or laser scans are currently supported");
185 throw std::runtime_error(
186 "Only topics that use point cloud2s or laser scans are currently supported");
190 double obstacle_max_range, obstacle_min_range;
191 node->get_parameter(name_ +
"." + source +
"." +
"obstacle_max_range", obstacle_max_range);
192 node->get_parameter(name_ +
"." + source +
"." +
"obstacle_min_range", obstacle_min_range);
195 double raytrace_max_range, raytrace_min_range;
196 node->get_parameter(name_ +
"." + source +
"." +
"raytrace_min_range", raytrace_min_range);
197 node->get_parameter(name_ +
"." + source +
"." +
"raytrace_max_range", raytrace_max_range);
203 "Creating an observation buffer for source %s, topic %s, frame %s",
204 source.c_str(), topic.c_str(),
205 sensor_frame.c_str());
212 node, topic, observation_keep_time, expected_update_rate,
214 max_obstacle_height, obstacle_max_range, obstacle_min_range, raytrace_max_range,
215 raytrace_min_range, *tf_,
217 sensor_frame, tf2::durationFromSec(transform_tolerance))));
231 "Created an observation buffer for source %s, topic %s, global frame: %s, "
232 "expected update rate: %.2f, observation persistence: %.2f",
233 source.c_str(), topic.c_str(),
234 global_frame_.c_str(), expected_update_rate, observation_keep_time);
239 if (data_type ==
"LaserScan") {
241 #if RCLCPP_VERSION_GTE(29, 6, 0)
242 std::shared_ptr<message_filters::Subscriber<sensor_msgs::msg::LaserScan>> sub;
244 std::shared_ptr<message_filters::Subscriber<sensor_msgs::msg::LaserScan,
245 rclcpp_lifecycle::LifecycleNode>> sub;
249 #if RCLCPP_VERSION_GTE(29, 6, 0)
250 sub = std::make_shared<message_filters::Subscriber<sensor_msgs::msg::LaserScan>>(
251 node, topic, custom_qos_profile, sub_opt);
253 #elif RCLCPP_VERSION_GTE(29, 0, 0)
254 sub = std::make_shared<message_filters::Subscriber<sensor_msgs::msg::LaserScan,
255 rclcpp_lifecycle::LifecycleNode>>(
256 std::static_pointer_cast<rclcpp_lifecycle::LifecycleNode>(node),
257 topic, custom_qos_profile, sub_opt);
260 sub = std::make_shared<message_filters::Subscriber<sensor_msgs::msg::LaserScan,
261 rclcpp_lifecycle::LifecycleNode>>(
262 std::static_pointer_cast<rclcpp_lifecycle::LifecycleNode>(node),
263 topic, custom_qos_profile.get_rmw_qos_profile(), sub_opt);
268 auto filter = std::make_shared<tf2_ros::MessageFilter<sensor_msgs::msg::LaserScan>>(
270 node->get_node_logging_interface(),
271 node->get_node_clock_interface(),
272 tf2::durationFromSec(transform_tolerance));
275 filter->registerCallback(
281 filter->registerCallback(
291 tf_filter_tolerance));
295 #if RCLCPP_VERSION_GTE(30, 0, 0)
296 std::shared_ptr<point_cloud_transport::SubscriberFilter> sub;
298 #elif RCLCPP_VERSION_GTE(29, 6, 0)
299 std::shared_ptr<message_filters::Subscriber<sensor_msgs::msg::PointCloud2>> sub;
301 std::shared_ptr<message_filters::Subscriber<sensor_msgs::msg::PointCloud2,
302 rclcpp_lifecycle::LifecycleNode>> sub;
306 #if RCLCPP_VERSION_GTE(30, 0, 0)
307 sub = std::make_shared<point_cloud_transport::SubscriberFilter>(
308 *node, topic, transport_type, custom_qos_profile, sub_opt);
310 #elif RCLCPP_VERSION_GTE(29, 6, 0)
311 sub = std::make_shared<message_filters::Subscriber<sensor_msgs::msg::PointCloud2>>(
312 node, topic, custom_qos_profile, sub_opt);
314 #elif RCLCPP_VERSION_GTE(29, 0, 0)
315 sub = std::make_shared<message_filters::Subscriber<sensor_msgs::msg::PointCloud2,
316 rclcpp_lifecycle::LifecycleNode>>(
317 std::static_pointer_cast<rclcpp_lifecycle::LifecycleNode>(node),
318 topic, custom_qos_profile, sub_opt);
321 sub = std::make_shared<message_filters::Subscriber<sensor_msgs::msg::PointCloud2,
322 rclcpp_lifecycle::LifecycleNode>>(
323 std::static_pointer_cast<rclcpp_lifecycle::LifecycleNode>(node),
324 topic, custom_qos_profile.get_rmw_qos_profile(), sub_opt);
332 "obstacle_layer: inf_is_valid option is not applicable to PointCloud observations.");
335 auto filter = std::make_shared<tf2_ros::MessageFilter<sensor_msgs::msg::PointCloud2>>(
337 node->get_node_logging_interface(),
338 node->get_node_clock_interface(),
339 tf2::durationFromSec(transform_tolerance));
341 filter->registerCallback(
350 if (sensor_frame !=
"") {
351 std::vector<std::string> target_frames;
353 target_frames.push_back(sensor_frame);
359 rcl_interfaces::msg::SetParametersResult
361 std::vector<rclcpp::Parameter> parameters)
363 std::lock_guard<Costmap2D::mutex_t> guard(*getMutex());
364 rcl_interfaces::msg::SetParametersResult result;
366 for (
auto parameter : parameters) {
367 const auto & param_type = parameter.get_type();
368 const auto & param_name = parameter.get_name();
369 if (param_name.find(name_ +
".") != 0) {
373 if (param_type == ParameterType::PARAMETER_DOUBLE) {
374 if (param_name == name_ +
"." +
"min_obstacle_height") {
376 }
else if (param_name == name_ +
"." +
"max_obstacle_height") {
379 }
else if (param_type == ParameterType::PARAMETER_BOOL) {
380 if (param_name == name_ +
"." +
"enabled" && enabled_ != parameter.as_bool()) {
381 enabled_ = parameter.as_bool();
385 }
else if (param_name == name_ +
"." +
"footprint_clearing_enabled") {
386 footprint_clearing_enabled_ = parameter.as_bool();
388 }
else if (param_type == ParameterType::PARAMETER_INTEGER) {
389 if (param_name == name_ +
"." +
"combination_method") {
395 result.successful =
true;
401 sensor_msgs::msg::LaserScan::ConstSharedPtr message,
402 const std::shared_ptr<nav2_costmap_2d::ObservationBuffer> & buffer)
405 sensor_msgs::msg::PointCloud2 cloud;
406 cloud.header = message->header;
410 projector_.transformLaserScanToPointCloud(message->header.frame_id, *message, cloud, *tf_);
411 }
catch (tf2::TransformException & ex) {
414 "High fidelity enabled, but TF returned a transform exception to frame %s: %s",
418 }
catch (std::runtime_error & ex) {
421 "transformLaserScanToPointCloud error, it seems the message from laser is malformed."
422 " Ignore this message. what(): %s",
429 buffer->bufferCloud(cloud);
435 sensor_msgs::msg::LaserScan::ConstSharedPtr raw_message,
436 const std::shared_ptr<nav2_costmap_2d::ObservationBuffer> & buffer)
439 float epsilon = 0.0001;
440 sensor_msgs::msg::LaserScan message = *raw_message;
441 for (
size_t i = 0; i < message.ranges.size(); i++) {
442 float range = message.ranges[i];
443 if (!std::isfinite(range) && range > 0) {
444 message.ranges[i] = message.range_max - epsilon;
449 sensor_msgs::msg::PointCloud2 cloud;
450 cloud.header = message.header;
454 projector_.transformLaserScanToPointCloud(message.header.frame_id, message, cloud, *tf_);
455 }
catch (tf2::TransformException & ex) {
458 "High fidelity enabled, but TF returned a transform exception to frame %s: %s",
461 }
catch (std::runtime_error & ex) {
464 "transformLaserScanToPointCloud error, it seems the message from laser is malformed."
465 " Ignore this message. what(): %s",
472 buffer->bufferCloud(cloud);
478 sensor_msgs::msg::PointCloud2::ConstSharedPtr message,
479 const std::shared_ptr<ObservationBuffer> & buffer)
483 buffer->bufferCloud(*message);
489 double robot_x,
double robot_y,
double robot_yaw,
double * min_x,
490 double * min_y,
double * max_x,
double * max_y)
492 std::lock_guard<Costmap2D::mutex_t> guard(*getMutex());
493 if (rolling_window_) {
499 useExtraBounds(min_x, min_y, max_x, max_y);
502 std::vector<Observation> observations, clearing_observations;
514 for (
unsigned int i = 0; i < clearing_observations.size(); ++i) {
519 for (std::vector<Observation>::const_iterator it = observations.begin();
520 it != observations.end(); ++it)
524 const sensor_msgs::msg::PointCloud2 & cloud = *(obs.cloud_);
526 double sq_obstacle_max_range = obs.obstacle_max_range_ * obs.obstacle_max_range_;
527 double sq_obstacle_min_range = obs.obstacle_min_range_ * obs.obstacle_min_range_;
529 sensor_msgs::PointCloud2ConstIterator<float> iter_x(cloud,
"x");
530 sensor_msgs::PointCloud2ConstIterator<float> iter_y(cloud,
"y");
531 sensor_msgs::PointCloud2ConstIterator<float> iter_z(cloud,
"z");
533 for (; iter_x != iter_x.end(); ++iter_x, ++iter_y, ++iter_z) {
534 double px = *iter_x, py = *iter_y, pz = *iter_z;
538 RCLCPP_DEBUG(logger_,
"The point is too low");
544 RCLCPP_DEBUG(logger_,
"The point is too high");
551 obs.origin_.x) * (px - obs.origin_.x) + (py - obs.origin_.y) * (py - obs.origin_.y) +
552 (pz - obs.origin_.z) * (pz - obs.origin_.z);
555 if (sq_dist >= sq_obstacle_max_range) {
556 RCLCPP_DEBUG(logger_,
"The point is too far away");
561 if (sq_dist < sq_obstacle_min_range) {
562 RCLCPP_DEBUG(logger_,
"The point is too close");
569 RCLCPP_DEBUG(logger_,
"Computing map coords failed");
573 unsigned int index =
getIndex(mx, my);
574 costmap_[index] = LETHAL_OBSTACLE;
575 touch(px, py, min_x, min_y, max_x, max_y);
579 updateFootprint(robot_x, robot_y, robot_yaw, min_x, min_y, max_x, max_y);
584 double robot_x,
double robot_y,
double robot_yaw,
585 double * min_x,
double * min_y,
589 if (!footprint_clearing_enabled_) {
return;}
592 for (
unsigned int i = 0; i < transformed_footprint_.size(); i++) {
593 touch(transformed_footprint_[i].x, transformed_footprint_[i].y, min_x, min_y, max_x, max_y);
603 std::lock_guard<Costmap2D::mutex_t> guard(*getMutex());
609 if (!current_ && was_reset_) {
614 if (footprint_clearing_enabled_) {
618 switch (combination_method_) {
620 updateWithOverwrite(master_grid, min_i, min_j, max_i, max_j);
623 updateWithMax(master_grid, min_i, min_j, max_i, max_j);
626 updateWithMaxWithoutUnknownOverwrite(master_grid, min_i, min_j, max_i, max_j);
634 ObstacleLayer::addStaticObservation(
636 bool marking,
bool clearing)
639 static_marking_observations_.push_back(obs);
642 static_clearing_observations_.push_back(obs);
647 ObstacleLayer::clearStaticObservations(
bool marking,
bool clearing)
650 static_marking_observations_.clear();
653 static_clearing_observations_.clear();
668 marking_observations.insert(
669 marking_observations.end(),
670 static_marking_observations_.begin(), static_marking_observations_.end());
685 clearing_observations.insert(
686 clearing_observations.end(),
687 static_clearing_observations_.begin(), static_clearing_observations_.end());
693 const Observation & clearing_observation,
double * min_x,
698 double ox = clearing_observation.origin_.x;
699 double oy = clearing_observation.origin_.y;
700 const sensor_msgs::msg::PointCloud2 & cloud = *(clearing_observation.cloud_);
707 "Sensor origin at (%.2f, %.2f) is out of map bounds (%.2f, %.2f) to (%.2f, %.2f). "
708 "The costmap cannot raytrace for it.",
710 origin_x_, origin_y_,
716 double origin_x = origin_x_, origin_y = origin_y_;
717 double map_end_x = origin_x + size_x_ * resolution_;
718 double map_end_y = origin_y + size_y_ * resolution_;
721 touch(ox, oy, min_x, min_y, max_x, max_y);
725 sensor_msgs::PointCloud2ConstIterator<float> iter_x(cloud,
"x");
726 sensor_msgs::PointCloud2ConstIterator<float> iter_y(cloud,
"y");
728 for (; iter_x != iter_x.end(); ++iter_x, ++iter_y) {
739 double t = (origin_x - ox) / a;
744 double t = (origin_y - oy) / b;
750 if (wx > map_end_x) {
751 double t = (map_end_x - ox) / a;
752 wx = map_end_x - .001;
755 if (wy > map_end_y) {
756 double t = (map_end_y - oy) / b;
758 wy = map_end_y - .001;
769 unsigned int cell_raytrace_max_range =
cellDistance(clearing_observation.raytrace_max_range_);
770 unsigned int cell_raytrace_min_range =
cellDistance(clearing_observation.raytrace_min_range_);
771 MarkCell marker(costmap_, FREE_SPACE);
773 nav2_util::raytraceLine(
774 marker, x0, y0, x1, y1, size_x_, cell_raytrace_max_range, cell_raytrace_min_range);
777 ox, oy, wx, wy, clearing_observation.raytrace_max_range_,
778 clearing_observation.raytrace_min_range_, min_x, min_y, max_x,
811 double ox,
double oy,
double wx,
double wy,
double max_range,
double min_range,
812 double * min_x,
double * min_y,
double * max_x,
double * max_y)
814 double dx = wx - ox, dy = wy - oy;
815 double full_distance = hypot(dx, dy);
816 if (full_distance < min_range) {
819 double scale = std::min(1.0, max_range / full_distance);
820 double ex = ox + dx * scale, ey = oy + dy * scale;
821 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.
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