36 #include <angles/angles.h>
43 #include "pluginlib/class_list_macros.hpp"
44 #include "geometry_msgs/msg/point_stamped.hpp"
45 #include "nav2_costmap_2d/range_sensor_layer.hpp"
49 using nav2_costmap_2d::LETHAL_OBSTACLE;
50 using nav2_costmap_2d::INSCRIBED_INFLATED_OBSTACLE;
51 using nav2_costmap_2d::NO_INFORMATION;
53 using namespace std::literals::chrono_literals;
58 RangeSensorLayer::RangeSensorLayer() {}
60 void RangeSensorLayer::onInitialize()
64 buffered_readings_ = 0;
65 last_reading_time_ = clock_->now();
66 default_value_ = to_cost(0.5);
71 auto node = node_.lock();
73 throw std::runtime_error{
"Failed to lock node"};
76 declareParameter(
"enabled", rclcpp::ParameterValue(
true));
77 node->get_parameter(name_ +
"." +
"enabled", enabled_);
78 declareParameter(
"phi", rclcpp::ParameterValue(1.2));
79 node->get_parameter(name_ +
"." +
"phi", phi_v_);
80 declareParameter(
"inflate_cone", rclcpp::ParameterValue(1.0));
81 node->get_parameter(name_ +
"." +
"inflate_cone", inflate_cone_);
82 declareParameter(
"no_readings_timeout", rclcpp::ParameterValue(0.0));
83 node->get_parameter(name_ +
"." +
"no_readings_timeout", no_readings_timeout_);
84 declareParameter(
"clear_threshold", rclcpp::ParameterValue(0.2));
85 node->get_parameter(name_ +
"." +
"clear_threshold", clear_threshold_);
86 declareParameter(
"mark_threshold", rclcpp::ParameterValue(0.8));
87 node->get_parameter(name_ +
"." +
"mark_threshold", mark_threshold_);
88 declareParameter(
"clear_on_max_reading", rclcpp::ParameterValue(
false));
89 node->get_parameter(name_ +
"." +
"clear_on_max_reading", clear_on_max_reading_);
91 double temp_tf_tol = 0.0;
92 node->get_parameter(
"transform_tolerance", temp_tf_tol);
93 transform_tolerance_ = tf2::durationFromSec(temp_tf_tol);
95 std::vector<std::string> topic_names{};
96 declareParameter(
"topics", rclcpp::ParameterValue(topic_names));
97 node->get_parameter(name_ +
"." +
"topics", topic_names);
99 InputSensorType input_sensor_type = InputSensorType::ALL;
100 std::string sensor_type_name;
101 declareParameter(
"input_sensor_type", rclcpp::ParameterValue(
"ALL"));
102 node->get_parameter(name_ +
"." +
"input_sensor_type", sensor_type_name);
105 sensor_type_name.begin(), sensor_type_name.end(),
106 sensor_type_name.begin(), ::toupper);
108 logger_,
"%s: %s as input_sensor_type given",
109 name_.c_str(), sensor_type_name.c_str());
111 if (sensor_type_name ==
"VARIABLE") {
112 input_sensor_type = InputSensorType::VARIABLE;
113 }
else if (sensor_type_name ==
"FIXED") {
114 input_sensor_type = InputSensorType::FIXED;
115 }
else if (sensor_type_name ==
"ALL") {
116 input_sensor_type = InputSensorType::ALL;
119 logger_,
"%s: Invalid input sensor type: %s. Defaulting to ALL.",
120 name_.c_str(), sensor_type_name.c_str());
124 if (topic_names.empty()) {
126 logger_,
"Invalid topic names list: it must"
127 "be a non-empty list of strings");
132 for (
auto & topic_name : topic_names) {
133 if (input_sensor_type == InputSensorType::VARIABLE) {
134 processRangeMessageFunc_ = std::bind(
135 &RangeSensorLayer::processVariableRangeMsg,
this,
136 std::placeholders::_1);
137 }
else if (input_sensor_type == InputSensorType::FIXED) {
138 processRangeMessageFunc_ = std::bind(
139 &RangeSensorLayer::processFixedRangeMsg,
this,
140 std::placeholders::_1);
141 }
else if (input_sensor_type == InputSensorType::ALL) {
142 processRangeMessageFunc_ = std::bind(
143 &RangeSensorLayer::processRangeMsg,
this,
144 std::placeholders::_1);
148 "%s: Invalid input sensor type: %s. Did you make a new type"
149 "and forgot to choose the subscriber for it?",
150 name_.c_str(), sensor_type_name.c_str());
152 range_subs_.push_back(
153 node->create_subscription<sensor_msgs::msg::Range>(
154 topic_name, rclcpp::SensorDataQoS(), std::bind(
155 &RangeSensorLayer::bufferIncomingRangeMsg,
this,
156 std::placeholders::_1)));
159 logger_,
"RangeSensorLayer: subscribed to "
160 "topic %s", range_subs_.back()->get_topic_name());
162 global_frame_ = layered_costmap_->getGlobalFrameID();
166 double RangeSensorLayer::gamma(
double theta)
168 if (fabs(theta) > max_angle_) {
171 return 1 - pow(theta / max_angle_, 2);
175 double RangeSensorLayer::delta(
double phi)
177 return 1 - (1 + tanh(2 * (phi - phi_v_))) / 2;
180 void RangeSensorLayer::get_deltas(
double angle,
double * dx,
double * dy)
182 double ta = tan(angle);
186 *dx = resolution_ / ta;
189 *dx = copysign(*dx, cos(angle));
190 *dy = copysign(resolution_, sin(angle));
193 double RangeSensorLayer::sensor_model(
double r,
double phi,
double theta)
195 double lbda = delta(phi) * gamma(theta);
197 double delta = resolution_;
199 if (phi >= 0.0 && phi < r - 2 * delta * r) {
200 return (1 - lbda) * (0.5);
201 }
else if (phi < r - delta * r) {
202 return lbda * 0.5 * pow((phi - (r - 2 * delta * r)) / (delta * r), 2) +
204 }
else if (phi < r + delta * r) {
205 double J = (r - phi) / (delta * r);
206 return lbda * ((1 - (0.5) * pow(J, 2)) - 0.5) + 0.5;
212 void RangeSensorLayer::bufferIncomingRangeMsg(
213 const sensor_msgs::msg::Range::SharedPtr range_message)
215 range_message_mutex_.lock();
216 range_msgs_buffer_.push_back(*range_message);
217 range_message_mutex_.unlock();
220 void RangeSensorLayer::updateCostmap()
222 std::list<sensor_msgs::msg::Range> range_msgs_buffer_copy;
224 range_message_mutex_.lock();
225 range_msgs_buffer_copy = std::list<sensor_msgs::msg::Range>(range_msgs_buffer_);
226 range_msgs_buffer_.clear();
227 range_message_mutex_.unlock();
229 for (
auto & range_msgs_it : range_msgs_buffer_copy) {
230 processRangeMessageFunc_(range_msgs_it);
234 void RangeSensorLayer::processRangeMsg(sensor_msgs::msg::Range & range_message)
236 if (range_message.min_range == range_message.max_range) {
237 processFixedRangeMsg(range_message);
239 processVariableRangeMsg(range_message);
243 void RangeSensorLayer::processFixedRangeMsg(sensor_msgs::msg::Range & range_message)
245 if (!std::isinf(range_message.range)) {
248 "Fixed distance ranger (min_range == max_range) in frame %s sent invalid value. "
249 "Only -Inf (== object detected) and Inf (== no object detected) are valid.",
250 range_message.header.frame_id.c_str());
254 bool clear_sensor_cone =
false;
256 if (range_message.range > 0) {
257 if (!clear_on_max_reading_) {
260 clear_sensor_cone =
true;
263 range_message.range = range_message.min_range;
265 updateCostmap(range_message, clear_sensor_cone);
268 void RangeSensorLayer::processVariableRangeMsg(sensor_msgs::msg::Range & range_message)
270 if (range_message.range < range_message.min_range || range_message.range >
271 range_message.max_range)
276 bool clear_sensor_cone =
false;
278 if (range_message.range >= range_message.max_range && clear_on_max_reading_) {
279 clear_sensor_cone =
true;
282 updateCostmap(range_message, clear_sensor_cone);
285 void RangeSensorLayer::updateCostmap(
286 sensor_msgs::msg::Range & range_message,
287 bool clear_sensor_cone)
289 max_angle_ = range_message.field_of_view / 2;
291 geometry_msgs::msg::PointStamped in, out;
292 in.header.stamp = range_message.header.stamp;
293 in.header.frame_id = range_message.header.frame_id;
295 if (!tf_->canTransform(
296 in.header.frame_id, global_frame_,
297 tf2_ros::fromMsg(in.header.stamp),
298 tf2_ros::fromRclcpp(transform_tolerance_)))
301 logger_,
"Range sensor layer can't transform from %s to %s",
302 global_frame_.c_str(), in.header.frame_id.c_str());
306 tf_->transform(in, out, global_frame_, transform_tolerance_);
308 double ox = out.point.x, oy = out.point.y;
310 in.point.x = range_message.range;
312 tf_->transform(in, out, global_frame_, transform_tolerance_);
314 double tx = out.point.x, ty = out.point.y;
317 double dx = tx - ox, dy = ty - oy, theta = atan2(dy, dx), d = sqrt(dx * dx + dy * dy);
320 int bx0, by0, bx1, by1;
324 int Ox, Oy, Ax, Ay, Bx, By;
327 worldToMapNoBounds(ox, oy, Ox, Oy);
330 touch(ox, oy, &min_x_, &min_y_, &max_x_, &max_y_);
334 if (worldToMap(tx, ty, aa, ab)) {
335 setCost(aa, ab, 233);
336 touch(tx, ty, &min_x_, &min_y_, &max_x_, &max_y_);
342 mx = ox + cos(theta - max_angle_) * d * 1.2;
343 my = oy + sin(theta - max_angle_) * d * 1.2;
344 worldToMapNoBounds(mx, my, Ax, Ay);
345 bx0 = std::min(bx0, Ax);
346 bx1 = std::max(bx1, Ax);
347 by0 = std::min(by0, Ay);
348 by1 = std::max(by1, Ay);
349 touch(mx, my, &min_x_, &min_y_, &max_x_, &max_y_);
352 mx = ox + cos(theta + max_angle_) * d * 1.2;
353 my = oy + sin(theta + max_angle_) * d * 1.2;
355 worldToMapNoBounds(mx, my, Bx, By);
356 bx0 = std::min(bx0, Bx);
357 bx1 = std::max(bx1, Bx);
358 by0 = std::min(by0, By);
359 by1 = std::max(by1, By);
360 touch(mx, my, &min_x_, &min_y_, &max_x_, &max_y_);
363 bx0 = std::max(0, bx0);
364 by0 = std::max(0, by0);
365 bx1 = std::min(
static_cast<int>(size_x_), bx1);
366 by1 = std::min(
static_cast<int>(size_y_), by1);
368 for (
unsigned int x = bx0; x <= (
unsigned int)bx1; x++) {
369 for (
unsigned int y = by0; y <= (
unsigned int)by1; y++) {
370 bool update_xy_cell =
true;
377 if (inflate_cone_ < 1.0) {
379 int w0 = orient2d(Ax, Ay, Bx, By, x, y);
380 int w1 = orient2d(Bx, By, Ox, Oy, x, y);
381 int w2 = orient2d(Ox, Oy, Ax, Ay, x, y);
385 float bcciath = -
static_cast<float>(inflate_cone_) * area(Ax, Ay, Bx, By, Ox, Oy);
386 update_xy_cell = w0 >= bcciath && w1 >= bcciath && w2 >= bcciath;
389 if (update_xy_cell) {
391 mapToWorld(x, y, wx, wy);
392 update_cell(ox, oy, theta, range_message.range, wx, wy, clear_sensor_cone);
397 buffered_readings_++;
398 last_reading_time_ = clock_->now();
401 void RangeSensorLayer::update_cell(
402 double ox,
double oy,
double ot,
double r,
403 double nx,
double ny,
bool clear)
406 if (worldToMap(nx, ny, x, y)) {
407 double dx = nx - ox, dy = ny - oy;
408 double theta = atan2(dy, dx) - ot;
409 theta = angles::normalize_angle(theta);
410 double phi = sqrt(dx * dx + dy * dy);
413 sensor = sensor_model(r, phi, theta);
415 double prior = to_prob(getCost(x, y));
416 double prob_occ = sensor * prior;
417 double prob_not = (1 - sensor) * (1 - prior);
418 double new_prob = prob_occ / (prob_occ + prob_not);
422 "%f %f | %f %f = %f", dx, dy, theta, phi, sensor);
425 "%f | %f %f | %f", prior, prob_occ, prob_not, new_prob);
426 unsigned char c = to_cost(new_prob);
431 void RangeSensorLayer::resetRange()
433 min_x_ = min_y_ = std::numeric_limits<double>::max();
434 max_x_ = max_y_ = -std::numeric_limits<double>::max();
437 void RangeSensorLayer::updateBounds(
438 double robot_x,
double robot_y,
439 double robot_yaw,
double * min_x,
double * min_y,
440 double * max_x,
double * max_y)
442 robot_yaw = 0 + robot_yaw;
443 if (layered_costmap_->isRolling()) {
444 updateOrigin(robot_x - getSizeInMetersX() / 2, robot_y - getSizeInMetersY() / 2);
449 *min_x = std::min(*min_x, min_x_);
450 *min_y = std::min(*min_y, min_y_);
451 *max_x = std::max(*max_x, max_x_);
452 *max_y = std::max(*max_y, max_y_);
461 if (buffered_readings_ == 0) {
462 if (no_readings_timeout_ > 0.0 &&
463 (clock_->now() - last_reading_time_).seconds() >
464 no_readings_timeout_)
468 "No range readings received for %.2f seconds, while expected at least every %.2f seconds.",
469 (clock_->now() - last_reading_time_).seconds(),
470 no_readings_timeout_);
476 void RangeSensorLayer::updateCosts(
478 int min_i,
int min_j,
int max_i,
int max_j)
484 unsigned char * master_array = master_grid.
getCharMap();
486 unsigned char clear = to_cost(clear_threshold_), mark = to_cost(mark_threshold_);
488 for (
int j = min_j; j < max_j; j++) {
489 unsigned int it = j * span + min_i;
490 for (
int i = min_i; i < max_i; i++) {
491 unsigned char prob = costmap_[it];
492 unsigned char current;
493 if (prob == nav2_costmap_2d::NO_INFORMATION) {
496 }
else if (prob > mark) {
497 current = nav2_costmap_2d::LETHAL_OBSTACLE;
498 }
else if (prob < clear) {
499 current = nav2_costmap_2d::FREE_SPACE;
505 unsigned char old_cost = master_array[it];
507 if (old_cost == NO_INFORMATION || old_cost < current) {
508 master_array[it] = current;
514 buffered_readings_ = 0;
517 if (!current_ && was_reset_) {
523 void RangeSensorLayer::reset()
525 RCLCPP_DEBUG(logger_,
"Reseting range sensor layer...");
532 void RangeSensorLayer::deactivate()
534 range_msgs_buffer_.clear();
537 void RangeSensorLayer::activate()
539 range_msgs_buffer_.clear();
A 2D costmap provides a mapping between points in the world and their associated "costs".
unsigned char * getCharMap() const
Will return a pointer to the underlying unsigned char array used as the costmap.
unsigned int getSizeInCellsX() const
Accessor for the x size of the costmap in cells.
Abstract class for layered costmap plugin implementations.
Takes in IR/Sonar/similar point measurement sensors and populates in costmap.