Nav2 Navigation Stack - lyrical  lyrical
ROS 2 Navigation Stack
controller_server.cpp
1 // Copyright (c) 2019 Intel Corporation
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <chrono>
16 #include <vector>
17 #include <memory>
18 #include <string>
19 #include <utility>
20 #include <limits>
21 
22 #include "lifecycle_msgs/msg/state.hpp"
23 #include "nav2_core/controller_exceptions.hpp"
24 #include "nav2_ros_common/node_utils.hpp"
25 #include "nav2_ros_common/rate.hpp"
26 #include "nav2_util/geometry_utils.hpp"
27 #include "nav2_util/path_utils.hpp"
28 #include "nav2_controller/controller_server.hpp"
29 
30 using namespace std::chrono_literals;
31 using rcl_interfaces::msg::ParameterType;
32 using std::placeholders::_1;
33 using nav2_util::geometry_utils::euclidean_distance;
34 
35 namespace nav2_controller
36 {
37 
38 ControllerServer::ControllerServer(const rclcpp::NodeOptions & options)
39 : nav2::LifecycleNode("controller_server", "", options),
40  progress_checker_loader_("nav2_core", "nav2_core::ProgressChecker"),
41  goal_checker_loader_("nav2_core", "nav2_core::GoalChecker"),
42  lp_loader_("nav2_core", "nav2_core::Controller"),
43  path_handler_loader_("nav2_core", "nav2_core::PathHandler"),
44  start_index_(0)
45 {
46  RCLCPP_INFO(get_logger(), "Creating controller server");
47 
48  // The costmap node is used in the implementation of the controller
49  costmap_ros_ = std::make_shared<nav2_costmap_2d::Costmap2DROS>(
50  "local_costmap", std::string{get_namespace()},
51  get_parameter("use_sim_time").as_bool(), options);
52 }
53 
55 {
56  progress_checkers_.clear();
57  goal_checkers_.clear();
58  controllers_.clear();
59  path_handlers_.clear();
60  costmap_thread_.reset();
61 }
62 
63 nav2::CallbackReturn
64 ControllerServer::on_configure(const rclcpp_lifecycle::State & state)
65 {
66  auto node = shared_from_this();
67 
68  RCLCPP_INFO(get_logger(), "Configuring controller interface");
69 
70  if (costmap_ros_->configure().id() !=
71  lifecycle_msgs::msg::State::PRIMARY_STATE_INACTIVE)
72  {
73  return nav2::CallbackReturn::FAILURE;
74  }
75  // Launch a thread to run the costmap node
76  costmap_thread_ = std::make_unique<nav2::NodeThread>(costmap_ros_);
77  transform_tolerance_ = costmap_ros_->getTransformTolerance();
78  try {
79  param_handler_ = std::make_unique<ParameterHandler>(
80  node, get_logger());
81  } catch (const std::exception & ex) {
82  RCLCPP_FATAL(get_logger(), "%s", ex.what());
83  on_cleanup(state);
84  return nav2::CallbackReturn::FAILURE;
85  }
86  params_ = param_handler_->getParams();
87 
88  for (size_t i = 0; i != params_->progress_checker_ids.size(); i++) {
89  try {
90  nav2_core::ProgressChecker::Ptr progress_checker =
91  progress_checker_loader_.createUniqueInstance(params_->progress_checker_types[i]);
92  RCLCPP_INFO(
93  get_logger(), "Created progress_checker : %s of type %s",
94  params_->progress_checker_ids[i].c_str(), params_->progress_checker_types[i].c_str());
95  progress_checkers_.insert({params_->progress_checker_ids[i], progress_checker});
96  } catch (const std::exception & ex) {
97  RCLCPP_FATAL(
98  get_logger(),
99  "Failed to create progress_checker. Exception: %s", ex.what());
100  on_cleanup(state);
101  return nav2::CallbackReturn::FAILURE;
102  }
103  }
104 
105  for (size_t i = 0; i != params_->progress_checker_ids.size(); i++) {
106  progress_checker_ids_concat_ += params_->progress_checker_ids[i] + std::string(" ");
107  }
108  if (progress_checker_ids_concat_.empty()) {
109  progress_checker_ids_concat_ = "(none)";
110  }
111 
112  RCLCPP_INFO(
113  get_logger(),
114  "Controller Server has %s progress checkers available.", progress_checker_ids_concat_.c_str());
115 
116  for (size_t i = 0; i != params_->goal_checker_ids.size(); i++) {
117  try {
118  nav2_core::GoalChecker::Ptr goal_checker =
119  goal_checker_loader_.createUniqueInstance(params_->goal_checker_types[i]);
120  RCLCPP_INFO(
121  get_logger(), "Created goal checker : %s of type %s",
122  params_->goal_checker_ids[i].c_str(), params_->goal_checker_types[i].c_str());
123  goal_checkers_.insert({params_->goal_checker_ids[i], goal_checker});
124  } catch (const pluginlib::PluginlibException & ex) {
125  RCLCPP_FATAL(
126  get_logger(),
127  "Failed to create goal checker. Exception: %s", ex.what());
128  on_cleanup(state);
129  return nav2::CallbackReturn::FAILURE;
130  }
131  }
132 
133  for (size_t i = 0; i != params_->goal_checker_ids.size(); i++) {
134  goal_checker_ids_concat_ += params_->goal_checker_ids[i] + std::string(" ");
135  }
136 
137  RCLCPP_INFO(
138  get_logger(),
139  "Controller Server has %s goal checkers available.", goal_checker_ids_concat_.c_str());
140 
141  for (size_t i = 0; i != params_->path_handler_ids.size(); i++) {
142  try {
143  nav2_core::PathHandler::Ptr path_handler =
144  path_handler_loader_.createUniqueInstance(params_->path_handler_types[i]);
145  RCLCPP_INFO(
146  get_logger(), "Created path handler : %s of type %s",
147  params_->path_handler_ids[i].c_str(), params_->path_handler_types[i].c_str());
148  path_handlers_.insert({params_->path_handler_ids[i], path_handler});
149  } catch (const pluginlib::PluginlibException & ex) {
150  RCLCPP_FATAL(
151  get_logger(),
152  "Failed to create path handler Exception: %s", ex.what());
153  on_cleanup(state);
154  return nav2::CallbackReturn::FAILURE;
155  }
156  }
157 
158  for (size_t i = 0; i != params_->path_handler_ids.size(); i++) {
159  path_handler_ids_concat_ += params_->path_handler_ids[i] + std::string(" ");
160  }
161 
162  RCLCPP_INFO(
163  get_logger(),
164  "Controller Server has %s path handlers available.", path_handler_ids_concat_.c_str());
165 
166  for (size_t i = 0; i != params_->controller_ids.size(); i++) {
167  try {
168  nav2_core::Controller::Ptr controller =
169  lp_loader_.createUniqueInstance(params_->controller_types[i]);
170  RCLCPP_INFO(
171  get_logger(), "Created controller : %s of type %s",
172  params_->controller_ids[i].c_str(), params_->controller_types[i].c_str());
173  controller->configure(
174  node, params_->controller_ids[i],
175  costmap_ros_->getTfBuffer(), costmap_ros_);
176  controllers_.insert({params_->controller_ids[i], controller});
177  } catch (const pluginlib::PluginlibException & ex) {
178  RCLCPP_FATAL(
179  get_logger(),
180  "Failed to create controller. Exception: %s", ex.what());
181  on_cleanup(state);
182  return nav2::CallbackReturn::FAILURE;
183  }
184  }
185 
186  for (size_t i = 0; i != params_->controller_ids.size(); i++) {
187  controller_ids_concat_ += params_->controller_ids[i] + std::string(" ");
188  }
189 
190  RCLCPP_INFO(
191  get_logger(),
192  "Controller Server has %s controllers available.", controller_ids_concat_.c_str());
193 
194  odom_sub_ = std::make_unique<nav2_util::OdomSmoother>(node, params_->odom_duration,
195  params_->odom_topic);
196  vel_publisher_ = std::make_unique<nav2_util::TwistPublisher>(node, "cmd_vel");
197  transformed_plan_pub_ = create_publisher<nav_msgs::msg::Path>("transformed_global_plan");
198  tracking_feedback_pub_ = create_publisher<nav2_msgs::msg::TrackingFeedback>("tracking_feedback");
199 
200  // Create the action server that we implement with our followPath method
201  // This may throw due to real-time prioritization if user doesn't have real-time permissions
202  try {
203  action_server_ = create_action_server<Action>(
204  "follow_path",
205  std::bind(&ControllerServer::computeControl, this),
206  std::bind(&ControllerServer::goalReceived, this, std::placeholders::_1),
207  nullptr,
208  std::chrono::milliseconds(500),
209  true /*spin thread*/, params_->use_realtime_priority /*soft realtime*/);
210  } catch (const std::runtime_error & e) {
211  RCLCPP_ERROR(get_logger(), "Error creating action server! %s", e.what());
212  on_cleanup(state);
213  return nav2::CallbackReturn::FAILURE;
214  }
215 
216  // Set subscription to the speed limiting topic
217  speed_limit_sub_ = create_subscription<nav2_msgs::msg::SpeedLimit>(
218  params_->speed_limit_topic,
219  std::bind(&ControllerServer::speedLimitCallback, this, std::placeholders::_1));
220 
221  return nav2::CallbackReturn::SUCCESS;
222 }
223 
224 nav2::CallbackReturn
225 ControllerServer::on_activate(const rclcpp_lifecycle::State & /*state*/)
226 {
227  RCLCPP_INFO(get_logger(), "Activating");
228 
229  const auto costmap_ros_state = costmap_ros_->activate();
230  if (costmap_ros_state.id() != lifecycle_msgs::msg::State::PRIMARY_STATE_ACTIVE) {
231  return nav2::CallbackReturn::FAILURE;
232  }
233  ControllerMap::iterator it;
234  for (it = controllers_.begin(); it != controllers_.end(); ++it) {
235  it->second->activate();
236  }
237  vel_publisher_->on_activate();
238  transformed_plan_pub_->on_activate();
239  tracking_feedback_pub_->on_activate();
240  action_server_->activate();
241  param_handler_->activate();
242 
243  // activate goal checker, progress checker and path handler
244  auto node = shared_from_this();
245  for (auto & pc : progress_checkers_) {
246  pc.second->initialize(node, pc.first);
247  }
248  for (auto & gc : goal_checkers_) {
249  gc.second->initialize(node, gc.first, costmap_ros_);
250  }
251  for (auto & ph : path_handlers_) {
252  ph.second->initialize(
253  node, get_logger(), ph.first, costmap_ros_,
254  costmap_ros_->getTfBuffer());
255  }
256 
257  // create bond connection
258  createBond();
259 
260  return nav2::CallbackReturn::SUCCESS;
261 }
262 
263 nav2::CallbackReturn
264 ControllerServer::on_deactivate(const rclcpp_lifecycle::State & /*state*/)
265 {
266  RCLCPP_INFO(get_logger(), "Deactivating");
267 
268  action_server_->deactivate();
269  ControllerMap::iterator it;
270  for (it = controllers_.begin(); it != controllers_.end(); ++it) {
271  it->second->deactivate();
272  }
273 
274  /*
275  * The costmap is also a lifecycle node, so it may have already fired on_deactivate
276  * via rcl preshutdown cb. Despite the rclcpp docs saying on_shutdown callbacks fire
277  * in the order added, the preshutdown callbacks clearly don't per se, due to using an
278  * unordered_set iteration. Once this issue is resolved, we can maybe make a stronger
279  * ordering assumption: https://github.com/ros2/rclcpp/issues/2096
280  */
281  costmap_ros_->deactivate();
282 
284  vel_publisher_->on_deactivate();
285  transformed_plan_pub_->on_deactivate();
286  tracking_feedback_pub_->on_deactivate();
287  param_handler_->deactivate();
288 
289  // destroy bond connection
290  destroyBond();
291 
292  return nav2::CallbackReturn::SUCCESS;
293 }
294 
295 nav2::CallbackReturn
296 ControllerServer::on_cleanup(const rclcpp_lifecycle::State & /*state*/)
297 {
298  RCLCPP_INFO(get_logger(), "Cleaning up");
299 
300  // Cleanup the helper classes
301  ControllerMap::iterator it;
302  for (it = controllers_.begin(); it != controllers_.end(); ++it) {
303  it->second->cleanup();
304  }
305  controllers_.clear();
306 
307  goal_checkers_.clear();
308  progress_checkers_.clear();
309  path_handlers_.clear();
310 
311  costmap_ros_->cleanup();
312 
313 
314  // Release any allocated resources
315  action_server_.reset();
316  odom_sub_.reset();
317  costmap_thread_.reset();
318  vel_publisher_.reset();
319  transformed_plan_pub_.reset();
320  tracking_feedback_pub_.reset();
321  speed_limit_sub_.reset();
322 
323  return nav2::CallbackReturn::SUCCESS;
324 }
325 
326 nav2::CallbackReturn
327 ControllerServer::on_shutdown(const rclcpp_lifecycle::State &)
328 {
329  RCLCPP_INFO(get_logger(), "Shutting down");
330  return nav2::CallbackReturn::SUCCESS;
331 }
332 
334  const std::string & c_name,
335  std::string & current_controller)
336 {
337  if (controllers_.find(c_name) == controllers_.end()) {
338  if (controllers_.size() == 1 && c_name.empty()) {
339  RCLCPP_WARN_ONCE(
340  get_logger(), "No controller was specified in action call."
341  " Server will use only plugin loaded %s. "
342  "This warning will appear once.", controller_ids_concat_.c_str());
343  current_controller = controllers_.begin()->first;
344  } else {
345  RCLCPP_ERROR(
346  get_logger(), "FollowPath called with controller name %s, "
347  "which does not exist. Available controllers are: %s.",
348  c_name.c_str(), controller_ids_concat_.c_str());
349  return false;
350  }
351  } else {
352  RCLCPP_DEBUG(get_logger(), "Selected controller: %s.", c_name.c_str());
353  current_controller = c_name;
354  }
355 
356  return true;
357 }
358 
360  const std::string & c_name,
361  std::string & current_goal_checker)
362 {
363  if (goal_checkers_.find(c_name) == goal_checkers_.end()) {
364  if (goal_checkers_.size() == 1 && c_name.empty()) {
365  RCLCPP_WARN_ONCE(
366  get_logger(), "No goal checker was specified in parameter 'current_goal_checker'."
367  " Server will use only plugin loaded %s. "
368  "This warning will appear once.", goal_checker_ids_concat_.c_str());
369  current_goal_checker = goal_checkers_.begin()->first;
370  } else {
371  RCLCPP_ERROR(
372  get_logger(), "FollowPath called with goal_checker name %s in parameter"
373  " 'current_goal_checker', which does not exist. Available goal checkers are: %s.",
374  c_name.c_str(), goal_checker_ids_concat_.c_str());
375  return false;
376  }
377  } else {
378  RCLCPP_DEBUG(get_logger(), "Selected goal checker: %s.", c_name.c_str());
379  current_goal_checker = c_name;
380  }
381 
382  return true;
383 }
384 
386  const std::string & c_name,
387  std::string & current_progress_checker)
388 {
389  if (progress_checkers_.size() == 0) {
390  if (c_name.empty()) {
391  RCLCPP_DEBUG(
392  get_logger(),
393  "No progress checker configured and none requested. Progress checking will be bypassed.");
394  current_progress_checker = "";
395  return true;
396  } else {
397  RCLCPP_ERROR(
398  get_logger(), "FollowPath called with progress_checker name %s in parameter"
399  " 'current_progress_checker', but no progress checkers are configured.",
400  c_name.c_str());
401  return false;
402  }
403  }
404 
405  if (progress_checkers_.find(c_name) == progress_checkers_.end()) {
406  if (progress_checkers_.size() == 1 && c_name.empty()) {
407  RCLCPP_WARN_ONCE(
408  get_logger(), "No progress checker was specified in parameter 'current_progress_checker'."
409  " Server will use only plugin loaded %s. "
410  "This warning will appear once.", progress_checker_ids_concat_.c_str());
411  current_progress_checker = progress_checkers_.begin()->first;
412  } else {
413  RCLCPP_ERROR(
414  get_logger(), "FollowPath called with progress_checker name %s in parameter"
415  " 'current_progress_checker', which does not exist. Available progress checkers are: %s.",
416  c_name.c_str(), progress_checker_ids_concat_.c_str());
417  return false;
418  }
419  } else {
420  RCLCPP_DEBUG(get_logger(), "Selected progress checker: %s.", c_name.c_str());
421  current_progress_checker = c_name;
422  }
423 
424  return true;
425 }
426 
428  const std::string & c_name,
429  std::string & current_path_handler)
430 {
431  if (path_handlers_.find(c_name) == path_handlers_.end()) {
432  if (path_handlers_.size() == 1 && c_name.empty()) {
433  RCLCPP_WARN_ONCE(
434  get_logger(), "No path handler was specified in parameter 'current_path_handler'."
435  " Server will use only plugin loaded %s. "
436  "This warning will appear once.", path_handler_ids_concat_.c_str());
437  current_path_handler = path_handlers_.begin()->first;
438  } else {
439  RCLCPP_ERROR(
440  get_logger(), "FollowPath called with path_handler name %s in parameter"
441  " 'current_path_handler', which does not exist. Available path handlers are: %s.",
442  c_name.c_str(), path_handler_ids_concat_.c_str());
443  return false;
444  }
445  } else {
446  RCLCPP_DEBUG(get_logger(), "Selected path handler: %s.", c_name.c_str());
447  current_path_handler = c_name;
448  }
449 
450  return true;
451 }
452 
453 bool ControllerServer::goalReceived(std::shared_ptr<const Action::Goal> goal)
454 {
455  std::string current_controller;
456  if (!findControllerId(goal->controller_id, current_controller)) {
457  RCLCPP_WARN(
458  get_logger(),
459  "Requested controller %s is not available.", goal->controller_id.c_str());
460  return false;
461  }
462 
463  std::string current_goal_checker;
464  if (!findGoalCheckerId(goal->goal_checker_id, current_goal_checker)) {
465  RCLCPP_WARN(
466  get_logger(),
467  "Requested goal checker %s is not available.", goal->goal_checker_id.c_str());
468  return false;
469  }
470 
471  std::string current_progress_checker;
472  if (!findProgressCheckerId(goal->progress_checker_id, current_progress_checker)) {
473  RCLCPP_WARN(
474  get_logger(),
475  "Requested progress checker %s is not available.", goal->progress_checker_id.c_str());
476  return false;
477  }
478 
479  std::string current_path_handler;
480  if (!findPathHandlerId(goal->path_handler_id, current_path_handler)) {
481  RCLCPP_WARN(
482  get_logger(),
483  "Requested path handler %s is not available.", goal->path_handler_id.c_str());
484  return false;
485  }
486 
487  if (goal->path.poses.empty()) {
488  RCLCPP_WARN(get_logger(), "Requested path to follow is empty.");
489  return false;
490  }
491 
492  return true;
493 }
494 
496 {
497  std::lock_guard<std::mutex> lock_reinit(param_handler_->getMutex());
498 
499  RCLCPP_INFO(get_logger(), "Received a goal, begin computing control effort.");
500 
501  try {
502  auto goal = action_server_->get_current_goal();
503  if (!goal) {
504  return; // goal would be nullptr if action_server_ is deactivate.
505  }
506 
507  std::string c_name = goal->controller_id;
508  std::string current_controller;
509  if (findControllerId(c_name, current_controller)) {
510  current_controller_ = current_controller;
511  } else {
512  throw nav2_core::InvalidController("Failed to find controller name: " + c_name);
513  }
514 
515  std::string gc_name = goal->goal_checker_id;
516  std::string current_goal_checker;
517  if (findGoalCheckerId(gc_name, current_goal_checker)) {
518  current_goal_checker_ = current_goal_checker;
519  } else {
520  throw nav2_core::ControllerException("Failed to find goal checker name: " + gc_name);
521  }
522 
523  std::string pc_name = goal->progress_checker_id;
524  std::string current_progress_checker;
525  if (findProgressCheckerId(pc_name, current_progress_checker)) {
526  current_progress_checker_ = current_progress_checker;
527  } else {
528  throw nav2_core::ControllerException("Failed to find progress checker name: " + pc_name);
529  }
530 
531  std::string ph_name = goal->path_handler_id;
532  std::string current_path_handler;
533  if(findPathHandlerId(ph_name, current_path_handler)) {
534  current_path_handler_ = current_path_handler;
535  } else {
536  throw nav2_core::ControllerException("Failed to find path handler name: " + ph_name);
537  }
538 
539  setPlannerPath(goal->path);
540  if (!current_progress_checker_.empty()) {
541  progress_checkers_[current_progress_checker_]->reset();
542  }
543 
544  last_valid_cmd_time_ = now();
545  nav2::Rate loop_rate(this, params_->controller_frequency);
546  while (rclcpp::ok()) {
547  auto start_time = this->now();
548 
549  if (action_server_ == nullptr || !action_server_->is_server_active()) {
550  RCLCPP_DEBUG(get_logger(), "Action server unavailable or inactive. Stopping.");
551  return;
552  }
553 
554  if (action_server_->is_cancel_requested()) {
555  if (controllers_[current_controller_]->cancel()) {
556  RCLCPP_INFO(get_logger(), "Cancellation was successful. Stopping the robot.");
557  action_server_->terminate_all();
558  onGoalExit(true);
559  return;
560  } else {
561  RCLCPP_INFO_THROTTLE(
562  get_logger(), *get_clock(), 1000, "Waiting for the controller to finish cancellation");
563  }
564  }
565 
566  // Don't compute a trajectory until costmap is valid (after clear costmap)
567  double costmap_wait = waitForCostmap();
568 
570 
571  // Refresh the transformed plan and goal together so they share a single map->odom snapshot
573 
574  if (isGoalReached()) {
575  RCLCPP_INFO(get_logger(), "Reached the goal!");
576  break;
577  }
578 
580 
581  auto cycle_duration = this->now() - start_time;
582  if (!loop_rate.sleep()) {
583  RCLCPP_WARN(
584  get_logger(),
585  "Control loop missed its desired rate of %.4f Hz. Current loop rate is %.4f Hz."
586  "%s",
587  params_->controller_frequency, 1 / cycle_duration.seconds(),
588  costmap_wait > 0.0 ?
589  (" Waited " + std::to_string(costmap_wait) + "s for costmap update.").c_str() : "");
590  loop_rate.reset();
591  }
592  }
593  } catch (nav2_core::InvalidController & e) {
594  RCLCPP_ERROR(this->get_logger(), "%s", e.what());
595  onGoalExit(true);
596  std::shared_ptr<Action::Result> result = std::make_shared<Action::Result>();
597  result->error_code = Action::Result::INVALID_CONTROLLER;
598  result->error_msg = e.what();
599  action_server_->terminate_current(result);
600  return;
601  } catch (nav2_core::ControllerTFError & e) {
602  RCLCPP_ERROR(this->get_logger(), "%s", e.what());
603  onGoalExit(true);
604  std::shared_ptr<Action::Result> result = std::make_shared<Action::Result>();
605  result->error_code = Action::Result::TF_ERROR;
606  result->error_msg = e.what();
607  action_server_->terminate_current(result);
608  return;
609  } catch (nav2_core::NoValidControl & e) {
610  RCLCPP_ERROR(this->get_logger(), "%s", e.what());
611  onGoalExit(true);
612  std::shared_ptr<Action::Result> result = std::make_shared<Action::Result>();
613  result->error_code = Action::Result::NO_VALID_CONTROL;
614  result->error_msg = e.what();
615  action_server_->terminate_current(result);
616  return;
617  } catch (nav2_core::FailedToMakeProgress & e) {
618  RCLCPP_ERROR(this->get_logger(), "%s", e.what());
619  onGoalExit(true);
620  std::shared_ptr<Action::Result> result = std::make_shared<Action::Result>();
621  result->error_code = Action::Result::FAILED_TO_MAKE_PROGRESS;
622  result->error_msg = e.what();
623  action_server_->terminate_current(result);
624  return;
625  } catch (nav2_core::PatienceExceeded & e) {
626  RCLCPP_ERROR(this->get_logger(), "%s", e.what());
627  onGoalExit(true);
628  std::shared_ptr<Action::Result> result = std::make_shared<Action::Result>();
629  result->error_code = Action::Result::PATIENCE_EXCEEDED;
630  result->error_msg = e.what();
631  action_server_->terminate_current(result);
632  return;
633  } catch (nav2_core::InvalidPath & e) {
634  RCLCPP_ERROR(this->get_logger(), "%s", e.what());
635  onGoalExit(true);
636  std::shared_ptr<Action::Result> result = std::make_shared<Action::Result>();
637  result->error_code = Action::Result::INVALID_PATH;
638  result->error_msg = e.what();
639  action_server_->terminate_current(result);
640  return;
641  } catch (nav2_core::ControllerTimedOut & e) {
642  RCLCPP_ERROR(this->get_logger(), "%s", e.what());
643  onGoalExit(true);
644  std::shared_ptr<Action::Result> result = std::make_shared<Action::Result>();
645  result->error_code = Action::Result::CONTROLLER_TIMED_OUT;
646  result->error_msg = e.what();
647  action_server_->terminate_current(result);
648  return;
649  } catch (nav2_core::ControllerException & e) {
650  RCLCPP_ERROR(this->get_logger(), "%s", e.what());
651  onGoalExit(true);
652  std::shared_ptr<Action::Result> result = std::make_shared<Action::Result>();
653  result->error_code = Action::Result::UNKNOWN;
654  result->error_msg = e.what();
655  action_server_->terminate_current(result);
656  return;
657  } catch (std::exception & e) {
658  RCLCPP_ERROR(this->get_logger(), "%s", e.what());
659  onGoalExit(true);
660  std::shared_ptr<Action::Result> result = std::make_shared<Action::Result>();
661  result->error_code = Action::Result::UNKNOWN;
662  result->error_msg = e.what();
663  action_server_->terminate_current(result);
664  return;
665  }
666 
667  RCLCPP_DEBUG(get_logger(), "Controller succeeded, setting result");
668 
669  onGoalExit(false);
670 
671  // TODO(orduno) #861 Handle a pending preemption and set controller name
672  action_server_->succeeded_current();
673 }
674 
676 {
677  if (params_->costmap_update_timeout > rclcpp::Duration(0, 0)) {
678  auto waiting_start = now();
679  bool was_waiting = !costmap_ros_->isCurrent();
680  try {
681  costmap_ros_->waitUntilCurrent(params_->costmap_update_timeout);
682  } catch (const std::runtime_error & ex) {
683  throw nav2_core::ControllerTimedOut(ex.what());
684  }
685  if (was_waiting) {
686  return (now() - waiting_start).seconds();
687  }
688  }
689  return 0.0;
690 }
691 
692 void ControllerServer::setPlannerPath(const nav_msgs::msg::Path & path)
693 {
694  RCLCPP_DEBUG(
695  get_logger(),
696  "Providing path to the controller %s", current_controller_.c_str());
697  if (path.poses.empty()) {
698  throw nav2_core::InvalidPath("Path is empty.");
699  }
700  controllers_[current_controller_]->newPathReceived(path);
701  path_handlers_[current_path_handler_]->setPlan(path);
702 
703  end_pose_ = path.poses.back();
704  end_pose_.header.frame_id = path.header.frame_id;
705  goal_checkers_[current_goal_checker_]->reset();
706 
707  RCLCPP_DEBUG(
708  get_logger(), "Path end point is (%.2f, %.2f)",
709  end_pose_.pose.position.x, end_pose_.pose.position.y);
710 
711  start_index_ = 0;
712  current_path_ = path;
713 }
714 
716 {
717  geometry_msgs::msg::PoseStamped pose;
718 
719  if (!getRobotPose(pose)) {
720  throw nav2_core::ControllerTFError("Failed to obtain robot pose");
721  }
722 
723  end_pose_.header.stamp = pose.header.stamp;
724  if (!nav2_util::transformPoseInTargetFrame(
725  end_pose_, transformed_end_pose_, *costmap_ros_->getTfBuffer(),
726  costmap_ros_->getGlobalFrameID(), transform_tolerance_))
727  {
728  throw nav2_core::ControllerTFError("Failed to transform end pose to global frame");
729  }
730 
731  auto [closest_point, pruned_plan_end] =
732  path_handlers_[current_path_handler_]->findPlanSegment(pose);
733  transformed_global_plan_ =
734  path_handlers_[current_path_handler_]->transformLocalPlan(closest_point, pruned_plan_end);
735 
736  auto path = std::make_unique<nav_msgs::msg::Path>(transformed_global_plan_);
737  if (transformed_plan_pub_->get_subscription_count() > 0) {
738  transformed_plan_pub_->publish(std::move(path));
739  }
740 }
741 
743 {
744  geometry_msgs::msg::PoseStamped pose;
745 
746  if (!getRobotPose(pose)) {
747  throw nav2_core::ControllerTFError("Failed to obtain robot pose");
748  }
749 
750  if (!current_progress_checker_.empty()) {
751  if (!progress_checkers_[current_progress_checker_]->check(pose)) {
752  throw nav2_core::FailedToMakeProgress("Failed to make progress");
753  }
754  }
755 
756  geometry_msgs::msg::Twist twist = getThresholdedTwist(odom_sub_->getRawTwist());
757 
758  geometry_msgs::msg::PoseStamped goal =
759  path_handlers_[current_path_handler_]->getTransformedGoal(pose.header.stamp);
760 
761  geometry_msgs::msg::TwistStamped cmd_vel_2d;
762 
763  try {
764  cmd_vel_2d =
765  controllers_[current_controller_]->computeVelocityCommands(
766  pose,
767  twist,
768  goal_checkers_[current_goal_checker_].get(),
769  transformed_global_plan_,
770  goal);
771  last_valid_cmd_time_ = now();
772  cmd_vel_2d.header.frame_id = costmap_ros_->getBaseFrameID();
773  cmd_vel_2d.header.stamp = last_valid_cmd_time_;
774  // Only no valid control exception types are valid to attempt to have control patience, as
775  // other types will not be resolved with more attempts
776  } catch (nav2_core::NoValidControl & e) {
777  if (params_->failure_tolerance > 0 || params_->failure_tolerance == -1.0) {
778  RCLCPP_WARN(this->get_logger(), "%s", e.what());
779  cmd_vel_2d.twist.angular.x = 0;
780  cmd_vel_2d.twist.angular.y = 0;
781  cmd_vel_2d.twist.angular.z = 0;
782  cmd_vel_2d.twist.linear.x = 0;
783  cmd_vel_2d.twist.linear.y = 0;
784  cmd_vel_2d.twist.linear.z = 0;
785  cmd_vel_2d.header.frame_id = costmap_ros_->getBaseFrameID();
786  cmd_vel_2d.header.stamp = now();
787  if ((now() - last_valid_cmd_time_).seconds() > params_->failure_tolerance &&
788  params_->failure_tolerance != -1.0)
789  {
790  throw nav2_core::PatienceExceeded("Controller patience exceeded");
791  }
792  } else {
793  throw nav2_core::NoValidControl(e.what());
794  }
795  }
796 
797  RCLCPP_DEBUG(get_logger(), "Publishing velocity at time %.2f", now().seconds());
798  publishVelocity(cmd_vel_2d);
799 
800  nav2_msgs::msg::TrackingFeedback current_tracking_feedback;
801 
802  if (current_path_.poses.size() >= 2) {
803  double current_distance_to_goal = nav2_util::geometry_utils::euclidean_distance(
804  pose, transformed_end_pose_);
805 
806  // Transform robot pose to path frame for path tracking calculations
807  geometry_msgs::msg::PoseStamped robot_pose_in_path_frame;
808  if (!nav2_util::transformPoseInTargetFrame(
809  pose, robot_pose_in_path_frame, *costmap_ros_->getTfBuffer(),
810  current_path_.header.frame_id, transform_tolerance_))
811  {
812  throw nav2_core::ControllerTFError("Failed to transform robot pose to path frame");
813  }
814 
815  // Calculate closest point and position error from path
816  const auto path_search_result = nav2_util::distance_from_path(
817  current_path_, robot_pose_in_path_frame.pose, start_index_, params_->search_window);
818 
819  // Calculate heading error
820  double heading_tracking_error = 0.0;
821  if (path_search_result.closest_segment_index <
822  current_path_.poses.size() - 1)
823  {
824  const auto & path_segment_start =
825  current_path_.poses[path_search_result.closest_segment_index].pose;
826  const auto & path_segment_end =
827  current_path_.poses[path_search_result.closest_segment_index + 1].pose;
828  double path_yaw = std::atan2(
829  path_segment_end.position.y - path_segment_start.position.y,
830  path_segment_end.position.x - path_segment_start.position.x);
831  double robot_yaw = tf2::getYaw(robot_pose_in_path_frame.pose.orientation);
832  heading_tracking_error = angles::shortest_angular_distance(
833  robot_yaw, path_yaw);
834  }
835 
836  // Create tracking error message
837  auto tracking_feedback_msg = std::make_unique<nav2_msgs::msg::TrackingFeedback>();
838  tracking_feedback_msg->header = pose.header;
839  tracking_feedback_msg->position_tracking_error = path_search_result.distance;
840  tracking_feedback_msg->heading_tracking_error = heading_tracking_error;
841  tracking_feedback_msg->current_path_index = path_search_result.closest_segment_index;
842  tracking_feedback_msg->robot_pose = pose;
843  tracking_feedback_msg->distance_to_goal = current_distance_to_goal;
844  tracking_feedback_msg->speed = std::hypot(twist.linear.x, twist.linear.y);
845  start_index_ = path_search_result.closest_segment_index;
846  tracking_feedback_msg->remaining_path_length =
847  nav2_util::geometry_utils::calculate_path_length(current_path_, start_index_);
848 
849  // Update current tracking error and publish
850  current_tracking_feedback = *tracking_feedback_msg;
851  if (tracking_feedback_pub_->get_subscription_count() > 0) {
852  tracking_feedback_pub_->publish(std::move(tracking_feedback_msg));
853  }
854  }
855 
856  // Publish action feedback
857  std::shared_ptr<Action::Feedback> feedback = std::make_shared<Action::Feedback>();
858  feedback->tracking_feedback = current_tracking_feedback;
859  action_server_->publish_feedback(feedback);
860 }
861 
863 {
864  if (action_server_->is_preempt_requested()) {
865  RCLCPP_INFO(get_logger(), "Passing new path to controller.");
866  auto goal = action_server_->accept_pending_goal();
867  std::string current_controller;
868  if (findControllerId(goal->controller_id, current_controller)) {
869  current_controller_ = current_controller;
870  } else {
871  std::shared_ptr<Action::Result> result = std::make_shared<Action::Result>();
872  result->error_code = Action::Result::INVALID_CONTROLLER;
873  result->error_msg = "Terminating action, invalid controller " +
874  goal->controller_id + " requested.";
875  action_server_->terminate_current(result);
876  return;
877  }
878  std::string current_goal_checker;
879  if (findGoalCheckerId(goal->goal_checker_id, current_goal_checker)) {
880  current_goal_checker_ = current_goal_checker;
881  } else {
882  std::shared_ptr<Action::Result> result = std::make_shared<Action::Result>();
883  result->error_code = Action::Result::INVALID_CONTROLLER;
884  result->error_msg = "Terminating action, invalid goal checker " +
885  goal->goal_checker_id + " requested.";
886  action_server_->terminate_current(result);
887  return;
888  }
889  std::string current_progress_checker;
890  if (findProgressCheckerId(goal->progress_checker_id, current_progress_checker)) {
891  if (current_progress_checker_ != current_progress_checker) {
892  RCLCPP_INFO(
893  get_logger(), "Change of progress checker %s requested, resetting it",
894  goal->progress_checker_id.c_str());
895  current_progress_checker_ = current_progress_checker;
896  if (!current_progress_checker_.empty()) {
897  progress_checkers_[current_progress_checker_]->reset();
898  }
899  }
900  } else {
901  std::shared_ptr<Action::Result> result = std::make_shared<Action::Result>();
902  result->error_code = Action::Result::INVALID_CONTROLLER;
903  result->error_msg = "Terminating action, invalid progress checker " +
904  goal->progress_checker_id + " requested.";
905  action_server_->terminate_current(result);
906  return;
907  }
908  std::string current_path_handler;
909  if (findPathHandlerId(goal->path_handler_id, current_path_handler)) {
910  if (current_path_handler_ != current_path_handler) {
911  RCLCPP_INFO(
912  get_logger(), "Change of path handler %s requested, resetting it",
913  goal->path_handler_id.c_str());
914  current_path_handler_ = current_path_handler;
915  }
916  } else {
917  std::shared_ptr<Action::Result> result = std::make_shared<Action::Result>();
918  result->error_code = Action::Result::INVALID_CONTROLLER;
919  result->error_msg = "Terminating action, invalid path handler" +
920  goal->path_handler_id + " requested.";
921  action_server_->terminate_current(result);
922  return;
923  }
924  setPlannerPath(goal->path);
925  }
926 }
927 
928 void ControllerServer::publishVelocity(const geometry_msgs::msg::TwistStamped & velocity)
929 {
930  auto cmd_vel = std::make_unique<geometry_msgs::msg::TwistStamped>(velocity);
931  if (!nav2_util::validateTwist(cmd_vel->twist)) {
932  RCLCPP_ERROR(get_logger(), "Velocity message contains NaNs or Infs! Ignoring as invalid!");
933  return;
934  }
935  if (vel_publisher_->is_activated() && vel_publisher_->get_subscription_count() > 0) {
936  vel_publisher_->publish(std::move(cmd_vel));
937  }
938 }
939 
941 {
942  geometry_msgs::msg::TwistStamped velocity;
943  velocity.twist.angular.x = 0;
944  velocity.twist.angular.y = 0;
945  velocity.twist.angular.z = 0;
946  velocity.twist.linear.x = 0;
947  velocity.twist.linear.y = 0;
948  velocity.twist.linear.z = 0;
949  velocity.header.frame_id = costmap_ros_->getBaseFrameID();
950  velocity.header.stamp = now();
951  publishVelocity(velocity);
952 }
953 
954 void ControllerServer::onGoalExit(bool force_stop)
955 {
956  if (params_->publish_zero_velocity || force_stop) {
958  }
959 
960  // Reset controller state
961  for (auto & controller : controllers_) {
962  controller.second->reset();
963  }
964 }
965 
967 {
968  geometry_msgs::msg::PoseStamped pose;
969 
970  if (!getRobotPose(pose)) {
971  return false;
972  }
973 
974  geometry_msgs::msg::Twist velocity = getThresholdedTwist(odom_sub_->getRawTwist());
975 
976  return goal_checkers_[current_goal_checker_]->isGoalReached(
977  pose.pose, transformed_end_pose_.pose,
978  velocity, transformed_global_plan_);
979 }
980 
981 bool ControllerServer::getRobotPose(geometry_msgs::msg::PoseStamped & pose)
982 {
983  geometry_msgs::msg::PoseStamped current_pose;
984  if (!costmap_ros_->getRobotPose(current_pose)) {
985  return false;
986  }
987  pose = current_pose;
988  return true;
989 }
990 
991 void ControllerServer::speedLimitCallback(const nav2_msgs::msg::SpeedLimit::ConstSharedPtr & msg)
992 {
993  ControllerMap::iterator it;
994  for (it = controllers_.begin(); it != controllers_.end(); ++it) {
995  it->second->setSpeedLimit(msg->speed_limit, msg->percentage);
996  }
997 }
998 
999 } // namespace nav2_controller
1000 
1001 #include "rclcpp_components/register_node_macro.hpp"
1002 
1003 // Register the component with class_loader.
1004 // This acts as a sort of entry point, allowing the component to be discoverable when its library
1005 // is being loaded into a running process.
1006 RCLCPP_COMPONENTS_REGISTER_NODE(nav2_controller::ControllerServer)
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.
A sim-time-aware rate for Nav2 loops.
Definition: rate.hpp:61
This class hosts variety of plugins of different algorithms to complete control tasks from the expose...
nav2::CallbackReturn on_cleanup(const rclcpp_lifecycle::State &state) override
Calls clean up states and resets member variables.
double waitForCostmap()
Wait for costmap to become current, with timeout.
void publishVelocity(const geometry_msgs::msg::TwistStamped &velocity)
Calls velocity publisher to publish the velocity on "cmd_vel" topic.
bool getRobotPose(geometry_msgs::msg::PoseStamped &pose)
Obtain current pose of the robot in costmap's frame.
void onGoalExit(bool force_stop)
Called on goal exit.
nav2::CallbackReturn on_configure(const rclcpp_lifecycle::State &state) override
Configures controller parameters and member variables.
void computeControl()
FollowPath action server callback. Handles action server updates and spins server until goal is reach...
bool isGoalReached()
Checks if goal is reached.
geometry_msgs::msg::Twist getThresholdedTwist(const geometry_msgs::msg::Twist &twist)
get the thresholded Twist
~ControllerServer()
Destructor for nav2_controller::ControllerServer.
nav2::CallbackReturn on_deactivate(const rclcpp_lifecycle::State &state) override
Deactivates member variables.
bool findGoalCheckerId(const std::string &c_name, std::string &name)
Find the valid goal checker ID name for the specified parameter.
void updateGlobalPath()
Calls setPlannerPath method with an updated path received from action server.
bool goalReceived(std::shared_ptr< const Action::Goal > goal)
Goal received callback to validate a new goal before acceptance.
void setPlannerPath(const nav_msgs::msg::Path &path)
Assigns path to controller.
void transformedPlanAndGoal()
Refreshes transformed_global_plan_ and transformed_end_pose_ for the current cycle.
nav2::CallbackReturn on_shutdown(const rclcpp_lifecycle::State &state) override
Called when in Shutdown state.
nav2::CallbackReturn on_activate(const rclcpp_lifecycle::State &state) override
Activates member variables.
void publishZeroVelocity()
Calls velocity publisher to publish zero velocity.
bool findControllerId(const std::string &c_name, std::string &name)
Find the valid controller ID name for the given request.
bool findProgressCheckerId(const std::string &c_name, std::string &name)
Find the valid progress checker ID name for the specified parameter.
bool findPathHandlerId(const std::string &c_name, std::string &name)
Find the valid path handler ID name for the specified parameter.
void computeAndPublishVelocity()
Calculates velocity and publishes to "cmd_vel" topic.