Nav2 Navigation Stack - rolling  main
ROS 2 Navigation Stack
costmap_2d_ros.cpp
1 /*********************************************************************
2  *
3  * Software License Agreement (BSD License)
4  *
5  * Copyright (c) 2008, 2013, Willow Garage, Inc.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * * Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  * * Redistributions in binary form must reproduce the above
15  * copyright notice, this list of conditions and the following
16  * disclaimer in the documentation and/or other materials provided
17  * with the distribution.
18  * * Neither the name of Willow Garage, Inc. nor the names of its
19  * contributors may be used to endorse or promote products derived
20  * from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33  * POSSIBILITY OF SUCH DAMAGE.
34  *
35  * Author: Eitan Marder-Eppstein
36  * David V. Lu!!
37  *********************************************************************/
38 
39 #include "nav2_costmap_2d/costmap_2d_ros.hpp"
40 
41 #include <memory>
42 #include <chrono>
43 #include <cmath>
44 #include <limits>
45 #include <stdexcept>
46 #include <string>
47 #include <vector>
48 #include <utility>
49 
50 #include "nav2_costmap_2d/layered_costmap.hpp"
51 #include "nav2_util/execution_timer.hpp"
52 #include "nav2_ros_common/node_utils.hpp"
53 #include "nav2_ros_common/rate.hpp"
54 #include "tf2_geometry_msgs/tf2_geometry_msgs.hpp"
55 #include "nav2_ros_common/tf2_factories.hpp"
56 #include "nav2_util/robot_utils.hpp"
57 #include "rcl_interfaces/msg/set_parameters_result.hpp"
58 
59 using namespace std::chrono_literals;
60 using std::placeholders::_1;
61 using rcl_interfaces::msg::ParameterType;
62 
63 namespace nav2_costmap_2d
64 {
65 Costmap2DROS::Costmap2DROS(const rclcpp::NodeOptions & options)
66 : nav2::LifecycleNode("costmap", "", options),
67  name_("costmap"),
68  default_plugins_{"static_layer", "obstacle_layer", "inflation_layer"},
69  default_types_{
70  "nav2_costmap_2d::StaticLayer",
71  "nav2_costmap_2d::ObstacleLayer",
72  "nav2_costmap_2d::InflationLayer"}
73 {
74  is_lifecycle_follower_ = false;
75  init();
76 }
77 
78 rclcpp::NodeOptions getChildNodeOptions(
79  const std::string & name,
80  const std::string & parent_namespace,
81  const bool & use_sim_time,
82  const rclcpp::NodeOptions & parent_options)
83 {
84  std::vector<std::string> new_arguments = parent_options.arguments();
85  bool use_intra_process_comms = parent_options.use_intra_process_comms();
86  nav2::replaceOrAddArgument(
87  new_arguments, "-r", "__ns",
88  "__ns:=" + nav2::add_namespaces(parent_namespace, name));
89  nav2::replaceOrAddArgument(new_arguments, "-r", "__node", name + ":" + "__node:=" + name);
90  nav2::replaceOrAddArgument(
91  new_arguments, "-p", "use_sim_time",
92  "use_sim_time:=" + std::string(use_sim_time ? "true" : "false"));
93  return rclcpp::NodeOptions().use_intra_process_comms(use_intra_process_comms).arguments(
94  new_arguments);
95 }
96 
98  const std::string & name,
99  const std::string & parent_namespace,
100  const bool & use_sim_time,
101  const rclcpp::NodeOptions & options)
102 : nav2::LifecycleNode(name, "",
103  getChildNodeOptions(name, parent_namespace, use_sim_time, options)
104 ),
105  name_(name),
106  default_plugins_{"static_layer", "obstacle_layer", "inflation_layer"},
107  default_types_{
108  "nav2_costmap_2d::StaticLayer",
109  "nav2_costmap_2d::ObstacleLayer",
110  "nav2_costmap_2d::InflationLayer"}
111 {
112  init();
113 }
114 
116 {
117  RCLCPP_INFO(get_logger(), "Creating Costmap");
118  declare_parameter("lethal_cost_threshold", rclcpp::ParameterValue(100));
119  declare_parameter("trinary_costmap", rclcpp::ParameterValue(true));
120  declare_parameter("unknown_cost_value", rclcpp::ParameterValue(static_cast<unsigned char>(0xff)));
121  declare_parameter("inscribed_obstacle_cost_value", rclcpp::ParameterValue(99));
122  declare_parameter("use_maximum", rclcpp::ParameterValue(false));
123 }
124 
126 {
127 }
128 
129 nav2::CallbackReturn
130 Costmap2DROS::on_configure(const rclcpp_lifecycle::State & /*state*/)
131 {
132  RCLCPP_INFO(get_logger(), "Configuring");
133  try {
134  getParameters();
135  } catch (const std::exception & e) {
136  RCLCPP_ERROR(
137  get_logger(), "Failed to configure costmap! %s.", e.what());
138  return nav2::CallbackReturn::FAILURE;
139  }
140 
141  callback_group_ = create_callback_group(
142  rclcpp::CallbackGroupType::MutuallyExclusive, false);
143 
144  // Create the costmap itself
145  layered_costmap_ = std::make_unique<LayeredCostmap>(
146  global_frame_, rolling_window_, track_unknown_space_);
147 
148  if (!layered_costmap_->isSizeLocked()) {
149  layered_costmap_->resizeMap(
150  (unsigned int)(map_width_meters_ / resolution_),
151  (unsigned int)(map_height_meters_ / resolution_), resolution_, origin_x_, origin_y_);
152  }
153 
154  // Create the transform-related objects
155  tf_buffer_ = nav2::create_transform_buffer(this, callback_group_);
156  tf_listener_ = nav2::create_transform_listener(*tf_buffer_);
157 
158  // Then load and add the plug-ins to the costmap
159  for (unsigned int i = 0; i < plugin_names_.size(); ++i) {
160  RCLCPP_INFO(get_logger(), "Using plugin \"%s\"", plugin_names_[i].c_str());
161 
162  std::shared_ptr<Layer> plugin = plugin_loader_.createSharedInstance(plugin_types_[i]);
163 
164  layered_costmap_->addPlugin(plugin);
165 
166  try {
167  plugin->initialize(
168  layered_costmap_.get(), plugin_names_[i], tf_buffer_.get(),
169  shared_from_this(), callback_group_);
170  } catch (const std::exception & e) {
171  RCLCPP_ERROR(
172  get_logger(), "Failed to initialize costmap plugin %s! %s.",
173  plugin_names_[i].c_str(), e.what());
174  return nav2::CallbackReturn::FAILURE;
175  }
176 
177  RCLCPP_INFO(get_logger(), "Initialized plugin \"%s\"", plugin_names_[i].c_str());
178  }
179  // and costmap filters as well
180  for (unsigned int i = 0; i < filter_names_.size(); ++i) {
181  RCLCPP_INFO(get_logger(), "Using costmap filter \"%s\"", filter_names_[i].c_str());
182 
183  std::shared_ptr<Layer> filter = plugin_loader_.createSharedInstance(filter_types_[i]);
184 
185  layered_costmap_->addFilter(filter);
186 
187  filter->initialize(
188  layered_costmap_.get(), filter_names_[i], tf_buffer_.get(),
189  shared_from_this(), callback_group_);
190 
191  RCLCPP_INFO(get_logger(), "Initialized costmap filter \"%s\"", filter_names_[i].c_str());
192  }
193 
194  // Create the publishers and subscribers
196  footprint_stamped_sub_ = create_subscription<geometry_msgs::msg::PolygonStamped>(
197  "footprint", [this](const geometry_msgs::msg::PolygonStamped::ConstSharedPtr & footprint)
198  {setRobotFootprintPolygon(footprint->polygon);});
199  } else {
200  footprint_sub_ = create_subscription<geometry_msgs::msg::Polygon>(
201  "footprint", [this](const geometry_msgs::msg::Polygon::ConstSharedPtr & footprint)
202  {setRobotFootprintPolygon(*footprint);});
203  }
204 
205  footprint_pub_ = create_publisher<geometry_msgs::msg::PolygonStamped>(
206  "published_footprint");
207 
208  costmap_publisher_ = std::make_unique<Costmap2DPublisher>(
210  layered_costmap_->getCostmap(), global_frame_,
211  "costmap", always_send_full_costmap_, map_vis_z_);
212 
213  auto layers = layered_costmap_->getPlugins();
214 
215  for (auto & layer : *layers) {
216  auto costmap_layer = std::dynamic_pointer_cast<CostmapLayer>(layer);
217  if (costmap_layer != nullptr) {
218  layer_publishers_.emplace_back(
219  std::make_unique<Costmap2DPublisher>(
221  costmap_layer.get(), global_frame_,
222  layer->getName(), always_send_full_costmap_, map_vis_z_)
223  );
224  }
225  }
226 
227  // Set the footprint
228  if (use_radius_) {
230  } else {
231  std::vector<geometry_msgs::msg::Point> new_footprint;
232  makeFootprintFromString(footprint_, new_footprint);
233  setRobotFootprint(new_footprint);
234  }
235 
236  // Service to get the cost at a point
237  get_cost_service_ = create_service<nav2_msgs::srv::GetCosts>(
238  std::string("get_cost_") + get_name(),
239  std::bind(
240  &Costmap2DROS::getCostsCallback, this, std::placeholders::_1, std::placeholders::_2,
241  std::placeholders::_3));
242 
243  // Add cleaning service
244  clear_costmap_service_ = std::make_unique<ClearCostmapService>(shared_from_this(), *this);
245 
246  executor_ = std::make_shared<rclcpp::executors::SingleThreadedExecutor>();
247  executor_->add_callback_group(callback_group_, get_node_base_interface());
248  executor_thread_ = std::make_unique<nav2::NodeThread>(executor_);
249  return nav2::CallbackReturn::SUCCESS;
250 }
251 
252 nav2::CallbackReturn
253 Costmap2DROS::on_activate(const rclcpp_lifecycle::State & /*state*/)
254 {
255  RCLCPP_INFO(get_logger(), "Activating");
256 
257  // First, make sure that the transform between the robot base frame
258  // and the global frame is available
259 
260  std::string tf_error;
261 
262  RCLCPP_INFO(get_logger(), "Checking transform");
263  rclcpp::Rate r(2);
264  const auto initial_transform_timeout = rclcpp::Duration::from_seconds(
266  const auto initial_transform_timeout_point = now() + initial_transform_timeout;
267  while (rclcpp::ok() &&
268  !tf_buffer_->canTransform(
269  global_frame_, robot_base_frame_, tf2::TimePointZero, &tf_error))
270  {
271  RCLCPP_INFO(
272  get_logger(), "Timed out waiting for transform from %s to %s"
273  " to become available, tf error: %s",
274  robot_base_frame_.c_str(), global_frame_.c_str(), tf_error.c_str());
275 
276  // Check timeout
277  if (now() > initial_transform_timeout_point) {
278  RCLCPP_ERROR(
279  get_logger(),
280  "Failed to activate %s because "
281  "transform from %s to %s did not become available before timeout",
282  get_name(), robot_base_frame_.c_str(), global_frame_.c_str());
283 
284  return nav2::CallbackReturn::FAILURE;
285  }
286 
287  // The error string will accumulate and errors will typically be the same, so the last
288  // will do for the warning above. Reset the string here to avoid accumulation
289  tf_error.clear();
290  r.sleep();
291  }
292 
293  // Activate publishers
294  footprint_pub_->on_activate();
295  costmap_publisher_->on_activate();
296 
297  for (auto & layer_pub : layer_publishers_) {
298  layer_pub->on_activate();
299  }
300 
301  // Create a thread to handle updating the map
302  stopped_ = true; // to active plugins
303  stop_updates_ = false;
304  map_update_thread_shutdown_ = false;
305  map_update_thread_ = std::make_unique<std::thread>(
306  std::bind(&Costmap2DROS::mapUpdateLoop, this, map_update_frequency_));
307 
308  start();
309 
310  // Add callback for dynamic parameters
311  post_set_params_handler_ = this->add_post_set_parameters_callback(
312  std::bind(
314  this, std::placeholders::_1));
315  on_set_params_handler = this->add_on_set_parameters_callback(
316  std::bind(&Costmap2DROS::validateParameterUpdatesCallback, this, _1));
317 
318  return nav2::CallbackReturn::SUCCESS;
319 }
320 
321 nav2::CallbackReturn
322 Costmap2DROS::on_deactivate(const rclcpp_lifecycle::State & /*state*/)
323 {
324  RCLCPP_INFO(get_logger(), "Deactivating");
325 
326  remove_post_set_parameters_callback(post_set_params_handler_.get());
327  post_set_params_handler_.reset();
328  remove_on_set_parameters_callback(on_set_params_handler.get());
329  on_set_params_handler.reset();
330 
331  stop();
332 
333  // Map thread stuff
334  map_update_thread_shutdown_ = true;
335 
336  if (map_update_thread_->joinable()) {
337  map_update_thread_->join();
338  }
339 
340  footprint_pub_->on_deactivate();
341  costmap_publisher_->on_deactivate();
342 
343  for (auto & layer_pub : layer_publishers_) {
344  layer_pub->on_deactivate();
345  }
346 
347  return nav2::CallbackReturn::SUCCESS;
348 }
349 
350 nav2::CallbackReturn
351 Costmap2DROS::on_cleanup(const rclcpp_lifecycle::State & /*state*/)
352 {
353  RCLCPP_INFO(get_logger(), "Cleaning up");
354  executor_thread_.reset();
355  get_cost_service_.reset();
356  costmap_publisher_.reset();
357  clear_costmap_service_.reset();
358 
359  layer_publishers_.clear();
360 
361  layered_costmap_.reset();
362 
363  tf_listener_.reset();
364  tf_buffer_.reset();
365 
366  footprint_sub_.reset();
367  footprint_pub_.reset();
368 
369  return nav2::CallbackReturn::SUCCESS;
370 }
371 
372 nav2::CallbackReturn
373 Costmap2DROS::on_shutdown(const rclcpp_lifecycle::State &)
374 {
375  RCLCPP_INFO(get_logger(), "Shutting down");
376  return nav2::CallbackReturn::SUCCESS;
377 }
378 
379 void
381 {
382  RCLCPP_DEBUG(get_logger(), " getParameters");
383 
384  // Get all of the required parameters
385  always_send_full_costmap_ = declare_or_get_parameter(
386  "always_send_full_costmap", false);
387  map_vis_z_ = declare_or_get_parameter("map_vis_z", 0.0);
388  footprint_padding_ = declare_or_get_parameter("footprint_padding", 0.01f);
389  footprint_ = declare_or_get_parameter(
390  "footprint", std::string("[]"));
392  "global_frame", std::string("map"));
393  map_height_meters_ = declare_or_get_parameter(
394  "height", 5);
395  map_width_meters_ = declare_or_get_parameter(
396  "width", 5);
397  origin_x_ = declare_or_get_parameter(
398  "origin_x", 0.0);
399  origin_y_ = declare_or_get_parameter(
400  "origin_y", 0.0);
401  plugin_names_ = declare_or_get_parameter(
402  "plugins", default_plugins_);
403  filter_names_ = declare_or_get_parameter(
404  "filters", std::vector<std::string>());
405  map_publish_frequency_ = declare_or_get_parameter(
406  "publish_frequency", 1.0);
407  resolution_ = declare_or_get_parameter(
408  "resolution", 0.1);
410  "robot_base_frame", std::string("base_link"));
411  robot_radius_ = declare_or_get_parameter(
412  "robot_radius", 0.1);
414  "rolling_window", false);
415  track_unknown_space_ = declare_or_get_parameter(
416  "track_unknown_space", false);
418  "transform_tolerance", 0.3);
420  "transform_staleness_threshold", 0.0);
422  "initial_transform_timeout", 60.0);
423  map_update_frequency_ = declare_or_get_parameter(
424  "update_frequency", 5.0);
426  "subscribe_to_stamped_footprint", false);
427 
428  auto node = shared_from_this();
429 
430  if (plugin_names_ == default_plugins_) {
431  for (size_t i = 0; i < default_plugins_.size(); ++i) {
432  nav2::declare_parameter_if_not_declared(
433  node, default_plugins_[i] + ".plugin", rclcpp::ParameterValue(default_types_[i]));
434  }
435  }
436  plugin_types_.resize(plugin_names_.size());
437  filter_types_.resize(filter_names_.size());
438 
439  // 1. All plugins must have 'plugin' param defined in their namespace to define the plugin type
440  for (size_t i = 0; i < plugin_names_.size(); ++i) {
441  plugin_types_[i] = nav2::get_plugin_type_param(node, plugin_names_[i]);
442  }
443  for (size_t i = 0; i < filter_names_.size(); ++i) {
444  filter_types_[i] = nav2::get_plugin_type_param(node, filter_names_[i]);
445  }
446 
447  // 2. The map publish frequency cannot be 0 (to avoid a divide-by-zero)
448  if (map_publish_frequency_ > 0) {
449  publish_cycle_ = rclcpp::Duration::from_seconds(1 / map_publish_frequency_);
450  } else {
451  publish_cycle_ = rclcpp::Duration(-1s);
452  }
453 
454  // 3. If the footprint has been specified, it must be in the correct format
455  use_radius_ = true;
456 
457  if (footprint_ != "" && footprint_ != "[]") {
458  // Footprint parameter has been specified, try to convert it
459  std::vector<geometry_msgs::msg::Point> new_footprint;
460  if (makeFootprintFromString(footprint_, new_footprint)) {
461  // The specified footprint is valid, so we'll use that instead of the radius
462  use_radius_ = false;
463  } else {
464  // Footprint provided but invalid, so stay with the radius
465  RCLCPP_ERROR(
466  get_logger(), "The footprint parameter is invalid: \"%s\", using radius (%lf) instead",
467  footprint_.c_str(), robot_radius_);
468  }
469  }
470 
471  // 4. The width, height, and resolution of map cannot be negative or 0
472  // (to avoid abnormal memory usage)
473  if (map_width_meters_ <= 0) {
474  throw std::invalid_argument(
475  "You try to set width of map to be negative or zero, "
476  "this isn't allowed, please give a positive value.");
477  }
478  if (map_height_meters_ <= 0) {
479  throw std::invalid_argument(
480  "You try to set height of map to be negative or zero, "
481  "this isn't allowed, please give a positive value.");
482  }
483  if (resolution_ <= 0.0) {
484  throw std::invalid_argument(
485  "Costmap resolution must be greater than zero.");
486  }
487 
488  const double size_x = map_width_meters_ / resolution_;
489  const double size_y = map_height_meters_ / resolution_;
490  const double max_cells = std::numeric_limits<int>::max();
491  if (size_x > max_cells / size_y) {
492  throw std::invalid_argument("Costmap cell count exceeds the supported signed index range");
493  }
494 }
495 
496 void
497 Costmap2DROS::setRobotFootprint(const std::vector<geometry_msgs::msg::Point> & points)
498 {
499  if (points.empty()) {
500  RCLCPP_ERROR(
501  get_logger(), "You try to set an empty footprint"
502  " this isn't allowed, a footprint must contain at least one point.");
503  return;
504  }
505  auto padded = std::make_shared<std::vector<geometry_msgs::msg::Point>>(points);
506  padFootprint(*padded, footprint_padding_);
507 
508 #ifdef __cpp_lib_atomic_shared_ptr
509  unpadded_footprint_.store(std::make_shared<std::vector<geometry_msgs::msg::Point>>(points));
510  padded_footprint_.store(padded);
511 #else
512  std::atomic_store(
513  &unpadded_footprint_, std::make_shared<std::vector<geometry_msgs::msg::Point>>(points));
514  std::atomic_store(&padded_footprint_, padded);
515 #endif
516  layered_costmap_->setFootprint(*padded);
517 }
518 
519 void
521  const geometry_msgs::msg::Polygon & footprint)
522 {
523  setRobotFootprint(toPointVector(footprint));
524 }
525 
526 void
527 Costmap2DROS::getOrientedFootprint(std::vector<geometry_msgs::msg::Point> & oriented_footprint)
528 {
529  geometry_msgs::msg::PoseStamped global_pose;
530  if (!getRobotPose(global_pose)) {
531  return;
532  }
533 
534  double yaw = tf2::getYaw(global_pose.pose.orientation);
535 #ifdef __cpp_lib_atomic_shared_ptr
536  auto padded_footprint = padded_footprint_.load();
537 #else
538  auto padded_footprint = std::atomic_load(&padded_footprint_);
539 #endif
541  global_pose.pose.position.x, global_pose.pose.position.y, yaw,
542  *padded_footprint, oriented_footprint);
543 }
544 
545 void
547 {
548  RCLCPP_DEBUG(get_logger(), "mapUpdateLoop frequency: %lf", frequency);
549 
550  // the user might not want to run the loop every cycle
551  if (frequency == 0.0) {
552  return;
553  }
554 
555  RCLCPP_DEBUG(get_logger(), "Entering loop");
556 
557  nav2::Rate r(this, frequency); // 200ms by default
558 
559  while (rclcpp::ok() && !map_update_thread_shutdown_) {
561 
562  // Execute after start() will complete plugins activation
563  if (!stopped_) {
564  // Lock while modifying layered costmap and publishing values
565  std::scoped_lock<std::mutex> lock(_dynamic_parameter_mutex);
566 
567  // Measure the execution time of the updateMap method
568  timer.start();
569  updateMap();
570  timer.end();
571 
572  RCLCPP_DEBUG(get_logger(), "Map update time: %.9f", timer.elapsed_time_in_seconds());
573  if (publish_cycle_ > rclcpp::Duration(0s) && layered_costmap_->isInitialized()) {
574  unsigned int x0, y0, xn, yn;
575  layered_costmap_->getBounds(&x0, &xn, &y0, &yn);
576  costmap_publisher_->updateBounds(x0, xn, y0, yn);
577 
578  for (auto & layer_pub : layer_publishers_) {
579  layer_pub->updateBounds(x0, xn, y0, yn);
580  }
581 
582  auto current_time = now();
583  const bool publish_due = last_publish_ + publish_cycle_ < current_time ||
584  current_time < last_publish_; // Time moved backwards, e.g. switching to sim_time.
585  if (publish_due || costmap_publisher_->isRepublishRequested()) {
586  RCLCPP_DEBUG(get_logger(), "Publish costmap at %s", name_.c_str());
587  costmap_publisher_->publishCostmap();
588  }
589 
590  for (auto & layer_pub : layer_publishers_) {
591  if (publish_due || layer_pub->isRepublishRequested()) {
592  layer_pub->publishCostmap();
593  }
594  }
595 
596  // Subscriber requests do not postpone the regular publication schedule.
597  if (publish_due) {
598  last_publish_ = current_time;
599  }
600  }
601  }
602 
603  // Make sure to sleep for the remainder of our cycle time
604  r.sleep();
605 
606 #if 0
607  // TODO(bpwilcox): find ROS2 equivalent or port for r.cycletime()
608  if (r.period() > tf2::durationFromSec(1 / frequency)) {
609  RCLCPP_WARN(
610  get_logger(),
611  "Costmap2DROS: Map update loop missed its desired rate of %.4fHz... "
612  "the loop actually took %.4f seconds", frequency, r.period());
613  }
614 #endif
615  }
616 }
617 
618 void
620 {
621  RCLCPP_DEBUG(get_logger(), "Updating map...");
622 
623  if (!stop_updates_) {
624  // get global pose
625  geometry_msgs::msg::PoseStamped pose;
626  if (getRobotPose(pose)) {
627  const double & x = pose.pose.position.x;
628  const double & y = pose.pose.position.y;
629  const double yaw = tf2::getYaw(pose.pose.orientation);
630  layered_costmap_->updateMap(x, y, yaw);
631 
632  auto footprint = std::make_unique<geometry_msgs::msg::PolygonStamped>();
633  footprint->header = pose.header;
634 #ifdef __cpp_lib_atomic_shared_ptr
635  auto padded_footprint = padded_footprint_.load();
636 #else
637  auto padded_footprint = std::atomic_load(&padded_footprint_);
638 #endif
639  transformFootprint(x, y, yaw, *padded_footprint, *footprint);
640 
641  RCLCPP_DEBUG(get_logger(), "Publishing footprint");
642  footprint_pub_->publish(std::move(footprint));
643  initialized_ = true;
644  }
645  }
646 }
647 
648 void
649 Costmap2DROS::waitUntilCurrent(const rclcpp::Duration & timeout)
650 {
651  rclcpp::Rate r(100);
652  auto waiting_start = now();
653  while (!isCurrent()) {
654  if (now() - waiting_start > timeout) {
655  throw std::runtime_error("Costmap timed out waiting for update");
656  }
657  r.sleep();
658  }
659 }
660 
661 void
663 {
664  RCLCPP_INFO(get_logger(), "start");
665  std::vector<std::shared_ptr<Layer>> * plugins = layered_costmap_->getPlugins();
666  std::vector<std::shared_ptr<Layer>> * filters = layered_costmap_->getFilters();
667 
668  // check if we're stopped or just paused
669  if (stopped_) {
670  // if we're stopped we need to re-subscribe to topics
671  for (std::vector<std::shared_ptr<Layer>>::iterator plugin = plugins->begin();
672  plugin != plugins->end();
673  ++plugin)
674  {
675  (*plugin)->activate();
676  }
677  for (std::vector<std::shared_ptr<Layer>>::iterator filter = filters->begin();
678  filter != filters->end();
679  ++filter)
680  {
681  (*filter)->activate();
682  }
683  stopped_ = false;
684  }
685  stop_updates_ = false;
686 
687  // block until the costmap is re-initialized.. meaning one update cycle has run
688  rclcpp::Rate r(20.0);
689  while (rclcpp::ok() && !initialized_) {
690  RCLCPP_DEBUG(get_logger(), "Sleeping, waiting for initialized_");
691  r.sleep();
692  }
693 }
694 
695 void
697 {
698  stop_updates_ = true;
699 
700  // layered_costmap_ is set only if on_configure has been called
701  if (layered_costmap_) {
702  std::vector<std::shared_ptr<Layer>> * plugins = layered_costmap_->getPlugins();
703  std::vector<std::shared_ptr<Layer>> * filters = layered_costmap_->getFilters();
704 
705  // unsubscribe from topics
706  for (std::vector<std::shared_ptr<Layer>>::iterator plugin = plugins->begin();
707  plugin != plugins->end(); ++plugin)
708  {
709  (*plugin)->deactivate();
710  }
711  for (std::vector<std::shared_ptr<Layer>>::iterator filter = filters->begin();
712  filter != filters->end(); ++filter)
713  {
714  (*filter)->deactivate();
715  }
716  }
717  initialized_ = false;
718  stopped_ = true;
719 }
720 
721 void
723 {
724  stop_updates_ = true;
725  initialized_ = false;
726 }
727 
728 void
730 {
731  stop_updates_ = false;
732 
733  // block until the costmap is re-initialized.. meaning one update cycle has run
734  rclcpp::Rate r(100.0);
735  while (!initialized_) {
736  r.sleep();
737  }
738 }
739 
740 void
742 {
743  Costmap2D * top = layered_costmap_->getCostmap();
744  top->resetMap(0, 0, top->getSizeInCellsX(), top->getSizeInCellsY());
745 
746  // Reset each of the plugins
747  std::vector<std::shared_ptr<Layer>> * plugins = layered_costmap_->getPlugins();
748  std::vector<std::shared_ptr<Layer>> * filters = layered_costmap_->getFilters();
749  for (std::vector<std::shared_ptr<Layer>>::iterator plugin = plugins->begin();
750  plugin != plugins->end(); ++plugin)
751  {
752  (*plugin)->reset();
753  }
754  for (std::vector<std::shared_ptr<Layer>>::iterator filter = filters->begin();
755  filter != filters->end(); ++filter)
756  {
757  (*filter)->reset();
758  }
759 }
760 
761 bool
762 Costmap2DROS::getRobotPose(geometry_msgs::msg::PoseStamped & global_pose)
763 {
764  if (!nav2_util::getFreshPose(
765  *tf_buffer_, global_frame_, robot_base_frame_, now(),
766  transform_staleness_threshold_, global_pose))
767  {
768  return false;
769  }
770  return true;
771 }
772 
773 bool
775  const geometry_msgs::msg::PoseStamped & input_pose,
776  geometry_msgs::msg::PoseStamped & transformed_pose)
777 {
778  if (input_pose.header.frame_id == global_frame_) {
779  transformed_pose = input_pose;
780  return true;
781  } else {
782  return nav2_util::transformPoseInTargetFrame(
783  input_pose, transformed_pose, *tf_buffer_,
785  }
786 }
787 
788 rcl_interfaces::msg::SetParametersResult Costmap2DROS::validateParameterUpdatesCallback(
789  const std::vector<rclcpp::Parameter> & parameters)
790 {
791  rcl_interfaces::msg::SetParametersResult result;
792  result.successful = true;
793 
794  for (const auto & parameter : parameters) {
795  const auto & param_type = parameter.get_type();
796  const auto & param_name = parameter.get_name();
797  if (param_name.find('.') != std::string::npos) {
798  continue;
799  }
800  if (param_type == ParameterType::PARAMETER_DOUBLE) {
801  if (parameter.as_double() <= 0.0 &&
802  (param_name == "resolution" || param_name == "publish_frequency"))
803  {
804  RCLCPP_WARN(
805  get_logger(), "The value of parameter '%s' is incorrectly set to %f, "
806  "it should be >0. Ignoring parameter update.",
807  param_name.c_str(), parameter.as_double());
808  result.successful = false;
809  } else if (parameter.as_double() < 0.0 && // NOLINT
810  (param_name != "origin_x" && param_name != "origin_y"))
811  {
812  RCLCPP_WARN(
813  get_logger(), "The value of parameter '%s' is incorrectly set to %f, "
814  "it should be >0. Ignoring parameter update.",
815  param_name.c_str(), parameter.as_double());
816  result.successful = false;
817  }
818  } else if (param_type == ParameterType::PARAMETER_INTEGER) {
819  if (parameter.as_int() <= 0.0) {
820  RCLCPP_WARN(
821  get_logger(), "The value of parameter '%s' is incorrectly set to %ld, "
822  "it should be >0. Ignoring parameter update.",
823  param_name.c_str(), parameter.as_int());
824  result.successful = false;
825  }
826  } else if (param_type == ParameterType::PARAMETER_STRING && param_name == "robot_base_frame") {
827  // First, make sure that the transform between the robot base frame
828  // and the global frame is available
829  std::string tf_error;
830  RCLCPP_INFO(get_logger(), "Checking transform");
831  if (!tf_buffer_->canTransform(
832  global_frame_, parameter.as_string(), tf2::TimePointZero,
833  tf2::durationFromSec(1.0), &tf_error))
834  {
835  RCLCPP_WARN(
836  get_logger(), "Timed out waiting for transform from %s to %s"
837  " to become available, tf error: %s",
838  parameter.as_string().c_str(), global_frame_.c_str(), tf_error.c_str());
839  RCLCPP_WARN(
840  get_logger(), "Rejecting robot_base_frame change to %s , leaving it to its original"
841  " value of %s", parameter.as_string().c_str(), robot_base_frame_.c_str());
842  result.successful = false;
843  }
844  }
845  }
846 
847  return result;
848 }
849 
850 void
851 Costmap2DROS::updateParametersCallback(const std::vector<rclcpp::Parameter> & parameters)
852 {
853  bool resize_map = false;
854  std::lock_guard<std::mutex> lock_reinit(_dynamic_parameter_mutex);
855 
856  for (const auto & parameter : parameters) {
857  const auto & param_type = parameter.get_type();
858  const auto & param_name = parameter.get_name();
859  if (param_name.find('.') != std::string::npos) {
860  continue;
861  }
862 
863  if (param_type == ParameterType::PARAMETER_DOUBLE) {
864  if (param_name == "robot_radius") {
865  robot_radius_ = parameter.as_double();
866  // Set the footprint
867  if (use_radius_) {
869  }
870  } else if (param_name == "footprint_padding") {
871  footprint_padding_ = parameter.as_double();
872 #ifdef __cpp_lib_atomic_shared_ptr
873  auto padded = std::make_shared<std::vector<geometry_msgs::msg::Point>>(
874  *unpadded_footprint_.load());
875  padFootprint(*padded, footprint_padding_);
876  padded_footprint_.store(padded);
877 #else
878  auto padded = std::make_shared<std::vector<geometry_msgs::msg::Point>>(
879  *std::atomic_load(&unpadded_footprint_));
880  padFootprint(*padded, footprint_padding_);
881  std::atomic_store(&padded_footprint_, padded);
882 #endif
883  layered_costmap_->setFootprint(*padded);
884  } else if (param_name == "transform_tolerance") {
885  transform_tolerance_ = parameter.as_double();
886  } else if (param_name == "transform_staleness_threshold") {
887  transform_staleness_threshold_ = parameter.as_double();
888  } else if (param_name == "publish_frequency") {
889  map_publish_frequency_ = parameter.as_double();
890  publish_cycle_ = rclcpp::Duration::from_seconds(1 / map_publish_frequency_);
891  } else if (param_name == "resolution") {
892  resize_map = true;
893  resolution_ = parameter.as_double();
894  } else if (param_name == "origin_x") {
895  resize_map = true;
896  origin_x_ = parameter.as_double();
897  } else if (param_name == "origin_y") {
898  resize_map = true;
899  origin_y_ = parameter.as_double();
900  }
901  } else if (param_type == ParameterType::PARAMETER_INTEGER) {
902  if (param_name == "width") {
903  resize_map = true;
904  map_width_meters_ = parameter.as_int();
905  } else if (param_name == "height") {
906  resize_map = true;
907  map_height_meters_ = parameter.as_int();
908  }
909  } else if (param_type == ParameterType::PARAMETER_STRING) {
910  if (param_name == "footprint") {
911  footprint_ = parameter.as_string();
912  std::vector<geometry_msgs::msg::Point> new_footprint;
913  if (makeFootprintFromString(footprint_, new_footprint)) {
914  setRobotFootprint(new_footprint);
915  }
916  } else if (param_name == "robot_base_frame") {
917  robot_base_frame_ = parameter.as_string();
918  }
919  }
920  }
921 
922  if (resize_map && !layered_costmap_->isSizeLocked()) {
923  layered_costmap_->resizeMap(
924  (unsigned int)(map_width_meters_ / resolution_),
925  (unsigned int)(map_height_meters_ / resolution_), resolution_, origin_x_, origin_y_);
926  updateMap();
927  }
928 }
929 
931  const std::shared_ptr<rmw_request_id_t>,
932  const std::shared_ptr<nav2_msgs::srv::GetCosts::Request> request,
933  const std::shared_ptr<nav2_msgs::srv::GetCosts::Response> response)
934 {
935  unsigned int mx, my;
936 
937  Costmap2D * costmap = layered_costmap_->getCostmap();
938  std::unique_lock<Costmap2D::mutex_t> lock(*(costmap->getMutex()));
939  response->success = true;
940  for (const auto & pose : request->poses) {
941  geometry_msgs::msg::PoseStamped pose_transformed;
942  if (!transformPoseToGlobalFrame(pose, pose_transformed)) {
943  RCLCPP_ERROR(
944  get_logger(), "Failed to transform, cannot get cost for pose (%.2f, %.2f)",
945  pose.pose.position.x, pose.pose.position.y);
946  response->success = false;
947  response->costs.push_back(NO_INFORMATION);
948  continue;
949  }
950  double yaw = tf2::getYaw(pose_transformed.pose.orientation);
951 
952  if (request->use_footprint) {
953  Footprint footprint = layered_costmap_->getFootprint();
954  FootprintCollisionChecker<Costmap2D *> collision_checker(costmap);
955 
956  RCLCPP_DEBUG(
957  get_logger(), "Received request to get cost at footprint pose (%.2f, %.2f, %.2f)",
958  pose_transformed.pose.position.x, pose_transformed.pose.position.y, yaw);
959 
960  response->costs.push_back(
961  collision_checker.footprintCostAtPose(
962  pose_transformed.pose.position.x,
963  pose_transformed.pose.position.y, yaw, footprint));
964  } else {
965  RCLCPP_DEBUG(
966  get_logger(), "Received request to get cost at point (%f, %f)",
967  pose_transformed.pose.position.x,
968  pose_transformed.pose.position.y);
969 
970  bool in_bounds = costmap->worldToMap(
971  pose_transformed.pose.position.x,
972  pose_transformed.pose.position.y, mx, my);
973 
974  if (!in_bounds) {
975  response->success = false;
976  response->costs.push_back(LETHAL_OBSTACLE);
977  continue;
978  }
979  // Get the cost at the map coordinates
980  response->costs.push_back(static_cast<float>(costmap->getCost(mx, my)));
981  }
982  }
983 }
984 
985 } // namespace nav2_costmap_2d
nav2::LifecycleNode::SharedPtr shared_from_this()
Get a shared pointer of this.
ParameterT declare_or_get_parameter(const std::string &parameter_name, const ParameterDescriptor &parameter_descriptor=ParameterDescriptor())
Declares or gets a parameter with specified type (not value). If the parameter is already declared,...
A sim-time-aware rate for Nav2 loops.
Definition: rate.hpp:61
Costmap2DROS(const rclcpp::NodeOptions &options=rclcpp::NodeOptions())
Constructor for the wrapper.
nav2::CallbackReturn on_cleanup(const rclcpp_lifecycle::State &state) override
Cleanup node.
void mapUpdateLoop(double frequency)
Function on timer for costmap update.
void getOrientedFootprint(std::vector< geometry_msgs::msg::Point > &oriented_footprint)
Build the oriented footprint of the robot at the robot's current pose.
bool getRobotPose(geometry_msgs::msg::PoseStamped &global_pose)
Get the pose of the robot in the global frame of the costmap.
void getParameters()
Get parameters for node.
void pause()
Stops the costmap from updating, but sensor data still comes in over the wire.
void getCostsCallback(const std::shared_ptr< rmw_request_id_t >, const std::shared_ptr< nav2_msgs::srv::GetCosts::Request > request, const std::shared_ptr< nav2_msgs::srv::GetCosts::Response > response)
Get the cost at a point in costmap.
nav2::CallbackReturn on_configure(const rclcpp_lifecycle::State &state) override
Configure node.
nav2::CallbackReturn on_deactivate(const rclcpp_lifecycle::State &state) override
Deactivate node.
rcl_interfaces::msg::SetParametersResult validateParameterUpdatesCallback(const std::vector< rclcpp::Parameter > &parameters)
Validate incoming parameter updates before applying them. This callback is triggered when one or more...
nav2::CallbackReturn on_activate(const rclcpp_lifecycle::State &state) override
Activate node.
double transform_tolerance_
The timeout before transform errors.
bool rolling_window_
Whether to use a rolling window version of the costmap.
double transform_staleness_threshold_
Maximum robot pose TF age; 0 disables the check.
void resume()
Resumes costmap updates.
double initial_transform_timeout_
The timeout before activation of the node errors.
void updateMap()
Update the map with the layered costmap / plugins.
void setRobotFootprint(const std::vector< geometry_msgs::msg::Point > &points)
Set the footprint of the robot to be the given set of points, padded by footprint_padding.
void resetLayers()
Reset each individual layer.
bool transformPoseToGlobalFrame(const geometry_msgs::msg::PoseStamped &input_pose, geometry_msgs::msg::PoseStamped &transformed_pose)
Transform the input_pose in the global frame of the costmap.
std::string global_frame_
The global frame for the costmap.
void start()
Subscribes to sensor topics if necessary and starts costmap updates, can be called to restart the cos...
void stop()
Stops costmap updates and unsubscribes from sensor topics.
std::string robot_base_frame_
The frame_id of the robot base.
bool subscribe_to_stamped_footprint_
If true, the footprint subscriber expects a PolygonStamped msg.
bool isCurrent()
Same as getLayeredCostmap()->isCurrent().
void updateParametersCallback(const std::vector< rclcpp::Parameter > &parameters)
Apply parameter updates after validation This callback is executed when parameters have been successf...
void waitUntilCurrent(const rclcpp::Duration &timeout)
Wait for the costmap to become current after updates or parameter changes.
void setRobotFootprintPolygon(const geometry_msgs::msg::Polygon &footprint)
Set the footprint of the robot to be the given polygon, padded by footprint_padding.
std::unique_ptr< std::thread > map_update_thread_
A thread for updating the map.
void init()
Common initialization for constructors.
bool is_lifecycle_follower_
whether is a child-LifecycleNode or an independent node
nav2::CallbackReturn on_shutdown(const rclcpp_lifecycle::State &state) override
shutdown node
A 2D costmap provides a mapping between points in the world and their associated "costs".
Definition: costmap_2d.hpp:69
void resetMap(unsigned int x0, unsigned int y0, unsigned int xn, unsigned int yn)
Reset the costmap in bounds.
Definition: costmap_2d.cpp:131
unsigned char getCost(unsigned int mx, unsigned int my) const
Get the cost of a cell in the costmap.
Definition: costmap_2d.cpp:265
bool worldToMap(double wx, double wy, unsigned int &mx, unsigned int &my) const
Convert from world coordinates to map coordinates.
Definition: costmap_2d.cpp:292
unsigned int getSizeInCellsX() const
Accessor for the x size of the costmap in cells.
Definition: costmap_2d.cpp:548
unsigned int getSizeInCellsY() const
Accessor for the y size of the costmap in cells.
Definition: costmap_2d.cpp:553
Checker for collision with a footprint on a costmap.
double footprintCostAtPose(double x, double y, double theta, const Footprint &footprint)
Find the footprint cost a a post with an unoriented footprint.
Measures execution time of code between calls to start and end.
void start()
Call just prior to code you want to measure.
double elapsed_time_in_seconds()
Extract the measured time as a floating point number of seconds.
void end()
Call just after the code you want to measure.
void transformFootprint(double x, double y, double theta, const std::vector< geometry_msgs::msg::Point > &footprint_spec, std::vector< geometry_msgs::msg::Point > &oriented_footprint)
Given a pose and base footprint, build the oriented footprint of the robot (list of Points)
Definition: footprint.cpp:112
bool makeFootprintFromString(const std::string &footprint_string, std::vector< geometry_msgs::msg::Point > &footprint)
Make the footprint from the given string.
Definition: footprint.cpp:177
void padFootprint(std::vector< geometry_msgs::msg::Point > &footprint, double padding)
Adds the specified amount of padding to the footprint (in place)
Definition: footprint.cpp:147
rclcpp::NodeOptions getChildNodeOptions(const std::string &name, const std::string &parent_namespace, const bool &use_sim_time, const rclcpp::NodeOptions &parent_options)
Given the node options of a parent node, expands of replaces the fields for the node name,...
std::vector< geometry_msgs::msg::Point > makeFootprintFromRadius(double radius)
Create a circular footprint from a given radius.
Definition: footprint.cpp:158
std::vector< geometry_msgs::msg::Point > toPointVector(const geometry_msgs::msg::Polygon &polygon)
Convert Polygon msg to vector of Points.
Definition: footprint.cpp:102