15 #include "nav2_map_server/vector_object_shapes.hpp"
17 #include <uuid/uuid.h>
26 #include "geometry_msgs/msg/pose_stamped.hpp"
28 #include "nav2_util/occ_grid_utils.hpp"
29 #include "nav2_util/occ_grid_values.hpp"
30 #include "nav2_util/geometry_utils.hpp"
31 #include "nav2_util/raytrace_line_2d.hpp"
32 #include "nav2_util/robot_utils.hpp"
33 #include "nav2_ros_common/tf2_factories.hpp"
35 namespace nav2_map_server
41 : type_(UNKNOWN), node_(node)
53 nav_msgs::msg::OccupancyGrid::SharedPtr map,
const OverlayType overlay_type)
55 double wx1, wy1, wx2, wy2;
56 unsigned int mx1, my1, mx2, my2;
59 !nav2_util::worldToMap(map, wx1, wy1, mx1, my1) ||
60 !nav2_util::worldToMap(map, wx2, wy2, mx2, my2))
65 const double origin_x = map->info.origin.position.x;
66 const double resolution = map->info.resolution;
68 std::vector<std::pair<double, double>> spans;
69 for (
unsigned int my = my1; my <= my2; my++) {
71 nav2_util::mapToWorld(map, mx1, my, row_x, row_y);
72 const auto inside = [&](
unsigned int mx) {
74 nav2_util::mapToWorld(map, mx, my, wx, wy);
78 int8_t * row = map->data.data() +
static_cast<size_t>(my) * map->info.width;
79 for (
const auto & [x_begin, x_end] : spans) {
81 const auto center_x = [&](int64_t mx) {
83 nav2_util::mapToWorld(map,
static_cast<unsigned int>(mx), my, wx, wy);
86 int64_t lo =
static_cast<int64_t
>(std::clamp(
87 std::ceil((x_begin - origin_x) / resolution - 0.5),
88 static_cast<double>(mx1),
static_cast<double>(mx2)));
89 int64_t hi =
static_cast<int64_t
>(std::clamp(
90 std::floor((x_end - origin_x) / resolution - 0.5),
91 static_cast<double>(mx1),
static_cast<double>(mx2)));
93 while (lo > mx1 && center_x(lo - 1) >= x_begin) {
96 while (lo <= mx2 && center_x(lo) < x_begin) {
99 while (hi < mx2 && center_x(hi + 1) < x_end) {
102 while (hi >= mx1 && center_x(hi) >= x_end) {
106 while (lo <= hi && !inside(
static_cast<unsigned int>(lo))) {
109 while (hi >= lo && !inside(
static_cast<unsigned int>(hi))) {
113 processRun(row + lo,
static_cast<size_t>(hi - lo + 1), value, overlay_type);
121 int8_t * cells,
const size_t count,
const int8_t shape_val,
122 const OverlayType overlay_type)
124 switch (overlay_type) {
125 case OverlayType::OVERLAY_SEQ:
126 std::fill_n(cells, count, shape_val);
128 case OverlayType::OVERLAY_MAX:
129 for (
size_t i = 0; i < count; i++) {
130 cells[i] = std::max(cells[i], shape_val);
133 case OverlayType::OVERLAY_MIN:
134 if (shape_val == nav2_util::OCC_GRID_UNKNOWN) {
137 for (
size_t i = 0; i < count; i++) {
138 if (cells[i] == nav2_util::OCC_GRID_UNKNOWN || shape_val < cells[i]) {
139 cells[i] = shape_val;
144 throw std::runtime_error{
"Unknown overlay type"};
150 auto node =
node_.lock();
152 throw std::runtime_error{
"Failed to lock node"};
157 std::string uuid_str = nav2::declare_or_get_parameter<std::string>(
158 node, shape_name +
".uuid");
159 if (uuid_parse(uuid_str.c_str(), out_uuid) != 0) {
162 "[%s] Can not parse UUID string for shape: %s",
163 shape_name.c_str(), uuid_str.c_str());
166 }
catch (
const std::exception &) {
168 uuid_generate(out_uuid);
171 uuid_unparse(out_uuid, uuid_str);
174 "[%s] No UUID is specified for shape. Generating a new one: %s",
175 shape_name.c_str(), uuid_str);
184 const nav2::LifecycleNode::WeakPtr & node)
197 return params_->header.frame_id;
202 return unparseUUID(
params_->uuid.uuid.data());
207 return uuid_compare(
params_->uuid.uuid.data(), uuid) == 0;
217 auto node =
node_.lock();
219 throw std::runtime_error{
"Failed to lock node"};
223 params_ = std::make_shared<nav2_msgs::msg::PolygonObject>();
226 polygon_ = std::make_shared<geometry_msgs::msg::Polygon>();
229 params_->header.frame_id = nav2::declare_or_get_parameter(
230 node, shape_name +
".frame_id", std::string{
"map"});
231 params_->value = nav2::declare_or_get_parameter(
232 node, shape_name +
".value",
static_cast<int>(nav2_util::OCC_GRID_OCCUPIED));
233 params_->closed = nav2::declare_or_get_parameter(
234 node, shape_name +
".closed",
true);
236 std::vector<double> poly_row;
238 poly_row = nav2::declare_or_get_parameter<std::vector<double>>(
239 node, shape_name +
".points");
240 }
catch (
const std::exception & ex) {
243 "[%s] Error while getting polygon parameters: %s",
244 shape_name.c_str(), ex.what());
248 if (poly_row.size() < 6 || poly_row.size() % 2 != 0) {
251 "[%s] Polygon has incorrect points description",
257 geometry_msgs::msg::Point32 point;
259 for (
double val : poly_row) {
264 params_->points.push_back(point);
286 polygon_ = std::make_shared<geometry_msgs::msg::Polygon>();
291 if (uuid_is_null(
params_->uuid.uuid.data())) {
292 uuid_generate(
params_->uuid.uuid.data());
299 const std::string & to_frame,
300 const nav2::TransformBuffer::SharedPtr tf_buffer,
301 const double transform_tolerance)
303 geometry_msgs::msg::PoseStamped from_pose, to_pose;
304 from_pose.header =
params_->header;
305 for (
unsigned int i = 0; i <
params_->points.size(); i++) {
306 from_pose.pose.position.x =
params_->points[i].x;
307 from_pose.pose.position.y =
params_->points[i].y;
308 from_pose.pose.position.z =
params_->points[i].z;
310 nav2_util::transformPoseInTargetFrame(
311 from_pose, to_pose, *tf_buffer, to_frame, transform_tolerance))
313 polygon_->points[i].x = to_pose.pose.position.x;
314 polygon_->points[i].y = to_pose.pose.position.y;
315 polygon_->points[i].z = to_pose.pose.position.z;
326 min_x = std::numeric_limits<double>::max();
327 min_y = std::numeric_limits<double>::max();
328 max_x = std::numeric_limits<double>::lowest();
329 max_y = std::numeric_limits<double>::lowest();
331 for (
auto point :
polygon_->points) {
332 min_x = std::min(min_x,
static_cast<double>(point.x));
333 min_y = std::min(min_y,
static_cast<double>(point.y));
334 max_x = std::max(max_x,
static_cast<double>(point.x));
335 max_y = std::max(max_y,
static_cast<double>(point.y));
341 return nav2_util::geometry_utils::isPointInsidePolygon(px, py,
polygon_->points);
345 const double py, std::vector<std::pair<double, double>> & spans)
const
348 const auto & points =
polygon_->points;
349 std::vector<double> crossings;
350 int i = points.size() - 1;
351 for (
int j = 0; j < static_cast<int>(points.size()); j++) {
352 if ((py <= points[i].y) == (py > points[j].y)) {
354 points[i].x + (py - points[i].y) * (points[j].x - points[i].x) /
355 (points[j].y - points[i].y));
359 std::sort(crossings.begin(), crossings.end());
362 const size_t count = crossings.size();
363 if (count % 2 == 1) {
364 spans.emplace_back(std::numeric_limits<double>::lowest(), crossings[0]);
366 for (
size_t k = 1; k < count; k++) {
367 if ((count - k) % 2 == 1) {
368 spans.emplace_back(crossings[k - 1], crossings[k]);
374 nav_msgs::msg::OccupancyGrid::SharedPtr map,
const OverlayType overlay_type)
376 unsigned int mx0, my0, mx1, my1;
378 auto node =
node_.lock();
380 throw std::runtime_error{
"Failed to lock node"};
383 if (!nav2_util::worldToMap(map,
polygon_->points[0].x,
polygon_->points[0].y, mx1, my1)) {
386 "[UUID: %s] Can not convert (%f, %f) point to map",
392 for (
unsigned int i = 1; i <
polygon_->points.size(); i++) {
395 if (!nav2_util::worldToMap(map,
polygon_->points[i].x,
polygon_->points[i].y, mx1, my1)) {
398 "[UUID: %s] Can not convert (%f, %f) point to map",
402 nav2_util::raytraceLine(ma, mx0, my0, mx1, my1, map->info.width);
408 if (
params_->points.size() < 3) {
409 auto node =
node_.lock();
411 throw std::runtime_error{
"Failed to lock node"};
416 "[UUID: %s] Polygon has incorrect number of vertices: %li",
427 const nav2::LifecycleNode::WeakPtr & node)
440 return params_->header.frame_id;
445 return unparseUUID(
params_->uuid.uuid.data());
450 return uuid_compare(
params_->uuid.uuid.data(), uuid) == 0;
460 auto node =
node_.lock();
462 throw std::runtime_error{
"Failed to lock node"};
466 params_ = std::make_shared<nav2_msgs::msg::CircleObject>();
469 center_ = std::make_shared<geometry_msgs::msg::Point32>();
472 params_->header.frame_id = nav2::declare_or_get_parameter(
473 node, shape_name +
".frame_id", std::string{
"map"});
474 params_->value = nav2::declare_or_get_parameter(
475 node, shape_name +
".value",
static_cast<int>(nav2_util::OCC_GRID_OCCUPIED));
476 params_->fill = nav2::declare_or_get_parameter(
477 node, shape_name +
".fill",
true);
479 std::vector<double> center_row;
481 center_row = nav2::declare_or_get_parameter<std::vector<double>>(
482 node, shape_name +
".center");
483 params_->radius = nav2::declare_or_get_parameter<double>(
484 node, shape_name +
".radius");
488 "[%s] Circle has incorrect radius less than zero",
492 }
catch (
const std::exception & ex) {
495 "[%s] Error while getting circle parameters: %s",
496 shape_name.c_str(), ex.what());
500 if (center_row.size() != 2) {
503 "[%s] Circle has incorrect center description",
509 params_->center.x = center_row[0];
510 params_->center.y = center_row[1];
528 center_ = std::make_shared<geometry_msgs::msg::Point32>();
533 if (uuid_is_null(
params_->uuid.uuid.data())) {
534 uuid_generate(
params_->uuid.uuid.data());
541 const std::string & to_frame,
542 const nav2::TransformBuffer::SharedPtr tf_buffer,
543 const double transform_tolerance)
545 geometry_msgs::msg::PoseStamped from_pose, to_pose;
546 from_pose.header =
params_->header;
547 from_pose.pose.position.x =
params_->center.x;
548 from_pose.pose.position.y =
params_->center.y;
549 from_pose.pose.position.z =
params_->center.z;
551 nav2_util::transformPoseInTargetFrame(
552 from_pose, to_pose, *tf_buffer, to_frame, transform_tolerance))
554 center_->x = to_pose.pose.position.x;
555 center_->y = to_pose.pose.position.y;
556 center_->z = to_pose.pose.position.z;
579 const double py, std::vector<std::pair<double, double>> & spans)
const
582 const double dy = py -
center_->y;
583 const double half_chord_sq =
params_->radius *
params_->radius - dy * dy;
584 if (half_chord_sq < 0.0) {
587 const double half_chord = std::sqrt(half_chord_sq);
589 const double padding = std::max(1.0,
static_cast<double>(
params_->radius)) * 1e-9;
590 spans.emplace_back(
center_->x - half_chord - padding,
center_->x + half_chord + padding);
594 nav_msgs::msg::OccupancyGrid::SharedPtr map,
const OverlayType overlay_type)
596 unsigned int mcx, mcy;
606 const int r =
static_cast<int>(std::round(
params_->radius / map->info.resolution));
616 putPoint(mcx + x, mcy + y, map, overlay_type);
617 putPoint(mcx + y, mcy + x, map, overlay_type);
618 putPoint(mcx - x + 1, mcy + y, map, overlay_type);
619 putPoint(mcx + y, mcy - x + 1, map, overlay_type);
620 putPoint(mcx - x + 1, mcy - y + 1, map, overlay_type);
621 putPoint(mcx - y + 1, mcy - x + 1, map, overlay_type);
622 putPoint(mcx + x, mcy - y + 1, map, overlay_type);
623 putPoint(mcx - y + 1, mcy + x, map, overlay_type);
635 putPoint(mcx + x, mcy + y, map, overlay_type);
636 putPoint(mcx - x + 1, mcy + y, map, overlay_type);
637 putPoint(mcx - x + 1, mcy - y + 1, map, overlay_type);
638 putPoint(mcx + x, mcy - y + 1, map, overlay_type);
645 auto node =
node_.lock();
647 throw std::runtime_error{
"Failed to lock node"};
652 "[UUID: %s] Circle has incorrect radius less than zero",
660 nav_msgs::msg::OccupancyGrid::ConstSharedPtr map,
661 unsigned int & mcx,
unsigned int & mcy)
663 auto node =
node_.lock();
665 throw std::runtime_error{
"Failed to lock node"};
669 if (
center_->x < map->info.origin.position.x ||
center_->y < map->info.origin.position.y) {
672 "[UUID: %s] Can not convert (%f, %f) circle center to map",
679 mcx =
static_cast<unsigned int>(
680 std::round((
center_->x - map->info.origin.position.x) / map->info.resolution)) - 1;
681 mcy =
static_cast<unsigned int>(
682 std::round((
center_->y - map->info.origin.position.y) / map->info.resolution)) - 1;
683 if (mcx >= map->info.width || mcy >= map->info.height) {
686 "[UUID: %s] Can not convert (%f, %f) point to map",
695 unsigned int mx,
unsigned int my,
696 nav_msgs::msg::OccupancyGrid::SharedPtr map,
697 const OverlayType overlay_type)
699 processCell(map, my * map->info.width + mx,
params_->value, overlay_type);
bool isUUID(const unsigned char *uuid) const
Checks whether the shape is equal to a given UUID.
int8_t getValue() const
Gets the value of the shape.
bool toFrame(const std::string &to_frame, const nav2::TransformBuffer::SharedPtr tf_buffer, const double transform_tolerance)
Transforms shape coordinates to a new frame.
void putPoint(unsigned int mx, unsigned int my, nav_msgs::msg::OccupancyGrid::SharedPtr map, const OverlayType overlay_type)
Put Circle's point on map.
bool isPointInside(const double px, const double py) const
Is the point inside the shape.
std::string getUUID() const
Gets UUID of the shape.
void getBoundaries(double &min_x, double &min_y, double &max_x, double &max_y)
Gets shape box-boundaries.
std::string getFrameID() const
Gets frame ID of the shape.
bool centerToMap(nav_msgs::msg::OccupancyGrid::ConstSharedPtr map, unsigned int &mcx, unsigned int &mcy)
Converts circle center to map coordinates considering FP-accuracy losing on small values when using c...
bool isFill() const
Whether the shape to be filled or only its borders to be put on map.
void getRowSpans(const double py, std::vector< std::pair< double, double >> &spans) const
Gets X-interval covered by the circle on the horizontal line y = py.
nav2_msgs::msg::CircleObject::SharedPtr params_
Input circle parameters (could be in any frame)
bool setParams(const nav2_msgs::msg::CircleObject::SharedPtr params)
Tries to update Circle parameters.
void putBorders(nav_msgs::msg::OccupancyGrid::SharedPtr map, const OverlayType overlay_type)
Puts shape borders on map.
nav2_msgs::msg::CircleObject::SharedPtr getParams() const
Gets Circle parameters.
bool checkConsistency()
Checks that shape is consistent for further operation.
geometry_msgs::msg::Point32::SharedPtr center_
Circle center in the map's frame.
bool obtainParams(const std::string &shape_name)
Supporting routine obtaining ROS-parameters for the given vector object.
Functor class used in raytraceLine algorithm.
std::string getUUID() const
Gets UUID of the shape.
void getRowSpans(const double py, std::vector< std::pair< double, double >> &spans) const
Gets X-intervals covered by the polygon on the horizontal line y = py.
void getBoundaries(double &min_x, double &min_y, double &max_x, double &max_y)
Gets shape box-boundaries.
void putBorders(nav_msgs::msg::OccupancyGrid::SharedPtr map, const OverlayType overlay_type)
Puts shape borders on map.
bool toFrame(const std::string &to_frame, const nav2::TransformBuffer::SharedPtr tf_buffer, const double transform_tolerance)
Transforms shape coordinates to a new frame.
bool isUUID(const unsigned char *uuid) const
Checks whether the shape is equal to a given UUID.
nav2_msgs::msg::PolygonObject::SharedPtr getParams() const
Gets Polygon parameters.
int8_t getValue() const
Gets the value of the shape.
bool setParams(const nav2_msgs::msg::PolygonObject::SharedPtr params)
Tries to update Polygon parameters.
std::string getFrameID() const
Gets frame ID of the shape.
nav2_msgs::msg::PolygonObject::SharedPtr params_
Input polygon parameters (could be in any frame)
bool checkConsistency()
Checks that shape is consistent for further operation.
bool isPointInside(const double px, const double py) const
Is the point inside the shape.
bool isFill() const
Whether the shape to be filled or only its borders to be put on map.
bool obtainParams(const std::string &shape_name)
Supporting routine obtaining ROS-parameters for the given vector object.
geometry_msgs::msg::Polygon::SharedPtr polygon_
Polygon in the map's frame.
Basic class, other vector objects to be inherited from.
bool obtainShapeUUID(const std::string &shape_name, unsigned char *out_uuid)
Supporting routine obtaining shape UUID from ROS-parameters for the given shape object.
virtual void getBoundaries(double &min_x, double &min_y, double &max_x, double &max_y)=0
Gets shape box-boundaries. Empty virtual method intended to be used in child implementations.
virtual bool isPointInside(const double px, const double py) const =0
Is the point inside the shape. Empty virtual method intended to be used in child implementations.
ShapeType type_
Type of shape.
ShapeType getType()
Returns type of the shape.
nav2::LifecycleNode::WeakPtr node_
VectorObjectServer node.
virtual void getRowSpans(const double py, std::vector< std::pair< double, double >> &spans) const =0
Gets X-intervals covered by the shape on the horizontal line y = py. Intervals may be slightly wider ...
virtual ~Shape()
Shape destructor.
Shape(const nav2::LifecycleNode::WeakPtr &node)
Shape basic class constructor.
bool putFill(nav_msgs::msg::OccupancyGrid::SharedPtr map, const OverlayType overlay_type)
Puts filled shape on map.
static void processRun(int8_t *cells, const size_t count, const int8_t shape_val, const OverlayType overlay_type)
Updates a run of consecutive cells with given shape value according to the overlay type.
virtual int8_t getValue() const =0
Gets the value of the shape. Empty virtual method intended to be used in child implementations.