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"
35 #include "tf2/utils.hpp"
37 #include "nav2_planner/planner_server.hpp"
39 using namespace std::chrono_literals;
40 using rcl_interfaces::msg::ParameterType;
41 using std::placeholders::_1;
43 namespace nav2_planner
46 PlannerServer::PlannerServer(
const rclcpp::NodeOptions & options)
47 : nav2::LifecycleNode(
"planner_server",
"", options),
48 gp_loader_(
"nav2_core",
"nav2_core::GlobalPlanner"),
51 RCLCPP_INFO(get_logger(),
"Creating");
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);
65 costmap_thread_.reset();
71 RCLCPP_INFO(get_logger(),
"Configuring");
74 if (costmap_ros_->configure().id() !=
75 lifecycle_msgs::msg::State::PRIMARY_STATE_INACTIVE)
77 return nav2::CallbackReturn::FAILURE;
79 costmap_ = costmap_ros_->getCostmap();
82 costmap_thread_ = std::make_unique<nav2::NodeThread>(costmap_ros_);
85 get_logger(),
"Costmap size: %d,%d",
88 tf_ = costmap_ros_->getTfBuffer();
90 param_handler_ = std::make_unique<ParameterHandler>(
92 }
catch (
const std::exception & ex) {
93 RCLCPP_FATAL(get_logger(),
"%s", ex.what());
95 return nav2::CallbackReturn::FAILURE;
97 params_ = param_handler_->getParams();
99 for (
size_t i = 0; i != params_->planner_ids.size(); i++) {
101 nav2_core::GlobalPlanner::Ptr planner =
102 gp_loader_.createUniqueInstance(params_->planner_types[i]);
104 get_logger(),
"Created global planner plugin %s of type %s",
105 params_->planner_ids[i].c_str(), params_->planner_types[i].c_str());
106 planner->configure(node, params_->planner_ids[i], tf_, costmap_ros_);
107 planners_.insert({params_->planner_ids[i], planner});
108 }
catch (
const std::exception & ex) {
110 get_logger(),
"Failed to create global planner. Exception: %s",
113 return nav2::CallbackReturn::FAILURE;
117 for (
size_t i = 0; i != params_->planner_ids.size(); i++) {
118 planner_ids_concat_ += params_->planner_ids[i] + std::string(
" ");
123 "Planner Server has %s planners available.", planner_ids_concat_.c_str());
126 plan_publisher_ = create_publisher<nav_msgs::msg::Path>(
"plan");
129 is_path_valid_service_ = std::make_unique<IsPathValidService>(
133 action_server_pose_ = create_action_server<ActionToPose>(
134 "compute_path_to_pose",
136 std::bind(&PlannerServer::goalReceived<ActionToPose>,
this, std::placeholders::_1),
138 std::chrono::milliseconds(500),
141 action_server_poses_ = create_action_server<ActionThroughPoses>(
142 "compute_path_through_poses",
144 std::bind(&PlannerServer::goalReceived<ActionThroughPoses>,
this, std::placeholders::_1),
146 std::chrono::milliseconds(500),
149 return nav2::CallbackReturn::SUCCESS;
155 RCLCPP_INFO(get_logger(),
"Activating");
157 plan_publisher_->on_activate();
158 action_server_pose_->activate();
159 action_server_poses_->activate();
160 param_handler_->activate();
161 const auto costmap_ros_state = costmap_ros_->activate();
162 if (costmap_ros_state.id() != lifecycle_msgs::msg::State::PRIMARY_STATE_ACTIVE) {
163 return nav2::CallbackReturn::FAILURE;
166 PlannerMap::iterator it;
167 for (it = planners_.begin(); it != planners_.end(); ++it) {
168 it->second->activate();
171 is_path_valid_service_->initialize();
176 return nav2::CallbackReturn::SUCCESS;
182 RCLCPP_INFO(get_logger(),
"Deactivating");
184 action_server_pose_->deactivate();
185 action_server_poses_->deactivate();
186 plan_publisher_->on_deactivate();
187 param_handler_->deactivate();
196 costmap_ros_->deactivate();
198 PlannerMap::iterator it;
199 for (it = planners_.begin(); it != planners_.end(); ++it) {
200 it->second->deactivate();
203 is_path_valid_service_->reset();
208 return nav2::CallbackReturn::SUCCESS;
214 RCLCPP_INFO(get_logger(),
"Cleaning up");
216 action_server_pose_.reset();
217 action_server_poses_.reset();
218 plan_publisher_.reset();
221 costmap_ros_->cleanup();
223 PlannerMap::iterator it;
224 for (it = planners_.begin(); it != planners_.end(); ++it) {
225 it->second->cleanup();
229 is_path_valid_service_.reset();
230 costmap_thread_.reset();
232 return nav2::CallbackReturn::SUCCESS;
238 RCLCPP_INFO(get_logger(),
"Shutting down");
239 return nav2::CallbackReturn::SUCCESS;
245 if (planners_.find(goal->planner_id) == planners_.end()) {
246 if (planners_.size() == 1 && goal->planner_id.empty()) {
248 get_logger(),
"No planner was specified in action call. "
249 "Server will use only plugin loaded %s. "
250 "This warning will appear once.", planner_ids_concat_.c_str());
255 get_logger(),
"Action called with planner name %s, "
256 "which does not exist. Available planners are: %s.",
257 goal->planner_id.c_str(), planner_ids_concat_.c_str());
261 RCLCPP_DEBUG(get_logger(),
"Selected planner: %s.", goal->planner_id.c_str());
267 typename nav2::SimpleActionServer<T>::SharedPtr & action_server)
270 RCLCPP_DEBUG(get_logger(),
"Action server unavailable or inactive. Stopping.");
279 if (params_->costmap_update_timeout > rclcpp::Duration(0, 0)) {
280 auto waiting_start = now();
281 bool was_waiting = !costmap_ros_->isCurrent();
283 costmap_ros_->waitUntilCurrent(params_->costmap_update_timeout);
284 }
catch (
const std::runtime_error & ex) {
288 return (now() - waiting_start).seconds();
296 typename nav2::SimpleActionServer<T>::SharedPtr & action_server)
299 RCLCPP_INFO(get_logger(),
"Goal was canceled. Canceling planning action.");
309 typename nav2::SimpleActionServer<T>::SharedPtr & action_server,
310 typename std::shared_ptr<const typename T::Goal> & goal)
319 typename std::shared_ptr<const typename T::Goal> goal,
320 geometry_msgs::msg::PoseStamped & start)
322 if (goal->use_start) {
324 }
else if (!costmap_ros_->getRobotPose(start)) {
332 geometry_msgs::msg::PoseStamped & curr_start,
333 geometry_msgs::msg::PoseStamped & curr_goal)
335 if (!costmap_ros_->transformPoseToGlobalFrame(curr_start, curr_start) ||
336 !costmap_ros_->transformPoseToGlobalFrame(curr_goal, curr_goal))
346 const geometry_msgs::msg::PoseStamped & goal,
347 const nav_msgs::msg::Path & path,
348 const std::string & planner_id)
350 if (path.poses.empty()) {
352 get_logger(),
"Planning algorithm %s failed to generate a valid"
353 " path to (%.2f, %.2f)", planner_id.c_str(),
354 goal.pose.position.x, goal.pose.position.y);
360 "Found valid path of size %zu to (%.2f, %.2f)",
361 path.poses.size(), goal.pose.position.x,
362 goal.pose.position.y);
369 std::lock_guard<std::mutex> lock_reinit(param_handler_->getMutex());
371 auto start_time = this->now();
374 auto goal = action_server_poses_->get_current_goal();
375 auto result = std::make_shared<ActionThroughPoses::Result>();
376 nav_msgs::msg::Path concat_path;
377 RCLCPP_INFO(get_logger(),
"Computing path through poses to goal.");
379 geometry_msgs::msg::PoseStamped curr_start, curr_goal;
382 if (isServerInactive<ActionThroughPoses>(action_server_poses_) ||
383 isCancelRequested<ActionThroughPoses>(action_server_poses_))
390 getPreemptedGoalIfRequested<ActionThroughPoses>(action_server_poses_, goal);
392 if (goal->goals.goals.empty()) {
397 geometry_msgs::msg::PoseStamped start;
398 if (!getStartPose<ActionThroughPoses>(goal, start)) {
402 auto cancel_checker = [
this]() {
403 return action_server_poses_->is_cancel_requested();
407 for (
unsigned int i = 0; i != goal->goals.goals.size(); i++) {
414 curr_start = concat_path.poses.back();
415 curr_start.header = concat_path.header;
417 curr_goal = goal->goals.goals[i];
425 nav_msgs::msg::Path curr_path;
426 std::vector<geometry_msgs::msg::PoseStamped> viapoints;
428 curr_path =
getPlan(curr_start, curr_goal, viapoints, goal->planner_id, cancel_checker);
430 if (i == 0 || !params_->partial_plan_allowed) {
434 exceptionWarning(curr_start, curr_goal, goal->planner_id, ex, result->error_msg);
435 RCLCPP_WARN(get_logger(),
436 "Planner server failed to compute full path. Outputting partial path instead.");
440 if (!validatePath<ActionThroughPoses>(curr_goal, curr_path, goal->planner_id)) {
444 if (i == 0 || !params_->partial_plan_allowed) {
448 exceptionWarning(curr_start, curr_goal, goal->planner_id, exception, result->error_msg);
449 RCLCPP_WARN(get_logger(),
450 "Planner server failed to compute full path. Outputting partial path instead.");
456 size_t curr_path_size = curr_path.poses.size();
457 if (i == 0 || curr_path_size == 1) {
459 concat_path.poses.insert(
460 concat_path.poses.end(), curr_path.poses.begin(), curr_path.poses.end());
461 }
else if (curr_path_size > 1) {
463 concat_path.poses.insert(
464 concat_path.poses.end(), curr_path.poses.begin() + 1, curr_path.poses.end());
466 concat_path.header = curr_path.header;
468 if (i == goal->goals.goals.size() - 1) {
469 result->last_reached_index = ActionThroughPosesResult::ALL_GOALS;
471 result->last_reached_index = i;
476 result->path = concat_path;
479 auto cycle_duration = this->now() - start_time;
480 result->planning_time = cycle_duration;
482 if (params_->max_planner_duration && cycle_duration.seconds() > params_->max_planner_duration) {
485 "Planner loop missed its desired rate of %.4f Hz. Current loop rate is %.4f Hz"
487 1 / params_->max_planner_duration, 1 / cycle_duration.seconds(),
489 (
" Waited " + std::to_string(costmap_wait) +
"s for costmap update.").c_str() :
"");
492 action_server_poses_->succeeded_current(result);
494 exceptionWarning(curr_start, curr_goal, goal->planner_id, ex, result->error_msg);
495 result->error_code = ActionThroughPosesResult::INVALID_PLANNER;
496 action_server_poses_->terminate_current(result);
498 exceptionWarning(curr_start, curr_goal, goal->planner_id, ex, result->error_msg);
499 result->error_code = ActionThroughPosesResult::START_OCCUPIED;
500 action_server_poses_->terminate_current(result);
502 exceptionWarning(curr_start, curr_goal, goal->planner_id, ex, result->error_msg);
503 result->error_code = ActionThroughPosesResult::GOAL_OCCUPIED;
504 action_server_poses_->terminate_current(result);
506 exceptionWarning(curr_start, curr_goal, goal->planner_id, ex, result->error_msg);
507 result->error_code = ActionThroughPosesResult::NO_VALID_PATH;
508 action_server_poses_->terminate_current(result);
510 exceptionWarning(curr_start, curr_goal, goal->planner_id, ex, result->error_msg);
511 result->error_code = ActionThroughPosesResult::TIMEOUT;
512 action_server_poses_->terminate_current(result);
514 exceptionWarning(curr_start, curr_goal, goal->planner_id, ex, result->error_msg);
515 result->error_code = ActionThroughPosesResult::START_OUTSIDE_MAP;
516 action_server_poses_->terminate_current(result);
518 exceptionWarning(curr_start, curr_goal, goal->planner_id, ex, result->error_msg);
519 result->error_code = ActionThroughPosesResult::GOAL_OUTSIDE_MAP;
520 action_server_poses_->terminate_current(result);
522 exceptionWarning(curr_start, curr_goal, goal->planner_id, ex, result->error_msg);
523 result->error_code = ActionThroughPosesResult::TF_ERROR;
524 action_server_poses_->terminate_current(result);
526 exceptionWarning(curr_start, curr_goal, goal->planner_id, ex, result->error_msg);
527 result->error_code = ActionThroughPosesResult::NO_VIAPOINTS_GIVEN;
528 action_server_poses_->terminate_current(result);
530 result->error_msg =
"Goal was canceled. Canceling planning action.";
531 RCLCPP_INFO(get_logger(),
"%s", result->error_msg.c_str());
532 action_server_poses_->terminate_all();
533 }
catch (std::exception & ex) {
534 exceptionWarning(curr_start, curr_goal, goal->planner_id, ex, result->error_msg);
535 result->error_code = ActionThroughPosesResult::UNKNOWN;
536 action_server_poses_->terminate_current(result);
543 std::lock_guard<std::mutex> lock_reinit(param_handler_->getMutex());
545 auto start_time = this->now();
548 auto goal = action_server_pose_->get_current_goal();
549 auto result = std::make_shared<ActionToPose::Result>();
550 RCLCPP_INFO(get_logger(),
"Computing path to goal.");
552 geometry_msgs::msg::PoseStamped start;
555 if (isServerInactive<ActionToPose>(action_server_pose_) ||
556 isCancelRequested<ActionToPose>(action_server_pose_))
563 getPreemptedGoalIfRequested<ActionToPose>(action_server_pose_, goal);
566 if (!getStartPose<ActionToPose>(goal, start)) {
571 geometry_msgs::msg::PoseStamped goal_pose = goal->goal;
576 auto cancel_checker = [
this]() {
577 return action_server_pose_->is_cancel_requested();
580 result->path =
getPlan(start, goal_pose, goal->viapoints, goal->planner_id, cancel_checker);
582 if (!validatePath<ActionThroughPoses>(goal_pose, result->path, goal->planner_id)) {
589 auto cycle_duration = this->now() - start_time;
590 result->planning_time = cycle_duration;
592 if (params_->max_planner_duration && cycle_duration.seconds() > params_->max_planner_duration) {
595 "Planner loop missed its desired rate of %.4f Hz. Current loop rate is %.4f Hz"
597 1 / params_->max_planner_duration, 1 / cycle_duration.seconds(),
599 (
" Waited " + std::to_string(costmap_wait) +
"s for costmap update.").c_str() :
"");
601 action_server_pose_->succeeded_current(result);
603 exceptionWarning(start, goal->goal, goal->planner_id, ex, result->error_msg);
604 result->error_code = ActionToPoseResult::INVALID_PLANNER;
605 action_server_pose_->terminate_current(result);
607 exceptionWarning(start, goal->goal, goal->planner_id, ex, result->error_msg);
608 result->error_code = ActionToPoseResult::START_OCCUPIED;
609 action_server_pose_->terminate_current(result);
611 exceptionWarning(start, goal->goal, goal->planner_id, ex, result->error_msg);
612 result->error_code = ActionToPoseResult::GOAL_OCCUPIED;
613 action_server_pose_->terminate_current(result);
615 exceptionWarning(start, goal->goal, goal->planner_id, ex, result->error_msg);
616 result->error_code = ActionToPoseResult::NO_VALID_PATH;
617 action_server_pose_->terminate_current(result);
619 exceptionWarning(start, goal->goal, goal->planner_id, ex, result->error_msg);
620 result->error_code = ActionToPoseResult::TIMEOUT;
621 action_server_pose_->terminate_current(result);
623 exceptionWarning(start, goal->goal, goal->planner_id, ex, result->error_msg);
624 result->error_code = ActionToPoseResult::START_OUTSIDE_MAP;
625 action_server_pose_->terminate_current(result);
627 exceptionWarning(start, goal->goal, goal->planner_id, ex, result->error_msg);
628 result->error_code = ActionToPoseResult::GOAL_OUTSIDE_MAP;
629 action_server_pose_->terminate_current(result);
631 exceptionWarning(start, goal->goal, goal->planner_id, ex, result->error_msg);
632 result->error_code = ActionToPoseResult::TF_ERROR;
633 action_server_pose_->terminate_current(result);
635 result->error_msg =
"Goal was canceled. Canceling planning action.";
636 RCLCPP_INFO(get_logger(),
"%s", result->error_msg.c_str());
637 action_server_pose_->terminate_all();
638 }
catch (std::exception & ex) {
639 exceptionWarning(start, goal->goal, goal->planner_id, ex, result->error_msg);
640 result->error_code = ActionToPoseResult::UNKNOWN;
641 action_server_pose_->terminate_current(result);
647 const geometry_msgs::msg::PoseStamped & start,
648 const geometry_msgs::msg::PoseStamped & goal,
649 const std::vector<geometry_msgs::msg::PoseStamped> & viapoints,
650 const std::string & planner_id,
651 std::function<
bool()> cancel_checker)
654 get_logger(),
"Attempting to a find path from (%.2f, %.2f) to "
655 "(%.2f, %.2f).", start.pose.position.x, start.pose.position.y,
656 goal.pose.position.x, goal.pose.position.y);
658 if (planners_.find(planner_id) != planners_.end()) {
659 return planners_[planner_id]->createPlan(start, goal, viapoints, cancel_checker);
661 if (planners_.size() == 1 && planner_id.empty()) {
663 get_logger(),
"No planners specified in action call. "
664 "Server will use only plugin %s in server."
665 " This warning will appear once.", planner_ids_concat_.c_str());
666 return planners_[planners_.begin()->first]->createPlan(start, goal, viapoints,
670 get_logger(),
"planner %s is not a valid planner. "
671 "Planner names are: %s", planner_id.c_str(),
672 planner_ids_concat_.c_str());
677 return nav_msgs::msg::Path();
683 auto msg = std::make_unique<nav_msgs::msg::Path>(path);
684 if (plan_publisher_->is_activated() && plan_publisher_->get_subscription_count() > 0) {
685 plan_publisher_->publish(std::move(msg));
689 void PlannerServer::exceptionWarning(
690 const geometry_msgs::msg::PoseStamped & start,
691 const geometry_msgs::msg::PoseStamped & goal,
692 const std::string & planner_id,
693 const std::exception & ex,
694 std::string & error_msg)
696 std::stringstream ss;
697 ss << std::fixed << std::setprecision(2)
698 << planner_id <<
"plugin failed to plan from ("
699 << start.pose.position.x <<
", " << start.pose.position.y
701 << start.pose.orientation.x <<
", " << start.pose.orientation.y <<
", "
702 << start.pose.orientation.z <<
", " << start.pose.orientation.w
703 <<
"] (yaw: " << tf2::getYaw(start.pose.orientation)
705 << goal.pose.position.x <<
", " << goal.pose.position.y <<
")"
707 << goal.pose.orientation.x <<
", " << goal.pose.orientation.y <<
", "
708 << goal.pose.orientation.z <<
", " << goal.pose.orientation.w
709 <<
"] (yaw: " << tf2::getYaw(goal.pose.orientation)
711 <<
": \"" << ex.what() <<
"\"";
713 error_msg = ss.str();
714 RCLCPP_WARN(get_logger(),
"%s", error_msg.c_str());
719 #include "rclcpp_components/register_node_macro.hpp"
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.
unsigned int getSizeInCellsY() const
Accessor for the y size of the costmap in cells.
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.
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 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....
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.