Nav2 Navigation Stack - kilted  kilted
ROS 2 Navigation Stack
collision_detector_node.cpp
1 // Copyright (c) 2022 Samsung R&D Institute Russia
2 // Copyright (c) 2023 Pixel Robotics GmbH
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 
16 #include "nav2_collision_monitor/collision_detector_node.hpp"
17 
18 #include <exception>
19 #include <utility>
20 #include <functional>
21 
22 #include "tf2_ros/create_timer_ros.h"
23 
24 #include "nav2_util/node_utils.hpp"
25 
26 using namespace std::chrono_literals;
27 
28 namespace nav2_collision_monitor
29 {
30 
31 CollisionDetector::CollisionDetector(const rclcpp::NodeOptions & options)
32 : nav2_util::LifecycleNode("collision_detector", "", options)
33 {
34 }
35 
37 {
38  polygons_.clear();
39  sources_.clear();
40 }
41 
42 nav2_util::CallbackReturn
43 CollisionDetector::on_configure(const rclcpp_lifecycle::State & state)
44 {
45  RCLCPP_INFO(get_logger(), "Configuring");
46 
47  // Transform buffer and listener initialization
48  tf_buffer_ = std::make_shared<tf2_ros::Buffer>(this->get_clock());
49  auto timer_interface = std::make_shared<tf2_ros::CreateTimerROS>(
50  this->get_node_base_interface(),
51  this->get_node_timers_interface());
52  tf_buffer_->setCreateTimerInterface(timer_interface);
53  tf_listener_ = std::make_shared<tf2_ros::TransformListener>(*tf_buffer_);
54 
55  state_pub_ = this->create_publisher<nav2_msgs::msg::CollisionDetectorState>(
56  "collision_detector_state", rclcpp::SystemDefaultsQoS());
57 
58  collision_points_marker_pub_ = this->create_publisher<visualization_msgs::msg::MarkerArray>(
59  "~/collision_points_marker", 1);
60 
61  // Obtaining ROS parameters
62  if (!getParameters()) {
63  on_cleanup(state);
64  return nav2_util::CallbackReturn::FAILURE;
65  }
66 
67  return nav2_util::CallbackReturn::SUCCESS;
68 }
69 
70 nav2_util::CallbackReturn
71 CollisionDetector::on_activate(const rclcpp_lifecycle::State & /*state*/)
72 {
73  RCLCPP_INFO(get_logger(), "Activating");
74 
75  // Activating lifecycle publisher
76  state_pub_->on_activate();
77  collision_points_marker_pub_->on_activate();
78 
79  // Activating polygons
80  for (std::shared_ptr<Polygon> polygon : polygons_) {
81  polygon->activate();
82  }
83 
84  // Creating timer
85  timer_ = this->create_timer(
86  std::chrono::duration<double>{1.0 / frequency_},
87  std::bind(&CollisionDetector::process, this));
88 
89  // Creating bond connection
90  createBond();
91 
92  return nav2_util::CallbackReturn::SUCCESS;
93 }
94 
95 nav2_util::CallbackReturn
96 CollisionDetector::on_deactivate(const rclcpp_lifecycle::State & /*state*/)
97 {
98  RCLCPP_INFO(get_logger(), "Deactivating");
99 
100  // Resetting timer
101  timer_.reset();
102 
103  // Deactivating lifecycle publishers
104  state_pub_->on_deactivate();
105  collision_points_marker_pub_->on_deactivate();
106 
107  // Deactivating polygons
108  for (std::shared_ptr<Polygon> polygon : polygons_) {
109  polygon->deactivate();
110  }
111 
112  // Destroying bond connection
113  destroyBond();
114 
115  return nav2_util::CallbackReturn::SUCCESS;
116 }
117 
118 nav2_util::CallbackReturn
119 CollisionDetector::on_cleanup(const rclcpp_lifecycle::State & /*state*/)
120 {
121  RCLCPP_INFO(get_logger(), "Cleaning up");
122 
123  state_pub_.reset();
125 
126  polygons_.clear();
127  sources_.clear();
128 
129  tf_listener_.reset();
130  tf_buffer_.reset();
131 
132  return nav2_util::CallbackReturn::SUCCESS;
133 }
134 
135 nav2_util::CallbackReturn
136 CollisionDetector::on_shutdown(const rclcpp_lifecycle::State & /*state*/)
137 {
138  RCLCPP_INFO(get_logger(), "Shutting down");
139  return nav2_util::CallbackReturn::SUCCESS;
140 }
141 
143 {
144  std::string base_frame_id, odom_frame_id;
145  tf2::Duration transform_tolerance;
146  rclcpp::Duration source_timeout(2.0, 0.0);
147 
148  auto node = shared_from_this();
149 
150  nav2_util::declare_parameter_if_not_declared(
151  node, "frequency", rclcpp::ParameterValue(10.0));
152  frequency_ = get_parameter("frequency").as_double();
153  nav2_util::declare_parameter_if_not_declared(
154  node, "base_frame_id", rclcpp::ParameterValue("base_footprint"));
155  base_frame_id = get_parameter("base_frame_id").as_string();
156  nav2_util::declare_parameter_if_not_declared(
157  node, "odom_frame_id", rclcpp::ParameterValue("odom"));
158  odom_frame_id = get_parameter("odom_frame_id").as_string();
159  nav2_util::declare_parameter_if_not_declared(
160  node, "transform_tolerance", rclcpp::ParameterValue(0.1));
161  transform_tolerance =
162  tf2::durationFromSec(get_parameter("transform_tolerance").as_double());
163  nav2_util::declare_parameter_if_not_declared(
164  node, "source_timeout", rclcpp::ParameterValue(2.0));
165  source_timeout =
166  rclcpp::Duration::from_seconds(get_parameter("source_timeout").as_double());
167  nav2_util::declare_parameter_if_not_declared(
168  node, "base_shift_correction", rclcpp::ParameterValue(true));
169  const bool base_shift_correction =
170  get_parameter("base_shift_correction").as_bool();
171 
172  if (!configureSources(
173  base_frame_id, odom_frame_id, transform_tolerance, source_timeout,
174  base_shift_correction))
175  {
176  return false;
177  }
178 
179  if (!configurePolygons(base_frame_id, transform_tolerance)) {
180  return false;
181  }
182 
183  return true;
184 }
185 
187  const std::string & base_frame_id,
188  const tf2::Duration & transform_tolerance)
189 {
190  try {
191  auto node = shared_from_this();
192 
193  // Leave it to be not initialized: to intentionally cause an error if it will not set
194  nav2_util::declare_parameter_if_not_declared(
195  node, "polygons", rclcpp::PARAMETER_STRING_ARRAY);
196  std::vector<std::string> polygon_names = get_parameter("polygons").as_string_array();
197  for (std::string polygon_name : polygon_names) {
198  // Leave it not initialized: the will cause an error if it will not set
199  nav2_util::declare_parameter_if_not_declared(
200  node, polygon_name + ".type", rclcpp::PARAMETER_STRING);
201  const std::string polygon_type = get_parameter(polygon_name + ".type").as_string();
202 
203  if (polygon_type == "polygon") {
204  polygons_.push_back(
205  std::make_shared<Polygon>(
206  node, polygon_name, tf_buffer_, base_frame_id, transform_tolerance));
207  } else if (polygon_type == "circle") {
208  polygons_.push_back(
209  std::make_shared<Circle>(
210  node, polygon_name, tf_buffer_, base_frame_id, transform_tolerance));
211  } else if (polygon_type == "velocity_polygon") {
212  polygons_.push_back(
213  std::make_shared<VelocityPolygon>(
214  node, polygon_name, tf_buffer_, base_frame_id, transform_tolerance));
215  } else { // Error if something else
216  RCLCPP_ERROR(
217  get_logger(),
218  "[%s]: Unknown polygon type: %s",
219  polygon_name.c_str(), polygon_type.c_str());
220  return false;
221  }
222 
223  // Configure last added polygon
224  if (!polygons_.back()->configure()) {
225  return false;
226  }
227 
228  // warn if the added polygon's action_type_ is not different than "none"
229  auto action_type = polygons_.back()->getActionType();
230  if (action_type != DO_NOTHING) {
231  RCLCPP_ERROR(
232  get_logger(),
233  "[%s]: The action_type of the polygon is different than \"none\" which is "
234  "not supported in the collision detector.",
235  polygon_name.c_str());
236  return false;
237  }
238  }
239  } catch (const std::exception & ex) {
240  RCLCPP_ERROR(get_logger(), "Error while getting parameters: %s", ex.what());
241  return false;
242  }
243 
244  return true;
245 }
246 
248  const std::string & base_frame_id,
249  const std::string & odom_frame_id,
250  const tf2::Duration & transform_tolerance,
251  const rclcpp::Duration & source_timeout,
252  const bool base_shift_correction)
253 {
254  try {
255  auto node = shared_from_this();
256 
257  // Leave it to be not initialized to intentionally cause an error if it will not set
258  nav2_util::declare_parameter_if_not_declared(
259  node, "observation_sources", rclcpp::PARAMETER_STRING_ARRAY);
260  std::vector<std::string> source_names = get_parameter("observation_sources").as_string_array();
261  for (std::string source_name : source_names) {
262  nav2_util::declare_parameter_if_not_declared(
263  node, source_name + ".type",
264  rclcpp::ParameterValue("scan")); // Laser scanner by default
265  const std::string source_type = get_parameter(source_name + ".type").as_string();
266 
267  if (source_type == "scan") {
268  std::shared_ptr<Scan> s = std::make_shared<Scan>(
269  node, source_name, tf_buffer_, base_frame_id, odom_frame_id,
270  transform_tolerance, source_timeout, base_shift_correction);
271 
272  s->configure();
273 
274  sources_.push_back(s);
275  } else if (source_type == "pointcloud") {
276  std::shared_ptr<PointCloud> p = std::make_shared<PointCloud>(
277  node, source_name, tf_buffer_, base_frame_id, odom_frame_id,
278  transform_tolerance, source_timeout, base_shift_correction);
279 
280  p->configure();
281 
282  sources_.push_back(p);
283  } else if (source_type == "range") {
284  std::shared_ptr<Range> r = std::make_shared<Range>(
285  node, source_name, tf_buffer_, base_frame_id, odom_frame_id,
286  transform_tolerance, source_timeout, base_shift_correction);
287 
288  r->configure();
289 
290  sources_.push_back(r);
291  } else if (source_type == "polygon") {
292  std::shared_ptr<PolygonSource> ps = std::make_shared<PolygonSource>(
293  node, source_name, tf_buffer_, base_frame_id, odom_frame_id,
294  transform_tolerance, source_timeout, base_shift_correction);
295  ps->configure();
296 
297  sources_.push_back(ps);
298  } else { // Error if something else
299  RCLCPP_ERROR(
300  get_logger(),
301  "[%s]: Unknown source type: %s",
302  source_name.c_str(), source_type.c_str());
303  return false;
304  }
305  }
306  } catch (const std::exception & ex) {
307  RCLCPP_ERROR(get_logger(), "Error while getting parameters: %s", ex.what());
308  return false;
309  }
310 
311  return true;
312 }
313 
315 {
316  // Current timestamp for all inner routines prolongation
317  rclcpp::Time curr_time = this->now();
318 
319  // Points array collected from different data sources in a robot base frame
320  std::vector<Point> collision_points;
321 
322  std::unique_ptr<nav2_msgs::msg::CollisionDetectorState> state_msg =
323  std::make_unique<nav2_msgs::msg::CollisionDetectorState>();
324 
325  // Fill collision_points array from different data sources
326  for (std::shared_ptr<Source> source : sources_) {
327  if (source->getEnabled()) {
328  if (!source->getData(curr_time, collision_points) &&
329  source->getSourceTimeout().seconds() != 0.0)
330  {
331  RCLCPP_WARN(
332  get_logger(),
333  "Invalid source %s detected."
334  " Either due to data not published yet, or to lack of new data received within the"
335  " sensor timeout, or if impossible to transform data to base frame",
336  source->getSourceName().c_str());
337  }
338  }
339  }
340 
341  if (collision_points_marker_pub_->get_subscription_count() > 0) {
342  // visualize collision points with markers
343  auto marker_array = std::make_unique<visualization_msgs::msg::MarkerArray>();
344  visualization_msgs::msg::Marker marker;
345  marker.header.frame_id = get_parameter("base_frame_id").as_string();
346  marker.header.stamp = rclcpp::Time(0, 0);
347  marker.ns = "collision_points";
348  marker.id = 0;
349  marker.type = visualization_msgs::msg::Marker::POINTS;
350  marker.action = visualization_msgs::msg::Marker::ADD;
351  marker.scale.x = 0.02;
352  marker.scale.y = 0.02;
353  marker.color.r = 1.0;
354  marker.color.a = 1.0;
355  marker.lifetime = rclcpp::Duration(0, 0);
356  marker.frame_locked = true;
357 
358  for (const auto & point : collision_points) {
359  geometry_msgs::msg::Point p;
360  p.x = point.x;
361  p.y = point.y;
362  p.z = 0.0;
363  marker.points.push_back(p);
364  }
365  marker_array->markers.push_back(marker);
366  collision_points_marker_pub_->publish(std::move(marker_array));
367  }
368 
369  for (std::shared_ptr<Polygon> polygon : polygons_) {
370  if (!polygon->getEnabled()) {
371  continue;
372  }
373  state_msg->polygons.push_back(polygon->getName());
374  state_msg->detections.push_back(
375  polygon->getPointsInside(
376  collision_points) >= polygon->getMinPoints());
377  }
378 
379  state_pub_->publish(std::move(state_msg));
380 
381  // Publish polygons for better visualization
382  publishPolygons();
383 }
384 
386 {
387  for (std::shared_ptr<Polygon> polygon : polygons_) {
388  if (polygon->getEnabled()) {
389  polygon->publish();
390  }
391  }
392 }
393 
394 } // namespace nav2_collision_monitor
395 
396 #include "rclcpp_components/register_node_macro.hpp"
397 
398 // Register the component with class_loader.
399 // This acts as a sort of entry point, allowing the component to be discoverable when its library
400 // is being loaded into a running process.
401 RCLCPP_COMPONENTS_REGISTER_NODE(nav2_collision_monitor::CollisionDetector)
bool getParameters()
Supporting routine obtaining all ROS-parameters.
nav2_util::CallbackReturn on_cleanup(const rclcpp_lifecycle::State &state) override
: Resets all subscribers/publishers, polygons/data sources arrays
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.
nav2_util::CallbackReturn on_activate(const rclcpp_lifecycle::State &state) override
: Activates LifecyclePublishers, polygons and main processor, creates bond connection
nav2_util::CallbackReturn on_shutdown(const rclcpp_lifecycle::State &state) override
Called in shutdown state.
void publishPolygons() const
Polygons publishing routine. Made for visualization.
nav2_util::CallbackReturn on_deactivate(const rclcpp_lifecycle::State &state) override
: Deactivates LifecyclePublishers, polygons and main processor, destroys bond connection
~CollisionDetector()
Destructor for the nav2_collision_monitor::CollisionDetector.
rclcpp_lifecycle::LifecyclePublisher< nav2_msgs::msg::CollisionDetectorState >::SharedPtr state_pub_
collision monitor state publisher
std::vector< std::shared_ptr< Source > > sources_
Data sources array.
std::shared_ptr< tf2_ros::Buffer > tf_buffer_
TF buffer.
std::shared_ptr< tf2_ros::TransformListener > tf_listener_
TF listener.
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
std::vector< std::shared_ptr< Polygon > > polygons_
Polygons array.
nav2_util::CallbackReturn on_configure(const rclcpp_lifecycle::State &state) override
: Initializes and obtains ROS-parameters, creates main subscribers and publishers,...
rclcpp_lifecycle::LifecyclePublisher< visualization_msgs::msg::MarkerArray >::SharedPtr collision_points_marker_pub_
Collision points marker publisher.
std::shared_ptr< nav2_util::LifecycleNode > shared_from_this()
Get a shared pointer of this.
void createBond()
Create bond connection to lifecycle manager.
void destroyBond()
Destroy bond connection to lifecycle manager.