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