Nav2 Navigation Stack - jazzy  jazzy
ROS 2 Navigation Stack
nav2_smoother.cpp
1 // Copyright (c) 2019 RoboTech Vision
2 // Copyright (c) 2019 Intel Corporation
3 // Copyright (c) 2022 Samsung Research America
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 
17 #include <chrono>
18 #include <memory>
19 #include <string>
20 #include <utility>
21 #include <vector>
22 
23 #include "nav2_core/smoother_exceptions.hpp"
24 #include "nav2_smoother/nav2_smoother.hpp"
25 #include "nav2_util/node_utils.hpp"
26 #include "nav_2d_utils/conversions.hpp"
27 #include "nav_2d_utils/tf_help.hpp"
28 #include "tf2_ros/create_timer_ros.h"
29 
30 using namespace std::chrono_literals;
31 
32 namespace nav2_smoother
33 {
34 
35 SmootherServer::SmootherServer(const rclcpp::NodeOptions & options)
36 : LifecycleNode("smoother_server", "", options),
37  lp_loader_("nav2_core", "nav2_core::Smoother"),
38  default_ids_{"simple_smoother"},
39  default_types_{"nav2_smoother::SimpleSmoother"}
40 {
41  RCLCPP_INFO(get_logger(), "Creating smoother server");
42 
43  declare_parameter(
44  "costmap_topic", rclcpp::ParameterValue(
45  std::string(
46  "global_costmap/costmap_raw")));
47  declare_parameter(
48  "footprint_topic",
49  rclcpp::ParameterValue(
50  std::string("global_costmap/published_footprint")));
51  declare_parameter(
52  "robot_base_frame",
53  rclcpp::ParameterValue(std::string("base_link")));
54  declare_parameter("transform_tolerance", rclcpp::ParameterValue(0.1));
55  declare_parameter("smoother_plugins", default_ids_);
56 
57  declare_parameter("action_server_result_timeout", 10.0);
58 }
59 
61 {
62  smoothers_.clear();
63 }
64 
65 nav2_util::CallbackReturn
66 SmootherServer::on_configure(const rclcpp_lifecycle::State & state)
67 {
68  RCLCPP_INFO(get_logger(), "Configuring smoother server");
69 
70  auto node = shared_from_this();
71 
72  get_parameter("smoother_plugins", smoother_ids_);
73  if (smoother_ids_ == default_ids_) {
74  for (size_t i = 0; i < default_ids_.size(); ++i) {
75  nav2_util::declare_parameter_if_not_declared(
76  node, default_ids_[i] + ".plugin",
77  rclcpp::ParameterValue(default_types_[i]));
78  }
79  }
80 
81  tf_ = std::make_shared<tf2_ros::Buffer>(get_clock());
82  auto timer_interface = std::make_shared<tf2_ros::CreateTimerROS>(
83  get_node_base_interface(), get_node_timers_interface());
84  tf_->setCreateTimerInterface(timer_interface);
85  transform_listener_ = std::make_shared<tf2_ros::TransformListener>(*tf_);
86 
87  std::string costmap_topic, footprint_topic, robot_base_frame;
88  double transform_tolerance;
89  this->get_parameter("costmap_topic", costmap_topic);
90  this->get_parameter("footprint_topic", footprint_topic);
91  this->get_parameter("transform_tolerance", transform_tolerance);
92  this->get_parameter("robot_base_frame", robot_base_frame);
93  costmap_sub_ = std::make_shared<nav2_costmap_2d::CostmapSubscriber>(
94  shared_from_this(), costmap_topic);
95  footprint_sub_ = std::make_shared<nav2_costmap_2d::FootprintSubscriber>(
96  shared_from_this(), footprint_topic, *tf_, robot_base_frame, transform_tolerance);
97 
98  collision_checker_ =
99  std::make_shared<nav2_costmap_2d::CostmapTopicCollisionChecker>(
100  *costmap_sub_, *footprint_sub_, this->get_name());
101 
102  if (!loadSmootherPlugins()) {
103  on_cleanup(state);
104  return nav2_util::CallbackReturn::FAILURE;
105  }
106 
107  // Initialize pubs & subs
108  plan_publisher_ = create_publisher<nav_msgs::msg::Path>("plan_smoothed", 1);
109 
110  double action_server_result_timeout;
111  get_parameter("action_server_result_timeout", action_server_result_timeout);
112  rcl_action_server_options_t server_options = rcl_action_server_get_default_options();
113  server_options.result_timeout.nanoseconds = RCL_S_TO_NS(action_server_result_timeout);
114 
115  // Create the action server that we implement with our smoothPath method
116  action_server_ = std::make_unique<ActionServer>(
118  "smooth_path",
119  std::bind(&SmootherServer::smoothPlan, this),
120  nullptr,
121  std::chrono::milliseconds(500),
122  true, server_options);
123 
124  return nav2_util::CallbackReturn::SUCCESS;
125 }
126 
128 {
129  auto node = shared_from_this();
130 
131  smoother_types_.resize(smoother_ids_.size());
132 
133  for (size_t i = 0; i != smoother_ids_.size(); i++) {
134  try {
135  smoother_types_[i] =
136  nav2_util::get_plugin_type_param(node, smoother_ids_[i]);
137  nav2_core::Smoother::Ptr smoother =
138  lp_loader_.createUniqueInstance(smoother_types_[i]);
139  RCLCPP_INFO(
140  get_logger(), "Created smoother : %s of type %s",
141  smoother_ids_[i].c_str(), smoother_types_[i].c_str());
142  smoother->configure(
143  node, smoother_ids_[i], tf_, costmap_sub_,
144  footprint_sub_);
145  smoothers_.insert({smoother_ids_[i], smoother});
146  } catch (const std::exception & ex) {
147  RCLCPP_FATAL(
148  get_logger(), "Failed to create smoother. Exception: %s",
149  ex.what());
150  return false;
151  }
152  }
153 
154  for (size_t i = 0; i != smoother_ids_.size(); i++) {
155  smoother_ids_concat_ += smoother_ids_[i] + std::string(" ");
156  }
157 
158  RCLCPP_INFO(
159  get_logger(), "Smoother Server has %s smoothers available.",
160  smoother_ids_concat_.c_str());
161 
162  return true;
163 }
164 
165 nav2_util::CallbackReturn
166 SmootherServer::on_activate(const rclcpp_lifecycle::State & /*state*/)
167 {
168  RCLCPP_INFO(get_logger(), "Activating");
169 
170  plan_publisher_->on_activate();
171  SmootherMap::iterator it;
172  for (it = smoothers_.begin(); it != smoothers_.end(); ++it) {
173  it->second->activate();
174  }
175  action_server_->activate();
176 
177  // create bond connection
178  createBond();
179 
180  return nav2_util::CallbackReturn::SUCCESS;
181 }
182 
183 nav2_util::CallbackReturn
184 SmootherServer::on_deactivate(const rclcpp_lifecycle::State &)
185 {
186  RCLCPP_INFO(get_logger(), "Deactivating");
187 
188  action_server_->deactivate();
189  SmootherMap::iterator it;
190  for (it = smoothers_.begin(); it != smoothers_.end(); ++it) {
191  it->second->deactivate();
192  }
193  plan_publisher_->on_deactivate();
194 
195  // destroy bond connection
196  destroyBond();
197 
198  return nav2_util::CallbackReturn::SUCCESS;
199 }
200 
201 nav2_util::CallbackReturn
202 SmootherServer::on_cleanup(const rclcpp_lifecycle::State &)
203 {
204  RCLCPP_INFO(get_logger(), "Cleaning up");
205 
206  // Cleanup the helper classes
207  SmootherMap::iterator it;
208  for (it = smoothers_.begin(); it != smoothers_.end(); ++it) {
209  it->second->cleanup();
210  }
211  smoothers_.clear();
212 
213  // Release any allocated resources
214  action_server_.reset();
215  plan_publisher_.reset();
216  transform_listener_.reset();
217  tf_.reset();
218  footprint_sub_.reset();
219  costmap_sub_.reset();
220  collision_checker_.reset();
221 
222  return nav2_util::CallbackReturn::SUCCESS;
223 }
224 
225 nav2_util::CallbackReturn
226 SmootherServer::on_shutdown(const rclcpp_lifecycle::State &)
227 {
228  RCLCPP_INFO(get_logger(), "Shutting down");
229  return nav2_util::CallbackReturn::SUCCESS;
230 }
231 
233  const std::string & c_name,
234  std::string & current_smoother)
235 {
236  if (smoothers_.find(c_name) == smoothers_.end()) {
237  if (smoothers_.size() == 1 && c_name.empty()) {
238  RCLCPP_WARN_ONCE(
239  get_logger(),
240  "No smoother was specified in action call."
241  " Server will use only plugin loaded %s. "
242  "This warning will appear once.",
243  smoother_ids_concat_.c_str());
244  current_smoother = smoothers_.begin()->first;
245  } else {
246  RCLCPP_ERROR(
247  get_logger(),
248  "SmoothPath called with smoother name %s, "
249  "which does not exist. Available smoothers are: %s.",
250  c_name.c_str(), smoother_ids_concat_.c_str());
251  return false;
252  }
253  } else {
254  RCLCPP_DEBUG(get_logger(), "Selected smoother: %s.", c_name.c_str());
255  current_smoother = c_name;
256  }
257 
258  return true;
259 }
260 
262 {
263  auto start_time = this->now();
264 
265  RCLCPP_INFO(get_logger(), "Received a path to smooth.");
266 
267  auto result = std::make_shared<Action::Result>();
268  try {
269  auto goal = action_server_->get_current_goal();
270  if (!goal) {
271  return; // if action_server_ is inactivate, goal would be a nullptr
272  }
273 
274  std::string c_name = goal->smoother_id;
275  std::string current_smoother;
276  if (findSmootherId(c_name, current_smoother)) {
277  current_smoother_ = current_smoother;
278  } else {
279  throw nav2_core::InvalidSmoother("Invalid Smoother: " + c_name);
280  }
281 
282  // Perform smoothing
283  result->path = goal->path;
284 
285  if (!validate(result->path)) {
286  throw nav2_core::InvalidPath("Requested path to smooth is invalid");
287  }
288 
289  result->was_completed = smoothers_[current_smoother_]->smooth(
290  result->path, goal->max_smoothing_duration);
291  result->smoothing_duration = this->now() - start_time;
292 
293  if (!result->was_completed) {
294  RCLCPP_INFO(
295  get_logger(),
296  "Smoother %s did not complete smoothing in specified time limit"
297  "(%lf seconds) and was interrupted after %lf seconds",
298  current_smoother_.c_str(),
299  rclcpp::Duration(goal->max_smoothing_duration).seconds(),
300  rclcpp::Duration(result->smoothing_duration).seconds());
301  }
302 
303  plan_publisher_->publish(result->path);
304 
305  // Check for collisions
306  if (goal->check_for_collisions) {
307  geometry_msgs::msg::Pose2D pose2d;
308  bool fetch_data = true;
309  for (const auto & pose : result->path.poses) {
310  pose2d.x = pose.pose.position.x;
311  pose2d.y = pose.pose.position.y;
312  pose2d.theta = tf2::getYaw(pose.pose.orientation);
313 
314  if (!collision_checker_->isCollisionFree(pose2d, fetch_data)) {
315  RCLCPP_ERROR(
316  get_logger(),
317  "Smoothed path leads to a collision at x: %lf, y: %lf, theta: %lf",
318  pose2d.x, pose2d.y, pose2d.theta);
320  "Smoothed Path collided at"
321  "X: " + std::to_string(pose2d.x) +
322  "Y: " + std::to_string(pose2d.y) +
323  "Theta: " + std::to_string(pose2d.theta));
324  }
325  fetch_data = false;
326  }
327  }
328 
329  RCLCPP_DEBUG(
330  get_logger(), "Smoother succeeded (time: %lf), setting result",
331  rclcpp::Duration(result->smoothing_duration).seconds());
332 
333  action_server_->succeeded_current(result);
334  } catch (nav2_core::InvalidSmoother & ex) {
335  RCLCPP_ERROR(this->get_logger(), "%s", ex.what());
336  result->error_code = ActionResult::INVALID_SMOOTHER;
337  action_server_->terminate_current(result);
338  return;
339  } catch (nav2_core::SmootherTimedOut & ex) {
340  RCLCPP_ERROR(this->get_logger(), "%s", ex.what());
341  result->error_code = ActionResult::TIMEOUT;
342  action_server_->terminate_current(result);
343  return;
344  } catch (nav2_core::SmoothedPathInCollision & ex) {
345  RCLCPP_ERROR(this->get_logger(), "%s", ex.what());
346  result->error_code = ActionResult::SMOOTHED_PATH_IN_COLLISION;
347  action_server_->terminate_current(result);
348  return;
349  } catch (nav2_core::FailedToSmoothPath & ex) {
350  RCLCPP_ERROR(this->get_logger(), "%s", ex.what());
351  result->error_code = ActionResult::FAILED_TO_SMOOTH_PATH;
352  action_server_->terminate_current(result);
353  return;
354  } catch (nav2_core::InvalidPath & ex) {
355  RCLCPP_ERROR(this->get_logger(), "%s", ex.what());
356  result->error_code = ActionResult::INVALID_PATH;
357  action_server_->terminate_current(result);
358  return;
359  } catch (nav2_core::SmootherException & ex) {
360  RCLCPP_ERROR(this->get_logger(), "%s", ex.what());
361  result->error_code = ActionResult::UNKNOWN;
362  action_server_->terminate_current(result);
363  return;
364  } catch (std::exception & ex) {
365  RCLCPP_ERROR(this->get_logger(), "%s", ex.what());
366  result->error_code = ActionResult::UNKNOWN;
367  action_server_->terminate_current(result);
368  return;
369  }
370 }
371 
372 bool SmootherServer::validate(const nav_msgs::msg::Path & path)
373 {
374  if (path.poses.empty()) {
375  RCLCPP_WARN(get_logger(), "Requested path to smooth is empty");
376  return false;
377  }
378 
379  RCLCPP_DEBUG(get_logger(), "Requested path to smooth is valid");
380  return true;
381 }
382 
383 } // namespace nav2_smoother
384 
385 #include "rclcpp_components/register_node_macro.hpp"
386 
387 // Register the component with class_loader.
388 // This acts as a sort of entry point, allowing the component to be discoverable when its library
389 // is being loaded into a running process.
390 RCLCPP_COMPONENTS_REGISTER_NODE(nav2_smoother::SmootherServer)
This class hosts variety of plugins of different algorithms to smooth or refine a path from the expos...
nav2_util::CallbackReturn on_activate(const rclcpp_lifecycle::State &state) override
Activates member variables.
~SmootherServer()
Destructor for nav2_smoother::SmootherServer.
nav2_util::CallbackReturn on_deactivate(const rclcpp_lifecycle::State &state) override
Deactivates member variables.
bool loadSmootherPlugins()
Loads smoother plugins from parameter file.
bool validate(const nav_msgs::msg::Path &path)
Validate that the path contains a meaningful path for smoothing.
nav2_util::CallbackReturn on_configure(const rclcpp_lifecycle::State &state) override
Configures smoother parameters and member variables.
nav2_util::CallbackReturn on_shutdown(const rclcpp_lifecycle::State &state) override
Called when in Shutdown state.
bool findSmootherId(const std::string &c_name, std::string &name)
Find the valid smoother ID name for the given request.
void smoothPlan()
SmoothPath action server callback. Handles action server updates and spins server until goal is reach...
nav2_util::CallbackReturn on_cleanup(const rclcpp_lifecycle::State &state) override
Calls clean up states and resets member variables.
std::shared_ptr< nav2_util::LifecycleNode > shared_from_this()
Get a shared pointer of this.
void createBond()
Create bond connection to lifecycle manager.
void destroyBond()
Destroy bond connection to lifecycle manager.