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 enabled_ = node->declare_or_get_parameter(name_ +
"." +
"enabled",
true);
77 phi_v_ = node->declare_or_get_parameter(name_ +
"." +
"phi", 1.2);
78 inflate_cone_ = node->declare_or_get_parameter(name_ +
"." +
"inflate_cone", 1.0);
79 no_readings_timeout_ = node->declare_or_get_parameter(
80 name_ +
"." +
"no_readings_timeout", 0.0);
81 clear_threshold_ = node->declare_or_get_parameter(
82 name_ +
"." +
"clear_threshold", 0.2);
83 mark_threshold_ = node->declare_or_get_parameter(
84 name_ +
"." +
"mark_threshold", 0.8);
85 clear_on_max_reading_ = node->declare_or_get_parameter(
86 name_ +
"." +
"clear_on_max_reading",
false);
88 double temp_tf_tol = 0.0;
89 node->get_parameter(
"transform_tolerance", temp_tf_tol);
90 transform_tolerance_ = tf2::durationFromSec(temp_tf_tol);
92 std::vector<std::string> topic_names = node->declare_or_get_parameter(
93 name_ +
"." +
"topics", std::vector<std::string>{});
95 InputSensorType input_sensor_type = InputSensorType::ALL;
96 std::string sensor_type_name = node->declare_or_get_parameter(
97 name_ +
"." +
"input_sensor_type", std::string(
"ALL"));
100 sensor_type_name.begin(), sensor_type_name.end(),
101 sensor_type_name.begin(), ::toupper);
103 logger_,
"%s: %s as input_sensor_type given",
104 name_.c_str(), sensor_type_name.c_str());
106 if (sensor_type_name ==
"VARIABLE") {
107 input_sensor_type = InputSensorType::VARIABLE;
108 }
else if (sensor_type_name ==
"FIXED") {
109 input_sensor_type = InputSensorType::FIXED;
110 }
else if (sensor_type_name ==
"ALL") {
111 input_sensor_type = InputSensorType::ALL;
114 logger_,
"%s: Invalid input sensor type: %s. Defaulting to ALL.",
115 name_.c_str(), sensor_type_name.c_str());
119 if (topic_names.empty()) {
121 logger_,
"Invalid topic names list: it must"
122 "be a non-empty list of strings");
127 for (
auto & topic_name : topic_names) {
128 topic_name = joinWithParentNamespace(topic_name);
129 if (input_sensor_type == InputSensorType::VARIABLE) {
130 processRangeMessageFunc_ = std::bind(
131 &RangeSensorLayer::processVariableRangeMsg,
this,
132 std::placeholders::_1);
133 }
else if (input_sensor_type == InputSensorType::FIXED) {
134 processRangeMessageFunc_ = std::bind(
135 &RangeSensorLayer::processFixedRangeMsg,
this,
136 std::placeholders::_1);
137 }
else if (input_sensor_type == InputSensorType::ALL) {
138 processRangeMessageFunc_ = std::bind(
139 &RangeSensorLayer::processRangeMsg,
this,
140 std::placeholders::_1);
144 "%s: Invalid input sensor type: %s. Did you make a new type"
145 "and forgot to choose the subscriber for it?",
146 name_.c_str(), sensor_type_name.c_str());
148 range_subs_.push_back(
149 node->create_subscription<sensor_msgs::msg::Range>(
152 &RangeSensorLayer::bufferIncomingRangeMsg,
this,
153 std::placeholders::_1),
157 logger_,
"RangeSensorLayer: subscribed to "
158 "topic %s", range_subs_.back()->get_topic_name());
160 global_frame_ = layered_costmap_->getGlobalFrameID();
164 double RangeSensorLayer::gamma(
double theta)
166 if (fabs(theta) > max_angle_) {
169 return 1 - pow(theta / max_angle_, 2);
173 double RangeSensorLayer::delta(
double phi)
175 return 1 - (1 + tanh(2 * (phi - phi_v_))) / 2;
178 void RangeSensorLayer::get_deltas(
double angle,
double * dx,
double * dy)
180 double ta = tan(angle);
184 *dx = resolution_ / ta;
187 *dx = copysign(*dx, cos(angle));
188 *dy = copysign(resolution_, sin(angle));
191 double RangeSensorLayer::sensor_model(
double r,
double phi,
double theta)
193 double lbda = delta(phi) * gamma(theta);
195 double delta = resolution_;
197 if (phi >= 0.0 && phi < r - 2 * delta * r) {
198 return (1 - lbda) * (0.5);
199 }
else if (phi < r - delta * r) {
200 return lbda * 0.5 * pow((phi - (r - 2 * delta * r)) / (delta * r), 2) +
202 }
else if (phi < r + delta * r) {
203 double J = (r - phi) / (delta * r);
204 return lbda * ((1 - (0.5) * pow(J, 2)) - 0.5) + 0.5;
210 void RangeSensorLayer::bufferIncomingRangeMsg(
211 const sensor_msgs::msg::Range::ConstSharedPtr & range_message)
213 range_message_mutex_.lock();
214 range_msgs_buffer_.push_back(*range_message);
215 range_message_mutex_.unlock();
218 void RangeSensorLayer::updateCostmap()
220 std::list<sensor_msgs::msg::Range> range_msgs_buffer_copy;
222 range_message_mutex_.lock();
223 range_msgs_buffer_copy = std::list<sensor_msgs::msg::Range>(range_msgs_buffer_);
224 range_msgs_buffer_.clear();
225 range_message_mutex_.unlock();
227 for (
auto & range_msgs_it : range_msgs_buffer_copy) {
228 processRangeMessageFunc_(range_msgs_it);
232 void RangeSensorLayer::processRangeMsg(sensor_msgs::msg::Range & range_message)
234 if (range_message.min_range == range_message.max_range) {
235 processFixedRangeMsg(range_message);
237 processVariableRangeMsg(range_message);
241 void RangeSensorLayer::processFixedRangeMsg(sensor_msgs::msg::Range & range_message)
243 if (!std::isinf(range_message.range)) {
246 "Fixed distance ranger (min_range == max_range) in frame %s sent invalid value. "
247 "Only -Inf (== object detected) and Inf (== no object detected) are valid.",
248 range_message.header.frame_id.c_str());
252 bool clear_sensor_cone =
false;
254 if (range_message.range > 0) {
255 if (!clear_on_max_reading_) {
258 clear_sensor_cone =
true;
261 range_message.range = range_message.min_range;
263 updateCostmap(range_message, clear_sensor_cone);
266 void RangeSensorLayer::processVariableRangeMsg(sensor_msgs::msg::Range & range_message)
268 if (range_message.range < range_message.min_range || range_message.range >
269 range_message.max_range)
274 bool clear_sensor_cone =
false;
276 if (range_message.range >= range_message.max_range && clear_on_max_reading_) {
277 clear_sensor_cone =
true;
280 updateCostmap(range_message, clear_sensor_cone);
283 void RangeSensorLayer::updateCostmap(
284 sensor_msgs::msg::Range & range_message,
285 bool clear_sensor_cone)
287 max_angle_ = range_message.field_of_view / 2;
289 geometry_msgs::msg::PointStamped in, out;
290 in.header.stamp = range_message.header.stamp;
291 in.header.frame_id = range_message.header.frame_id;
293 if (!tf_->canTransform(
294 in.header.frame_id, global_frame_,
295 tf2_ros::fromMsg(in.header.stamp),
296 tf2_ros::fromRclcpp(transform_tolerance_)))
299 logger_,
"Range sensor layer can't transform from %s to %s",
300 global_frame_.c_str(), in.header.frame_id.c_str());
304 tf_->transform(in, out, global_frame_, transform_tolerance_);
306 double ox = out.point.x, oy = out.point.y;
308 in.point.x = range_message.range;
310 tf_->transform(in, out, global_frame_, transform_tolerance_);
312 double tx = out.point.x, ty = out.point.y;
315 double dx = tx - ox, dy = ty - oy, theta = atan2(dy, dx), d = sqrt(dx * dx + dy * dy);
318 int bx0, by0, bx1, by1;
322 int Ox, Oy, Ax, Ay, Bx, By;
325 worldToMapNoBounds(ox, oy, Ox, Oy);
328 touch(ox, oy, &min_x_, &min_y_, &max_x_, &max_y_);
332 if (worldToMap(tx, ty, aa, ab)) {
333 setCost(aa, ab, 233);
334 touch(tx, ty, &min_x_, &min_y_, &max_x_, &max_y_);
340 mx = ox + cos(theta - max_angle_) * d * 1.2;
341 my = oy + sin(theta - max_angle_) * d * 1.2;
342 worldToMapNoBounds(mx, my, Ax, Ay);
343 bx0 = std::min(bx0, Ax);
344 bx1 = std::max(bx1, Ax);
345 by0 = std::min(by0, Ay);
346 by1 = std::max(by1, Ay);
347 touch(mx, my, &min_x_, &min_y_, &max_x_, &max_y_);
350 mx = ox + cos(theta + max_angle_) * d * 1.2;
351 my = oy + sin(theta + max_angle_) * d * 1.2;
353 worldToMapNoBounds(mx, my, Bx, By);
354 bx0 = std::min(bx0, Bx);
355 bx1 = std::max(bx1, Bx);
356 by0 = std::min(by0, By);
357 by1 = std::max(by1, By);
358 touch(mx, my, &min_x_, &min_y_, &max_x_, &max_y_);
361 bx0 = std::max(0, bx0);
362 by0 = std::max(0, by0);
363 bx1 = std::min(
static_cast<int>(size_x_), bx1);
364 by1 = std::min(
static_cast<int>(size_y_), by1);
366 for (
unsigned int x = bx0; x <= (
unsigned int)bx1; x++) {
367 for (
unsigned int y = by0; y <= (
unsigned int)by1; y++) {
368 bool update_xy_cell =
true;
375 if (inflate_cone_ < 1.0) {
377 int w0 = orient2d(Ax, Ay, Bx, By, x, y);
378 int w1 = orient2d(Bx, By, Ox, Oy, x, y);
379 int w2 = orient2d(Ox, Oy, Ax, Ay, x, y);
383 float bcciath = -
static_cast<float>(inflate_cone_) * area(Ax, Ay, Bx, By, Ox, Oy);
384 update_xy_cell = w0 >= bcciath && w1 >= bcciath && w2 >= bcciath;
387 if (update_xy_cell) {
389 mapToWorld(x, y, wx, wy);
390 update_cell(ox, oy, theta, range_message.range, wx, wy, clear_sensor_cone);
395 buffered_readings_++;
396 last_reading_time_ = clock_->now();
399 void RangeSensorLayer::update_cell(
400 double ox,
double oy,
double ot,
double r,
401 double nx,
double ny,
bool clear)
404 if (worldToMap(nx, ny, x, y)) {
405 double dx = nx - ox, dy = ny - oy;
406 double theta = atan2(dy, dx) - ot;
407 theta = angles::normalize_angle(theta);
408 double phi = sqrt(dx * dx + dy * dy);
411 sensor = sensor_model(r, phi, theta);
413 double prior = to_prob(getCost(x, y));
414 double prob_occ = sensor * prior;
415 double prob_not = (1 - sensor) * (1 - prior);
416 double new_prob = prob_occ / (prob_occ + prob_not);
420 "%f %f | %f %f = %f", dx, dy, theta, phi, sensor);
423 "%f | %f %f | %f", prior, prob_occ, prob_not, new_prob);
424 unsigned char c = to_cost(new_prob);
429 void RangeSensorLayer::resetRange()
431 min_x_ = min_y_ = std::numeric_limits<double>::max();
432 max_x_ = max_y_ = -std::numeric_limits<double>::max();
435 void RangeSensorLayer::updateBounds(
436 double robot_x,
double robot_y,
437 double robot_yaw,
double * min_x,
double * min_y,
438 double * max_x,
double * max_y)
440 robot_yaw = 0 + robot_yaw;
441 if (layered_costmap_->isRolling()) {
442 updateOrigin(robot_x - getSizeInMetersX() / 2, robot_y - getSizeInMetersY() / 2);
447 *min_x = std::min(*min_x, min_x_);
448 *min_y = std::min(*min_y, min_y_);
449 *max_x = std::max(*max_x, max_x_);
450 *max_y = std::max(*max_y, max_y_);
459 if (buffered_readings_ == 0) {
460 if (no_readings_timeout_ > 0.0 &&
461 (clock_->now() - last_reading_time_).seconds() >
462 no_readings_timeout_)
466 "No range readings received for %.2f seconds, while expected at least every %.2f seconds.",
467 (clock_->now() - last_reading_time_).seconds(),
468 no_readings_timeout_);
474 void RangeSensorLayer::updateCosts(
476 int min_i,
int min_j,
int max_i,
int max_j)
482 unsigned char * master_array = master_grid.
getCharMap();
484 unsigned char clear = to_cost(clear_threshold_), mark = to_cost(mark_threshold_);
486 for (
int j = min_j; j < max_j; j++) {
487 unsigned int it = j * span + min_i;
488 for (
int i = min_i; i < max_i; i++) {
489 unsigned char prob = costmap_[it];
490 unsigned char current;
491 if (prob == nav2_costmap_2d::NO_INFORMATION) {
494 }
else if (prob > mark) {
495 current = nav2_costmap_2d::LETHAL_OBSTACLE;
496 }
else if (prob < clear) {
497 current = nav2_costmap_2d::FREE_SPACE;
503 unsigned char old_cost = master_array[it];
505 if (old_cost == NO_INFORMATION || old_cost < current) {
506 master_array[it] = current;
512 buffered_readings_ = 0;
515 if (!isCurrent() && was_reset_) {
521 void RangeSensorLayer::reset()
523 RCLCPP_DEBUG(logger_,
"Resetting range sensor layer...");
530 void RangeSensorLayer::deactivate()
532 range_msgs_buffer_.clear();
535 void RangeSensorLayer::activate()
537 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.