Nav2 Navigation Stack - jazzy  jazzy
ROS 2 Navigation Stack
collision_monitor_node.cpp
1 // Copyright (c) 2022 Samsung R&D Institute Russia
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "nav2_collision_monitor/collision_monitor_node.hpp"
16 
17 #include <exception>
18 #include <utility>
19 #include <functional>
20 
21 #include "tf2_ros/create_timer_ros.h"
22 
23 #include "nav2_util/node_utils.hpp"
24 #include "nav2_util/robot_utils.hpp"
25 
26 #include "nav2_collision_monitor/kinematics.hpp"
27 
28 using namespace std::placeholders;
29 
30 namespace nav2_collision_monitor
31 {
32 
33 CollisionMonitor::CollisionMonitor(const rclcpp::NodeOptions & options)
34 : nav2_util::LifecycleNode("collision_monitor", "", options),
35  enabled_{true}, process_active_(false), robot_action_prev_{DO_NOTHING, {-1.0, -1.0, -1.0}, ""},
36  stop_stamp_{0, 0, get_clock()->get_clock_type()}, stop_pub_timeout_(1.0, 0.0)
37 {
38 }
39 
40 CollisionMonitor::~CollisionMonitor()
41 {
42  polygons_.clear();
43  sources_.clear();
44 }
45 
46 nav2_util::CallbackReturn
47 CollisionMonitor::on_configure(const rclcpp_lifecycle::State & state)
48 {
49  RCLCPP_INFO(get_logger(), "Configuring");
50 
51  // Transform buffer and listener initialization
52  tf_buffer_ = std::make_shared<tf2_ros::Buffer>(this->get_clock());
53  auto timer_interface = std::make_shared<tf2_ros::CreateTimerROS>(
54  this->get_node_base_interface(),
55  this->get_node_timers_interface());
56  tf_buffer_->setCreateTimerInterface(timer_interface);
57  tf_listener_ = std::make_shared<tf2_ros::TransformListener>(*tf_buffer_, this, true);
58 
59  std::string cmd_vel_in_topic;
60  std::string cmd_vel_out_topic;
61  std::string state_topic;
62 
63  // Obtaining ROS parameters
64  if (!getParameters(cmd_vel_in_topic, cmd_vel_out_topic, state_topic)) {
65  on_cleanup(state);
66  return nav2_util::CallbackReturn::FAILURE;
67  }
68 
69  cmd_vel_in_sub_ = std::make_unique<nav2_util::TwistSubscriber>(
70  shared_from_this(),
71  cmd_vel_in_topic,
72  1,
73  std::bind(&CollisionMonitor::cmdVelInCallbackUnstamped, this, std::placeholders::_1),
74  std::bind(&CollisionMonitor::cmdVelInCallbackStamped, this, std::placeholders::_1));
75 
76  auto node = shared_from_this();
77  cmd_vel_out_pub_ = std::make_unique<nav2_util::TwistPublisher>(node, cmd_vel_out_topic, 1);
78 
79  if (!state_topic.empty()) {
80  state_pub_ = this->create_publisher<nav2_msgs::msg::CollisionMonitorState>(
81  state_topic, 1);
82  }
83 
84  collision_points_marker_pub_ = this->create_publisher<visualization_msgs::msg::MarkerArray>(
85  "~/collision_points_marker", 1);
86 
87  // Toggle service initialization
88  toggle_cm_service_ = create_service<nav2_msgs::srv::Toggle>(
89  "~/toggle",
90  std::bind(&CollisionMonitor::toggleCMServiceCallback, this, _1, _2, _3));
91 
92  nav2_util::declare_parameter_if_not_declared(
93  node, "use_realtime_priority", rclcpp::ParameterValue(false));
94  bool use_realtime_priority = false;
95  node->get_parameter("use_realtime_priority", use_realtime_priority);
96  if (use_realtime_priority) {
97  try {
98  nav2_util::setSoftRealTimePriority();
99  } catch (const std::runtime_error & e) {
100  RCLCPP_ERROR(get_logger(), "%s", e.what());
101  on_cleanup(state);
102  return nav2_util::CallbackReturn::FAILURE;
103  }
104  }
105 
106  return nav2_util::CallbackReturn::SUCCESS;
107 }
108 
109 nav2_util::CallbackReturn
110 CollisionMonitor::on_activate(const rclcpp_lifecycle::State & /*state*/)
111 {
112  RCLCPP_INFO(get_logger(), "Activating");
113 
114  // Activating lifecycle publisher
115  cmd_vel_out_pub_->on_activate();
116  if (state_pub_) {
117  state_pub_->on_activate();
118  }
119  collision_points_marker_pub_->on_activate();
120 
121  // Activating polygons
122  for (std::shared_ptr<Polygon> polygon : polygons_) {
123  polygon->activate();
124  }
125 
126  // Since polygons are being published when cmd_vel_in appears,
127  // we need to publish polygons first time to display them at startup
128  publishPolygons();
129 
130  // Activating main worker
131  process_active_ = true;
132 
133  // Creating bond connection
134  createBond();
135 
136  return nav2_util::CallbackReturn::SUCCESS;
137 }
138 
139 nav2_util::CallbackReturn
140 CollisionMonitor::on_deactivate(const rclcpp_lifecycle::State & /*state*/)
141 {
142  RCLCPP_INFO(get_logger(), "Deactivating");
143 
144  // Deactivating main worker
145  process_active_ = false;
146 
147  // Reset action type to default after worker deactivating
148  robot_action_prev_ = {DO_NOTHING, {-1.0, -1.0, -1.0}, ""};
149 
150  // Deactivating polygons
151  for (std::shared_ptr<Polygon> polygon : polygons_) {
152  polygon->deactivate();
153  }
154 
155  // Deactivating lifecycle publishers
156  cmd_vel_out_pub_->on_deactivate();
157  if (state_pub_) {
158  state_pub_->on_deactivate();
159  }
160  collision_points_marker_pub_->on_deactivate();
161 
162  // Destroying bond connection
163  destroyBond();
164 
165  return nav2_util::CallbackReturn::SUCCESS;
166 }
167 
168 nav2_util::CallbackReturn
169 CollisionMonitor::on_cleanup(const rclcpp_lifecycle::State & /*state*/)
170 {
171  RCLCPP_INFO(get_logger(), "Cleaning up");
172 
173  cmd_vel_in_sub_.reset();
174  cmd_vel_out_pub_.reset();
175  state_pub_.reset();
176  collision_points_marker_pub_.reset();
177 
178  polygons_.clear();
179  sources_.clear();
180 
181  tf_listener_.reset();
182  tf_buffer_.reset();
183 
184  return nav2_util::CallbackReturn::SUCCESS;
185 }
186 
187 nav2_util::CallbackReturn
188 CollisionMonitor::on_shutdown(const rclcpp_lifecycle::State & /*state*/)
189 {
190  RCLCPP_INFO(get_logger(), "Shutting down");
191 
192  return nav2_util::CallbackReturn::SUCCESS;
193 }
194 
195 void CollisionMonitor::cmdVelInCallbackStamped(geometry_msgs::msg::TwistStamped::SharedPtr msg)
196 {
197  // If message contains NaN or Inf, ignore
198  if (!nav2_util::validateTwist(*msg)) {
199  RCLCPP_ERROR(get_logger(), "Velocity message contains NaNs or Infs! Ignoring as invalid!");
200  return;
201  }
202 
203  process({msg->twist.linear.x, msg->twist.linear.y, msg->twist.angular.z}, msg->header);
204 }
205 
206 void CollisionMonitor::cmdVelInCallbackUnstamped(geometry_msgs::msg::Twist::SharedPtr msg)
207 {
208  auto twist_stamped = std::make_shared<geometry_msgs::msg::TwistStamped>();
209  twist_stamped->twist = *msg;
210  cmdVelInCallbackStamped(twist_stamped);
211 }
212 
213 void CollisionMonitor::publishVelocity(
214  const Action & robot_action, const std_msgs::msg::Header & header)
215 {
216  if (robot_action.req_vel.isZero()) {
217  if (!robot_action_prev_.req_vel.isZero()) {
218  // Robot just stopped: saving stop timestamp and continue
219  stop_stamp_ = this->now();
220  } else if (this->now() - stop_stamp_ > stop_pub_timeout_) {
221  // More than stop_pub_timeout_ passed after robot has been stopped.
222  // Cease publishing output cmd_vel.
223  return;
224  }
225  }
226 
227  auto cmd_vel_out_msg = std::make_unique<geometry_msgs::msg::TwistStamped>();
228  cmd_vel_out_msg->header = header;
229  cmd_vel_out_msg->twist.linear.x = robot_action.req_vel.x;
230  cmd_vel_out_msg->twist.linear.y = robot_action.req_vel.y;
231  cmd_vel_out_msg->twist.angular.z = robot_action.req_vel.tw;
232  // linear.z, angular.x and angular.y will remain 0.0
233 
234  cmd_vel_out_pub_->publish(std::move(cmd_vel_out_msg));
235 }
236 
237 bool CollisionMonitor::getParameters(
238  std::string & cmd_vel_in_topic,
239  std::string & cmd_vel_out_topic,
240  std::string & state_topic)
241 {
242  std::string base_frame_id, odom_frame_id;
243  tf2::Duration transform_tolerance;
244  rclcpp::Duration source_timeout(2.0, 0.0);
245 
246  auto node = shared_from_this();
247 
248  nav2_util::declare_parameter_if_not_declared(
249  node, "cmd_vel_in_topic", rclcpp::ParameterValue("cmd_vel_smoothed"));
250  cmd_vel_in_topic = get_parameter("cmd_vel_in_topic").as_string();
251  nav2_util::declare_parameter_if_not_declared(
252  node, "cmd_vel_out_topic", rclcpp::ParameterValue("cmd_vel"));
253  cmd_vel_out_topic = get_parameter("cmd_vel_out_topic").as_string();
254  nav2_util::declare_parameter_if_not_declared(
255  node, "state_topic", rclcpp::ParameterValue(""));
256  state_topic = get_parameter("state_topic").as_string();
257 
258  nav2_util::declare_parameter_if_not_declared(
259  node, "base_frame_id", rclcpp::ParameterValue("base_footprint"));
260  base_frame_id = get_parameter("base_frame_id").as_string();
261  nav2_util::declare_parameter_if_not_declared(
262  node, "odom_frame_id", rclcpp::ParameterValue("odom"));
263  odom_frame_id = get_parameter("odom_frame_id").as_string();
264  nav2_util::declare_parameter_if_not_declared(
265  node, "transform_tolerance", rclcpp::ParameterValue(0.1));
266  transform_tolerance =
267  tf2::durationFromSec(get_parameter("transform_tolerance").as_double());
268  nav2_util::declare_parameter_if_not_declared(
269  node, "source_timeout", rclcpp::ParameterValue(2.0));
270  source_timeout =
271  rclcpp::Duration::from_seconds(get_parameter("source_timeout").as_double());
272  nav2_util::declare_parameter_if_not_declared(
273  node, "base_shift_correction", rclcpp::ParameterValue(true));
274  const bool base_shift_correction =
275  get_parameter("base_shift_correction").as_bool();
276 
277  nav2_util::declare_parameter_if_not_declared(
278  node, "stop_pub_timeout", rclcpp::ParameterValue(1.0));
279  stop_pub_timeout_ =
280  rclcpp::Duration::from_seconds(get_parameter("stop_pub_timeout").as_double());
281 
282  if (
283  !configureSources(
284  base_frame_id, odom_frame_id, transform_tolerance, source_timeout, base_shift_correction))
285  {
286  return false;
287  }
288 
289  if (!configurePolygons(base_frame_id, transform_tolerance)) {
290  return false;
291  }
292 
293  return true;
294 }
295 
296 bool CollisionMonitor::configurePolygons(
297  const std::string & base_frame_id,
298  const tf2::Duration & transform_tolerance)
299 {
300  try {
301  auto node = shared_from_this();
302 
303  // Leave it to be not initialized: to intentionally cause an error if it will not set
304  nav2_util::declare_parameter_if_not_declared(
305  node, "polygons", rclcpp::PARAMETER_STRING_ARRAY);
306  std::vector<std::string> polygon_names = get_parameter("polygons").as_string_array();
307  for (std::string polygon_name : polygon_names) {
308  // Leave it not initialized: the will cause an error if it will not set
309  nav2_util::declare_parameter_if_not_declared(
310  node, polygon_name + ".type", rclcpp::PARAMETER_STRING);
311  const std::string polygon_type = get_parameter(polygon_name + ".type").as_string();
312 
313  if (polygon_type == "polygon") {
314  polygons_.push_back(
315  std::make_shared<Polygon>(
316  node, polygon_name, tf_buffer_, base_frame_id, transform_tolerance));
317  } else if (polygon_type == "circle") {
318  polygons_.push_back(
319  std::make_shared<Circle>(
320  node, polygon_name, tf_buffer_, base_frame_id, transform_tolerance));
321  } else if (polygon_type == "velocity_polygon") {
322  polygons_.push_back(
323  std::make_shared<VelocityPolygon>(
324  node, polygon_name, tf_buffer_, base_frame_id, transform_tolerance));
325  } else { // Error if something else
326  RCLCPP_ERROR(
327  get_logger(),
328  "[%s]: Unknown polygon type: %s",
329  polygon_name.c_str(), polygon_type.c_str());
330  return false;
331  }
332 
333  // Configure last added polygon
334  if (!polygons_.back()->configure()) {
335  return false;
336  }
337  }
338  } catch (const std::exception & ex) {
339  RCLCPP_ERROR(get_logger(), "Error while getting parameters: %s", ex.what());
340  return false;
341  }
342 
343  return true;
344 }
345 
346 bool CollisionMonitor::configureSources(
347  const std::string & base_frame_id,
348  const std::string & odom_frame_id,
349  const tf2::Duration & transform_tolerance,
350  const rclcpp::Duration & source_timeout,
351  const bool base_shift_correction)
352 {
353  try {
354  auto node = shared_from_this();
355 
356  // Leave it to be not initialized: to intentionally cause an error if it will not set
357  nav2_util::declare_parameter_if_not_declared(
358  node, "observation_sources", rclcpp::PARAMETER_STRING_ARRAY);
359  std::vector<std::string> source_names = get_parameter("observation_sources").as_string_array();
360  for (std::string source_name : source_names) {
361  nav2_util::declare_parameter_if_not_declared(
362  node, source_name + ".type",
363  rclcpp::ParameterValue("scan")); // Laser scanner by default
364  const std::string source_type = get_parameter(source_name + ".type").as_string();
365 
366  if (source_type == "scan") {
367  std::shared_ptr<Scan> s = std::make_shared<Scan>(
368  node, source_name, tf_buffer_, base_frame_id, odom_frame_id,
369  transform_tolerance, source_timeout, base_shift_correction);
370 
371  s->configure();
372 
373  sources_.push_back(s);
374  } else if (source_type == "pointcloud") {
375  std::shared_ptr<PointCloud> p = std::make_shared<PointCloud>(
376  node, source_name, tf_buffer_, base_frame_id, odom_frame_id,
377  transform_tolerance, source_timeout, base_shift_correction);
378 
379  p->configure();
380 
381  sources_.push_back(p);
382  } else if (source_type == "range") {
383  std::shared_ptr<Range> r = std::make_shared<Range>(
384  node, source_name, tf_buffer_, base_frame_id, odom_frame_id,
385  transform_tolerance, source_timeout, base_shift_correction);
386 
387  r->configure();
388 
389  sources_.push_back(r);
390  } else if (source_type == "polygon") {
391  std::shared_ptr<PolygonSource> ps = std::make_shared<PolygonSource>(
392  node, source_name, tf_buffer_, base_frame_id, odom_frame_id,
393  transform_tolerance, source_timeout, base_shift_correction);
394  ps->configure();
395 
396  sources_.push_back(ps);
397  } else { // Error if something else
398  RCLCPP_ERROR(
399  get_logger(),
400  "[%s]: Unknown source type: %s",
401  source_name.c_str(), source_type.c_str());
402  return false;
403  }
404  }
405  } catch (const std::exception & ex) {
406  RCLCPP_ERROR(get_logger(), "Error while getting parameters: %s", ex.what());
407  return false;
408  }
409 
410  return true;
411 }
412 
413 void CollisionMonitor::process(const Velocity & cmd_vel_in, const std_msgs::msg::Header & header)
414 {
415  // Current timestamp for all inner routines prolongation
416  rclcpp::Time curr_time = this->now();
417 
418  // Do nothing if main worker in non-active state
419  if (!process_active_) {
420  return;
421  }
422 
423  // Points array collected from different data sources in a robot base frame
424  std::unordered_map<std::string, std::vector<Point>> sources_collision_points_map;
425 
426  // By default - there is no action
427  Action robot_action{DO_NOTHING, cmd_vel_in, ""};
428  // Polygon causing robot action (if any)
429  std::shared_ptr<Polygon> action_polygon;
430 
431  // Fill collision points array from different data sources
432  auto marker_array = std::make_unique<visualization_msgs::msg::MarkerArray>();
433  for (std::shared_ptr<Source> source : sources_) {
434  auto iter = sources_collision_points_map.insert(
435  {source->getSourceName(), std::vector<Point>()});
436 
437  if (source->getEnabled()) {
438  if (!source->getData(curr_time, iter.first->second) &&
439  source->getSourceTimeout().seconds() != 0.0)
440  {
441  action_polygon = nullptr;
442  robot_action.polygon_name = "invalid source";
443  robot_action.action_type = STOP;
444  robot_action.req_vel.x = 0.0;
445  robot_action.req_vel.y = 0.0;
446  robot_action.req_vel.tw = 0.0;
447  break;
448  }
449  }
450 
451  if (collision_points_marker_pub_->get_subscription_count() > 0) {
452  // visualize collision points with markers
453  visualization_msgs::msg::Marker marker;
454  marker.header.frame_id = get_parameter("base_frame_id").as_string();
455  marker.header.stamp = rclcpp::Time(0, 0);
456  marker.ns = "collision_points_" + source->getSourceName();
457  marker.id = 0;
458  marker.type = visualization_msgs::msg::Marker::POINTS;
459  marker.action = visualization_msgs::msg::Marker::ADD;
460  marker.scale.x = 0.02;
461  marker.scale.y = 0.02;
462  marker.color.r = 1.0;
463  marker.color.a = 1.0;
464  marker.lifetime = rclcpp::Duration(0, 0);
465  marker.frame_locked = true;
466 
467  for (const auto & point : iter.first->second) {
468  geometry_msgs::msg::Point p;
469  p.x = point.x;
470  p.y = point.y;
471  p.z = 0.0;
472  marker.points.push_back(p);
473  }
474  marker_array->markers.push_back(marker);
475  }
476  }
477 
478  if (collision_points_marker_pub_->get_subscription_count() > 0) {
479  collision_points_marker_pub_->publish(std::move(marker_array));
480  }
481 
482  for (std::shared_ptr<Polygon> polygon : polygons_) {
483  if (!polygon->getEnabled() || !enabled_) {
484  continue;
485  }
486  if (robot_action.action_type == STOP) {
487  // If robot already should stop, do nothing
488  break;
489  }
490 
491  // Update polygon coordinates
492  polygon->updatePolygon(cmd_vel_in);
493 
494  const ActionType at = polygon->getActionType();
495  if (at == STOP || at == SLOWDOWN || at == LIMIT) {
496  // Process STOP/SLOWDOWN for the selected polygon
497  if (processStopSlowdownLimit(
498  polygon, sources_collision_points_map, cmd_vel_in, robot_action))
499  {
500  action_polygon = polygon;
501  }
502  } else if (at == APPROACH) {
503  // Process APPROACH for the selected polygon
504  if (processApproach(polygon, sources_collision_points_map, cmd_vel_in, robot_action)) {
505  action_polygon = polygon;
506  }
507  }
508  }
509 
510  if ((robot_action.polygon_name != robot_action_prev_.polygon_name) && enabled_) {
511  // Report changed robot behavior
512  notifyActionState(robot_action, action_polygon);
513  }
514 
515  // Publish required robot velocity
516  publishVelocity(robot_action, header);
517 
518  // Publish polygons for better visualization
519  publishPolygons();
520 
521  robot_action_prev_ = robot_action;
522 }
523 
524 bool CollisionMonitor::processStopSlowdownLimit(
525  const std::shared_ptr<Polygon> polygon,
526  const std::unordered_map<std::string, std::vector<Point>> & sources_collision_points_map,
527  const Velocity & velocity,
528  Action & robot_action) const
529 {
530  if (!polygon->isShapeSet()) {
531  return false;
532  }
533 
534  if (polygon->getPointsInside(sources_collision_points_map) >= polygon->getMinPoints()) {
535  if (polygon->getActionType() == STOP) {
536  // Setting up zero velocity for STOP model
537  robot_action.polygon_name = polygon->getName();
538  robot_action.action_type = STOP;
539  robot_action.req_vel.x = 0.0;
540  robot_action.req_vel.y = 0.0;
541  robot_action.req_vel.tw = 0.0;
542  return true;
543  } else if (polygon->getActionType() == SLOWDOWN) {
544  const Velocity safe_vel = velocity * polygon->getSlowdownRatio();
545  // Check that currently calculated velocity is safer than
546  // chosen for previous shapes one
547  if (safe_vel < robot_action.req_vel) {
548  robot_action.polygon_name = polygon->getName();
549  robot_action.action_type = SLOWDOWN;
550  robot_action.req_vel = safe_vel;
551  return true;
552  }
553  } else { // Limit
554  // Compute linear velocity
555  const double linear_vel = std::hypot(velocity.x, velocity.y); // absolute
556  Velocity safe_vel;
557  double ratio = 1.0;
558 
559  // Calculate the most restrictive ratio to preserve curvature
560  if (linear_vel != 0.0) {
561  ratio = std::min(ratio, polygon->getLinearLimit() / linear_vel);
562  }
563  if (velocity.tw != 0.0) {
564  ratio = std::min(ratio, polygon->getAngularLimit() / std::abs(velocity.tw));
565  }
566  ratio = std::clamp(ratio, 0.0, 1.0);
567  // Apply the same ratio to all components to preserve curvature
568  safe_vel = velocity * ratio;
569  // Check that currently calculated velocity is safer than
570  // chosen for previous shapes one
571  if (safe_vel < robot_action.req_vel) {
572  robot_action.polygon_name = polygon->getName();
573  robot_action.action_type = LIMIT;
574  robot_action.req_vel = safe_vel;
575  return true;
576  }
577  }
578  }
579 
580  return false;
581 }
582 
583 bool CollisionMonitor::processApproach(
584  const std::shared_ptr<Polygon> polygon,
585  const std::unordered_map<std::string, std::vector<Point>> & sources_collision_points_map,
586  const Velocity & velocity,
587  Action & robot_action) const
588 {
589  if (!polygon->isShapeSet()) {
590  return false;
591  }
592 
593  // Obtain time before a collision
594  const double collision_time = polygon->getCollisionTime(sources_collision_points_map, velocity);
595  if (collision_time >= 0.0) {
596  // If collision will occurr, reduce robot speed
597  const double change_ratio = collision_time / polygon->getTimeBeforeCollision();
598  const Velocity safe_vel = velocity * change_ratio;
599  // Check that currently calculated velocity is safer than
600  // chosen for previous shapes one
601  if (safe_vel < robot_action.req_vel) {
602  robot_action.polygon_name = polygon->getName();
603  robot_action.action_type = APPROACH;
604  robot_action.req_vel = safe_vel;
605  return true;
606  }
607  }
608 
609  return false;
610 }
611 
612 void CollisionMonitor::notifyActionState(
613  const Action & robot_action, const std::shared_ptr<Polygon> action_polygon) const
614 {
615  if (robot_action.action_type == STOP) {
616  if (robot_action.polygon_name == "invalid source") {
617  RCLCPP_WARN(
618  get_logger(),
619  "Robot to stop due to invalid source."
620  " Either due to data not published yet, or to lack of new data received within the"
621  " sensor timeout, or if impossible to transform data to base frame");
622  } else {
623  RCLCPP_INFO(
624  get_logger(),
625  "Robot to stop due to %s polygon",
626  action_polygon->getName().c_str());
627  }
628  } else if (robot_action.action_type == SLOWDOWN) {
629  RCLCPP_INFO(
630  get_logger(),
631  "Robot to slowdown for %f percents due to %s polygon",
632  action_polygon->getSlowdownRatio() * 100,
633  action_polygon->getName().c_str());
634  } else if (robot_action.action_type == LIMIT) {
635  RCLCPP_INFO(
636  get_logger(),
637  "Robot to limit speed due to %s polygon",
638  action_polygon->getName().c_str());
639  } else if (robot_action.action_type == APPROACH) {
640  RCLCPP_INFO(
641  get_logger(),
642  "Robot to approach for %f seconds away from collision",
643  action_polygon->getTimeBeforeCollision());
644  } else { // robot_action.action_type == DO_NOTHING
645  RCLCPP_INFO(
646  get_logger(),
647  "Robot to continue normal operation");
648  }
649 
650  if (state_pub_) {
651  std::unique_ptr<nav2_msgs::msg::CollisionMonitorState> state_msg =
652  std::make_unique<nav2_msgs::msg::CollisionMonitorState>();
653  state_msg->polygon_name = robot_action.polygon_name;
654  state_msg->action_type = robot_action.action_type;
655 
656  state_pub_->publish(std::move(state_msg));
657  }
658 }
659 
660 void CollisionMonitor::publishPolygons() const
661 {
662  for (std::shared_ptr<Polygon> polygon : polygons_) {
663  if (polygon->getEnabled() || !enabled_) {
664  polygon->publish();
665  }
666  }
667 }
668 
669 void CollisionMonitor::toggleCMServiceCallback(
670  const std::shared_ptr<rmw_request_id_t>/*request_header*/,
671  const std::shared_ptr<nav2_msgs::srv::Toggle::Request> request,
672  std::shared_ptr<nav2_msgs::srv::Toggle::Response> response)
673 {
674  enabled_ = request->enable;
675 
676  std::stringstream message;
677  message << "Collision monitor toggled " << (enabled_ ? "on" : "off") << " successfully";
678 
679  response->success = true;
680  response->message = message.str();
681 }
682 
683 } // namespace nav2_collision_monitor
684 
685 #include "rclcpp_components/register_node_macro.hpp"
686 
687 // Register the component with class_loader.
688 // This acts as a sort of entry point, allowing the component to be discoverable when its library
689 // is being loaded into a running process.
690 RCLCPP_COMPONENTS_REGISTER_NODE(nav2_collision_monitor::CollisionMonitor)
Action for robot.
Definition: types.hpp:76
Velocity for 2D model of motion.
Definition: types.hpp:25