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