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