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 topic_name = joinWithParentNamespace(topic_name);
134 if (input_sensor_type == InputSensorType::VARIABLE) {
135 processRangeMessageFunc_ = std::bind(
136 &RangeSensorLayer::processVariableRangeMsg,
this,
137 std::placeholders::_1);
138 }
else if (input_sensor_type == InputSensorType::FIXED) {
139 processRangeMessageFunc_ = std::bind(
140 &RangeSensorLayer::processFixedRangeMsg,
this,
141 std::placeholders::_1);
142 }
else if (input_sensor_type == InputSensorType::ALL) {
143 processRangeMessageFunc_ = std::bind(
144 &RangeSensorLayer::processRangeMsg,
this,
145 std::placeholders::_1);
149 "%s: Invalid input sensor type: %s. Did you make a new type"
150 "and forgot to choose the subscriber for it?",
151 name_.c_str(), sensor_type_name.c_str());
153 range_subs_.push_back(
154 node->create_subscription<sensor_msgs::msg::Range>(
157 &RangeSensorLayer::bufferIncomingRangeMsg,
this,
158 std::placeholders::_1),
162 logger_,
"RangeSensorLayer: subscribed to "
163 "topic %s", range_subs_.back()->get_topic_name());
165 global_frame_ = layered_costmap_->getGlobalFrameID();
169 double RangeSensorLayer::gamma(
double theta)
171 if (fabs(theta) > max_angle_) {
174 return 1 - pow(theta / max_angle_, 2);
178 double RangeSensorLayer::delta(
double phi)
180 return 1 - (1 + tanh(2 * (phi - phi_v_))) / 2;
183 void RangeSensorLayer::get_deltas(
double angle,
double * dx,
double * dy)
185 double ta = tan(angle);
189 *dx = resolution_ / ta;
192 *dx = copysign(*dx, cos(angle));
193 *dy = copysign(resolution_, sin(angle));
196 double RangeSensorLayer::sensor_model(
double r,
double phi,
double theta)
198 double lbda = delta(phi) * gamma(theta);
200 double delta = resolution_;
202 if (phi >= 0.0 && phi < r - 2 * delta * r) {
203 return (1 - lbda) * (0.5);
204 }
else if (phi < r - delta * r) {
205 return lbda * 0.5 * pow((phi - (r - 2 * delta * r)) / (delta * r), 2) +
207 }
else if (phi < r + delta * r) {
208 double J = (r - phi) / (delta * r);
209 return lbda * ((1 - (0.5) * pow(J, 2)) - 0.5) + 0.5;
215 void RangeSensorLayer::bufferIncomingRangeMsg(
216 const sensor_msgs::msg::Range::SharedPtr range_message)
218 range_message_mutex_.lock();
219 range_msgs_buffer_.push_back(*range_message);
220 range_message_mutex_.unlock();
223 void RangeSensorLayer::updateCostmap()
225 std::list<sensor_msgs::msg::Range> range_msgs_buffer_copy;
227 range_message_mutex_.lock();
228 range_msgs_buffer_copy = std::list<sensor_msgs::msg::Range>(range_msgs_buffer_);
229 range_msgs_buffer_.clear();
230 range_message_mutex_.unlock();
232 for (
auto & range_msgs_it : range_msgs_buffer_copy) {
233 processRangeMessageFunc_(range_msgs_it);
237 void RangeSensorLayer::processRangeMsg(sensor_msgs::msg::Range & range_message)
239 if (range_message.min_range == range_message.max_range) {
240 processFixedRangeMsg(range_message);
242 processVariableRangeMsg(range_message);
246 void RangeSensorLayer::processFixedRangeMsg(sensor_msgs::msg::Range & range_message)
248 if (!std::isinf(range_message.range)) {
251 "Fixed distance ranger (min_range == max_range) in frame %s sent invalid value. "
252 "Only -Inf (== object detected) and Inf (== no object detected) are valid.",
253 range_message.header.frame_id.c_str());
257 bool clear_sensor_cone =
false;
259 if (range_message.range > 0) {
260 if (!clear_on_max_reading_) {
263 clear_sensor_cone =
true;
266 range_message.range = range_message.min_range;
268 updateCostmap(range_message, clear_sensor_cone);
271 void RangeSensorLayer::processVariableRangeMsg(sensor_msgs::msg::Range & range_message)
273 if (range_message.range < range_message.min_range || range_message.range >
274 range_message.max_range)
279 bool clear_sensor_cone =
false;
281 if (range_message.range >= range_message.max_range && clear_on_max_reading_) {
282 clear_sensor_cone =
true;
285 updateCostmap(range_message, clear_sensor_cone);
288 void RangeSensorLayer::updateCostmap(
289 sensor_msgs::msg::Range & range_message,
290 bool clear_sensor_cone)
292 max_angle_ = range_message.field_of_view / 2;
294 geometry_msgs::msg::PointStamped in, out;
295 in.header.stamp = range_message.header.stamp;
296 in.header.frame_id = range_message.header.frame_id;
298 if (!tf_->canTransform(
299 in.header.frame_id, global_frame_,
300 tf2_ros::fromMsg(in.header.stamp),
301 tf2_ros::fromRclcpp(transform_tolerance_)))
304 logger_,
"Range sensor layer can't transform from %s to %s",
305 global_frame_.c_str(), in.header.frame_id.c_str());
309 tf_->transform(in, out, global_frame_, transform_tolerance_);
311 double ox = out.point.x, oy = out.point.y;
313 in.point.x = range_message.range;
315 tf_->transform(in, out, global_frame_, transform_tolerance_);
317 double tx = out.point.x, ty = out.point.y;
320 double dx = tx - ox, dy = ty - oy, theta = atan2(dy, dx), d = sqrt(dx * dx + dy * dy);
323 int bx0, by0, bx1, by1;
327 int Ox, Oy, Ax, Ay, Bx, By;
330 worldToMapNoBounds(ox, oy, Ox, Oy);
333 touch(ox, oy, &min_x_, &min_y_, &max_x_, &max_y_);
337 if (worldToMap(tx, ty, aa, ab)) {
338 setCost(aa, ab, 233);
339 touch(tx, ty, &min_x_, &min_y_, &max_x_, &max_y_);
345 mx = ox + cos(theta - max_angle_) * d * 1.2;
346 my = oy + sin(theta - max_angle_) * d * 1.2;
347 worldToMapNoBounds(mx, my, Ax, Ay);
348 bx0 = std::min(bx0, Ax);
349 bx1 = std::max(bx1, Ax);
350 by0 = std::min(by0, Ay);
351 by1 = std::max(by1, Ay);
352 touch(mx, my, &min_x_, &min_y_, &max_x_, &max_y_);
355 mx = ox + cos(theta + max_angle_) * d * 1.2;
356 my = oy + sin(theta + max_angle_) * d * 1.2;
358 worldToMapNoBounds(mx, my, Bx, By);
359 bx0 = std::min(bx0, Bx);
360 bx1 = std::max(bx1, Bx);
361 by0 = std::min(by0, By);
362 by1 = std::max(by1, By);
363 touch(mx, my, &min_x_, &min_y_, &max_x_, &max_y_);
366 bx0 = std::max(0, bx0);
367 by0 = std::max(0, by0);
368 bx1 = std::min(
static_cast<int>(size_x_), bx1);
369 by1 = std::min(
static_cast<int>(size_y_), by1);
371 for (
unsigned int x = bx0; x <= (
unsigned int)bx1; x++) {
372 for (
unsigned int y = by0; y <= (
unsigned int)by1; y++) {
373 bool update_xy_cell =
true;
380 if (inflate_cone_ < 1.0) {
382 int w0 = orient2d(Ax, Ay, Bx, By, x, y);
383 int w1 = orient2d(Bx, By, Ox, Oy, x, y);
384 int w2 = orient2d(Ox, Oy, Ax, Ay, x, y);
388 float bcciath = -
static_cast<float>(inflate_cone_) * area(Ax, Ay, Bx, By, Ox, Oy);
389 update_xy_cell = w0 >= bcciath && w1 >= bcciath && w2 >= bcciath;
392 if (update_xy_cell) {
394 mapToWorld(x, y, wx, wy);
395 update_cell(ox, oy, theta, range_message.range, wx, wy, clear_sensor_cone);
400 buffered_readings_++;
401 last_reading_time_ = clock_->now();
404 void RangeSensorLayer::update_cell(
405 double ox,
double oy,
double ot,
double r,
406 double nx,
double ny,
bool clear)
409 if (worldToMap(nx, ny, x, y)) {
410 double dx = nx - ox, dy = ny - oy;
411 double theta = atan2(dy, dx) - ot;
412 theta = angles::normalize_angle(theta);
413 double phi = sqrt(dx * dx + dy * dy);
416 sensor = sensor_model(r, phi, theta);
418 double prior = to_prob(getCost(x, y));
419 double prob_occ = sensor * prior;
420 double prob_not = (1 - sensor) * (1 - prior);
421 double new_prob = prob_occ / (prob_occ + prob_not);
425 "%f %f | %f %f = %f", dx, dy, theta, phi, sensor);
428 "%f | %f %f | %f", prior, prob_occ, prob_not, new_prob);
429 unsigned char c = to_cost(new_prob);
434 void RangeSensorLayer::resetRange()
436 min_x_ = min_y_ = std::numeric_limits<double>::max();
437 max_x_ = max_y_ = -std::numeric_limits<double>::max();
440 void RangeSensorLayer::updateBounds(
441 double robot_x,
double robot_y,
442 double robot_yaw,
double * min_x,
double * min_y,
443 double * max_x,
double * max_y)
445 robot_yaw = 0 + robot_yaw;
446 if (layered_costmap_->isRolling()) {
447 updateOrigin(robot_x - getSizeInMetersX() / 2, robot_y - getSizeInMetersY() / 2);
452 *min_x = std::min(*min_x, min_x_);
453 *min_y = std::min(*min_y, min_y_);
454 *max_x = std::max(*max_x, max_x_);
455 *max_y = std::max(*max_y, max_y_);
464 if (buffered_readings_ == 0) {
465 if (no_readings_timeout_ > 0.0 &&
466 (clock_->now() - last_reading_time_).seconds() >
467 no_readings_timeout_)
471 "No range readings received for %.2f seconds, while expected at least every %.2f seconds.",
472 (clock_->now() - last_reading_time_).seconds(),
473 no_readings_timeout_);
479 void RangeSensorLayer::updateCosts(
481 int min_i,
int min_j,
int max_i,
int max_j)
487 unsigned char * master_array = master_grid.
getCharMap();
489 unsigned char clear = to_cost(clear_threshold_), mark = to_cost(mark_threshold_);
491 for (
int j = min_j; j < max_j; j++) {
492 unsigned int it = j * span + min_i;
493 for (
int i = min_i; i < max_i; i++) {
494 unsigned char prob = costmap_[it];
495 unsigned char current;
496 if (prob == nav2_costmap_2d::NO_INFORMATION) {
499 }
else if (prob > mark) {
500 current = nav2_costmap_2d::LETHAL_OBSTACLE;
501 }
else if (prob < clear) {
502 current = nav2_costmap_2d::FREE_SPACE;
508 unsigned char old_cost = master_array[it];
510 if (old_cost == NO_INFORMATION || old_cost < current) {
511 master_array[it] = current;
517 buffered_readings_ = 0;
520 if (!current_ && was_reset_) {
526 void RangeSensorLayer::reset()
528 RCLCPP_DEBUG(logger_,
"Resetting range sensor layer...");
535 void RangeSensorLayer::deactivate()
537 range_msgs_buffer_.clear();
540 void RangeSensorLayer::activate()
542 range_msgs_buffer_.clear();
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 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.