Nav2 Navigation Stack - jazzy  jazzy
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 "nav_2d_utils/conversions.hpp"
25 #include "nav_2d_utils/tf_help.hpp"
26 #include "nav2_util/node_utils.hpp"
27 #include "nav2_util/geometry_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 
34 namespace nav2_controller
35 {
36 
37 ControllerServer::ControllerServer(const rclcpp::NodeOptions & options)
38 : nav2_util::LifecycleNode("controller_server", "", options),
39  progress_checker_loader_("nav2_core", "nav2_core::ProgressChecker"),
40  default_progress_checker_ids_{"progress_checker"},
41  default_progress_checker_types_{"nav2_controller::SimpleProgressChecker"},
42  goal_checker_loader_("nav2_core", "nav2_core::GoalChecker"),
43  default_goal_checker_ids_{"goal_checker"},
44  default_goal_checker_types_{"nav2_controller::SimpleGoalChecker"},
45  lp_loader_("nav2_core", "nav2_core::Controller"),
46  default_ids_{"FollowPath"},
47  default_types_{"dwb_core::DWBLocalPlanner"},
48  costmap_update_timeout_(300ms)
49 {
50  RCLCPP_INFO(get_logger(), "Creating controller server");
51 
52  declare_parameter("controller_frequency", 20.0);
53 
54  declare_parameter("action_server_result_timeout", 10.0);
55 
56  declare_parameter("progress_checker_plugins", default_progress_checker_ids_);
57  declare_parameter("goal_checker_plugins", default_goal_checker_ids_);
58  declare_parameter("controller_plugins", default_ids_);
59  declare_parameter("min_x_velocity_threshold", rclcpp::ParameterValue(0.0001));
60  declare_parameter("min_y_velocity_threshold", rclcpp::ParameterValue(0.0001));
61  declare_parameter("min_theta_velocity_threshold", rclcpp::ParameterValue(0.0001));
62 
63  declare_parameter("speed_limit_topic", rclcpp::ParameterValue("speed_limit"));
64 
65  declare_parameter("failure_tolerance", rclcpp::ParameterValue(0.0));
66  declare_parameter("use_realtime_priority", rclcpp::ParameterValue(false));
67  declare_parameter("publish_zero_velocity", rclcpp::ParameterValue(true));
68  declare_parameter("costmap_update_timeout", 0.30); // 300ms
69 
70  // The costmap node is used in the implementation of the controller
71  costmap_ros_ = std::make_shared<nav2_costmap_2d::Costmap2DROS>(
72  "local_costmap", std::string{get_namespace()}, "local_costmap",
73  get_parameter("use_sim_time").as_bool());
74 }
75 
77 {
78  progress_checkers_.clear();
79  goal_checkers_.clear();
80  controllers_.clear();
81  costmap_thread_.reset();
82 }
83 
84 nav2_util::CallbackReturn
85 ControllerServer::on_configure(const rclcpp_lifecycle::State & state)
86 {
87  auto node = shared_from_this();
88 
89  RCLCPP_INFO(get_logger(), "Configuring controller interface");
90 
91  RCLCPP_INFO(get_logger(), "getting progress checker plugins..");
92  get_parameter("progress_checker_plugins", progress_checker_ids_);
93  if (progress_checker_ids_ == default_progress_checker_ids_) {
94  for (size_t i = 0; i < default_progress_checker_ids_.size(); ++i) {
95  nav2_util::declare_parameter_if_not_declared(
96  node, default_progress_checker_ids_[i] + ".plugin",
97  rclcpp::ParameterValue(default_progress_checker_types_[i]));
98  }
99  }
100 
101  RCLCPP_INFO(get_logger(), "getting goal checker plugins..");
102  get_parameter("goal_checker_plugins", goal_checker_ids_);
103  if (goal_checker_ids_ == default_goal_checker_ids_) {
104  for (size_t i = 0; i < default_goal_checker_ids_.size(); ++i) {
105  nav2_util::declare_parameter_if_not_declared(
106  node, default_goal_checker_ids_[i] + ".plugin",
107  rclcpp::ParameterValue(default_goal_checker_types_[i]));
108  }
109  }
110 
111  get_parameter("controller_plugins", controller_ids_);
112  if (controller_ids_ == default_ids_) {
113  for (size_t i = 0; i < default_ids_.size(); ++i) {
114  nav2_util::declare_parameter_if_not_declared(
115  node, default_ids_[i] + ".plugin",
116  rclcpp::ParameterValue(default_types_[i]));
117  }
118  }
119 
120  controller_types_.resize(controller_ids_.size());
121  goal_checker_types_.resize(goal_checker_ids_.size());
122  progress_checker_types_.resize(progress_checker_ids_.size());
123 
124  get_parameter("controller_frequency", controller_frequency_);
125  get_parameter("min_x_velocity_threshold", min_x_velocity_threshold_);
126  get_parameter("min_y_velocity_threshold", min_y_velocity_threshold_);
127  get_parameter("min_theta_velocity_threshold", min_theta_velocity_threshold_);
128  RCLCPP_INFO(get_logger(), "Controller frequency set to %.4fHz", controller_frequency_);
129 
130  std::string speed_limit_topic;
131  get_parameter("speed_limit_topic", speed_limit_topic);
132  get_parameter("failure_tolerance", failure_tolerance_);
133  get_parameter("use_realtime_priority", use_realtime_priority_);
134 
135  costmap_ros_->configure();
136  // Launch a thread to run the costmap node
137  costmap_thread_ = std::make_unique<nav2_util::NodeThread>(costmap_ros_);
138 
139  for (size_t i = 0; i != progress_checker_ids_.size(); i++) {
140  try {
141  progress_checker_types_[i] = nav2_util::get_plugin_type_param(
142  node, progress_checker_ids_[i]);
143  nav2_core::ProgressChecker::Ptr progress_checker =
144  progress_checker_loader_.createUniqueInstance(progress_checker_types_[i]);
145  RCLCPP_INFO(
146  get_logger(), "Created progress_checker : %s of type %s",
147  progress_checker_ids_[i].c_str(), progress_checker_types_[i].c_str());
148  progress_checker->initialize(node, progress_checker_ids_[i]);
149  progress_checkers_.insert({progress_checker_ids_[i], progress_checker});
150  } catch (const std::exception & ex) {
151  RCLCPP_FATAL(
152  get_logger(),
153  "Failed to create progress_checker. Exception: %s", ex.what());
154  on_cleanup(state);
155  return nav2_util::CallbackReturn::FAILURE;
156  }
157  }
158 
159  for (size_t i = 0; i != progress_checker_ids_.size(); i++) {
160  progress_checker_ids_concat_ += progress_checker_ids_[i] + std::string(" ");
161  }
162  if (progress_checker_ids_concat_.empty()) {
163  progress_checker_ids_concat_ = "(none)";
164  }
165 
166  RCLCPP_INFO(
167  get_logger(),
168  "Controller Server has %s progress checkers available.", progress_checker_ids_concat_.c_str());
169 
170  for (size_t i = 0; i != goal_checker_ids_.size(); i++) {
171  try {
172  goal_checker_types_[i] = nav2_util::get_plugin_type_param(node, goal_checker_ids_[i]);
173  nav2_core::GoalChecker::Ptr goal_checker =
174  goal_checker_loader_.createUniqueInstance(goal_checker_types_[i]);
175  RCLCPP_INFO(
176  get_logger(), "Created goal checker : %s of type %s",
177  goal_checker_ids_[i].c_str(), goal_checker_types_[i].c_str());
178  goal_checker->initialize(node, goal_checker_ids_[i], costmap_ros_);
179  goal_checkers_.insert({goal_checker_ids_[i], goal_checker});
180  } catch (const pluginlib::PluginlibException & ex) {
181  RCLCPP_FATAL(
182  get_logger(),
183  "Failed to create goal checker. Exception: %s", ex.what());
184  on_cleanup(state);
185  return nav2_util::CallbackReturn::FAILURE;
186  }
187  }
188 
189  for (size_t i = 0; i != goal_checker_ids_.size(); i++) {
190  goal_checker_ids_concat_ += goal_checker_ids_[i] + std::string(" ");
191  }
192 
193  RCLCPP_INFO(
194  get_logger(),
195  "Controller Server has %s goal checkers available.", goal_checker_ids_concat_.c_str());
196 
197  for (size_t i = 0; i != controller_ids_.size(); i++) {
198  try {
199  controller_types_[i] = nav2_util::get_plugin_type_param(node, controller_ids_[i]);
200  nav2_core::Controller::Ptr controller =
201  lp_loader_.createUniqueInstance(controller_types_[i]);
202  RCLCPP_INFO(
203  get_logger(), "Created controller : %s of type %s",
204  controller_ids_[i].c_str(), controller_types_[i].c_str());
205  controller->configure(
206  node, controller_ids_[i],
207  costmap_ros_->getTfBuffer(), costmap_ros_);
208  controllers_.insert({controller_ids_[i], controller});
209  } catch (const pluginlib::PluginlibException & ex) {
210  RCLCPP_FATAL(
211  get_logger(),
212  "Failed to create controller. Exception: %s", ex.what());
213  on_cleanup(state);
214  return nav2_util::CallbackReturn::FAILURE;
215  }
216  }
217 
218  for (size_t i = 0; i != controller_ids_.size(); i++) {
219  controller_ids_concat_ += controller_ids_[i] + std::string(" ");
220  }
221 
222  RCLCPP_INFO(
223  get_logger(),
224  "Controller Server has %s controllers available.", controller_ids_concat_.c_str());
225 
226  odom_sub_ = std::make_unique<nav_2d_utils::OdomSubscriber>(node);
227  vel_publisher_ = std::make_unique<nav2_util::TwistPublisher>(node, "cmd_vel", 1);
228 
229  double action_server_result_timeout;
230  get_parameter("action_server_result_timeout", action_server_result_timeout);
231  rcl_action_server_options_t server_options = rcl_action_server_get_default_options();
232  server_options.result_timeout.nanoseconds = RCL_S_TO_NS(action_server_result_timeout);
233 
234  double costmap_update_timeout_dbl;
235  get_parameter("costmap_update_timeout", costmap_update_timeout_dbl);
236  costmap_update_timeout_ = rclcpp::Duration::from_seconds(costmap_update_timeout_dbl);
237 
238  // Create the action server that we implement with our followPath method
239  // This may throw due to real-time prioritzation if user doesn't have real-time permissions
240  try {
241  action_server_ = std::make_unique<ActionServer>(
243  "follow_path",
244  std::bind(&ControllerServer::computeControl, this),
245  nullptr,
246  std::chrono::milliseconds(500),
247  true /*spin thread*/, server_options, use_realtime_priority_ /*soft realtime*/);
248  } catch (const std::runtime_error & e) {
249  RCLCPP_ERROR(get_logger(), "Error creating action server! %s", e.what());
250  on_cleanup(state);
251  return nav2_util::CallbackReturn::FAILURE;
252  }
253 
254  // Set subscribtion to the speed limiting topic
255  speed_limit_sub_ = create_subscription<nav2_msgs::msg::SpeedLimit>(
256  speed_limit_topic, rclcpp::QoS(10),
257  std::bind(&ControllerServer::speedLimitCallback, this, std::placeholders::_1));
258 
259  return nav2_util::CallbackReturn::SUCCESS;
260 }
261 
262 nav2_util::CallbackReturn
263 ControllerServer::on_activate(const rclcpp_lifecycle::State & /*state*/)
264 {
265  RCLCPP_INFO(get_logger(), "Activating");
266 
267  const auto costmap_ros_state = costmap_ros_->activate();
268  if (costmap_ros_state.id() != lifecycle_msgs::msg::State::PRIMARY_STATE_ACTIVE) {
269  return nav2_util::CallbackReturn::FAILURE;
270  }
271  ControllerMap::iterator it;
272  for (it = controllers_.begin(); it != controllers_.end(); ++it) {
273  it->second->activate();
274  }
275  vel_publisher_->on_activate();
276  action_server_->activate();
277 
278  auto node = shared_from_this();
279  // Add callback for dynamic parameters
280  dyn_params_handler_ = node->add_on_set_parameters_callback(
281  std::bind(&ControllerServer::dynamicParametersCallback, this, _1));
282 
283  // create bond connection
284  createBond();
285 
286  return nav2_util::CallbackReturn::SUCCESS;
287 }
288 
289 nav2_util::CallbackReturn
290 ControllerServer::on_deactivate(const rclcpp_lifecycle::State & /*state*/)
291 {
292  RCLCPP_INFO(get_logger(), "Deactivating");
293 
294  action_server_->deactivate();
295  ControllerMap::iterator it;
296  for (it = controllers_.begin(); it != controllers_.end(); ++it) {
297  it->second->deactivate();
298  }
299 
300  /*
301  * The costmap is also a lifecycle node, so it may have already fired on_deactivate
302  * via rcl preshutdown cb. Despite the rclcpp docs saying on_shutdown callbacks fire
303  * in the order added, the preshutdown callbacks clearly don't per se, due to using an
304  * unordered_set iteration. Once this issue is resolved, we can maybe make a stronger
305  * ordering assumption: https://github.com/ros2/rclcpp/issues/2096
306  */
307  costmap_ros_->deactivate();
308 
309  // Always publish a zero velocity when deactivating the controller server
310  geometry_msgs::msg::TwistStamped velocity;
311  velocity.twist.angular.x = 0;
312  velocity.twist.angular.y = 0;
313  velocity.twist.angular.z = 0;
314  velocity.twist.linear.x = 0;
315  velocity.twist.linear.y = 0;
316  velocity.twist.linear.z = 0;
317  velocity.header.frame_id = costmap_ros_->getBaseFrameID();
318  velocity.header.stamp = now();
319  publishVelocity(velocity);
320 
321  vel_publisher_->on_deactivate();
322 
323  remove_on_set_parameters_callback(dyn_params_handler_.get());
324  dyn_params_handler_.reset();
325 
326  // destroy bond connection
327  destroyBond();
328 
329  return nav2_util::CallbackReturn::SUCCESS;
330 }
331 
332 nav2_util::CallbackReturn
333 ControllerServer::on_cleanup(const rclcpp_lifecycle::State & /*state*/)
334 {
335  RCLCPP_INFO(get_logger(), "Cleaning up");
336 
337  // Cleanup the helper classes
338  ControllerMap::iterator it;
339  for (it = controllers_.begin(); it != controllers_.end(); ++it) {
340  it->second->cleanup();
341  }
342  controllers_.clear();
343 
344  goal_checkers_.clear();
345  progress_checkers_.clear();
346 
347  costmap_ros_->cleanup();
348 
349 
350  // Release any allocated resources
351  action_server_.reset();
352  odom_sub_.reset();
353  costmap_thread_.reset();
354  vel_publisher_.reset();
355  speed_limit_sub_.reset();
356 
357  return nav2_util::CallbackReturn::SUCCESS;
358 }
359 
360 nav2_util::CallbackReturn
361 ControllerServer::on_shutdown(const rclcpp_lifecycle::State &)
362 {
363  RCLCPP_INFO(get_logger(), "Shutting down");
364  return nav2_util::CallbackReturn::SUCCESS;
365 }
366 
368  const std::string & c_name,
369  std::string & current_controller)
370 {
371  if (controllers_.find(c_name) == controllers_.end()) {
372  if (controllers_.size() == 1 && c_name.empty()) {
373  RCLCPP_WARN_ONCE(
374  get_logger(), "No controller was specified in action call."
375  " Server will use only plugin loaded %s. "
376  "This warning will appear once.", controller_ids_concat_.c_str());
377  current_controller = controllers_.begin()->first;
378  } else {
379  RCLCPP_ERROR(
380  get_logger(), "FollowPath called with controller name %s, "
381  "which does not exist. Available controllers are: %s.",
382  c_name.c_str(), controller_ids_concat_.c_str());
383  return false;
384  }
385  } else {
386  RCLCPP_DEBUG(get_logger(), "Selected controller: %s.", c_name.c_str());
387  current_controller = c_name;
388  }
389 
390  return true;
391 }
392 
394  const std::string & c_name,
395  std::string & current_goal_checker)
396 {
397  if (goal_checkers_.find(c_name) == goal_checkers_.end()) {
398  if (goal_checkers_.size() == 1 && c_name.empty()) {
399  RCLCPP_WARN_ONCE(
400  get_logger(), "No goal checker was specified in parameter 'current_goal_checker'."
401  " Server will use only plugin loaded %s. "
402  "This warning will appear once.", goal_checker_ids_concat_.c_str());
403  current_goal_checker = goal_checkers_.begin()->first;
404  } else {
405  RCLCPP_ERROR(
406  get_logger(), "FollowPath called with goal_checker name %s in parameter"
407  " 'current_goal_checker', which does not exist. Available goal checkers are: %s.",
408  c_name.c_str(), goal_checker_ids_concat_.c_str());
409  return false;
410  }
411  } else {
412  RCLCPP_DEBUG(get_logger(), "Selected goal checker: %s.", c_name.c_str());
413  current_goal_checker = c_name;
414  }
415 
416  return true;
417 }
418 
420  const std::string & c_name,
421  std::string & current_progress_checker)
422 {
423  if (progress_checkers_.size() == 0) {
424  if (c_name.empty()) {
425  RCLCPP_DEBUG(
426  get_logger(),
427  "No progress checker configured and none requested. Progress checking will be bypassed.");
428  current_progress_checker = "";
429  return true;
430  } else {
431  RCLCPP_ERROR(
432  get_logger(), "FollowPath called with progress_checker name %s in parameter"
433  " 'current_progress_checker', but no progress checkers are configured.",
434  c_name.c_str());
435  return false;
436  }
437  }
438 
439  if (progress_checkers_.find(c_name) == progress_checkers_.end()) {
440  if (progress_checkers_.size() == 1 && c_name.empty()) {
441  RCLCPP_WARN_ONCE(
442  get_logger(), "No progress checker was specified in parameter 'current_progress_checker'."
443  " Server will use only plugin loaded %s. "
444  "This warning will appear once.", progress_checker_ids_concat_.c_str());
445  current_progress_checker = progress_checkers_.begin()->first;
446  } else {
447  RCLCPP_ERROR(
448  get_logger(), "FollowPath called with progress_checker name %s in parameter"
449  " 'current_progress_checker', which does not exist. Available progress checkers are: %s.",
450  c_name.c_str(), progress_checker_ids_concat_.c_str());
451  return false;
452  }
453  } else {
454  RCLCPP_DEBUG(get_logger(), "Selected progress checker: %s.", c_name.c_str());
455  current_progress_checker = c_name;
456  }
457 
458  return true;
459 }
460 
462 {
463  std::lock_guard<std::mutex> lock(dynamic_params_lock_);
464 
465  RCLCPP_INFO(get_logger(), "Received a goal, begin computing control effort.");
466 
467  try {
468  auto goal = action_server_->get_current_goal();
469  if (!goal) {
470  return; // goal would be nullptr if action_server_ is inactivate.
471  }
472 
473  std::string c_name = goal->controller_id;
474  std::string current_controller;
475  if (findControllerId(c_name, current_controller)) {
476  current_controller_ = current_controller;
477  } else {
478  throw nav2_core::InvalidController("Failed to find controller name: " + c_name);
479  }
480 
481  std::string gc_name = goal->goal_checker_id;
482  std::string current_goal_checker;
483  if (findGoalCheckerId(gc_name, current_goal_checker)) {
484  current_goal_checker_ = current_goal_checker;
485  } else {
486  throw nav2_core::ControllerException("Failed to find goal checker name: " + gc_name);
487  }
488 
489  std::string pc_name = goal->progress_checker_id;
490  std::string current_progress_checker;
491  if (findProgressCheckerId(pc_name, current_progress_checker)) {
492  current_progress_checker_ = current_progress_checker;
493  } else {
494  throw nav2_core::ControllerException("Failed to find progress checker name: " + pc_name);
495  }
496 
497  setPlannerPath(goal->path);
498  if (!current_progress_checker_.empty()) {
499  progress_checkers_[current_progress_checker_]->reset();
500  }
501 
502  last_valid_cmd_time_ = now();
503  rclcpp::WallRate loop_rate(controller_frequency_);
504  while (rclcpp::ok()) {
505  auto start_time = this->now();
506 
507  if (action_server_ == nullptr || !action_server_->is_server_active()) {
508  RCLCPP_DEBUG(get_logger(), "Action server unavailable or inactive. Stopping.");
509  return;
510  }
511 
512  if (action_server_->is_cancel_requested()) {
513  if (controllers_[current_controller_]->cancel()) {
514  RCLCPP_INFO(get_logger(), "Cancellation was successful. Stopping the robot.");
515  action_server_->terminate_all();
517  return;
518  } else {
519  RCLCPP_INFO_THROTTLE(
520  get_logger(), *get_clock(), 1000, "Waiting for the controller to finish cancellation");
521  }
522  }
523 
524  // Don't compute a trajectory until costmap is valid (after clear costmap)
525  rclcpp::Rate r(100);
526  auto waiting_start = now();
527  while (!costmap_ros_->isCurrent()) {
528  if (now() - waiting_start > costmap_update_timeout_) {
529  throw nav2_core::ControllerTimedOut("Costmap timed out waiting for update");
530  }
531  r.sleep();
532  }
533 
535 
536  if (isGoalReached()) {
537  RCLCPP_INFO(get_logger(), "Reached the goal!");
538  break;
539  }
540 
542 
543  auto cycle_duration = this->now() - start_time;
544  if (!loop_rate.sleep()) {
545  RCLCPP_WARN(
546  get_logger(),
547  "Control loop missed its desired rate of %.4f Hz. Current loop rate is %.4f Hz.",
548  controller_frequency_, 1 / cycle_duration.seconds());
549  loop_rate.reset();
550  }
551  }
552  } catch (nav2_core::InvalidController & e) {
553  RCLCPP_ERROR(this->get_logger(), "%s", e.what());
555  std::shared_ptr<Action::Result> result = std::make_shared<Action::Result>();
556  result->error_code = Action::Result::INVALID_CONTROLLER;
557  action_server_->terminate_current(result);
558  return;
559  } catch (nav2_core::ControllerTFError & e) {
560  RCLCPP_ERROR(this->get_logger(), "%s", e.what());
562  std::shared_ptr<Action::Result> result = std::make_shared<Action::Result>();
563  result->error_code = Action::Result::TF_ERROR;
564  action_server_->terminate_current(result);
565  return;
566  } catch (nav2_core::NoValidControl & e) {
567  RCLCPP_ERROR(this->get_logger(), "%s", e.what());
569  std::shared_ptr<Action::Result> result = std::make_shared<Action::Result>();
570  result->error_code = Action::Result::NO_VALID_CONTROL;
571  action_server_->terminate_current(result);
572  return;
573  } catch (nav2_core::FailedToMakeProgress & e) {
574  RCLCPP_ERROR(this->get_logger(), "%s", e.what());
576  std::shared_ptr<Action::Result> result = std::make_shared<Action::Result>();
577  result->error_code = Action::Result::FAILED_TO_MAKE_PROGRESS;
578  action_server_->terminate_current(result);
579  return;
580  } catch (nav2_core::PatienceExceeded & e) {
581  RCLCPP_ERROR(this->get_logger(), "%s", e.what());
583  std::shared_ptr<Action::Result> result = std::make_shared<Action::Result>();
584  result->error_code = Action::Result::PATIENCE_EXCEEDED;
585  action_server_->terminate_current(result);
586  return;
587  } catch (nav2_core::InvalidPath & e) {
588  RCLCPP_ERROR(this->get_logger(), "%s", e.what());
590  std::shared_ptr<Action::Result> result = std::make_shared<Action::Result>();
591  result->error_code = Action::Result::INVALID_PATH;
592  action_server_->terminate_current(result);
593  return;
594  } catch (nav2_core::ControllerTimedOut & e) {
595  RCLCPP_ERROR(this->get_logger(), "%s", e.what());
597  std::shared_ptr<Action::Result> result = std::make_shared<Action::Result>();
598  result->error_code = Action::Result::CONTROLLER_TIMED_OUT;
599  action_server_->terminate_current(result);
600  return;
601  } catch (nav2_core::ControllerException & e) {
602  RCLCPP_ERROR(this->get_logger(), "%s", e.what());
604  std::shared_ptr<Action::Result> result = std::make_shared<Action::Result>();
605  result->error_code = Action::Result::UNKNOWN;
606  action_server_->terminate_current(result);
607  return;
608  } catch (std::exception & e) {
609  RCLCPP_ERROR(this->get_logger(), "%s", e.what());
611  std::shared_ptr<Action::Result> result = std::make_shared<Action::Result>();
612  result->error_code = Action::Result::UNKNOWN;
613  action_server_->terminate_current(result);
614  return;
615  }
616 
617  RCLCPP_DEBUG(get_logger(), "Controller succeeded, setting result");
618 
620 
621  // TODO(orduno) #861 Handle a pending preemption and set controller name
622  action_server_->succeeded_current();
623 }
624 
625 void ControllerServer::setPlannerPath(const nav_msgs::msg::Path & path)
626 {
627  RCLCPP_DEBUG(
628  get_logger(),
629  "Providing path to the controller %s", current_controller_.c_str());
630  if (path.poses.empty()) {
631  throw nav2_core::InvalidPath("Path is empty.");
632  }
633  controllers_[current_controller_]->setPlan(path);
634 
635  end_pose_ = path.poses.back();
636  end_pose_.header.frame_id = path.header.frame_id;
637  goal_checkers_[current_goal_checker_]->reset();
638 
639  RCLCPP_DEBUG(
640  get_logger(), "Path end point is (%.2f, %.2f)",
641  end_pose_.pose.position.x, end_pose_.pose.position.y);
642 
643  current_path_ = path;
644 }
645 
647 {
648  geometry_msgs::msg::PoseStamped pose;
649 
650  if (!getRobotPose(pose)) {
651  throw nav2_core::ControllerTFError("Failed to obtain robot pose");
652  }
653 
654  if (!current_progress_checker_.empty()) {
655  if (!progress_checkers_[current_progress_checker_]->check(pose)) {
656  throw nav2_core::FailedToMakeProgress("Failed to make progress");
657  }
658  }
659 
660  nav_2d_msgs::msg::Twist2D twist = getThresholdedTwist(odom_sub_->getTwist());
661 
662  geometry_msgs::msg::TwistStamped cmd_vel_2d;
663 
664  try {
665  cmd_vel_2d =
666  controllers_[current_controller_]->computeVelocityCommands(
667  pose,
668  nav_2d_utils::twist2Dto3D(twist),
669  goal_checkers_[current_goal_checker_].get());
670  last_valid_cmd_time_ = now();
671  cmd_vel_2d.header.frame_id = costmap_ros_->getBaseFrameID();
672  cmd_vel_2d.header.stamp = last_valid_cmd_time_;
673  // Only no valid control exception types are valid to attempt to have control patience, as
674  // other types will not be resolved with more attempts
675  } catch (nav2_core::NoValidControl & e) {
676  if (failure_tolerance_ > 0 || failure_tolerance_ == -1.0) {
677  RCLCPP_WARN(this->get_logger(), "%s", e.what());
678  cmd_vel_2d.twist.angular.x = 0;
679  cmd_vel_2d.twist.angular.y = 0;
680  cmd_vel_2d.twist.angular.z = 0;
681  cmd_vel_2d.twist.linear.x = 0;
682  cmd_vel_2d.twist.linear.y = 0;
683  cmd_vel_2d.twist.linear.z = 0;
684  cmd_vel_2d.header.frame_id = costmap_ros_->getBaseFrameID();
685  cmd_vel_2d.header.stamp = now();
686  if ((now() - last_valid_cmd_time_).seconds() > failure_tolerance_ &&
687  failure_tolerance_ != -1.0)
688  {
689  throw nav2_core::PatienceExceeded("Controller patience exceeded");
690  }
691  } else {
692  throw nav2_core::NoValidControl(e.what());
693  }
694  }
695 
696  std::shared_ptr<Action::Feedback> feedback = std::make_shared<Action::Feedback>();
697  feedback->speed = std::hypot(cmd_vel_2d.twist.linear.x, cmd_vel_2d.twist.linear.y);
698 
699  // Find the closest pose to current pose on global path
700  nav_msgs::msg::Path & current_path = current_path_;
701  auto find_closest_pose_idx =
702  [&pose, &current_path]() {
703  size_t closest_pose_idx = 0;
704  double curr_min_dist = std::numeric_limits<double>::max();
705  for (size_t curr_idx = 0; curr_idx < current_path.poses.size(); ++curr_idx) {
706  double curr_dist = nav2_util::geometry_utils::euclidean_distance(
707  pose, current_path.poses[curr_idx]);
708  if (curr_dist < curr_min_dist) {
709  curr_min_dist = curr_dist;
710  closest_pose_idx = curr_idx;
711  }
712  }
713  return closest_pose_idx;
714  };
715 
716  feedback->distance_to_goal =
717  nav2_util::geometry_utils::calculate_path_length(current_path_, find_closest_pose_idx());
718  action_server_->publish_feedback(feedback);
719 
720  RCLCPP_DEBUG(get_logger(), "Publishing velocity at time %.2f", now().seconds());
721  publishVelocity(cmd_vel_2d);
722 }
723 
725 {
726  if (action_server_->is_preempt_requested()) {
727  RCLCPP_INFO(get_logger(), "Passing new path to controller.");
728  auto goal = action_server_->accept_pending_goal();
729  std::string current_controller;
730  if (findControllerId(goal->controller_id, current_controller)) {
731  current_controller_ = current_controller;
732  } else {
733  RCLCPP_INFO(
734  get_logger(), "Terminating action, invalid controller %s requested.",
735  goal->controller_id.c_str());
736  action_server_->terminate_current();
737  return;
738  }
739  std::string current_goal_checker;
740  if (findGoalCheckerId(goal->goal_checker_id, current_goal_checker)) {
741  current_goal_checker_ = current_goal_checker;
742  } else {
743  RCLCPP_INFO(
744  get_logger(), "Terminating action, invalid goal checker %s requested.",
745  goal->goal_checker_id.c_str());
746  action_server_->terminate_current();
747  return;
748  }
749  std::string current_progress_checker;
750  if (findProgressCheckerId(goal->progress_checker_id, current_progress_checker)) {
751  if (current_progress_checker_ != current_progress_checker) {
752  RCLCPP_INFO(
753  get_logger(), "Change of progress checker %s requested, resetting it",
754  goal->progress_checker_id.c_str());
755  current_progress_checker_ = current_progress_checker;
756  if (!current_progress_checker_.empty()) {
757  progress_checkers_[current_progress_checker_]->reset();
758  }
759  }
760  } else {
761  RCLCPP_INFO(
762  get_logger(), "Terminating action, invalid progress checker %s requested.",
763  goal->progress_checker_id.c_str());
764  action_server_->terminate_current();
765  return;
766  }
767  setPlannerPath(goal->path);
768  }
769 }
770 
771 void ControllerServer::publishVelocity(const geometry_msgs::msg::TwistStamped & velocity)
772 {
773  auto cmd_vel = std::make_unique<geometry_msgs::msg::TwistStamped>(velocity);
774  if (vel_publisher_->is_activated() && vel_publisher_->get_subscription_count() > 0) {
775  vel_publisher_->publish(std::move(cmd_vel));
776  }
777 }
778 
780 {
781  if (get_parameter("publish_zero_velocity").as_bool()) {
782  geometry_msgs::msg::TwistStamped velocity;
783  velocity.twist.angular.x = 0;
784  velocity.twist.angular.y = 0;
785  velocity.twist.angular.z = 0;
786  velocity.twist.linear.x = 0;
787  velocity.twist.linear.y = 0;
788  velocity.twist.linear.z = 0;
789  velocity.header.frame_id = costmap_ros_->getBaseFrameID();
790  velocity.header.stamp = now();
791  publishVelocity(velocity);
792  }
793 
794  // Reset the state of the controllers after the task has ended
795  ControllerMap::iterator it;
796  for (it = controllers_.begin(); it != controllers_.end(); ++it) {
797  it->second->reset();
798  }
799 }
800 
802 {
803  geometry_msgs::msg::PoseStamped pose;
804 
805  if (!getRobotPose(pose)) {
806  return false;
807  }
808 
809  nav_2d_msgs::msg::Twist2D twist = getThresholdedTwist(odom_sub_->getTwist());
810  geometry_msgs::msg::Twist velocity = nav_2d_utils::twist2Dto3D(twist);
811 
812  geometry_msgs::msg::PoseStamped transformed_end_pose;
813  rclcpp::Duration tolerance(rclcpp::Duration::from_seconds(costmap_ros_->getTransformTolerance()));
814  if(!nav_2d_utils::transformPose(
815  costmap_ros_->getTfBuffer(), costmap_ros_->getGlobalFrameID(),
816  end_pose_, transformed_end_pose, tolerance))
817  {
818  throw nav2_core::ControllerTFError("Failed to transform end pose to global frame");
819  }
820 
821  return goal_checkers_[current_goal_checker_]->isGoalReached(
822  pose.pose, transformed_end_pose.pose,
823  velocity);
824 }
825 
826 bool ControllerServer::getRobotPose(geometry_msgs::msg::PoseStamped & pose)
827 {
828  geometry_msgs::msg::PoseStamped current_pose;
829  if (!costmap_ros_->getRobotPose(current_pose)) {
830  return false;
831  }
832  pose = current_pose;
833  return true;
834 }
835 
836 void ControllerServer::speedLimitCallback(const nav2_msgs::msg::SpeedLimit::SharedPtr msg)
837 {
838  ControllerMap::iterator it;
839  for (it = controllers_.begin(); it != controllers_.end(); ++it) {
840  it->second->setSpeedLimit(msg->speed_limit, msg->percentage);
841  }
842 }
843 
844 rcl_interfaces::msg::SetParametersResult
845 ControllerServer::dynamicParametersCallback(std::vector<rclcpp::Parameter> parameters)
846 {
847  rcl_interfaces::msg::SetParametersResult result;
848 
849  for (auto parameter : parameters) {
850  const auto & type = parameter.get_type();
851  const auto & name = parameter.get_name();
852 
853  // If we are trying to change the parameter of a plugin we can just skip it at this point
854  // as they handle parameter changes themselves and don't need to lock the mutex
855  if (name.find('.') != std::string::npos) {
856  continue;
857  }
858 
859  if (!dynamic_params_lock_.try_lock()) {
860  RCLCPP_WARN(
861  get_logger(),
862  "Unable to dynamically change Parameters while the controller is currently running");
863  result.successful = false;
864  result.reason =
865  "Unable to dynamically change Parameters while the controller is currently running";
866  return result;
867  }
868 
869  if (type == ParameterType::PARAMETER_DOUBLE) {
870  if (name == "controller_frequency") {
871  controller_frequency_ = parameter.as_double();
872  } else if (name == "min_x_velocity_threshold") {
873  min_x_velocity_threshold_ = parameter.as_double();
874  } else if (name == "min_y_velocity_threshold") {
875  min_y_velocity_threshold_ = parameter.as_double();
876  } else if (name == "min_theta_velocity_threshold") {
877  min_theta_velocity_threshold_ = parameter.as_double();
878  } else if (name == "failure_tolerance") {
879  failure_tolerance_ = parameter.as_double();
880  }
881  }
882 
883  dynamic_params_lock_.unlock();
884  }
885 
886  result.successful = true;
887  return result;
888 }
889 
890 } // namespace nav2_controller
891 
892 #include "rclcpp_components/register_node_macro.hpp"
893 
894 // Register the component with class_loader.
895 // This acts as a sort of entry point, allowing the component to be discoverable when its library
896 // is being loaded into a running process.
897 RCLCPP_COMPONENTS_REGISTER_NODE(nav2_controller::ControllerServer)
This class hosts variety of plugins of different algorithms to complete control tasks from the expose...
rcl_interfaces::msg::SetParametersResult dynamicParametersCallback(std::vector< rclcpp::Parameter > parameters)
Callback executed when a parameter change is detected.
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.
nav_2d_msgs::msg::Twist2D getThresholdedTwist(const nav_2d_msgs::msg::Twist2D &twist)
get the thresholded Twist
void computeControl()
FollowPath action server callback. Handles action server updates and spins server until goal is reach...
bool isGoalReached()
Checks if goal is reached.
nav2_util::CallbackReturn on_deactivate(const rclcpp_lifecycle::State &state) override
Deactivates member variables.
~ControllerServer()
Destructor for nav2_controller::ControllerServer.
nav2_util::CallbackReturn on_activate(const rclcpp_lifecycle::State &state) override
Activates member variables.
bool findGoalCheckerId(const std::string &c_name, std::string &name)
Find the valid goal checker ID name for the specified parameter.
nav2_util::CallbackReturn on_cleanup(const rclcpp_lifecycle::State &state) override
Calls clean up states and resets member variables.
void updateGlobalPath()
Calls setPlannerPath method with an updated path received from action server.
void setPlannerPath(const nav_msgs::msg::Path &path)
Assigns path to controller.
void publishZeroVelocity()
Calls velocity publisher to publish zero velocity.
nav2_util::CallbackReturn on_configure(const rclcpp_lifecycle::State &state) override
Configures controller parameters and member variables.
nav2_util::CallbackReturn on_shutdown(const rclcpp_lifecycle::State &state) override
Called when in Shutdown state.
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.
void computeAndPublishVelocity()
Calculates velocity and publishes to "cmd_vel" topic.
std::shared_ptr< nav2_util::LifecycleNode > shared_from_this()
Get a shared pointer of this.
void createBond()
Create bond connection to lifecycle manager.
void destroyBond()
Destroy bond connection to lifecycle manager.