16 #include "nav2_collision_monitor/collision_detector_node.hpp"
22 #include "nav2_ros_common/node_utils.hpp"
24 using namespace std::chrono_literals;
26 namespace nav2_collision_monitor
29 CollisionDetector::CollisionDetector(
const rclcpp::NodeOptions & options)
30 : nav2::LifecycleNode(
"collision_detector", options)
43 RCLCPP_INFO(get_logger(),
"Configuring");
46 tf_buffer_ = nav2::create_transform_buffer(
this);
49 state_pub_ = this->create_publisher<nav2_msgs::msg::CollisionDetectorState>(
50 "collision_detector_state");
53 "~/collision_points_marker");
56 "~/triggering_points");
61 return nav2::CallbackReturn::FAILURE;
64 return nav2::CallbackReturn::SUCCESS;
70 RCLCPP_INFO(get_logger(),
"Activating");
78 for (std::shared_ptr<Polygon> polygon :
polygons_) {
83 for (std::shared_ptr<Source> source :
sources_) {
89 std::chrono::duration<double>{1.0 /
frequency_},
95 return nav2::CallbackReturn::SUCCESS;
101 RCLCPP_INFO(get_logger(),
"Deactivating");
112 for (std::shared_ptr<Polygon> polygon :
polygons_) {
113 polygon->deactivate();
117 for (std::shared_ptr<Source> source :
sources_) {
118 source->deactivate();
124 return nav2::CallbackReturn::SUCCESS;
130 RCLCPP_INFO(get_logger(),
"Cleaning up");
142 return nav2::CallbackReturn::SUCCESS;
148 RCLCPP_INFO(get_logger(),
"Shutting down");
149 return nav2::CallbackReturn::SUCCESS;
154 std::string odom_frame_id;
155 tf2::Duration transform_tolerance;
156 rclcpp::Duration source_timeout(2.0, 0.0);
160 frequency_ = node->declare_or_get_parameter(
"frequency", 10.0);
161 base_frame_id_ = node->declare_or_get_parameter(
"base_frame_id", std::string(
"base_footprint"));
162 odom_frame_id = node->declare_or_get_parameter(
"odom_frame_id", std::string(
"odom"));
163 transform_tolerance = tf2::durationFromSec(
164 node->declare_or_get_parameter(
"transform_tolerance", 0.1));
165 source_timeout = rclcpp::Duration::from_seconds(
166 node->declare_or_get_parameter(
"source_timeout", 2.0));
167 const bool base_shift_correction = node->declare_or_get_parameter(
"base_shift_correction",
true);
171 base_frame_id_, odom_frame_id, transform_tolerance, source_timeout,
172 base_shift_correction))
185 const std::string & base_frame_id,
186 const tf2::Duration & transform_tolerance)
192 std::vector<std::string> polygon_names =
193 node->declare_or_get_parameter<std::vector<std::string>>(
"polygons");
194 for (std::string polygon_name : polygon_names) {
196 const std::string polygon_type =
197 node->declare_or_get_parameter<std::string>(polygon_name +
".type");
199 if (polygon_type ==
"polygon") {
201 std::make_shared<Polygon>(
202 node, polygon_name,
tf_buffer_, base_frame_id, transform_tolerance));
203 }
else if (polygon_type ==
"circle") {
205 std::make_shared<Circle>(
206 node, polygon_name,
tf_buffer_, base_frame_id, transform_tolerance));
207 }
else if (polygon_type ==
"velocity_polygon") {
209 std::make_shared<VelocityPolygon>(
210 node, polygon_name,
tf_buffer_, base_frame_id, transform_tolerance));
214 "[%s]: Unknown polygon type: %s",
215 polygon_name.c_str(), polygon_type.c_str());
225 auto action_type =
polygons_.back()->getActionType();
226 if (action_type != DO_NOTHING) {
229 "[%s]: The action_type of the polygon is different than \"none\" which is "
230 "not supported in the collision detector.",
231 polygon_name.c_str());
235 }
catch (
const std::exception & ex) {
236 RCLCPP_ERROR(get_logger(),
"Error while getting parameters: %s", ex.what());
244 const std::string & base_frame_id,
245 const std::string & odom_frame_id,
246 const tf2::Duration & transform_tolerance,
247 const rclcpp::Duration & source_timeout,
248 const bool base_shift_correction)
254 std::vector<std::string> source_names =
255 node->declare_or_get_parameter<std::vector<std::string>>(
"observation_sources");
256 for (std::string source_name : source_names) {
257 const std::string source_type = node->declare_or_get_parameter(
258 source_name +
".type", std::string(
"scan"));
260 if (source_type ==
"scan") {
261 std::shared_ptr<Scan> s = std::make_shared<Scan>(
262 node, source_name,
tf_buffer_, base_frame_id, odom_frame_id,
263 transform_tolerance, source_timeout, base_shift_correction);
265 if (!s->configure()) {
270 }
else if (source_type ==
"pointcloud") {
271 std::shared_ptr<PointCloud> p = std::make_shared<PointCloud>(
272 node, source_name,
tf_buffer_, base_frame_id, odom_frame_id,
273 transform_tolerance, source_timeout, base_shift_correction);
275 if (!p->configure()) {
280 }
else if (source_type ==
"range") {
281 std::shared_ptr<Range> r = std::make_shared<Range>(
282 node, source_name,
tf_buffer_, base_frame_id, odom_frame_id,
283 transform_tolerance, source_timeout, base_shift_correction);
285 if (!r->configure()) {
290 }
else if (source_type ==
"polygon") {
291 std::shared_ptr<PolygonSource> ps = std::make_shared<PolygonSource>(
292 node, source_name,
tf_buffer_, base_frame_id, odom_frame_id,
293 transform_tolerance, source_timeout, base_shift_correction);
294 if (!ps->configure()) {
299 }
else if (source_type ==
"costmap") {
300 auto src = std::make_shared<CostmapSource>(
301 node, source_name,
tf_buffer_, base_frame_id, odom_frame_id,
302 transform_tolerance, source_timeout, base_shift_correction);
304 if (!src->configure()) {
312 "[%s]: Unknown source type: %s",
313 source_name.c_str(), source_type.c_str());
317 }
catch (
const std::exception & ex) {
318 RCLCPP_ERROR(get_logger(),
"Error while getting parameters: %s", ex.what());
328 rclcpp::Time curr_time = this->now();
331 std::unordered_map<std::string, std::vector<Point>> sources_collision_points_map;
333 std::unique_ptr<nav2_msgs::msg::CollisionDetectorState> state_msg =
334 std::make_unique<nav2_msgs::msg::CollisionDetectorState>();
337 for (std::shared_ptr<Source> source :
sources_) {
338 auto iter = sources_collision_points_map.insert(
339 {source->getSourceName(), std::vector<Point>()});
341 if (source->getEnabled()) {
342 if (!source->getData(curr_time, iter.first->second) &&
343 source->getSourceTimeout().seconds() != 0.0)
347 "Invalid source %s detected."
348 " Either due to data not published yet, or to lack of new data received within the"
349 " sensor timeout, or if impossible to transform data to base frame",
350 source->getSourceName().c_str());
357 auto marker_array = std::make_unique<visualization_msgs::msg::MarkerArray>();
358 visualization_msgs::msg::Marker marker;
360 marker.header.stamp = rclcpp::Time(0, 0);
361 marker.ns =
"collision_points";
363 marker.type = visualization_msgs::msg::Marker::POINTS;
364 marker.action = visualization_msgs::msg::Marker::ADD;
365 marker.scale.x = 0.02;
366 marker.scale.y = 0.02;
367 marker.color.r = 1.0;
368 marker.color.a = 1.0;
369 marker.lifetime = rclcpp::Duration(0, 0);
370 marker.frame_locked =
true;
372 for (
const auto & [_, points] : sources_collision_points_map) {
373 for (
const auto & point : points) {
374 geometry_msgs::msg::Point p;
378 marker.points.push_back(p);
381 marker_array->markers.push_back(marker);
386 std::unordered_map<std::string, std::vector<Point>> all_triggering_points;
388 for (std::shared_ptr<Polygon> polygon :
polygons_) {
389 if (!polygon->getEnabled()) {
392 std::vector<Point> triggering_points;
393 const bool detected = polygon->isTriggered(sources_collision_points_map, triggering_points);
394 state_msg->polygons.push_back(polygon->getName());
395 state_msg->detections.push_back(detected);
397 all_triggering_points[polygon->getName()] = std::move(triggering_points);
412 const std::unordered_map<std::string, std::vector<Point>> & all_triggering_points)
414 auto marker_array = std::make_unique<visualization_msgs::msg::MarkerArray>();
417 visualization_msgs::msg::Marker clear;
418 clear.action = visualization_msgs::msg::Marker::DELETEALL;
419 marker_array->markers.push_back(clear);
421 std::unordered_map<std::string, size_t> marker_index;
422 for (
const auto & [polygon_name, points] : all_triggering_points) {
423 for (
const auto & p : points) {
424 const std::string key = polygon_name +
"/" + p.source;
425 auto [it, new_source] = marker_index.try_emplace(key, marker_array->markers.size());
428 visualization_msgs::msg::Marker marker;
430 marker.header.stamp = rclcpp::Time(0, 0);
433 marker.type = visualization_msgs::msg::Marker::POINTS;
434 marker.action = visualization_msgs::msg::Marker::ADD;
435 marker.scale.x = 0.05;
436 marker.scale.y = 0.05;
437 marker.color.r = 1.0f;
438 marker.color.g = 0.0f;
439 marker.color.b = 0.0f;
440 marker.color.a = 1.0f;
441 marker.lifetime = rclcpp::Duration(0, 0);
442 marker.frame_locked =
true;
443 marker_array->markers.push_back(std::move(marker));
446 geometry_msgs::msg::Point gp;
450 marker_array->markers[it->second].points.push_back(gp);
459 for (std::shared_ptr<Polygon> polygon :
polygons_) {
460 if (polygon->getEnabled()) {
465 for (std::shared_ptr<Source> source :
sources_) {
466 source->publishExclusionZones();
472 #include "rclcpp_components/register_node_macro.hpp"
void destroyBond()
Destroy bond connection to lifecycle manager.
nav2::LifecycleNode::SharedPtr shared_from_this()
Get a shared pointer of this.
rclcpp::GenericTimer< CallbackT >::SharedPtr create_timer(std::chrono::duration< DurationRepT, DurationT > period, CallbackT callback, rclcpp::CallbackGroup::SharedPtr group=nullptr)
Create a sim-time-aware timer for Nav2 lifecycle nodes.
void createBond()
Create bond connection to lifecycle manager.
Collision Monitor ROS2 node.
nav2::CallbackReturn on_activate(const rclcpp_lifecycle::State &state) override
: Activates LifecyclePublishers, polygons and main processor, creates bond connection
nav2::TransformBuffer::SharedPtr tf_buffer_
TF buffer.
nav2::Publisher< visualization_msgs::msg::MarkerArray >::SharedPtr collision_points_marker_pub_
Collision points marker publisher.
bool getParameters()
Supporting routine obtaining all ROS-parameters.
nav2::TransformListener::SharedPtr tf_listener_
TF listener.
bool configureSources(const std::string &base_frame_id, const std::string &odom_frame_id, const tf2::Duration &transform_tolerance, const rclcpp::Duration &source_timeout, const bool base_shift_correction)
Supporting routine creating and configuring all data sources.
void process()
Main processing routine.
nav2::CallbackReturn on_shutdown(const rclcpp_lifecycle::State &state) override
Called in shutdown state.
void publishTriggeringPoints(const std::unordered_map< std::string, std::vector< Point >> &all_triggering_points)
Publishes the points inside each detected polygon as markers, bucketed by polygon name and per-point ...
nav2::Publisher< nav2_msgs::msg::CollisionDetectorState >::SharedPtr state_pub_
collision monitor state publisher
~CollisionDetector()
Destructor for the nav2_collision_monitor::CollisionDetector.
nav2::CallbackReturn on_cleanup(const rclcpp_lifecycle::State &state) override
: Resets all subscribers/publishers, polygons/data sources arrays
std::vector< std::shared_ptr< Source > > sources_
Data sources array.
void publishVisualizations() const
Publishes all visualization topics (polygons and exclusion zones).
bool configurePolygons(const std::string &base_frame_id, const tf2::Duration &transform_tolerance)
Supporting routine creating and configuring all polygons.
rclcpp::TimerBase::SharedPtr timer_
timer that runs actions
nav2::Publisher< visualization_msgs::msg::MarkerArray >::SharedPtr triggering_points_pub_
Triggering points marker publisher (points inside each detected polygon)
std::string base_frame_id_
Robot base frame ID.
nav2::CallbackReturn on_deactivate(const rclcpp_lifecycle::State &state) override
: Deactivates LifecyclePublishers, polygons and main processor, destroys bond connection
double frequency_
main loop frequency
nav2::CallbackReturn on_configure(const rclcpp_lifecycle::State &state) override
: Initializes and obtains ROS-parameters, creates main subscribers and publishers,...
std::vector< std::shared_ptr< Polygon > > polygons_
Polygons array.
bool collision_points_marker_3d_
Whether to include z in the collision_points_marker.