Nav2 Navigation Stack - lyrical  lyrical
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  "initial_transform_timeout", 60.0);
421  map_update_frequency_ = declare_or_get_parameter(
422  "update_frequency", 5.0);
424  "subscribe_to_stamped_footprint", false);
425 
426  auto node = shared_from_this();
427 
428  if (plugin_names_ == default_plugins_) {
429  for (size_t i = 0; i < default_plugins_.size(); ++i) {
430  nav2::declare_parameter_if_not_declared(
431  node, default_plugins_[i] + ".plugin", rclcpp::ParameterValue(default_types_[i]));
432  }
433  }
434  plugin_types_.resize(plugin_names_.size());
435  filter_types_.resize(filter_names_.size());
436 
437  // 1. All plugins must have 'plugin' param defined in their namespace to define the plugin type
438  for (size_t i = 0; i < plugin_names_.size(); ++i) {
439  plugin_types_[i] = nav2::get_plugin_type_param(node, plugin_names_[i]);
440  }
441  for (size_t i = 0; i < filter_names_.size(); ++i) {
442  filter_types_[i] = nav2::get_plugin_type_param(node, filter_names_[i]);
443  }
444 
445  // 2. The map publish frequency cannot be 0 (to avoid a divide-by-zero)
446  if (map_publish_frequency_ > 0) {
447  publish_cycle_ = rclcpp::Duration::from_seconds(1 / map_publish_frequency_);
448  } else {
449  publish_cycle_ = rclcpp::Duration(-1s);
450  }
451 
452  // 3. If the footprint has been specified, it must be in the correct format
453  use_radius_ = true;
454 
455  if (footprint_ != "" && footprint_ != "[]") {
456  // Footprint parameter has been specified, try to convert it
457  std::vector<geometry_msgs::msg::Point> new_footprint;
458  if (makeFootprintFromString(footprint_, new_footprint)) {
459  // The specified footprint is valid, so we'll use that instead of the radius
460  use_radius_ = false;
461  } else {
462  // Footprint provided but invalid, so stay with the radius
463  RCLCPP_ERROR(
464  get_logger(), "The footprint parameter is invalid: \"%s\", using radius (%lf) instead",
465  footprint_.c_str(), robot_radius_);
466  }
467  }
468 
469  // 4. The width, height, and resolution of map cannot be negative or 0
470  // (to avoid abnormal memory usage)
471  if (map_width_meters_ <= 0) {
472  throw std::invalid_argument(
473  "You try to set width of map to be negative or zero, "
474  "this isn't allowed, please give a positive value.");
475  }
476  if (map_height_meters_ <= 0) {
477  throw std::invalid_argument(
478  "You try to set height of map to be negative or zero, "
479  "this isn't allowed, please give a positive value.");
480  }
481  if (resolution_ <= 0.0) {
482  throw std::invalid_argument(
483  "Costmap resolution must be greater than zero.");
484  }
485 
486  const double size_x = map_width_meters_ / resolution_;
487  const double size_y = map_height_meters_ / resolution_;
488  const double max_cells = std::numeric_limits<int>::max();
489  if (size_x > max_cells / size_y) {
490  throw std::invalid_argument("Costmap cell count exceeds the supported signed index range");
491  }
492 }
493 
494 void
495 Costmap2DROS::setRobotFootprint(const std::vector<geometry_msgs::msg::Point> & points)
496 {
497  if (points.empty()) {
498  RCLCPP_ERROR(
499  get_logger(), "You try to set an empty footprint"
500  " this isn't allowed, a footprint must contain at least one point.");
501  return;
502  }
503  auto padded = std::make_shared<std::vector<geometry_msgs::msg::Point>>(points);
504  padFootprint(*padded, footprint_padding_);
505 
506 #ifdef __cpp_lib_atomic_shared_ptr
507  unpadded_footprint_.store(std::make_shared<std::vector<geometry_msgs::msg::Point>>(points));
508  padded_footprint_.store(padded);
509 #else
510  std::atomic_store(
511  &unpadded_footprint_, std::make_shared<std::vector<geometry_msgs::msg::Point>>(points));
512  std::atomic_store(&padded_footprint_, padded);
513 #endif
514  layered_costmap_->setFootprint(*padded);
515 }
516 
517 void
519  const geometry_msgs::msg::Polygon & footprint)
520 {
521  setRobotFootprint(toPointVector(footprint));
522 }
523 
524 void
525 Costmap2DROS::getOrientedFootprint(std::vector<geometry_msgs::msg::Point> & oriented_footprint)
526 {
527  geometry_msgs::msg::PoseStamped global_pose;
528  if (!getRobotPose(global_pose)) {
529  return;
530  }
531 
532  double yaw = tf2::getYaw(global_pose.pose.orientation);
533 #ifdef __cpp_lib_atomic_shared_ptr
534  auto padded_footprint = padded_footprint_.load();
535 #else
536  auto padded_footprint = std::atomic_load(&padded_footprint_);
537 #endif
539  global_pose.pose.position.x, global_pose.pose.position.y, yaw,
540  *padded_footprint, oriented_footprint);
541 }
542 
543 void
545 {
546  RCLCPP_DEBUG(get_logger(), "mapUpdateLoop frequency: %lf", frequency);
547 
548  // the user might not want to run the loop every cycle
549  if (frequency == 0.0) {
550  return;
551  }
552 
553  RCLCPP_DEBUG(get_logger(), "Entering loop");
554 
555  nav2::Rate r(this, frequency); // 200ms by default
556 
557  while (rclcpp::ok() && !map_update_thread_shutdown_) {
559 
560  // Execute after start() will complete plugins activation
561  if (!stopped_) {
562  // Lock while modifying layered costmap and publishing values
563  std::scoped_lock<std::mutex> lock(_dynamic_parameter_mutex);
564 
565  // Measure the execution time of the updateMap method
566  timer.start();
567  updateMap();
568  timer.end();
569 
570  RCLCPP_DEBUG(get_logger(), "Map update time: %.9f", timer.elapsed_time_in_seconds());
571  if (publish_cycle_ > rclcpp::Duration(0s) && layered_costmap_->isInitialized()) {
572  unsigned int x0, y0, xn, yn;
573  layered_costmap_->getBounds(&x0, &xn, &y0, &yn);
574  costmap_publisher_->updateBounds(x0, xn, y0, yn);
575 
576  for (auto & layer_pub : layer_publishers_) {
577  layer_pub->updateBounds(x0, xn, y0, yn);
578  }
579 
580  auto current_time = now();
581  const bool publish_due = last_publish_ + publish_cycle_ < current_time ||
582  current_time < last_publish_; // Time moved backwards, e.g. switching to sim_time.
583  if (publish_due || costmap_publisher_->isRepublishRequested()) {
584  RCLCPP_DEBUG(get_logger(), "Publish costmap at %s", name_.c_str());
585  costmap_publisher_->publishCostmap();
586  }
587 
588  for (auto & layer_pub : layer_publishers_) {
589  if (publish_due || layer_pub->isRepublishRequested()) {
590  layer_pub->publishCostmap();
591  }
592  }
593 
594  // Subscriber requests do not postpone the regular publication schedule.
595  if (publish_due) {
596  last_publish_ = current_time;
597  }
598  }
599  }
600 
601  // Make sure to sleep for the remainder of our cycle time
602  r.sleep();
603 
604 #if 0
605  // TODO(bpwilcox): find ROS2 equivalent or port for r.cycletime()
606  if (r.period() > tf2::durationFromSec(1 / frequency)) {
607  RCLCPP_WARN(
608  get_logger(),
609  "Costmap2DROS: Map update loop missed its desired rate of %.4fHz... "
610  "the loop actually took %.4f seconds", frequency, r.period());
611  }
612 #endif
613  }
614 }
615 
616 void
618 {
619  RCLCPP_DEBUG(get_logger(), "Updating map...");
620 
621  if (!stop_updates_) {
622  // get global pose
623  geometry_msgs::msg::PoseStamped pose;
624  if (getRobotPose(pose)) {
625  const double & x = pose.pose.position.x;
626  const double & y = pose.pose.position.y;
627  const double yaw = tf2::getYaw(pose.pose.orientation);
628  layered_costmap_->updateMap(x, y, yaw);
629 
630  auto footprint = std::make_unique<geometry_msgs::msg::PolygonStamped>();
631  footprint->header = pose.header;
632 #ifdef __cpp_lib_atomic_shared_ptr
633  auto padded_footprint = padded_footprint_.load();
634 #else
635  auto padded_footprint = std::atomic_load(&padded_footprint_);
636 #endif
637  transformFootprint(x, y, yaw, *padded_footprint, *footprint);
638 
639  RCLCPP_DEBUG(get_logger(), "Publishing footprint");
640  footprint_pub_->publish(std::move(footprint));
641  initialized_ = true;
642  }
643  }
644 }
645 
646 void
647 Costmap2DROS::waitUntilCurrent(const rclcpp::Duration & timeout)
648 {
649  rclcpp::Rate r(100);
650  auto waiting_start = now();
651  while (!isCurrent()) {
652  if (now() - waiting_start > timeout) {
653  throw std::runtime_error("Costmap timed out waiting for update");
654  }
655  r.sleep();
656  }
657 }
658 
659 void
661 {
662  RCLCPP_INFO(get_logger(), "start");
663  std::vector<std::shared_ptr<Layer>> * plugins = layered_costmap_->getPlugins();
664  std::vector<std::shared_ptr<Layer>> * filters = layered_costmap_->getFilters();
665 
666  // check if we're stopped or just paused
667  if (stopped_) {
668  // if we're stopped we need to re-subscribe to topics
669  for (std::vector<std::shared_ptr<Layer>>::iterator plugin = plugins->begin();
670  plugin != plugins->end();
671  ++plugin)
672  {
673  (*plugin)->activate();
674  }
675  for (std::vector<std::shared_ptr<Layer>>::iterator filter = filters->begin();
676  filter != filters->end();
677  ++filter)
678  {
679  (*filter)->activate();
680  }
681  stopped_ = false;
682  }
683  stop_updates_ = false;
684 
685  // block until the costmap is re-initialized.. meaning one update cycle has run
686  rclcpp::Rate r(20.0);
687  while (rclcpp::ok() && !initialized_) {
688  RCLCPP_DEBUG(get_logger(), "Sleeping, waiting for initialized_");
689  r.sleep();
690  }
691 }
692 
693 void
695 {
696  stop_updates_ = true;
697 
698  // layered_costmap_ is set only if on_configure has been called
699  if (layered_costmap_) {
700  std::vector<std::shared_ptr<Layer>> * plugins = layered_costmap_->getPlugins();
701  std::vector<std::shared_ptr<Layer>> * filters = layered_costmap_->getFilters();
702 
703  // unsubscribe from topics
704  for (std::vector<std::shared_ptr<Layer>>::iterator plugin = plugins->begin();
705  plugin != plugins->end(); ++plugin)
706  {
707  (*plugin)->deactivate();
708  }
709  for (std::vector<std::shared_ptr<Layer>>::iterator filter = filters->begin();
710  filter != filters->end(); ++filter)
711  {
712  (*filter)->deactivate();
713  }
714  }
715  initialized_ = false;
716  stopped_ = true;
717 }
718 
719 void
721 {
722  stop_updates_ = true;
723  initialized_ = false;
724 }
725 
726 void
728 {
729  stop_updates_ = false;
730 
731  // block until the costmap is re-initialized.. meaning one update cycle has run
732  rclcpp::Rate r(100.0);
733  while (!initialized_) {
734  r.sleep();
735  }
736 }
737 
738 void
740 {
741  Costmap2D * top = layered_costmap_->getCostmap();
742  top->resetMap(0, 0, top->getSizeInCellsX(), top->getSizeInCellsY());
743 
744  // Reset each of the plugins
745  std::vector<std::shared_ptr<Layer>> * plugins = layered_costmap_->getPlugins();
746  std::vector<std::shared_ptr<Layer>> * filters = layered_costmap_->getFilters();
747  for (std::vector<std::shared_ptr<Layer>>::iterator plugin = plugins->begin();
748  plugin != plugins->end(); ++plugin)
749  {
750  (*plugin)->reset();
751  }
752  for (std::vector<std::shared_ptr<Layer>>::iterator filter = filters->begin();
753  filter != filters->end(); ++filter)
754  {
755  (*filter)->reset();
756  }
757 }
758 
759 bool
760 Costmap2DROS::getRobotPose(geometry_msgs::msg::PoseStamped & global_pose)
761 {
762  return nav2_util::getCurrentPose(
763  global_pose, *tf_buffer_,
765 }
766 
767 bool
769  const geometry_msgs::msg::PoseStamped & input_pose,
770  geometry_msgs::msg::PoseStamped & transformed_pose)
771 {
772  if (input_pose.header.frame_id == global_frame_) {
773  transformed_pose = input_pose;
774  return true;
775  } else {
776  return nav2_util::transformPoseInTargetFrame(
777  input_pose, transformed_pose, *tf_buffer_,
779  }
780 }
781 
782 rcl_interfaces::msg::SetParametersResult Costmap2DROS::validateParameterUpdatesCallback(
783  const std::vector<rclcpp::Parameter> & parameters)
784 {
785  rcl_interfaces::msg::SetParametersResult result;
786  result.successful = true;
787 
788  for (const auto & parameter : parameters) {
789  const auto & param_type = parameter.get_type();
790  const auto & param_name = parameter.get_name();
791  if (param_name.find('.') != std::string::npos) {
792  continue;
793  }
794  if (param_type == ParameterType::PARAMETER_DOUBLE) {
795  if (parameter.as_double() <= 0.0 &&
796  (param_name == "resolution" || param_name == "publish_frequency"))
797  {
798  RCLCPP_WARN(
799  get_logger(), "The value of parameter '%s' is incorrectly set to %f, "
800  "it should be >0. Ignoring parameter update.",
801  param_name.c_str(), parameter.as_double());
802  result.successful = false;
803  } else if (parameter.as_double() < 0.0 && // NOLINT
804  (param_name != "origin_x" && param_name != "origin_y"))
805  {
806  RCLCPP_WARN(
807  get_logger(), "The value of parameter '%s' is incorrectly set to %f, "
808  "it should be >0. Ignoring parameter update.",
809  param_name.c_str(), parameter.as_double());
810  result.successful = false;
811  }
812  } else if (param_type == ParameterType::PARAMETER_INTEGER) {
813  if (parameter.as_int() <= 0.0) {
814  RCLCPP_WARN(
815  get_logger(), "The value of parameter '%s' is incorrectly set to %ld, "
816  "it should be >0. Ignoring parameter update.",
817  param_name.c_str(), parameter.as_int());
818  result.successful = false;
819  }
820  } else if (param_type == ParameterType::PARAMETER_STRING && param_name == "robot_base_frame") {
821  // First, make sure that the transform between the robot base frame
822  // and the global frame is available
823  std::string tf_error;
824  RCLCPP_INFO(get_logger(), "Checking transform");
825  if (!tf_buffer_->canTransform(
826  global_frame_, parameter.as_string(), tf2::TimePointZero,
827  tf2::durationFromSec(1.0), &tf_error))
828  {
829  RCLCPP_WARN(
830  get_logger(), "Timed out waiting for transform from %s to %s"
831  " to become available, tf error: %s",
832  parameter.as_string().c_str(), global_frame_.c_str(), tf_error.c_str());
833  RCLCPP_WARN(
834  get_logger(), "Rejecting robot_base_frame change to %s , leaving it to its original"
835  " value of %s", parameter.as_string().c_str(), robot_base_frame_.c_str());
836  result.successful = false;
837  }
838  }
839  }
840 
841  return result;
842 }
843 
844 void
845 Costmap2DROS::updateParametersCallback(const std::vector<rclcpp::Parameter> & parameters)
846 {
847  bool resize_map = false;
848  std::lock_guard<std::mutex> lock_reinit(_dynamic_parameter_mutex);
849 
850  for (const auto & parameter : parameters) {
851  const auto & param_type = parameter.get_type();
852  const auto & param_name = parameter.get_name();
853  if (param_name.find('.') != std::string::npos) {
854  continue;
855  }
856 
857  if (param_type == ParameterType::PARAMETER_DOUBLE) {
858  if (param_name == "robot_radius") {
859  robot_radius_ = parameter.as_double();
860  // Set the footprint
861  if (use_radius_) {
863  }
864  } else if (param_name == "footprint_padding") {
865  footprint_padding_ = parameter.as_double();
866 #ifdef __cpp_lib_atomic_shared_ptr
867  auto padded = std::make_shared<std::vector<geometry_msgs::msg::Point>>(
868  *unpadded_footprint_.load());
869  padFootprint(*padded, footprint_padding_);
870  padded_footprint_.store(padded);
871 #else
872  auto padded = std::make_shared<std::vector<geometry_msgs::msg::Point>>(
873  *std::atomic_load(&unpadded_footprint_));
874  padFootprint(*padded, footprint_padding_);
875  std::atomic_store(&padded_footprint_, padded);
876 #endif
877  layered_costmap_->setFootprint(*padded);
878  } else if (param_name == "transform_tolerance") {
879  transform_tolerance_ = parameter.as_double();
880  } else if (param_name == "publish_frequency") {
881  map_publish_frequency_ = parameter.as_double();
882  publish_cycle_ = rclcpp::Duration::from_seconds(1 / map_publish_frequency_);
883  } else if (param_name == "resolution") {
884  resize_map = true;
885  resolution_ = parameter.as_double();
886  } else if (param_name == "origin_x") {
887  resize_map = true;
888  origin_x_ = parameter.as_double();
889  } else if (param_name == "origin_y") {
890  resize_map = true;
891  origin_y_ = parameter.as_double();
892  }
893  } else if (param_type == ParameterType::PARAMETER_INTEGER) {
894  if (param_name == "width") {
895  resize_map = true;
896  map_width_meters_ = parameter.as_int();
897  } else if (param_name == "height") {
898  resize_map = true;
899  map_height_meters_ = parameter.as_int();
900  }
901  } else if (param_type == ParameterType::PARAMETER_STRING) {
902  if (param_name == "footprint") {
903  footprint_ = parameter.as_string();
904  std::vector<geometry_msgs::msg::Point> new_footprint;
905  if (makeFootprintFromString(footprint_, new_footprint)) {
906  setRobotFootprint(new_footprint);
907  }
908  } else if (param_name == "robot_base_frame") {
909  robot_base_frame_ = parameter.as_string();
910  }
911  }
912  }
913 
914  if (resize_map && !layered_costmap_->isSizeLocked()) {
915  layered_costmap_->resizeMap(
916  (unsigned int)(map_width_meters_ / resolution_),
917  (unsigned int)(map_height_meters_ / resolution_), resolution_, origin_x_, origin_y_);
918  updateMap();
919  }
920 }
921 
923  const std::shared_ptr<rmw_request_id_t>,
924  const std::shared_ptr<nav2_msgs::srv::GetCosts::Request> request,
925  const std::shared_ptr<nav2_msgs::srv::GetCosts::Response> response)
926 {
927  unsigned int mx, my;
928 
929  Costmap2D * costmap = layered_costmap_->getCostmap();
930  std::unique_lock<Costmap2D::mutex_t> lock(*(costmap->getMutex()));
931  response->success = true;
932  for (const auto & pose : request->poses) {
933  geometry_msgs::msg::PoseStamped pose_transformed;
934  if (!transformPoseToGlobalFrame(pose, pose_transformed)) {
935  RCLCPP_ERROR(
936  get_logger(), "Failed to transform, cannot get cost for pose (%.2f, %.2f)",
937  pose.pose.position.x, pose.pose.position.y);
938  response->success = false;
939  response->costs.push_back(NO_INFORMATION);
940  continue;
941  }
942  double yaw = tf2::getYaw(pose_transformed.pose.orientation);
943 
944  if (request->use_footprint) {
945  Footprint footprint = layered_costmap_->getFootprint();
946  FootprintCollisionChecker<Costmap2D *> collision_checker(costmap);
947 
948  RCLCPP_DEBUG(
949  get_logger(), "Received request to get cost at footprint pose (%.2f, %.2f, %.2f)",
950  pose_transformed.pose.position.x, pose_transformed.pose.position.y, yaw);
951 
952  response->costs.push_back(
953  collision_checker.footprintCostAtPose(
954  pose_transformed.pose.position.x,
955  pose_transformed.pose.position.y, yaw, footprint));
956  } else {
957  RCLCPP_DEBUG(
958  get_logger(), "Received request to get cost at point (%f, %f)",
959  pose_transformed.pose.position.x,
960  pose_transformed.pose.position.y);
961 
962  bool in_bounds = costmap->worldToMap(
963  pose_transformed.pose.position.x,
964  pose_transformed.pose.position.y, mx, my);
965 
966  if (!in_bounds) {
967  response->success = false;
968  response->costs.push_back(LETHAL_OBSTACLE);
969  continue;
970  }
971  // Get the cost at the map coordinates
972  response->costs.push_back(static_cast<float>(costmap->getCost(mx, my)));
973  }
974  }
975 }
976 
977 } // 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.
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