Nav2 Navigation Stack - lyrical  lyrical
ROS 2 Navigation Stack
planner_server.cpp
1 // Copyright (c) 2018 Intel Corporation
2 // Copyright (c) 2019 Samsung Research America
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 
16 #include <chrono>
17 #include <cmath>
18 #include <iomanip>
19 #include <iostream>
20 #include <limits>
21 #include <iterator>
22 #include <memory>
23 #include <string>
24 #include <vector>
25 #include <utility>
26 
27 #include "lifecycle_msgs/msg/state.hpp"
28 #include "nav2_util/costmap.hpp"
29 #include "nav2_ros_common/node_utils.hpp"
30 #include "nav2_util/geometry_utils.hpp"
31 #include "nav2_costmap_2d/cost_values.hpp"
32 #include "nav2_costmap_2d/costmap_layer.hpp"
33 #include "nav2_costmap_2d/layered_costmap.hpp"
34 
35 #include "tf2/utils.hpp"
36 
37 #include "nav2_planner/planner_server.hpp"
38 
39 using namespace std::chrono_literals;
40 using rcl_interfaces::msg::ParameterType;
41 using std::placeholders::_1;
42 
43 namespace nav2_planner
44 {
45 
46 PlannerServer::PlannerServer(const rclcpp::NodeOptions & options)
47 : nav2::LifecycleNode("planner_server", "", options),
48  gp_loader_("nav2_core", "nav2_core::GlobalPlanner"),
49  costmap_(nullptr)
50 {
51  RCLCPP_INFO(get_logger(), "Creating");
52  // Setup the global costmap
53  costmap_ros_ = std::make_shared<nav2_costmap_2d::Costmap2DROS>(
54  "global_costmap", std::string{get_namespace()},
55  get_parameter("use_sim_time").as_bool(), options);
56 }
57 
59 {
60  /*
61  * Backstop ensuring this state is destroyed, even if deactivate/cleanup are
62  * never called.
63  */
64  planners_.clear();
65  costmap_thread_.reset();
66 }
67 
68 nav2::CallbackReturn
69 PlannerServer::on_configure(const rclcpp_lifecycle::State & state)
70 {
71  RCLCPP_INFO(get_logger(), "Configuring");
72  auto node = shared_from_this();
73 
74  costmap_ros_->configure();
75  costmap_ = costmap_ros_->getCostmap();
76 
77  // Launch a thread to run the costmap node
78  costmap_thread_ = std::make_unique<nav2::NodeThread>(costmap_ros_);
79 
80  RCLCPP_DEBUG(
81  get_logger(), "Costmap size: %d,%d",
82  costmap_->getSizeInCellsX(), costmap_->getSizeInCellsY());
83 
84  tf_ = costmap_ros_->getTfBuffer();
85  try {
86  param_handler_ = std::make_unique<ParameterHandler>(
87  node, get_logger());
88  } catch (const std::exception & ex) {
89  RCLCPP_FATAL(get_logger(), "%s", ex.what());
90  on_cleanup(state);
91  return nav2::CallbackReturn::FAILURE;
92  }
93  params_ = param_handler_->getParams();
94 
95  for (size_t i = 0; i != params_->planner_ids.size(); i++) {
96  try {
97  nav2_core::GlobalPlanner::Ptr planner =
98  gp_loader_.createUniqueInstance(params_->planner_types[i]);
99  RCLCPP_INFO(
100  get_logger(), "Created global planner plugin %s of type %s",
101  params_->planner_ids[i].c_str(), params_->planner_types[i].c_str());
102  planner->configure(node, params_->planner_ids[i], tf_, costmap_ros_);
103  planners_.insert({params_->planner_ids[i], planner});
104  } catch (const std::exception & ex) {
105  RCLCPP_FATAL(
106  get_logger(), "Failed to create global planner. Exception: %s",
107  ex.what());
108  on_cleanup(state);
109  return nav2::CallbackReturn::FAILURE;
110  }
111  }
112 
113  for (size_t i = 0; i != params_->planner_ids.size(); i++) {
114  planner_ids_concat_ += params_->planner_ids[i] + std::string(" ");
115  }
116 
117  RCLCPP_INFO(
118  get_logger(),
119  "Planner Server has %s planners available.", planner_ids_concat_.c_str());
120 
121  // Initialize pubs & subs
122  plan_publisher_ = create_publisher<nav_msgs::msg::Path>("plan");
123 
124  // Create is path valid service
125  is_path_valid_service_ = std::make_unique<IsPathValidService>(
126  shared_from_this(), costmap_ros_, params_->costmap_update_timeout);
127 
128  // Create the action servers for path planning to a pose and through poses
129  action_server_pose_ = create_action_server<ActionToPose>(
130  "compute_path_to_pose",
131  std::bind(&PlannerServer::computePlan, this),
132  std::bind(&PlannerServer::goalReceived<ActionToPose>, this, std::placeholders::_1),
133  nullptr,
134  std::chrono::milliseconds(500),
135  true);
136 
137  action_server_poses_ = create_action_server<ActionThroughPoses>(
138  "compute_path_through_poses",
139  std::bind(&PlannerServer::computePlanThroughPoses, this),
140  std::bind(&PlannerServer::goalReceived<ActionThroughPoses>, this, std::placeholders::_1),
141  nullptr,
142  std::chrono::milliseconds(500),
143  true);
144 
145  return nav2::CallbackReturn::SUCCESS;
146 }
147 
148 nav2::CallbackReturn
149 PlannerServer::on_activate(const rclcpp_lifecycle::State & /*state*/)
150 {
151  RCLCPP_INFO(get_logger(), "Activating");
152 
153  plan_publisher_->on_activate();
154  action_server_pose_->activate();
155  action_server_poses_->activate();
156  param_handler_->activate();
157  const auto costmap_ros_state = costmap_ros_->activate();
158  if (costmap_ros_state.id() != lifecycle_msgs::msg::State::PRIMARY_STATE_ACTIVE) {
159  return nav2::CallbackReturn::FAILURE;
160  }
161 
162  PlannerMap::iterator it;
163  for (it = planners_.begin(); it != planners_.end(); ++it) {
164  it->second->activate();
165  }
166 
167  is_path_valid_service_->initialize();
168 
169  // create bond connection
170  createBond();
171 
172  return nav2::CallbackReturn::SUCCESS;
173 }
174 
175 nav2::CallbackReturn
176 PlannerServer::on_deactivate(const rclcpp_lifecycle::State & /*state*/)
177 {
178  RCLCPP_INFO(get_logger(), "Deactivating");
179 
180  action_server_pose_->deactivate();
181  action_server_poses_->deactivate();
182  plan_publisher_->on_deactivate();
183  param_handler_->deactivate();
184 
185  /*
186  * The costmap is also a lifecycle node, so it may have already fired on_deactivate
187  * via rcl preshutdown cb. Despite the rclcpp docs saying on_shutdown callbacks fire
188  * in the order added, the preshutdown callbacks clearly don't per se, due to using an
189  * unordered_set iteration. Once this issue is resolved, we can maybe make a stronger
190  * ordering assumption: https://github.com/ros2/rclcpp/issues/2096
191  */
192  costmap_ros_->deactivate();
193 
194  PlannerMap::iterator it;
195  for (it = planners_.begin(); it != planners_.end(); ++it) {
196  it->second->deactivate();
197  }
198 
199  is_path_valid_service_->reset();
200 
201  // destroy bond connection
202  destroyBond();
203 
204  return nav2::CallbackReturn::SUCCESS;
205 }
206 
207 nav2::CallbackReturn
208 PlannerServer::on_cleanup(const rclcpp_lifecycle::State & /*state*/)
209 {
210  RCLCPP_INFO(get_logger(), "Cleaning up");
211 
212  action_server_pose_.reset();
213  action_server_poses_.reset();
214  plan_publisher_.reset();
215  tf_.reset();
216 
217  costmap_ros_->cleanup();
218 
219  PlannerMap::iterator it;
220  for (it = planners_.begin(); it != planners_.end(); ++it) {
221  it->second->cleanup();
222  }
223 
224  planners_.clear();
225  is_path_valid_service_.reset();
226  costmap_thread_.reset();
227  costmap_ = nullptr;
228  return nav2::CallbackReturn::SUCCESS;
229 }
230 
231 nav2::CallbackReturn
232 PlannerServer::on_shutdown(const rclcpp_lifecycle::State &)
233 {
234  RCLCPP_INFO(get_logger(), "Shutting down");
235  return nav2::CallbackReturn::SUCCESS;
236 }
237 
238 template<typename T>
239 bool PlannerServer::goalReceived(std::shared_ptr<const typename T::Goal> goal)
240 {
241  if (planners_.find(goal->planner_id) == planners_.end()) {
242  if (planners_.size() == 1 && goal->planner_id.empty()) {
243  RCLCPP_WARN_ONCE(
244  get_logger(), "No planner was specified in action call. "
245  "Server will use only plugin loaded %s. "
246  "This warning will appear once.", planner_ids_concat_.c_str());
247  return true;
248  }
249 
250  RCLCPP_ERROR(
251  get_logger(), "Action called with planner name %s, "
252  "which does not exist. Available planners are: %s.",
253  goal->planner_id.c_str(), planner_ids_concat_.c_str());
254  return false;
255  }
256 
257  RCLCPP_DEBUG(get_logger(), "Selected planner: %s.", goal->planner_id.c_str());
258  return true;
259 }
260 
261 template<typename T>
263  typename nav2::SimpleActionServer<T>::SharedPtr & action_server)
264 {
265  if (action_server == nullptr || !action_server->is_server_active()) {
266  RCLCPP_DEBUG(get_logger(), "Action server unavailable or inactive. Stopping.");
267  return true;
268  }
269 
270  return false;
271 }
272 
274 {
275  if (params_->costmap_update_timeout > rclcpp::Duration(0, 0)) {
276  auto waiting_start = now();
277  bool was_waiting = !costmap_ros_->isCurrent();
278  try {
279  costmap_ros_->waitUntilCurrent(params_->costmap_update_timeout);
280  } catch (const std::runtime_error & ex) {
281  throw nav2_core::PlannerTimedOut(ex.what());
282  }
283  if (was_waiting) {
284  return (now() - waiting_start).seconds();
285  }
286  }
287  return 0.0;
288 }
289 
290 template<typename T>
292  typename nav2::SimpleActionServer<T>::SharedPtr & action_server)
293 {
294  if (action_server->is_cancel_requested()) {
295  RCLCPP_INFO(get_logger(), "Goal was canceled. Canceling planning action.");
296  action_server->terminate_all();
297  return true;
298  }
299 
300  return false;
301 }
302 
303 template<typename T>
305  typename nav2::SimpleActionServer<T>::SharedPtr & action_server,
306  typename std::shared_ptr<const typename T::Goal> goal)
307 {
308  if (action_server->is_preempt_requested()) {
309  goal = action_server->accept_pending_goal();
310  }
311 }
312 
313 template<typename T>
315  typename std::shared_ptr<const typename T::Goal> goal,
316  geometry_msgs::msg::PoseStamped & start)
317 {
318  if (goal->use_start) {
319  start = goal->start;
320  } else if (!costmap_ros_->getRobotPose(start)) {
321  return false;
322  }
323 
324  return true;
325 }
326 
328  geometry_msgs::msg::PoseStamped & curr_start,
329  geometry_msgs::msg::PoseStamped & curr_goal)
330 {
331  if (!costmap_ros_->transformPoseToGlobalFrame(curr_start, curr_start) ||
332  !costmap_ros_->transformPoseToGlobalFrame(curr_goal, curr_goal))
333  {
334  return false;
335  }
336 
337  return true;
338 }
339 
340 template<typename T>
342  const geometry_msgs::msg::PoseStamped & goal,
343  const nav_msgs::msg::Path & path,
344  const std::string & planner_id)
345 {
346  if (path.poses.empty()) {
347  RCLCPP_WARN(
348  get_logger(), "Planning algorithm %s failed to generate a valid"
349  " path to (%.2f, %.2f)", planner_id.c_str(),
350  goal.pose.position.x, goal.pose.position.y);
351  return false;
352  }
353 
354  RCLCPP_DEBUG(
355  get_logger(),
356  "Found valid path of size %zu to (%.2f, %.2f)",
357  path.poses.size(), goal.pose.position.x,
358  goal.pose.position.y);
359 
360  return true;
361 }
362 
364 {
365  std::lock_guard<std::mutex> lock_reinit(param_handler_->getMutex());
366 
367  auto start_time = this->now();
368 
369  // Initialize the ComputePathThroughPoses goal and result
370  auto goal = action_server_poses_->get_current_goal();
371  auto result = std::make_shared<ActionThroughPoses::Result>();
372  nav_msgs::msg::Path concat_path;
373  RCLCPP_INFO(get_logger(), "Computing path through poses to goal.");
374 
375  geometry_msgs::msg::PoseStamped curr_start, curr_goal;
376 
377  try {
378  if (isServerInactive<ActionThroughPoses>(action_server_poses_) ||
379  isCancelRequested<ActionThroughPoses>(action_server_poses_))
380  {
381  return;
382  }
383 
384  double costmap_wait = waitForCostmap();
385 
386  getPreemptedGoalIfRequested<ActionThroughPoses>(action_server_poses_, goal);
387 
388  if (goal->goals.goals.empty()) {
389  throw nav2_core::NoViapointsGiven("No viapoints given");
390  }
391 
392  // Use start pose if provided otherwise use current robot pose
393  geometry_msgs::msg::PoseStamped start;
394  if (!getStartPose<ActionThroughPoses>(goal, start)) {
395  throw nav2_core::PlannerTFError("Unable to get start pose");
396  }
397 
398  auto cancel_checker = [this]() {
399  return action_server_poses_->is_cancel_requested();
400  };
401 
402  // Get consecutive paths through these points
403  for (unsigned int i = 0; i != goal->goals.goals.size(); i++) {
404  // Get starting point
405  if (i == 0) {
406  curr_start = start;
407  } else {
408  // pick the end of the last planning task as the start for the next one
409  // to allow for path tolerance deviations
410  curr_start = concat_path.poses.back();
411  curr_start.header = concat_path.header;
412  }
413  curr_goal = goal->goals.goals[i];
414 
415  // Transform them into the global frame
416  if (!transformPosesToGlobalFrame(curr_start, curr_goal)) {
417  throw nav2_core::PlannerTFError("Unable to transform poses to global frame");
418  }
419 
420  // Get plan from start -> goal
421  nav_msgs::msg::Path curr_path;
422  std::vector<geometry_msgs::msg::PoseStamped> viapoints;
423  try {
424  curr_path = getPlan(curr_start, curr_goal, viapoints, goal->planner_id, cancel_checker);
425  } catch (nav2_core::PlannerException & ex) {
426  if (i == 0 || !params_->partial_plan_allowed) {
427  throw;
428  }
429 
430  exceptionWarning(curr_start, curr_goal, goal->planner_id, ex, result->error_msg);
431  RCLCPP_WARN(get_logger(),
432  "Planner server failed to compute full path. Outputting partial path instead.");
433  break;
434  }
435 
436  if (!validatePath<ActionThroughPoses>(curr_goal, curr_path, goal->planner_id)) {
437  auto exception =
438  nav2_core::NoValidPathCouldBeFound(goal->planner_id + " generated a empty path");
439 
440  if (i == 0 || !params_->partial_plan_allowed) {
441  throw exception;
442  }
443 
444  exceptionWarning(curr_start, curr_goal, goal->planner_id, exception, result->error_msg);
445  RCLCPP_WARN(get_logger(),
446  "Planner server failed to compute full path. Outputting partial path instead.");
447  break;
448  }
449 
450  // Concatenate paths together, but skip the first pose of subsequent paths
451  // to avoid duplicating the connection point
452  if (i == 0) {
453  // First path: add all poses
454  concat_path.poses.insert(
455  concat_path.poses.end(), curr_path.poses.begin(), curr_path.poses.end());
456  } else if (curr_path.poses.size() > 1) {
457  // Subsequent paths: skip the first pose to avoid duplication
458  concat_path.poses.insert(
459  concat_path.poses.end(), curr_path.poses.begin() + 1, curr_path.poses.end());
460  }
461  concat_path.header = curr_path.header;
462 
463  if (i == goal->goals.goals.size() - 1) {
464  result->last_reached_index = ActionThroughPosesResult::ALL_GOALS;
465  } else {
466  result->last_reached_index = i;
467  }
468  }
469 
470  // Publish the plan for visualization purposes
471  result->path = concat_path;
472  publishPlan(result->path);
473 
474  auto cycle_duration = this->now() - start_time;
475  result->planning_time = cycle_duration;
476 
477  if (params_->max_planner_duration && cycle_duration.seconds() > params_->max_planner_duration) {
478  RCLCPP_WARN(
479  get_logger(),
480  "Planner loop missed its desired rate of %.4f Hz. Current loop rate is %.4f Hz"
481  "%s",
482  1 / params_->max_planner_duration, 1 / cycle_duration.seconds(),
483  costmap_wait > 0.0 ?
484  (" Waited " + std::to_string(costmap_wait) + "s for costmap update.").c_str() : "");
485  }
486 
487  action_server_poses_->succeeded_current(result);
488  } catch (nav2_core::InvalidPlanner & ex) {
489  exceptionWarning(curr_start, curr_goal, goal->planner_id, ex, result->error_msg);
490  result->error_code = ActionThroughPosesResult::INVALID_PLANNER;
491  action_server_poses_->terminate_current(result);
492  } catch (nav2_core::StartOccupied & ex) {
493  exceptionWarning(curr_start, curr_goal, goal->planner_id, ex, result->error_msg);
494  result->error_code = ActionThroughPosesResult::START_OCCUPIED;
495  action_server_poses_->terminate_current(result);
496  } catch (nav2_core::GoalOccupied & ex) {
497  exceptionWarning(curr_start, curr_goal, goal->planner_id, ex, result->error_msg);
498  result->error_code = ActionThroughPosesResult::GOAL_OCCUPIED;
499  action_server_poses_->terminate_current(result);
500  } catch (nav2_core::NoValidPathCouldBeFound & ex) {
501  exceptionWarning(curr_start, curr_goal, goal->planner_id, ex, result->error_msg);
502  result->error_code = ActionThroughPosesResult::NO_VALID_PATH;
503  action_server_poses_->terminate_current(result);
504  } catch (nav2_core::PlannerTimedOut & ex) {
505  exceptionWarning(curr_start, curr_goal, goal->planner_id, ex, result->error_msg);
506  result->error_code = ActionThroughPosesResult::TIMEOUT;
507  action_server_poses_->terminate_current(result);
508  } catch (nav2_core::StartOutsideMapBounds & ex) {
509  exceptionWarning(curr_start, curr_goal, goal->planner_id, ex, result->error_msg);
510  result->error_code = ActionThroughPosesResult::START_OUTSIDE_MAP;
511  action_server_poses_->terminate_current(result);
512  } catch (nav2_core::GoalOutsideMapBounds & ex) {
513  exceptionWarning(curr_start, curr_goal, goal->planner_id, ex, result->error_msg);
514  result->error_code = ActionThroughPosesResult::GOAL_OUTSIDE_MAP;
515  action_server_poses_->terminate_current(result);
516  } catch (nav2_core::PlannerTFError & ex) {
517  exceptionWarning(curr_start, curr_goal, goal->planner_id, ex, result->error_msg);
518  result->error_code = ActionThroughPosesResult::TF_ERROR;
519  action_server_poses_->terminate_current(result);
520  } catch (nav2_core::NoViapointsGiven & ex) {
521  exceptionWarning(curr_start, curr_goal, goal->planner_id, ex, result->error_msg);
522  result->error_code = ActionThroughPosesResult::NO_VIAPOINTS_GIVEN;
523  action_server_poses_->terminate_current(result);
524  } catch (nav2_core::PlannerCancelled &) {
525  result->error_msg = "Goal was canceled. Canceling planning action.";
526  RCLCPP_INFO(get_logger(), "%s", result->error_msg.c_str());
527  action_server_poses_->terminate_all();
528  } catch (std::exception & ex) {
529  exceptionWarning(curr_start, curr_goal, goal->planner_id, ex, result->error_msg);
530  result->error_code = ActionThroughPosesResult::UNKNOWN;
531  action_server_poses_->terminate_current(result);
532  }
533 }
534 
535 void
537 {
538  std::lock_guard<std::mutex> lock_reinit(param_handler_->getMutex());
539 
540  auto start_time = this->now();
541 
542  // Initialize the ComputePathToPose goal and result
543  auto goal = action_server_pose_->get_current_goal();
544  auto result = std::make_shared<ActionToPose::Result>();
545  RCLCPP_INFO(get_logger(), "Computing path to goal.");
546 
547  geometry_msgs::msg::PoseStamped start;
548 
549  try {
550  if (isServerInactive<ActionToPose>(action_server_pose_) ||
551  isCancelRequested<ActionToPose>(action_server_pose_))
552  {
553  return;
554  }
555 
556  double costmap_wait = waitForCostmap();
557 
558  getPreemptedGoalIfRequested<ActionToPose>(action_server_pose_, goal);
559 
560  // Use start pose if provided otherwise use current robot pose
561  if (!getStartPose<ActionToPose>(goal, start)) {
562  throw nav2_core::PlannerTFError("Unable to get start pose");
563  }
564 
565  // Transform them into the global frame
566  geometry_msgs::msg::PoseStamped goal_pose = goal->goal;
567  if (!transformPosesToGlobalFrame(start, goal_pose)) {
568  throw nav2_core::PlannerTFError("Unable to transform poses to global frame");
569  }
570 
571  auto cancel_checker = [this]() {
572  return action_server_pose_->is_cancel_requested();
573  };
574 
575  result->path = getPlan(start, goal_pose, goal->viapoints, goal->planner_id, cancel_checker);
576 
577  if (!validatePath<ActionThroughPoses>(goal_pose, result->path, goal->planner_id)) {
578  throw nav2_core::NoValidPathCouldBeFound(goal->planner_id + " generated a empty path");
579  }
580 
581  // Publish the plan for visualization purposes
582  publishPlan(result->path);
583 
584  auto cycle_duration = this->now() - start_time;
585  result->planning_time = cycle_duration;
586 
587  if (params_->max_planner_duration && cycle_duration.seconds() > params_->max_planner_duration) {
588  RCLCPP_WARN(
589  get_logger(),
590  "Planner loop missed its desired rate of %.4f Hz. Current loop rate is %.4f Hz"
591  "%s",
592  1 / params_->max_planner_duration, 1 / cycle_duration.seconds(),
593  costmap_wait > 0.0 ?
594  (" Waited " + std::to_string(costmap_wait) + "s for costmap update.").c_str() : "");
595  }
596  action_server_pose_->succeeded_current(result);
597  } catch (nav2_core::InvalidPlanner & ex) {
598  exceptionWarning(start, goal->goal, goal->planner_id, ex, result->error_msg);
599  result->error_code = ActionToPoseResult::INVALID_PLANNER;
600  action_server_pose_->terminate_current(result);
601  } catch (nav2_core::StartOccupied & ex) {
602  exceptionWarning(start, goal->goal, goal->planner_id, ex, result->error_msg);
603  result->error_code = ActionToPoseResult::START_OCCUPIED;
604  action_server_pose_->terminate_current(result);
605  } catch (nav2_core::GoalOccupied & ex) {
606  exceptionWarning(start, goal->goal, goal->planner_id, ex, result->error_msg);
607  result->error_code = ActionToPoseResult::GOAL_OCCUPIED;
608  action_server_pose_->terminate_current(result);
609  } catch (nav2_core::NoValidPathCouldBeFound & ex) {
610  exceptionWarning(start, goal->goal, goal->planner_id, ex, result->error_msg);
611  result->error_code = ActionToPoseResult::NO_VALID_PATH;
612  action_server_pose_->terminate_current(result);
613  } catch (nav2_core::PlannerTimedOut & ex) {
614  exceptionWarning(start, goal->goal, goal->planner_id, ex, result->error_msg);
615  result->error_code = ActionToPoseResult::TIMEOUT;
616  action_server_pose_->terminate_current(result);
617  } catch (nav2_core::StartOutsideMapBounds & ex) {
618  exceptionWarning(start, goal->goal, goal->planner_id, ex, result->error_msg);
619  result->error_code = ActionToPoseResult::START_OUTSIDE_MAP;
620  action_server_pose_->terminate_current(result);
621  } catch (nav2_core::GoalOutsideMapBounds & ex) {
622  exceptionWarning(start, goal->goal, goal->planner_id, ex, result->error_msg);
623  result->error_code = ActionToPoseResult::GOAL_OUTSIDE_MAP;
624  action_server_pose_->terminate_current(result);
625  } catch (nav2_core::PlannerTFError & ex) {
626  exceptionWarning(start, goal->goal, goal->planner_id, ex, result->error_msg);
627  result->error_code = ActionToPoseResult::TF_ERROR;
628  action_server_pose_->terminate_current(result);
629  } catch (nav2_core::PlannerCancelled &) {
630  result->error_msg = "Goal was canceled. Canceling planning action.";
631  RCLCPP_INFO(get_logger(), "%s", result->error_msg.c_str());
632  action_server_pose_->terminate_all();
633  } catch (std::exception & ex) {
634  exceptionWarning(start, goal->goal, goal->planner_id, ex, result->error_msg);
635  result->error_code = ActionToPoseResult::UNKNOWN;
636  action_server_pose_->terminate_current(result);
637  }
638 }
639 
640 nav_msgs::msg::Path
642  const geometry_msgs::msg::PoseStamped & start,
643  const geometry_msgs::msg::PoseStamped & goal,
644  const std::vector<geometry_msgs::msg::PoseStamped> & viapoints,
645  const std::string & planner_id,
646  std::function<bool()> cancel_checker)
647 {
648  RCLCPP_DEBUG(
649  get_logger(), "Attempting to a find path from (%.2f, %.2f) to "
650  "(%.2f, %.2f).", start.pose.position.x, start.pose.position.y,
651  goal.pose.position.x, goal.pose.position.y);
652 
653  if (planners_.find(planner_id) != planners_.end()) {
654  return planners_[planner_id]->createPlan(start, goal, viapoints, cancel_checker);
655  } else {
656  if (planners_.size() == 1 && planner_id.empty()) {
657  RCLCPP_WARN_ONCE(
658  get_logger(), "No planners specified in action call. "
659  "Server will use only plugin %s in server."
660  " This warning will appear once.", planner_ids_concat_.c_str());
661  return planners_[planners_.begin()->first]->createPlan(start, goal, viapoints,
662  cancel_checker);
663  } else {
664  RCLCPP_ERROR(
665  get_logger(), "planner %s is not a valid planner. "
666  "Planner names are: %s", planner_id.c_str(),
667  planner_ids_concat_.c_str());
668  throw nav2_core::InvalidPlanner("Planner id " + planner_id + " is invalid");
669  }
670  }
671 
672  return nav_msgs::msg::Path();
673 }
674 
675 void
676 PlannerServer::publishPlan(const nav_msgs::msg::Path & path)
677 {
678  auto msg = std::make_unique<nav_msgs::msg::Path>(path);
679  if (plan_publisher_->is_activated() && plan_publisher_->get_subscription_count() > 0) {
680  plan_publisher_->publish(std::move(msg));
681  }
682 }
683 
684 void PlannerServer::exceptionWarning(
685  const geometry_msgs::msg::PoseStamped & start,
686  const geometry_msgs::msg::PoseStamped & goal,
687  const std::string & planner_id,
688  const std::exception & ex,
689  std::string & error_msg)
690 {
691  std::stringstream ss;
692  ss << std::fixed << std::setprecision(2)
693  << planner_id << "plugin failed to plan from ("
694  << start.pose.position.x << ", " << start.pose.position.y
695  << ") [q: "
696  << start.pose.orientation.x << ", " << start.pose.orientation.y << ", "
697  << start.pose.orientation.z << ", " << start.pose.orientation.w
698  << "] (yaw: " << tf2::getYaw(start.pose.orientation)
699  << ") to ("
700  << goal.pose.position.x << ", " << goal.pose.position.y << ")"
701  << " [q: "
702  << goal.pose.orientation.x << ", " << goal.pose.orientation.y << ", "
703  << goal.pose.orientation.z << ", " << goal.pose.orientation.w
704  << "] (yaw: " << tf2::getYaw(goal.pose.orientation)
705  << ")"
706  << ": \"" << ex.what() << "\"";
707 
708  error_msg = ss.str();
709  RCLCPP_WARN(get_logger(), "%s", error_msg.c_str());
710 }
711 
712 } // namespace nav2_planner
713 
714 #include "rclcpp_components/register_node_macro.hpp"
715 
716 // Register the component with class_loader.
717 // This acts as a sort of entry point, allowing the component to be discoverable when its library
718 // is being loaded into a running process.
719 RCLCPP_COMPONENTS_REGISTER_NODE(nav2_planner::PlannerServer)
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.
bool is_cancel_requested() const
Whether or not a cancel command has come in.
void terminate_all(typename std::shared_ptr< typename ActionT::Result > result=std::make_shared< typename ActionT::Result >())
Terminate all pending and active actions.
bool is_preempt_requested() const
Whether the action server has been asked to be preempted with a new goal.
bool is_server_active()
Whether the action server is active or not.
const std::shared_ptr< const typename ActionT::Goal > accept_pending_goal()
Accept pending goals.
unsigned int getSizeInCellsX() const
Accessor for the x size of the costmap in cells.
Definition: costmap_2d.cpp:548
unsigned int getSizeInCellsY() const
Accessor for the y size of the costmap in cells.
Definition: costmap_2d.cpp:553
An action server implements the behavior tree's ComputePathToPose interface and hosts various plugins...
nav2::CallbackReturn on_configure(const rclcpp_lifecycle::State &state) override
Configure member variables and initializes planner.
void publishPlan(const nav_msgs::msg::Path &path)
Publish a path for visualization purposes.
void computePlan()
The action server callback which calls planner to get the path ComputePathToPose.
nav2::CallbackReturn on_deactivate(const rclcpp_lifecycle::State &state) override
Deactivate member variables.
bool isServerInactive(typename nav2::SimpleActionServer< T >::SharedPtr &action_server)
Check if an action server is valid / active.
bool getStartPose(typename std::shared_ptr< const typename T::Goal > goal, geometry_msgs::msg::PoseStamped &start)
Get the starting pose from costmap or message, if valid.
~PlannerServer()
A destructor for nav2_planner::PlannerServer.
bool goalReceived(std::shared_ptr< const typename T::Goal > goal)
Goal received callback to validate a new goal before acceptance.
nav2::CallbackReturn on_shutdown(const rclcpp_lifecycle::State &state) override
Called when in shutdown state.
void computePlanThroughPoses()
The action server callback which calls planner to get the path ComputePathThroughPoses.
bool isCancelRequested(typename nav2::SimpleActionServer< T >::SharedPtr &action_server)
Check if an action server has a cancellation request pending.
nav2::CallbackReturn on_cleanup(const rclcpp_lifecycle::State &state) override
Reset member variables.
double waitForCostmap()
Wait for costmap to be valid with updated sensor data or repopulate after a clearing recovery....
void getPreemptedGoalIfRequested(typename nav2::SimpleActionServer< T >::SharedPtr &action_server, typename std::shared_ptr< const typename T::Goal > goal)
Check if an action server has a preemption request and replaces the goal with the new preemption goal...
bool validatePath(const geometry_msgs::msg::PoseStamped &curr_goal, const nav_msgs::msg::Path &path, const std::string &planner_id)
Validate that the path contains a meaningful path.
nav_msgs::msg::Path getPlan(const geometry_msgs::msg::PoseStamped &start, const geometry_msgs::msg::PoseStamped &goal, const std::vector< geometry_msgs::msg::PoseStamped > &viapoints, const std::string &planner_id, std::function< bool()> cancel_checker)
Method to get plan from the desired plugin.
nav2::CallbackReturn on_activate(const rclcpp_lifecycle::State &state) override
Activate member variables.
bool transformPosesToGlobalFrame(geometry_msgs::msg::PoseStamped &curr_start, geometry_msgs::msg::PoseStamped &curr_goal)
Transform start and goal poses into the costmap global frame for path planning plugins to utilize.