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