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 <stdexcept>
45 #include <string>
46 #include <vector>
47 #include <utility>
48 
49 #include "nav2_costmap_2d/layered_costmap.hpp"
50 #include "nav2_util/execution_timer.hpp"
51 #include "nav2_ros_common/node_utils.hpp"
52 #include "nav2_ros_common/rate.hpp"
53 #include "tf2_geometry_msgs/tf2_geometry_msgs.hpp"
54 #include "nav2_ros_common/tf2_factories.hpp"
55 #include "nav2_util/robot_utils.hpp"
56 #include "rcl_interfaces/msg/set_parameters_result.hpp"
57 
58 using namespace std::chrono_literals;
59 using std::placeholders::_1;
60 using rcl_interfaces::msg::ParameterType;
61 
62 namespace nav2_costmap_2d
63 {
64 Costmap2DROS::Costmap2DROS(const rclcpp::NodeOptions & options)
65 : nav2::LifecycleNode("costmap", "", options),
66  name_("costmap"),
67  default_plugins_{"static_layer", "obstacle_layer", "inflation_layer"},
68  default_types_{
69  "nav2_costmap_2d::StaticLayer",
70  "nav2_costmap_2d::ObstacleLayer",
71  "nav2_costmap_2d::InflationLayer"}
72 {
73  is_lifecycle_follower_ = false;
74  init();
75 }
76 
77 rclcpp::NodeOptions getChildNodeOptions(
78  const std::string & name,
79  const std::string & parent_namespace,
80  const bool & use_sim_time,
81  const rclcpp::NodeOptions & parent_options)
82 {
83  std::vector<std::string> new_arguments = parent_options.arguments();
84  bool use_intra_process_comms = parent_options.use_intra_process_comms();
85  nav2::replaceOrAddArgument(
86  new_arguments, "-r", "__ns",
87  "__ns:=" + nav2::add_namespaces(parent_namespace, name));
88  nav2::replaceOrAddArgument(new_arguments, "-r", "__node", name + ":" + "__node:=" + name);
89  nav2::replaceOrAddArgument(
90  new_arguments, "-p", "use_sim_time",
91  "use_sim_time:=" + std::string(use_sim_time ? "true" : "false"));
92  return rclcpp::NodeOptions().use_intra_process_comms(use_intra_process_comms).arguments(
93  new_arguments);
94 }
95 
97  const std::string & name,
98  const std::string & parent_namespace,
99  const bool & use_sim_time,
100  const rclcpp::NodeOptions & options)
101 : nav2::LifecycleNode(name, "",
102  getChildNodeOptions(name, parent_namespace, use_sim_time, options)
103 ),
104  name_(name),
105  default_plugins_{"static_layer", "obstacle_layer", "inflation_layer"},
106  default_types_{
107  "nav2_costmap_2d::StaticLayer",
108  "nav2_costmap_2d::ObstacleLayer",
109  "nav2_costmap_2d::InflationLayer"}
110 {
111  init();
112 }
113 
115 {
116  RCLCPP_INFO(get_logger(), "Creating Costmap");
117  declare_parameter("lethal_cost_threshold", rclcpp::ParameterValue(100));
118  declare_parameter("trinary_costmap", rclcpp::ParameterValue(true));
119  declare_parameter("unknown_cost_value", rclcpp::ParameterValue(static_cast<unsigned char>(0xff)));
120  declare_parameter("inscribed_obstacle_cost_value", rclcpp::ParameterValue(99));
121  declare_parameter("use_maximum", rclcpp::ParameterValue(false));
122 }
123 
125 {
126 }
127 
128 nav2::CallbackReturn
129 Costmap2DROS::on_configure(const rclcpp_lifecycle::State & /*state*/)
130 {
131  RCLCPP_INFO(get_logger(), "Configuring");
132  try {
133  getParameters();
134  } catch (const std::exception & e) {
135  RCLCPP_ERROR(
136  get_logger(), "Failed to configure costmap! %s.", e.what());
137  return nav2::CallbackReturn::FAILURE;
138  }
139 
140  callback_group_ = create_callback_group(
141  rclcpp::CallbackGroupType::MutuallyExclusive, false);
142 
143  // Create the costmap itself
144  layered_costmap_ = std::make_unique<LayeredCostmap>(
145  global_frame_, rolling_window_, track_unknown_space_);
146 
147  if (!layered_costmap_->isSizeLocked()) {
148  layered_costmap_->resizeMap(
149  (unsigned int)(map_width_meters_ / resolution_),
150  (unsigned int)(map_height_meters_ / resolution_), resolution_, origin_x_, origin_y_);
151  }
152 
153  // Create the transform-related objects
154  tf_buffer_ = nav2::create_transform_buffer(this, callback_group_);
155  tf_listener_ = nav2::create_transform_listener(*tf_buffer_);
156 
157  // Then load and add the plug-ins to the costmap
158  for (unsigned int i = 0; i < plugin_names_.size(); ++i) {
159  RCLCPP_INFO(get_logger(), "Using plugin \"%s\"", plugin_names_[i].c_str());
160 
161  std::shared_ptr<Layer> plugin = plugin_loader_.createSharedInstance(plugin_types_[i]);
162 
163  layered_costmap_->addPlugin(plugin);
164 
165  try {
166  plugin->initialize(
167  layered_costmap_.get(), plugin_names_[i], tf_buffer_.get(),
168  shared_from_this(), callback_group_);
169  } catch (const std::exception & e) {
170  RCLCPP_ERROR(
171  get_logger(), "Failed to initialize costmap plugin %s! %s.",
172  plugin_names_[i].c_str(), e.what());
173  return nav2::CallbackReturn::FAILURE;
174  }
175 
176  RCLCPP_INFO(get_logger(), "Initialized plugin \"%s\"", plugin_names_[i].c_str());
177  }
178  // and costmap filters as well
179  for (unsigned int i = 0; i < filter_names_.size(); ++i) {
180  RCLCPP_INFO(get_logger(), "Using costmap filter \"%s\"", filter_names_[i].c_str());
181 
182  std::shared_ptr<Layer> filter = plugin_loader_.createSharedInstance(filter_types_[i]);
183 
184  layered_costmap_->addFilter(filter);
185 
186  filter->initialize(
187  layered_costmap_.get(), filter_names_[i], tf_buffer_.get(),
188  shared_from_this(), callback_group_);
189 
190  RCLCPP_INFO(get_logger(), "Initialized costmap filter \"%s\"", filter_names_[i].c_str());
191  }
192 
193  // Create the publishers and subscribers
195  footprint_stamped_sub_ = create_subscription<geometry_msgs::msg::PolygonStamped>(
196  "footprint", [this](const geometry_msgs::msg::PolygonStamped::ConstSharedPtr & footprint)
197  {setRobotFootprintPolygon(footprint->polygon);});
198  } else {
199  footprint_sub_ = create_subscription<geometry_msgs::msg::Polygon>(
200  "footprint", [this](const geometry_msgs::msg::Polygon::ConstSharedPtr & footprint)
201  {setRobotFootprintPolygon(*footprint);});
202  }
203 
204  footprint_pub_ = create_publisher<geometry_msgs::msg::PolygonStamped>(
205  "published_footprint");
206 
207  costmap_publisher_ = std::make_unique<Costmap2DPublisher>(
209  layered_costmap_->getCostmap(), global_frame_,
210  "costmap", always_send_full_costmap_, map_vis_z_);
211 
212  auto layers = layered_costmap_->getPlugins();
213 
214  for (auto & layer : *layers) {
215  auto costmap_layer = std::dynamic_pointer_cast<CostmapLayer>(layer);
216  if (costmap_layer != nullptr) {
217  layer_publishers_.emplace_back(
218  std::make_unique<Costmap2DPublisher>(
220  costmap_layer.get(), global_frame_,
221  layer->getName(), always_send_full_costmap_, map_vis_z_)
222  );
223  }
224  }
225 
226  // Set the footprint
227  if (use_radius_) {
229  } else {
230  std::vector<geometry_msgs::msg::Point> new_footprint;
231  makeFootprintFromString(footprint_, new_footprint);
232  setRobotFootprint(new_footprint);
233  }
234 
235  // Service to get the cost at a point
236  get_cost_service_ = create_service<nav2_msgs::srv::GetCosts>(
237  std::string("get_cost_") + get_name(),
238  std::bind(
239  &Costmap2DROS::getCostsCallback, this, std::placeholders::_1, std::placeholders::_2,
240  std::placeholders::_3));
241 
242  // Add cleaning service
243  clear_costmap_service_ = std::make_unique<ClearCostmapService>(shared_from_this(), *this);
244 
245  executor_ = std::make_shared<rclcpp::executors::SingleThreadedExecutor>();
246  executor_->add_callback_group(callback_group_, get_node_base_interface());
247  executor_thread_ = std::make_unique<nav2::NodeThread>(executor_);
248  return nav2::CallbackReturn::SUCCESS;
249 }
250 
251 nav2::CallbackReturn
252 Costmap2DROS::on_activate(const rclcpp_lifecycle::State & /*state*/)
253 {
254  RCLCPP_INFO(get_logger(), "Activating");
255 
256  // First, make sure that the transform between the robot base frame
257  // and the global frame is available
258 
259  std::string tf_error;
260 
261  RCLCPP_INFO(get_logger(), "Checking transform");
262  rclcpp::Rate r(2);
263  const auto initial_transform_timeout = rclcpp::Duration::from_seconds(
265  const auto initial_transform_timeout_point = now() + initial_transform_timeout;
266  while (rclcpp::ok() &&
267  !tf_buffer_->canTransform(
268  global_frame_, robot_base_frame_, tf2::TimePointZero, &tf_error))
269  {
270  RCLCPP_INFO(
271  get_logger(), "Timed out waiting for transform from %s to %s"
272  " to become available, tf error: %s",
273  robot_base_frame_.c_str(), global_frame_.c_str(), tf_error.c_str());
274 
275  // Check timeout
276  if (now() > initial_transform_timeout_point) {
277  RCLCPP_ERROR(
278  get_logger(),
279  "Failed to activate %s because "
280  "transform from %s to %s did not become available before timeout",
281  get_name(), robot_base_frame_.c_str(), global_frame_.c_str());
282 
283  return nav2::CallbackReturn::FAILURE;
284  }
285 
286  // The error string will accumulate and errors will typically be the same, so the last
287  // will do for the warning above. Reset the string here to avoid accumulation
288  tf_error.clear();
289  r.sleep();
290  }
291 
292  // Activate publishers
293  footprint_pub_->on_activate();
294  costmap_publisher_->on_activate();
295 
296  for (auto & layer_pub : layer_publishers_) {
297  layer_pub->on_activate();
298  }
299 
300  // Create a thread to handle updating the map
301  stopped_ = true; // to active plugins
302  stop_updates_ = false;
303  map_update_thread_shutdown_ = false;
304  map_update_thread_ = std::make_unique<std::thread>(
305  std::bind(&Costmap2DROS::mapUpdateLoop, this, map_update_frequency_));
306 
307  start();
308 
309  // Add callback for dynamic parameters
310  post_set_params_handler_ = this->add_post_set_parameters_callback(
311  std::bind(
313  this, std::placeholders::_1));
314  on_set_params_handler = this->add_on_set_parameters_callback(
315  std::bind(&Costmap2DROS::validateParameterUpdatesCallback, this, _1));
316 
317  return nav2::CallbackReturn::SUCCESS;
318 }
319 
320 nav2::CallbackReturn
321 Costmap2DROS::on_deactivate(const rclcpp_lifecycle::State & /*state*/)
322 {
323  RCLCPP_INFO(get_logger(), "Deactivating");
324 
325  remove_post_set_parameters_callback(post_set_params_handler_.get());
326  post_set_params_handler_.reset();
327  remove_on_set_parameters_callback(on_set_params_handler.get());
328  on_set_params_handler.reset();
329 
330  stop();
331 
332  // Map thread stuff
333  map_update_thread_shutdown_ = true;
334 
335  if (map_update_thread_->joinable()) {
336  map_update_thread_->join();
337  }
338 
339  footprint_pub_->on_deactivate();
340  costmap_publisher_->on_deactivate();
341 
342  for (auto & layer_pub : layer_publishers_) {
343  layer_pub->on_deactivate();
344  }
345 
346  return nav2::CallbackReturn::SUCCESS;
347 }
348 
349 nav2::CallbackReturn
350 Costmap2DROS::on_cleanup(const rclcpp_lifecycle::State & /*state*/)
351 {
352  RCLCPP_INFO(get_logger(), "Cleaning up");
353  executor_thread_.reset();
354  get_cost_service_.reset();
355  costmap_publisher_.reset();
356  clear_costmap_service_.reset();
357 
358  layer_publishers_.clear();
359 
360  layered_costmap_.reset();
361 
362  tf_listener_.reset();
363  tf_buffer_.reset();
364 
365  footprint_sub_.reset();
366  footprint_pub_.reset();
367 
368  return nav2::CallbackReturn::SUCCESS;
369 }
370 
371 nav2::CallbackReturn
372 Costmap2DROS::on_shutdown(const rclcpp_lifecycle::State &)
373 {
374  RCLCPP_INFO(get_logger(), "Shutting down");
375  return nav2::CallbackReturn::SUCCESS;
376 }
377 
378 void
380 {
381  RCLCPP_DEBUG(get_logger(), " getParameters");
382 
383  // Get all of the required parameters
384  always_send_full_costmap_ = declare_or_get_parameter(
385  "always_send_full_costmap", false);
386  map_vis_z_ = declare_or_get_parameter("map_vis_z", 0.0);
387  footprint_padding_ = declare_or_get_parameter("footprint_padding", 0.01f);
388  footprint_ = declare_or_get_parameter(
389  "footprint", std::string("[]"));
391  "global_frame", std::string("map"));
392  map_height_meters_ = declare_or_get_parameter(
393  "height", 5);
394  map_width_meters_ = declare_or_get_parameter(
395  "width", 5);
396  origin_x_ = declare_or_get_parameter(
397  "origin_x", 0.0);
398  origin_y_ = declare_or_get_parameter(
399  "origin_y", 0.0);
400  plugin_names_ = declare_or_get_parameter(
401  "plugins", default_plugins_);
402  filter_names_ = declare_or_get_parameter(
403  "filters", std::vector<std::string>());
404  map_publish_frequency_ = declare_or_get_parameter(
405  "publish_frequency", 1.0);
406  resolution_ = declare_or_get_parameter(
407  "resolution", 0.1);
409  "robot_base_frame", std::string("base_link"));
410  robot_radius_ = declare_or_get_parameter(
411  "robot_radius", 0.1);
413  "rolling_window", false);
414  track_unknown_space_ = declare_or_get_parameter(
415  "track_unknown_space", false);
417  "transform_tolerance", 0.3);
419  "initial_transform_timeout", 60.0);
420  map_update_frequency_ = declare_or_get_parameter(
421  "update_frequency", 5.0);
423  "subscribe_to_stamped_footprint", false);
424 
425  auto node = shared_from_this();
426 
427  if (plugin_names_ == default_plugins_) {
428  for (size_t i = 0; i < default_plugins_.size(); ++i) {
429  nav2::declare_parameter_if_not_declared(
430  node, default_plugins_[i] + ".plugin", rclcpp::ParameterValue(default_types_[i]));
431  }
432  }
433  plugin_types_.resize(plugin_names_.size());
434  filter_types_.resize(filter_names_.size());
435 
436  // 1. All plugins must have 'plugin' param defined in their namespace to define the plugin type
437  for (size_t i = 0; i < plugin_names_.size(); ++i) {
438  plugin_types_[i] = nav2::get_plugin_type_param(node, plugin_names_[i]);
439  }
440  for (size_t i = 0; i < filter_names_.size(); ++i) {
441  filter_types_[i] = nav2::get_plugin_type_param(node, filter_names_[i]);
442  }
443 
444  // 2. The map publish frequency cannot be 0 (to avoid a divide-by-zero)
445  if (map_publish_frequency_ > 0) {
446  publish_cycle_ = rclcpp::Duration::from_seconds(1 / map_publish_frequency_);
447  } else {
448  publish_cycle_ = rclcpp::Duration(-1s);
449  }
450 
451  // 3. If the footprint has been specified, it must be in the correct format
452  use_radius_ = true;
453 
454  if (footprint_ != "" && footprint_ != "[]") {
455  // Footprint parameter has been specified, try to convert it
456  std::vector<geometry_msgs::msg::Point> new_footprint;
457  if (makeFootprintFromString(footprint_, new_footprint)) {
458  // The specified footprint is valid, so we'll use that instead of the radius
459  use_radius_ = false;
460  } else {
461  // Footprint provided but invalid, so stay with the radius
462  RCLCPP_ERROR(
463  get_logger(), "The footprint parameter is invalid: \"%s\", using radius (%lf) instead",
464  footprint_.c_str(), robot_radius_);
465  }
466  }
467 
468  // 4. The width, height, and resolution of map cannot be negative or 0
469  // (to avoid abnormal memory usage)
470  if (map_width_meters_ <= 0) {
471  RCLCPP_ERROR(
472  get_logger(), "You try to set width of map to be negative or zero,"
473  " this isn't allowed, please give a positive value.");
474  }
475  if (map_height_meters_ <= 0) {
476  RCLCPP_ERROR(
477  get_logger(), "You try to set height of map to be negative or zero,"
478  " this isn't allowed, please give a positive value.");
479  }
480  if (resolution_ <= 0.0 || !std::isfinite(resolution_)) {
481  throw std::invalid_argument(
482  "Costmap resolution must be a positive finite value.");
483  }
484 }
485 
486 void
487 Costmap2DROS::setRobotFootprint(const std::vector<geometry_msgs::msg::Point> & points)
488 {
489  if (points.empty()) {
490  RCLCPP_ERROR(
491  get_logger(), "You try to set an empty footprint"
492  " this isn't allowed, a footprint must contain at least one point.");
493  return;
494  }
495  auto padded = std::make_shared<std::vector<geometry_msgs::msg::Point>>(points);
496  padFootprint(*padded, footprint_padding_);
497 
498 #ifdef __cpp_lib_atomic_shared_ptr
499  unpadded_footprint_.store(std::make_shared<std::vector<geometry_msgs::msg::Point>>(points));
500  padded_footprint_.store(padded);
501 #else
502  std::atomic_store(
503  &unpadded_footprint_, std::make_shared<std::vector<geometry_msgs::msg::Point>>(points));
504  std::atomic_store(&padded_footprint_, padded);
505 #endif
506  layered_costmap_->setFootprint(*padded);
507 }
508 
509 void
511  const geometry_msgs::msg::Polygon & footprint)
512 {
513  setRobotFootprint(toPointVector(footprint));
514 }
515 
516 void
517 Costmap2DROS::getOrientedFootprint(std::vector<geometry_msgs::msg::Point> & oriented_footprint)
518 {
519  geometry_msgs::msg::PoseStamped global_pose;
520  if (!getRobotPose(global_pose)) {
521  return;
522  }
523 
524  double yaw = tf2::getYaw(global_pose.pose.orientation);
525 #ifdef __cpp_lib_atomic_shared_ptr
526  auto padded_footprint = padded_footprint_.load();
527 #else
528  auto padded_footprint = std::atomic_load(&padded_footprint_);
529 #endif
531  global_pose.pose.position.x, global_pose.pose.position.y, yaw,
532  *padded_footprint, oriented_footprint);
533 }
534 
535 void
537 {
538  RCLCPP_DEBUG(get_logger(), "mapUpdateLoop frequency: %lf", frequency);
539 
540  // the user might not want to run the loop every cycle
541  if (frequency == 0.0) {
542  return;
543  }
544 
545  RCLCPP_DEBUG(get_logger(), "Entering loop");
546 
547  nav2::Rate r(this, frequency); // 200ms by default
548 
549  while (rclcpp::ok() && !map_update_thread_shutdown_) {
551 
552  // Execute after start() will complete plugins activation
553  if (!stopped_) {
554  // Lock while modifying layered costmap and publishing values
555  std::scoped_lock<std::mutex> lock(_dynamic_parameter_mutex);
556 
557  // Measure the execution time of the updateMap method
558  timer.start();
559  updateMap();
560  timer.end();
561 
562  RCLCPP_DEBUG(get_logger(), "Map update time: %.9f", timer.elapsed_time_in_seconds());
563  if (publish_cycle_ > rclcpp::Duration(0s) && layered_costmap_->isInitialized()) {
564  unsigned int x0, y0, xn, yn;
565  layered_costmap_->getBounds(&x0, &xn, &y0, &yn);
566  costmap_publisher_->updateBounds(x0, xn, y0, yn);
567 
568  for (auto & layer_pub : layer_publishers_) {
569  layer_pub->updateBounds(x0, xn, y0, yn);
570  }
571 
572  auto current_time = now();
573  if ((last_publish_ + publish_cycle_ < current_time) || // publish_cycle_ is due
574  (current_time <
575  last_publish_)) // time has moved backwards, probably due to a switch to sim_time // NOLINT
576  {
577  RCLCPP_DEBUG(get_logger(), "Publish costmap at %s", name_.c_str());
578  costmap_publisher_->publishCostmap();
579 
580  for (auto & layer_pub : layer_publishers_) {
581  layer_pub->publishCostmap();
582  }
583 
584  last_publish_ = current_time;
585  }
586  }
587  }
588 
589  // Make sure to sleep for the remainder of our cycle time
590  r.sleep();
591 
592 #if 0
593  // TODO(bpwilcox): find ROS2 equivalent or port for r.cycletime()
594  if (r.period() > tf2::durationFromSec(1 / frequency)) {
595  RCLCPP_WARN(
596  get_logger(),
597  "Costmap2DROS: Map update loop missed its desired rate of %.4fHz... "
598  "the loop actually took %.4f seconds", frequency, r.period());
599  }
600 #endif
601  }
602 }
603 
604 void
606 {
607  RCLCPP_DEBUG(get_logger(), "Updating map...");
608 
609  if (!stop_updates_) {
610  // get global pose
611  geometry_msgs::msg::PoseStamped pose;
612  if (getRobotPose(pose)) {
613  const double & x = pose.pose.position.x;
614  const double & y = pose.pose.position.y;
615  const double yaw = tf2::getYaw(pose.pose.orientation);
616  layered_costmap_->updateMap(x, y, yaw);
617 
618  auto footprint = std::make_unique<geometry_msgs::msg::PolygonStamped>();
619  footprint->header = pose.header;
620 #ifdef __cpp_lib_atomic_shared_ptr
621  auto padded_footprint = padded_footprint_.load();
622 #else
623  auto padded_footprint = std::atomic_load(&padded_footprint_);
624 #endif
625  transformFootprint(x, y, yaw, *padded_footprint, *footprint);
626 
627  RCLCPP_DEBUG(get_logger(), "Publishing footprint");
628  footprint_pub_->publish(std::move(footprint));
629  initialized_ = true;
630  }
631  }
632 }
633 
634 void
635 Costmap2DROS::waitUntilCurrent(const rclcpp::Duration & timeout)
636 {
637  rclcpp::Rate r(100);
638  auto waiting_start = now();
639  while (!isCurrent()) {
640  if (now() - waiting_start > timeout) {
641  throw std::runtime_error("Costmap timed out waiting for update");
642  }
643  r.sleep();
644  }
645 }
646 
647 void
649 {
650  RCLCPP_INFO(get_logger(), "start");
651  std::vector<std::shared_ptr<Layer>> * plugins = layered_costmap_->getPlugins();
652  std::vector<std::shared_ptr<Layer>> * filters = layered_costmap_->getFilters();
653 
654  // check if we're stopped or just paused
655  if (stopped_) {
656  // if we're stopped we need to re-subscribe to topics
657  for (std::vector<std::shared_ptr<Layer>>::iterator plugin = plugins->begin();
658  plugin != plugins->end();
659  ++plugin)
660  {
661  (*plugin)->activate();
662  }
663  for (std::vector<std::shared_ptr<Layer>>::iterator filter = filters->begin();
664  filter != filters->end();
665  ++filter)
666  {
667  (*filter)->activate();
668  }
669  stopped_ = false;
670  }
671  stop_updates_ = false;
672 
673  // block until the costmap is re-initialized.. meaning one update cycle has run
674  rclcpp::Rate r(20.0);
675  while (rclcpp::ok() && !initialized_) {
676  RCLCPP_DEBUG(get_logger(), "Sleeping, waiting for initialized_");
677  r.sleep();
678  }
679 }
680 
681 void
683 {
684  stop_updates_ = true;
685 
686  // layered_costmap_ is set only if on_configure has been called
687  if (layered_costmap_) {
688  std::vector<std::shared_ptr<Layer>> * plugins = layered_costmap_->getPlugins();
689  std::vector<std::shared_ptr<Layer>> * filters = layered_costmap_->getFilters();
690 
691  // unsubscribe from topics
692  for (std::vector<std::shared_ptr<Layer>>::iterator plugin = plugins->begin();
693  plugin != plugins->end(); ++plugin)
694  {
695  (*plugin)->deactivate();
696  }
697  for (std::vector<std::shared_ptr<Layer>>::iterator filter = filters->begin();
698  filter != filters->end(); ++filter)
699  {
700  (*filter)->deactivate();
701  }
702  }
703  initialized_ = false;
704  stopped_ = true;
705 }
706 
707 void
709 {
710  stop_updates_ = true;
711  initialized_ = false;
712 }
713 
714 void
716 {
717  stop_updates_ = false;
718 
719  // block until the costmap is re-initialized.. meaning one update cycle has run
720  rclcpp::Rate r(100.0);
721  while (!initialized_) {
722  r.sleep();
723  }
724 }
725 
726 void
728 {
729  Costmap2D * top = layered_costmap_->getCostmap();
730  top->resetMap(0, 0, top->getSizeInCellsX(), top->getSizeInCellsY());
731 
732  // Reset each of the plugins
733  std::vector<std::shared_ptr<Layer>> * plugins = layered_costmap_->getPlugins();
734  std::vector<std::shared_ptr<Layer>> * filters = layered_costmap_->getFilters();
735  for (std::vector<std::shared_ptr<Layer>>::iterator plugin = plugins->begin();
736  plugin != plugins->end(); ++plugin)
737  {
738  (*plugin)->reset();
739  }
740  for (std::vector<std::shared_ptr<Layer>>::iterator filter = filters->begin();
741  filter != filters->end(); ++filter)
742  {
743  (*filter)->reset();
744  }
745 }
746 
747 bool
748 Costmap2DROS::getRobotPose(geometry_msgs::msg::PoseStamped & global_pose)
749 {
750  return nav2_util::getCurrentPose(
751  global_pose, *tf_buffer_,
753 }
754 
755 bool
757  const geometry_msgs::msg::PoseStamped & input_pose,
758  geometry_msgs::msg::PoseStamped & transformed_pose)
759 {
760  if (input_pose.header.frame_id == global_frame_) {
761  transformed_pose = input_pose;
762  return true;
763  } else {
764  return nav2_util::transformPoseInTargetFrame(
765  input_pose, transformed_pose, *tf_buffer_,
767  }
768 }
769 
770 rcl_interfaces::msg::SetParametersResult Costmap2DROS::validateParameterUpdatesCallback(
771  const std::vector<rclcpp::Parameter> & parameters)
772 {
773  rcl_interfaces::msg::SetParametersResult result;
774  result.successful = true;
775  for (const auto & parameter : parameters) {
776  const auto & param_type = parameter.get_type();
777  const auto & param_name = parameter.get_name();
778  if (param_name.find('.') != std::string::npos) {
779  continue;
780  }
781  if (param_type == ParameterType::PARAMETER_DOUBLE) {
782  if (parameter.as_double() <= 0.0 &&
783  (param_name == "resolution" || param_name == "publish_frequency"))
784  {
785  RCLCPP_WARN(
786  get_logger(), "The value of parameter '%s' is incorrectly set to %f, "
787  "it should be >0. Ignoring parameter update.",
788  param_name.c_str(), parameter.as_double());
789  result.successful = false;
790  } else if (parameter.as_double() < 0.0 && // NOLINT
791  (param_name != "origin_x" && param_name != "origin_y"))
792  {
793  RCLCPP_WARN(
794  get_logger(), "The value of parameter '%s' is incorrectly set to %f, "
795  "it should be >0. Ignoring parameter update.",
796  param_name.c_str(), parameter.as_double());
797  result.successful = false;
798  }
799  } else if (param_type == ParameterType::PARAMETER_INTEGER) {
800  if (parameter.as_int() <= 0.0) {
801  RCLCPP_WARN(
802  get_logger(), "The value of parameter '%s' is incorrectly set to %ld, "
803  "it should be >0. Ignoring parameter update.",
804  param_name.c_str(), parameter.as_int());
805  result.successful = false;
806  }
807  } else if (param_type == ParameterType::PARAMETER_STRING && param_name == "robot_base_frame") {
808  // First, make sure that the transform between the robot base frame
809  // and the global frame is available
810  std::string tf_error;
811  RCLCPP_INFO(get_logger(), "Checking transform");
812  if (!tf_buffer_->canTransform(
813  global_frame_, parameter.as_string(), tf2::TimePointZero,
814  tf2::durationFromSec(1.0), &tf_error))
815  {
816  RCLCPP_WARN(
817  get_logger(), "Timed out waiting for transform from %s to %s"
818  " to become available, tf error: %s",
819  parameter.as_string().c_str(), global_frame_.c_str(), tf_error.c_str());
820  RCLCPP_WARN(
821  get_logger(), "Rejecting robot_base_frame change to %s , leaving it to its original"
822  " value of %s", parameter.as_string().c_str(), robot_base_frame_.c_str());
823  result.successful = false;
824  }
825  }
826  }
827  return result;
828 }
829 
830 void
831 Costmap2DROS::updateParametersCallback(const std::vector<rclcpp::Parameter> & parameters)
832 {
833  bool resize_map = false;
834  std::lock_guard<std::mutex> lock_reinit(_dynamic_parameter_mutex);
835 
836  for (const auto & parameter : parameters) {
837  const auto & param_type = parameter.get_type();
838  const auto & param_name = parameter.get_name();
839  if (param_name.find('.') != std::string::npos) {
840  continue;
841  }
842 
843  if (param_type == ParameterType::PARAMETER_DOUBLE) {
844  if (param_name == "robot_radius") {
845  robot_radius_ = parameter.as_double();
846  // Set the footprint
847  if (use_radius_) {
849  }
850  } else if (param_name == "footprint_padding") {
851  footprint_padding_ = parameter.as_double();
852 #ifdef __cpp_lib_atomic_shared_ptr
853  auto padded = std::make_shared<std::vector<geometry_msgs::msg::Point>>(
854  *unpadded_footprint_.load());
855  padFootprint(*padded, footprint_padding_);
856  padded_footprint_.store(padded);
857 #else
858  auto padded = std::make_shared<std::vector<geometry_msgs::msg::Point>>(
859  *std::atomic_load(&unpadded_footprint_));
860  padFootprint(*padded, footprint_padding_);
861  std::atomic_store(&padded_footprint_, padded);
862 #endif
863  layered_costmap_->setFootprint(*padded);
864  } else if (param_name == "transform_tolerance") {
865  transform_tolerance_ = parameter.as_double();
866  } else if (param_name == "publish_frequency") {
867  map_publish_frequency_ = parameter.as_double();
868  publish_cycle_ = rclcpp::Duration::from_seconds(1 / map_publish_frequency_);
869  } else if (param_name == "resolution") {
870  resize_map = true;
871  resolution_ = parameter.as_double();
872  } else if (param_name == "origin_x") {
873  resize_map = true;
874  origin_x_ = parameter.as_double();
875  } else if (param_name == "origin_y") {
876  resize_map = true;
877  origin_y_ = parameter.as_double();
878  }
879  } else if (param_type == ParameterType::PARAMETER_INTEGER) {
880  if (param_name == "width") {
881  resize_map = true;
882  map_width_meters_ = parameter.as_int();
883  } else if (param_name == "height") {
884  resize_map = true;
885  map_height_meters_ = parameter.as_int();
886  }
887  } else if (param_type == ParameterType::PARAMETER_STRING) {
888  if (param_name == "footprint") {
889  footprint_ = parameter.as_string();
890  std::vector<geometry_msgs::msg::Point> new_footprint;
891  if (makeFootprintFromString(footprint_, new_footprint)) {
892  setRobotFootprint(new_footprint);
893  }
894  } else if (param_name == "robot_base_frame") {
895  robot_base_frame_ = parameter.as_string();
896  }
897  }
898  }
899 
900  if (resize_map && !layered_costmap_->isSizeLocked()) {
901  layered_costmap_->resizeMap(
902  (unsigned int)(map_width_meters_ / resolution_),
903  (unsigned int)(map_height_meters_ / resolution_), resolution_, origin_x_, origin_y_);
904  updateMap();
905  }
906 }
907 
909  const std::shared_ptr<rmw_request_id_t>,
910  const std::shared_ptr<nav2_msgs::srv::GetCosts::Request> request,
911  const std::shared_ptr<nav2_msgs::srv::GetCosts::Response> response)
912 {
913  unsigned int mx, my;
914 
915  Costmap2D * costmap = layered_costmap_->getCostmap();
916  std::unique_lock<Costmap2D::mutex_t> lock(*(costmap->getMutex()));
917  response->success = true;
918  for (const auto & pose : request->poses) {
919  geometry_msgs::msg::PoseStamped pose_transformed;
920  if (!transformPoseToGlobalFrame(pose, pose_transformed)) {
921  RCLCPP_ERROR(
922  get_logger(), "Failed to transform, cannot get cost for pose (%.2f, %.2f)",
923  pose.pose.position.x, pose.pose.position.y);
924  response->success = false;
925  response->costs.push_back(NO_INFORMATION);
926  continue;
927  }
928  double yaw = tf2::getYaw(pose_transformed.pose.orientation);
929 
930  if (request->use_footprint) {
931  Footprint footprint = layered_costmap_->getFootprint();
932  FootprintCollisionChecker<Costmap2D *> collision_checker(costmap);
933 
934  RCLCPP_DEBUG(
935  get_logger(), "Received request to get cost at footprint pose (%.2f, %.2f, %.2f)",
936  pose_transformed.pose.position.x, pose_transformed.pose.position.y, yaw);
937 
938  response->costs.push_back(
939  collision_checker.footprintCostAtPose(
940  pose_transformed.pose.position.x,
941  pose_transformed.pose.position.y, yaw, footprint));
942  } else {
943  RCLCPP_DEBUG(
944  get_logger(), "Received request to get cost at point (%f, %f)",
945  pose_transformed.pose.position.x,
946  pose_transformed.pose.position.y);
947 
948  bool in_bounds = costmap->worldToMap(
949  pose_transformed.pose.position.x,
950  pose_transformed.pose.position.y, mx, my);
951 
952  if (!in_bounds) {
953  response->success = false;
954  response->costs.push_back(LETHAL_OBSTACLE);
955  continue;
956  }
957  // Get the cost at the map coordinates
958  response->costs.push_back(static_cast<float>(costmap->getCost(mx, my)));
959  }
960  }
961 }
962 
963 } // 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