15 #include "nav2_collision_monitor/polygon.hpp"
21 #include "geometry_msgs/msg/point.hpp"
22 #include "geometry_msgs/msg/point32.hpp"
23 #include "tf2/transform_datatypes.hpp"
24 #include "nav2_ros_common/tf2_factories.hpp"
26 #include "nav2_ros_common/node_utils.hpp"
27 #include "nav2_util/geometry_utils.hpp"
28 #include "nav2_util/robot_utils.hpp"
30 #include "nav2_collision_monitor/kinematics.hpp"
31 #include "nav2_collision_monitor/polygon_utils.hpp"
33 namespace nav2_collision_monitor
37 const nav2::LifecycleNode::WeakPtr & node,
38 const std::string & polygon_name,
39 const nav2::TransformBuffer::SharedPtr tf_buffer,
40 const std::string & base_frame_id,
41 const tf2::Duration & transform_tolerance)
42 : node_(node), polygon_name_(polygon_name), action_type_(DO_NOTHING),
43 slowdown_ratio_(0.0), linear_limit_(0.0), angular_limit_(0.0),
44 footprint_sub_(nullptr), tf_buffer_(tf_buffer),
45 base_frame_id_(base_frame_id), transform_tolerance_(transform_tolerance),
58 auto node =
node_.lock();
59 if (post_set_params_handler_ && node) {
60 node->remove_post_set_parameters_callback(post_set_params_handler_.get());
62 post_set_params_handler_.reset();
63 if (on_set_params_handler_ && node) {
64 node->remove_on_set_parameters_callback(on_set_params_handler_.get());
66 on_set_params_handler_.reset();
71 auto node =
node_.lock();
73 throw std::runtime_error{
"Failed to lock node"};
77 std::string polygon_sub_topic, polygon_pub_topic, footprint_topic;
79 if (!
getParameters(polygon_sub_topic, polygon_pub_topic, footprint_topic)) {
85 if (!footprint_topic.empty()) {
88 "[%s]: Making footprint subscriber on %s topic",
90 footprint_sub_ = std::make_unique<nav2_costmap_2d::FootprintSubscriber>(
98 std::vector<Point> poly;
100 for (
const Point & p : poly) {
101 geometry_msgs::msg::Point32 p_s;
105 polygon_.polygon.points.push_back(p_s);
108 polygon_pub_ = node->create_publisher<geometry_msgs::msg::PolygonStamped>(
113 post_set_params_handler_ = node->add_post_set_parameters_callback(
116 this, std::placeholders::_1));
117 on_set_params_handler_ = node->add_on_set_parameters_callback(
120 this, std::placeholders::_1));
153 std::lock_guard<std::mutex> lock_reinit(
mutex_);
163 const std::unordered_map<std::string, std::vector<Point>> & sources_collision_points_map,
164 std::vector<Point> & out_triggering_points)
166 const int points_inside =
getPointsInside(sources_collision_points_map, out_triggering_points);
167 return isTriggeredInternal(points_inside);
170 bool Polygon::isTriggeredInternal(
int points_inside)
250 std::vector<geometry_msgs::msg::Point> footprint_vec;
251 std_msgs::msg::Header footprint_header;
252 footprint_sub_->getFootprintInRobotFrame(footprint_vec, footprint_header);
254 std::size_t new_size = footprint_vec.size();
255 poly_.resize(new_size);
257 polygon_.polygon.points.resize(new_size);
259 geometry_msgs::msg::Point32 p_s;
260 for (std::size_t i = 0; i < new_size; i++) {
261 poly_[i] = {footprint_vec[i].x, footprint_vec[i].y};
262 p_s.x = footprint_vec[i].x;
263 p_s.y = footprint_vec[i].y;
268 std::size_t new_size =
polygon_.polygon.points.size();
271 tf2::Stamped<tf2::Transform> tf_transform;
273 !nav2_util::getTransform(
281 poly_.resize(new_size);
282 for (std::size_t i = 0; i < new_size; i++) {
284 tf2::Vector3 p_v3_s(
polygon_.polygon.points[i].x,
polygon_.polygon.points[i].y, 0.0);
285 tf2::Vector3 p_v3_b = tf_transform * p_v3_s;
288 poly_[i] = {p_v3_b.x(), p_v3_b.y()};
294 const std::vector<Point> & points,
295 std::vector<Point> & out_triggering_points)
const
298 for (
const Point & point : points) {
299 if (nav2_util::geometry_utils::isPointInsidePolygon(point.x, point.y,
poly_)) {
300 out_triggering_points.push_back(point);
308 const std::vector<Point> & points,
309 std::vector<std::size_t> & out_triggering_indices)
const
312 for (std::size_t i = 0; i < points.size(); ++i) {
313 if (nav2_util::geometry_utils::isPointInsidePolygon(points[i].x, points[i].y,
poly_)) {
314 out_triggering_indices.push_back(i);
322 const std::unordered_map<std::string, std::vector<Point>> & sources_collision_points_map,
323 std::vector<Point> & out_triggering_points)
const
329 for (
const auto & source_name : polygon_sources_names) {
330 const auto & iter = sources_collision_points_map.find(source_name);
331 if (iter != sources_collision_points_map.end()) {
340 const std::unordered_map<std::string, std::vector<Point>> & sources_collision_points_map,
342 std::vector<Point> & out_triggering_points)
const
345 Pose pose = {0.0, 0.0, 0.0};
349 std::vector<Point> collision_points;
352 for (
const auto & source_name : polygon_sources_names) {
353 const auto & iter = sources_collision_points_map.find(source_name);
354 if (iter != sources_collision_points_map.end()) {
355 collision_points.insert(collision_points.end(), iter->second.begin(), iter->second.end());
360 std::vector<Point> points_transformed = collision_points;
373 points_transformed = collision_points;
374 transformPoints(pose, points_transformed);
377 std::vector<std::size_t> triggering_indices;
379 for (std::size_t i : triggering_indices) {
380 out_triggering_points.push_back(collision_points[i]);
396 auto node =
node_.lock();
398 throw std::runtime_error{
"Failed to lock node"};
402 polygon_.header.stamp = node->now();
403 auto msg = std::make_unique<geometry_msgs::msg::PolygonStamped>(
polygon_);
408 std::string & polygon_sub_topic,
409 std::string & polygon_pub_topic,
410 std::string & footprint_topic,
411 bool use_dynamic_sub_topic)
413 auto node =
node_.lock();
415 throw std::runtime_error{
"Failed to lock node"};
421 const std::string at_str = node->declare_or_get_parameter<std::string>(
423 if (at_str ==
"stop") {
425 }
else if (at_str ==
"slowdown") {
427 }
else if (at_str ==
"limit") {
429 }
else if (at_str ==
"approach") {
431 }
else if (at_str ==
"none") {
448 "[%s]: trigger_consecutive_points and release_consecutive_points must be >= 1",
459 "[%s]: \"max_points\" parameter was deprecated. Use \"min_points\" instead to specify "
460 "the minimum number of data readings within a zone to trigger the action",
462 }
catch (
const std::exception &) {
485 polygon_pub_topic = node->declare_or_get_parameter(
490 polygon_name_ +
".polygon_subscribe_transient_local",
false);
492 if (use_dynamic_sub_topic) {
495 polygon_sub_topic = node->declare_or_get_parameter<std::string>(
499 footprint_topic = node->declare_or_get_parameter(
501 std::string(
"local_costmap/published_footprint"));
506 const std::vector<std::string> observation_sources =
507 node->declare_or_get_parameter<std::vector<std::string>>(
"observation_sources");
513 if (std::find(observation_sources.begin(), observation_sources.end(), source_name) ==
514 observation_sources.end())
518 "Observation source [" << source_name <<
519 "] configured for polygon [" <<
getName() <<
520 "] is not defined as one of the node's observation_source!");
524 }
catch (
const std::exception & ex) {
527 "[%s]: Error while getting common polygon parameters: %s",
536 std::string & polygon_sub_topic,
537 std::string & polygon_pub_topic,
538 std::string & footprint_topic)
540 auto node =
node_.lock();
542 throw std::runtime_error{
"Failed to lock node"};
546 polygon_sub_topic.clear();
547 footprint_topic.clear();
549 bool use_dynamic_sub =
true;
552 std::string poly_string = node->declare_or_get_parameter<std::string>(
556 }
catch (
const rclcpp::exceptions::InvalidParameterValueException &) {
559 "[%s]: Polygon points are not defined. Using dynamic subscription instead.",
564 polygon_sub_topic, polygon_pub_topic, footprint_topic, use_dynamic_sub))
566 if (use_dynamic_sub && polygon_sub_topic.empty() && footprint_topic.empty()) {
569 "[%s]: Error while getting polygon parameters:"
570 " static points and sub topic both not defined",
581 auto node =
node_.lock();
583 throw std::runtime_error{
"Failed to lock node"};
586 if (!polygon_sub_topic.empty()) {
589 "[%s]: Subscribing on %s topic for polygon",
593 polygon_qos.transient_local();
595 polygon_sub_ = node->create_subscription<geometry_msgs::msg::PolygonStamped>(
604 std::size_t new_size = msg->polygon.points.size();
609 "[%s]: Polygon should have at least 3 points",
615 tf2::Stamped<tf2::Transform> tf_transform;
617 !nav2_util::getTransform(
625 poly_.resize(new_size);
626 for (std::size_t i = 0; i < new_size; i++) {
628 tf2::Vector3 p_v3_s(msg->polygon.points[i].x, msg->polygon.points[i].y, 0.0);
629 tf2::Vector3 p_v3_b = tf_transform * p_v3_s;
632 poly_[i] = {p_v3_b.x(), p_v3_b.y()};
643 const std::vector<rclcpp::Parameter> & )
645 rcl_interfaces::msg::SetParametersResult result;
646 result.successful =
true;
651 const std::vector<rclcpp::Parameter> & parameters)
653 std::lock_guard<std::mutex> lock_reinit(
mutex_);
655 for (
const auto & parameter : parameters) {
656 const auto & param_type = parameter.get_type();
657 const auto & param_name = parameter.get_name();
661 if (param_type == rcl_interfaces::msg::ParameterType::PARAMETER_BOOL) {
668 if (param_type == rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER) {
670 min_points_ = std::max(1,
static_cast<int>(parameter.as_int()));
672 }
else if (param_name ==
polygon_name_ +
"." +
"trigger_consecutive_points") {
673 const auto value =
static_cast<int>(parameter.as_int());
675 throw rclcpp::exceptions::InvalidParameterValueException(
676 "Parameter 'trigger_consecutive_points' must be >= 1");
680 }
else if (param_name ==
polygon_name_ +
"." +
"release_consecutive_points") {
681 const auto value =
static_cast<int>(parameter.as_int());
683 throw rclcpp::exceptions::InvalidParameterValueException(
684 "Parameter 'release_consecutive_points' must be >= 1");
695 RCLCPP_INFO_THROTTLE(
699 "[%s]: Polygon shape update has arrived",
705 std::string & poly_string,
706 std::vector<Point> & polygon)
710 if (!parsePolygonPoints(poly_string, 4, polygon, error)) {
A QoS profile for standard reliable topics with a history of 10 messages.
virtual bool getParameters(std::string &polygon_sub_topic, std::string &polygon_pub_topic, std::string &footprint_topic)
Supporting routine obtaining polygon-specific ROS-parameters.
int release_consecutive_points_
Number of consecutive misses required to release action.
int trigger_consecutive_points_
Number of consecutive hits required to trigger action.
int getMinPoints() const
Obtains polygon minimum points to enter inside polygon causing the action.
nav2::Publisher< geometry_msgs::msg::PolygonStamped >::SharedPtr polygon_pub_
Polygon publisher for visualization purposes.
double getTimeBeforeCollision() const
Obtains required time before collision for current polygon. Applicable for APPROACH model.
rclcpp::Clock::SharedPtr node_clock_
Collision monitor node's clock.
virtual void updatePolygon(const Velocity &)
Updates polygon from footprint subscriber (if any)
bool getCommonParameters(std::string &polygon_sub_topic, std::string &polygon_pub_topic, std::string &footprint_topic, bool use_dynamic_sub=false)
Supporting routine obtaining ROS-parameters common for all shapes.
std::mutex mutex_
Dynamic parameters handler.
double time_before_collision_
Time before collision in seconds.
double getCollisionTime(const std::unordered_map< std::string, std::vector< Point >> &sources_collision_points_map, const Velocity &velocity, std::vector< Point > &out_triggering_points) const
Obtains estimated (simulated) time before a collision. Applicable for APPROACH model.
bool enabled_
Whether polygon is enabled.
rcl_interfaces::msg::SetParametersResult validateParameterUpdatesCallback(const std::vector< rclcpp::Parameter > ¶meters)
Validate incoming parameter updates before applying them. This callback is triggered when one or more...
rclcpp::Logger logger_
Collision monitor node logger stored for further usage.
ActionType action_type_
Action type for the polygon.
geometry_msgs::msg::PolygonStamped polygon_
Polygon, used for: 1. visualization; 2. storing latest dynamic polygon message.
int trigger_hits_
Current consecutive hit counter.
virtual void createSubscription(std::string &polygon_sub_topic)
Creates polygon or radius topic subscription.
nav2::Subscription< geometry_msgs::msg::PolygonStamped >::SharedPtr polygon_sub_
Polygon subscription.
bool polygon_subscribe_transient_local_
Whether the subscription to polygon topic has transient local QoS durability.
virtual bool isShapeSet()
Returns true if polygon points were set. Otherwise, prints a warning and returns false.
double simulation_time_step_
Time step for robot movement simulation.
bool trigger_active_
Latched trigger state after temporal debounce.
void activate()
Activates polygon lifecycle publisher.
bool configure()
Shape configuration routine. Obtains ROS-parameters related to shape object and creates polygon lifec...
double getLinearLimit() const
Obtains speed linear limit for current polygon. Applicable for LIMIT model.
std::string getName() const
Returns the name of polygon.
bool isTriggered(const std::unordered_map< std::string, std::vector< Point >> &sources_collision_points_map, std::vector< Point > &out_triggering_points)
Temporal debounce for min_points trigger.
virtual void getPolygon(std::vector< Point > &poly) const
Gets polygon points.
tf2::Duration transform_tolerance_
Transform tolerance.
void resetTriggerState()
Reset temporal debounce state.
ActionType getActionType() const
Obtains polygon action type.
int min_points_
Minimum number of data readings within a zone to trigger the action.
std::vector< Point > poly_
Polygon points (vertices) in a base_frame_id_.
std::vector< std::string > sources_names_
Name of the observation sources to check for polygon.
void deactivate()
Deactivates polygon lifecycle publisher.
std::unique_ptr< nav2_costmap_2d::FootprintSubscriber > footprint_sub_
Footprint subscriber.
void polygonCallback(geometry_msgs::msg::PolygonStamped::ConstSharedPtr msg)
Dynamic polygon callback.
double getSlowdownRatio() const
Obtains speed slowdown ratio for current polygon. Applicable for SLOWDOWN model.
double getAngularLimit() const
Obtains speed angular z limit for current polygon. Applicable for LIMIT model.
void updateParametersCallback(const std::vector< rclcpp::Parameter > ¶meters)
Apply parameter updates after validation This callback is executed when parameters have been successf...
std::string polygon_name_
Name of polygon.
nav2::LifecycleNode::WeakPtr node_
Collision Monitor node.
virtual int getPointsInside(const std::vector< Point > &points, std::vector< Point > &out_triggering_points) const
Gets number of points inside given polygon.
double linear_limit_
Robot linear limit.
virtual ~Polygon()
Polygon destructor.
int release_hits_
Current consecutive miss counter.
bool getEnabled() const
Obtains polygon enabled state.
double angular_limit_
Robot angular limit.
bool getPolygonFromString(std::string &poly_string, std::vector< Point > &polygon)
Extracts Polygon points from a string with of the form [[x1,y1],[x2,y2],[x3,y3]......
bool visualize_
Whether to publish the polygon.
std::vector< std::string > getSourcesNames() const
Obtains the name of the observation sources for current polygon.
nav2::TransformBuffer::SharedPtr tf_buffer_
TF buffer.
std::string base_frame_id_
Base frame ID.
Polygon(const nav2::LifecycleNode::WeakPtr &node, const std::string &polygon_name, const nav2::TransformBuffer::SharedPtr tf_buffer, const std::string &base_frame_id, const tf2::Duration &transform_tolerance)
Polygon constructor.
double slowdown_ratio_
Robot slowdown (share of its actual speed)
void publish()
Publishes polygon message into a its own topic.
Point with 2D collision-check coordinates and optional z from the source.
Velocity for 2D model of motion.