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