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  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  if (isGoalReached()) {
568  RCLCPP_INFO(get_logger(), "Reached the goal!");
569  break;
570  }
571 
573 
574  auto cycle_duration = this->now() - start_time;
575  if (!loop_rate.sleep()) {
576  RCLCPP_WARN(
577  get_logger(),
578  "Control loop missed its desired rate of %.4f Hz. Current loop rate is %.4f Hz."
579  "%s",
580  params_->controller_frequency, 1 / cycle_duration.seconds(),
581  costmap_wait > 0.0 ?
582  (" Waited " + std::to_string(costmap_wait) + "s for costmap update.").c_str() : "");
583  loop_rate.reset();
584  }
585  }
586  } catch (nav2_core::InvalidController & e) {
587  RCLCPP_ERROR(this->get_logger(), "%s", e.what());
588  onGoalExit(true);
589  std::shared_ptr<Action::Result> result = std::make_shared<Action::Result>();
590  result->error_code = Action::Result::INVALID_CONTROLLER;
591  result->error_msg = e.what();
592  action_server_->terminate_current(result);
593  return;
594  } catch (nav2_core::ControllerTFError & e) {
595  RCLCPP_ERROR(this->get_logger(), "%s", e.what());
596  onGoalExit(true);
597  std::shared_ptr<Action::Result> result = std::make_shared<Action::Result>();
598  result->error_code = Action::Result::TF_ERROR;
599  result->error_msg = e.what();
600  action_server_->terminate_current(result);
601  return;
602  } catch (nav2_core::NoValidControl & e) {
603  RCLCPP_ERROR(this->get_logger(), "%s", e.what());
604  onGoalExit(true);
605  std::shared_ptr<Action::Result> result = std::make_shared<Action::Result>();
606  result->error_code = Action::Result::NO_VALID_CONTROL;
607  result->error_msg = e.what();
608  action_server_->terminate_current(result);
609  return;
610  } catch (nav2_core::FailedToMakeProgress & e) {
611  RCLCPP_ERROR(this->get_logger(), "%s", e.what());
612  onGoalExit(true);
613  std::shared_ptr<Action::Result> result = std::make_shared<Action::Result>();
614  result->error_code = Action::Result::FAILED_TO_MAKE_PROGRESS;
615  result->error_msg = e.what();
616  action_server_->terminate_current(result);
617  return;
618  } catch (nav2_core::PatienceExceeded & e) {
619  RCLCPP_ERROR(this->get_logger(), "%s", e.what());
620  onGoalExit(true);
621  std::shared_ptr<Action::Result> result = std::make_shared<Action::Result>();
622  result->error_code = Action::Result::PATIENCE_EXCEEDED;
623  result->error_msg = e.what();
624  action_server_->terminate_current(result);
625  return;
626  } catch (nav2_core::InvalidPath & e) {
627  RCLCPP_ERROR(this->get_logger(), "%s", e.what());
628  onGoalExit(true);
629  std::shared_ptr<Action::Result> result = std::make_shared<Action::Result>();
630  result->error_code = Action::Result::INVALID_PATH;
631  result->error_msg = e.what();
632  action_server_->terminate_current(result);
633  return;
634  } catch (nav2_core::ControllerTimedOut & e) {
635  RCLCPP_ERROR(this->get_logger(), "%s", e.what());
636  onGoalExit(true);
637  std::shared_ptr<Action::Result> result = std::make_shared<Action::Result>();
638  result->error_code = Action::Result::CONTROLLER_TIMED_OUT;
639  result->error_msg = e.what();
640  action_server_->terminate_current(result);
641  return;
642  } catch (nav2_core::ControllerException & e) {
643  RCLCPP_ERROR(this->get_logger(), "%s", e.what());
644  onGoalExit(true);
645  std::shared_ptr<Action::Result> result = std::make_shared<Action::Result>();
646  result->error_code = Action::Result::UNKNOWN;
647  result->error_msg = e.what();
648  action_server_->terminate_current(result);
649  return;
650  } catch (std::exception & e) {
651  RCLCPP_ERROR(this->get_logger(), "%s", e.what());
652  onGoalExit(true);
653  std::shared_ptr<Action::Result> result = std::make_shared<Action::Result>();
654  result->error_code = Action::Result::UNKNOWN;
655  result->error_msg = e.what();
656  action_server_->terminate_current(result);
657  return;
658  }
659 
660  RCLCPP_DEBUG(get_logger(), "Controller succeeded, setting result");
661 
662  onGoalExit(false);
663 
664  // TODO(orduno) #861 Handle a pending preemption and set controller name
665  action_server_->succeeded_current();
666 }
667 
669 {
670  if (params_->costmap_update_timeout > rclcpp::Duration(0, 0)) {
671  auto waiting_start = now();
672  bool was_waiting = !costmap_ros_->isCurrent();
673  try {
674  costmap_ros_->waitUntilCurrent(params_->costmap_update_timeout);
675  } catch (const std::runtime_error & ex) {
676  throw nav2_core::ControllerTimedOut(ex.what());
677  }
678  if (was_waiting) {
679  return (now() - waiting_start).seconds();
680  }
681  }
682  return 0.0;
683 }
684 
685 void ControllerServer::setPlannerPath(const nav_msgs::msg::Path & path)
686 {
687  RCLCPP_DEBUG(
688  get_logger(),
689  "Providing path to the controller %s", current_controller_.c_str());
690  if (path.poses.empty()) {
691  throw nav2_core::InvalidPath("Path is empty.");
692  }
693  controllers_[current_controller_]->newPathReceived(path);
694  path_handlers_[current_path_handler_]->setPlan(path);
695 
696  end_pose_ = path.poses.back();
697  end_pose_.header.frame_id = path.header.frame_id;
698  goal_checkers_[current_goal_checker_]->reset();
699 
700  RCLCPP_DEBUG(
701  get_logger(), "Path end point is (%.2f, %.2f)",
702  end_pose_.pose.position.x, end_pose_.pose.position.y);
703 
704  start_index_ = 0;
705  current_path_ = path;
706 }
707 
709 {
710  geometry_msgs::msg::PoseStamped pose;
711 
712  if (!getRobotPose(pose)) {
713  throw nav2_core::ControllerTFError("Failed to obtain robot pose");
714  }
715 
716  if (!current_progress_checker_.empty()) {
717  if (!progress_checkers_[current_progress_checker_]->check(pose)) {
718  throw nav2_core::FailedToMakeProgress("Failed to make progress");
719  }
720  }
721 
722  geometry_msgs::msg::Twist twist = getThresholdedTwist(odom_sub_->getRawTwist());
723 
724  geometry_msgs::msg::PoseStamped goal =
725  path_handlers_[current_path_handler_]->getTransformedGoal(pose.header.stamp);
726  // Get the [start, end) iterators under map frame to be used for control.
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  auto path = std::make_unique<nav_msgs::msg::Path>(transformed_global_plan_);
732  if (transformed_plan_pub_->get_subscription_count() > 0) {
733  transformed_plan_pub_->publish(std::move(path));
734  }
735 
736  geometry_msgs::msg::TwistStamped cmd_vel_2d;
737 
738  try {
739  cmd_vel_2d =
740  controllers_[current_controller_]->computeVelocityCommands(
741  pose,
742  twist,
743  goal_checkers_[current_goal_checker_].get(),
744  transformed_global_plan_,
745  goal);
746  last_valid_cmd_time_ = now();
747  cmd_vel_2d.header.frame_id = costmap_ros_->getBaseFrameID();
748  cmd_vel_2d.header.stamp = last_valid_cmd_time_;
749  // Only no valid control exception types are valid to attempt to have control patience, as
750  // other types will not be resolved with more attempts
751  } catch (nav2_core::NoValidControl & e) {
752  if (params_->failure_tolerance > 0 || params_->failure_tolerance == -1.0) {
753  RCLCPP_WARN(this->get_logger(), "%s", e.what());
754  cmd_vel_2d.twist.angular.x = 0;
755  cmd_vel_2d.twist.angular.y = 0;
756  cmd_vel_2d.twist.angular.z = 0;
757  cmd_vel_2d.twist.linear.x = 0;
758  cmd_vel_2d.twist.linear.y = 0;
759  cmd_vel_2d.twist.linear.z = 0;
760  cmd_vel_2d.header.frame_id = costmap_ros_->getBaseFrameID();
761  cmd_vel_2d.header.stamp = now();
762  if ((now() - last_valid_cmd_time_).seconds() > params_->failure_tolerance &&
763  params_->failure_tolerance != -1.0)
764  {
765  throw nav2_core::PatienceExceeded("Controller patience exceeded");
766  }
767  } else {
768  throw nav2_core::NoValidControl(e.what());
769  }
770  }
771 
772  RCLCPP_DEBUG(get_logger(), "Publishing velocity at time %.2f", now().seconds());
773  publishVelocity(cmd_vel_2d);
774 
775  nav2_msgs::msg::TrackingFeedback current_tracking_feedback;
776 
777  if (current_path_.poses.size() >= 2) {
778  double current_distance_to_goal = nav2_util::geometry_utils::euclidean_distance(
779  pose, transformed_end_pose_);
780 
781  // Transform robot pose to path frame for path tracking calculations
782  geometry_msgs::msg::PoseStamped robot_pose_in_path_frame;
783  if (!nav2_util::transformPoseInTargetFrame(
784  pose, robot_pose_in_path_frame, *costmap_ros_->getTfBuffer(),
785  current_path_.header.frame_id, transform_tolerance_))
786  {
787  throw nav2_core::ControllerTFError("Failed to transform robot pose to path frame");
788  }
789 
790  // Calculate closest point and position error from path
791  const auto path_search_result = nav2_util::distance_from_path(
792  current_path_, robot_pose_in_path_frame.pose, start_index_, params_->search_window);
793 
794  // Calculate heading error
795  double heading_tracking_error = 0.0;
796  if (path_search_result.closest_segment_index <
797  current_path_.poses.size() - 1)
798  {
799  const auto & path_segment_start =
800  current_path_.poses[path_search_result.closest_segment_index].pose;
801  const auto & path_segment_end =
802  current_path_.poses[path_search_result.closest_segment_index + 1].pose;
803  double path_yaw = std::atan2(
804  path_segment_end.position.y - path_segment_start.position.y,
805  path_segment_end.position.x - path_segment_start.position.x);
806  double robot_yaw = tf2::getYaw(robot_pose_in_path_frame.pose.orientation);
807  heading_tracking_error = angles::shortest_angular_distance(
808  robot_yaw, path_yaw);
809  }
810 
811  // Create tracking error message
812  auto tracking_feedback_msg = std::make_unique<nav2_msgs::msg::TrackingFeedback>();
813  tracking_feedback_msg->header = pose.header;
814  tracking_feedback_msg->position_tracking_error = path_search_result.distance;
815  tracking_feedback_msg->heading_tracking_error = heading_tracking_error;
816  tracking_feedback_msg->current_path_index = path_search_result.closest_segment_index;
817  tracking_feedback_msg->robot_pose = pose;
818  tracking_feedback_msg->distance_to_goal = current_distance_to_goal;
819  tracking_feedback_msg->speed = std::hypot(twist.linear.x, twist.linear.y);
820  start_index_ = path_search_result.closest_segment_index;
821  tracking_feedback_msg->remaining_path_length =
822  nav2_util::geometry_utils::calculate_path_length(current_path_, start_index_);
823 
824  // Update current tracking error and publish
825  current_tracking_feedback = *tracking_feedback_msg;
826  if (tracking_feedback_pub_->get_subscription_count() > 0) {
827  tracking_feedback_pub_->publish(std::move(tracking_feedback_msg));
828  }
829  }
830 
831  // Publish action feedback
832  std::shared_ptr<Action::Feedback> feedback = std::make_shared<Action::Feedback>();
833  feedback->tracking_feedback = current_tracking_feedback;
834  action_server_->publish_feedback(feedback);
835 }
836 
838 {
839  if (action_server_->is_preempt_requested()) {
840  RCLCPP_INFO(get_logger(), "Passing new path to controller.");
841  auto goal = action_server_->accept_pending_goal();
842  std::string current_controller;
843  if (findControllerId(goal->controller_id, current_controller)) {
844  current_controller_ = current_controller;
845  } else {
846  std::shared_ptr<Action::Result> result = std::make_shared<Action::Result>();
847  result->error_code = Action::Result::INVALID_CONTROLLER;
848  result->error_msg = "Terminating action, invalid controller " +
849  goal->controller_id + " requested.";
850  action_server_->terminate_current(result);
851  return;
852  }
853  std::string current_goal_checker;
854  if (findGoalCheckerId(goal->goal_checker_id, current_goal_checker)) {
855  current_goal_checker_ = current_goal_checker;
856  } else {
857  std::shared_ptr<Action::Result> result = std::make_shared<Action::Result>();
858  result->error_code = Action::Result::INVALID_CONTROLLER;
859  result->error_msg = "Terminating action, invalid goal checker " +
860  goal->goal_checker_id + " requested.";
861  action_server_->terminate_current(result);
862  return;
863  }
864  std::string current_progress_checker;
865  if (findProgressCheckerId(goal->progress_checker_id, current_progress_checker)) {
866  if (current_progress_checker_ != current_progress_checker) {
867  RCLCPP_INFO(
868  get_logger(), "Change of progress checker %s requested, resetting it",
869  goal->progress_checker_id.c_str());
870  current_progress_checker_ = current_progress_checker;
871  if (!current_progress_checker_.empty()) {
872  progress_checkers_[current_progress_checker_]->reset();
873  }
874  }
875  } else {
876  std::shared_ptr<Action::Result> result = std::make_shared<Action::Result>();
877  result->error_code = Action::Result::INVALID_CONTROLLER;
878  result->error_msg = "Terminating action, invalid progress checker " +
879  goal->progress_checker_id + " requested.";
880  action_server_->terminate_current(result);
881  return;
882  }
883  std::string current_path_handler;
884  if (findPathHandlerId(goal->path_handler_id, current_path_handler)) {
885  if (current_path_handler_ != current_path_handler) {
886  RCLCPP_INFO(
887  get_logger(), "Change of path handler %s requested, resetting it",
888  goal->path_handler_id.c_str());
889  current_path_handler_ = current_path_handler;
890  }
891  } else {
892  std::shared_ptr<Action::Result> result = std::make_shared<Action::Result>();
893  result->error_code = Action::Result::INVALID_CONTROLLER;
894  result->error_msg = "Terminating action, invalid path handler" +
895  goal->path_handler_id + " requested.";
896  action_server_->terminate_current(result);
897  return;
898  }
899  setPlannerPath(goal->path);
900  }
901 }
902 
903 void ControllerServer::publishVelocity(const geometry_msgs::msg::TwistStamped & velocity)
904 {
905  auto cmd_vel = std::make_unique<geometry_msgs::msg::TwistStamped>(velocity);
906  if (!nav2_util::validateTwist(cmd_vel->twist)) {
907  RCLCPP_ERROR(get_logger(), "Velocity message contains NaNs or Infs! Ignoring as invalid!");
908  return;
909  }
910  if (vel_publisher_->is_activated() && vel_publisher_->get_subscription_count() > 0) {
911  vel_publisher_->publish(std::move(cmd_vel));
912  }
913 }
914 
916 {
917  geometry_msgs::msg::TwistStamped velocity;
918  velocity.twist.angular.x = 0;
919  velocity.twist.angular.y = 0;
920  velocity.twist.angular.z = 0;
921  velocity.twist.linear.x = 0;
922  velocity.twist.linear.y = 0;
923  velocity.twist.linear.z = 0;
924  velocity.header.frame_id = costmap_ros_->getBaseFrameID();
925  velocity.header.stamp = now();
926  publishVelocity(velocity);
927 }
928 
929 void ControllerServer::onGoalExit(bool force_stop)
930 {
931  if (params_->publish_zero_velocity || force_stop) {
933  }
934 
935  // Reset controller state
936  for (auto & controller : controllers_) {
937  controller.second->reset();
938  }
939 }
940 
942 {
943  geometry_msgs::msg::PoseStamped pose;
944 
945  if (!getRobotPose(pose)) {
946  return false;
947  }
948 
949  end_pose_.header.stamp = pose.header.stamp;
950  if (!nav2_util::transformPoseInTargetFrame(
951  end_pose_, transformed_end_pose_, *costmap_ros_->getTfBuffer(),
952  costmap_ros_->getGlobalFrameID(), transform_tolerance_))
953  {
954  throw nav2_core::ControllerTFError("Failed to transform end pose to global frame");
955  }
956 
957  geometry_msgs::msg::Twist velocity = getThresholdedTwist(odom_sub_->getRawTwist());
958 
959  return goal_checkers_[current_goal_checker_]->isGoalReached(
960  pose.pose, transformed_end_pose_.pose,
961  velocity, transformed_global_plan_);
962 }
963 
964 bool ControllerServer::getRobotPose(geometry_msgs::msg::PoseStamped & pose)
965 {
966  geometry_msgs::msg::PoseStamped current_pose;
967  if (!costmap_ros_->getRobotPose(current_pose)) {
968  return false;
969  }
970  pose = current_pose;
971  return true;
972 }
973 
974 void ControllerServer::speedLimitCallback(const nav2_msgs::msg::SpeedLimit::ConstSharedPtr & msg)
975 {
976  ControllerMap::iterator it;
977  for (it = controllers_.begin(); it != controllers_.end(); ++it) {
978  it->second->setSpeedLimit(msg->speed_limit, msg->percentage);
979  }
980 }
981 
982 } // namespace nav2_controller
983 
984 #include "rclcpp_components/register_node_macro.hpp"
985 
986 // Register the component with class_loader.
987 // This acts as a sort of entry point, allowing the component to be discoverable when its library
988 // is being loaded into a running process.
989 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.
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.