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 costmap_ros_->configure();
75 costmap_ = costmap_ros_->getCostmap();
78 costmap_thread_ = std::make_unique<nav2::NodeThread>(costmap_ros_);
81 get_logger(),
"Costmap size: %d,%d",
84 tf_ = costmap_ros_->getTfBuffer();
86 param_handler_ = std::make_unique<ParameterHandler>(
88 }
catch (
const std::exception & ex) {
89 RCLCPP_FATAL(get_logger(),
"%s", ex.what());
91 return nav2::CallbackReturn::FAILURE;
93 params_ = param_handler_->getParams();
95 for (
size_t i = 0; i != params_->planner_ids.size(); i++) {
97 nav2_core::GlobalPlanner::Ptr planner =
98 gp_loader_.createUniqueInstance(params_->planner_types[i]);
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) {
106 get_logger(),
"Failed to create global planner. Exception: %s",
109 return nav2::CallbackReturn::FAILURE;
113 for (
size_t i = 0; i != params_->planner_ids.size(); i++) {
114 planner_ids_concat_ += params_->planner_ids[i] + std::string(
" ");
119 "Planner Server has %s planners available.", planner_ids_concat_.c_str());
122 plan_publisher_ = create_publisher<nav_msgs::msg::Path>(
"plan");
125 is_path_valid_service_ = std::make_unique<IsPathValidService>(
129 action_server_pose_ = create_action_server<ActionToPose>(
130 "compute_path_to_pose",
132 std::bind(&PlannerServer::goalReceived<ActionToPose>,
this, std::placeholders::_1),
134 std::chrono::milliseconds(500),
137 action_server_poses_ = create_action_server<ActionThroughPoses>(
138 "compute_path_through_poses",
140 std::bind(&PlannerServer::goalReceived<ActionThroughPoses>,
this, std::placeholders::_1),
142 std::chrono::milliseconds(500),
145 return nav2::CallbackReturn::SUCCESS;
151 RCLCPP_INFO(get_logger(),
"Activating");
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;
162 PlannerMap::iterator it;
163 for (it = planners_.begin(); it != planners_.end(); ++it) {
164 it->second->activate();
167 is_path_valid_service_->initialize();
172 return nav2::CallbackReturn::SUCCESS;
178 RCLCPP_INFO(get_logger(),
"Deactivating");
180 action_server_pose_->deactivate();
181 action_server_poses_->deactivate();
182 plan_publisher_->on_deactivate();
183 param_handler_->deactivate();
192 costmap_ros_->deactivate();
194 PlannerMap::iterator it;
195 for (it = planners_.begin(); it != planners_.end(); ++it) {
196 it->second->deactivate();
199 is_path_valid_service_->reset();
204 return nav2::CallbackReturn::SUCCESS;
210 RCLCPP_INFO(get_logger(),
"Cleaning up");
212 action_server_pose_.reset();
213 action_server_poses_.reset();
214 plan_publisher_.reset();
217 costmap_ros_->cleanup();
219 PlannerMap::iterator it;
220 for (it = planners_.begin(); it != planners_.end(); ++it) {
221 it->second->cleanup();
225 is_path_valid_service_.reset();
226 costmap_thread_.reset();
228 return nav2::CallbackReturn::SUCCESS;
234 RCLCPP_INFO(get_logger(),
"Shutting down");
235 return nav2::CallbackReturn::SUCCESS;
241 if (planners_.find(goal->planner_id) == planners_.end()) {
242 if (planners_.size() == 1 && goal->planner_id.empty()) {
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());
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());
257 RCLCPP_DEBUG(get_logger(),
"Selected planner: %s.", goal->planner_id.c_str());
263 typename nav2::SimpleActionServer<T>::SharedPtr & action_server)
266 RCLCPP_DEBUG(get_logger(),
"Action server unavailable or inactive. Stopping.");
275 if (params_->costmap_update_timeout > rclcpp::Duration(0, 0)) {
276 auto waiting_start = now();
277 bool was_waiting = !costmap_ros_->isCurrent();
279 costmap_ros_->waitUntilCurrent(params_->costmap_update_timeout);
280 }
catch (
const std::runtime_error & ex) {
284 return (now() - waiting_start).seconds();
292 typename nav2::SimpleActionServer<T>::SharedPtr & action_server)
295 RCLCPP_INFO(get_logger(),
"Goal was canceled. Canceling planning action.");
305 typename nav2::SimpleActionServer<T>::SharedPtr & action_server,
306 typename std::shared_ptr<const typename T::Goal> goal)
315 typename std::shared_ptr<const typename T::Goal> goal,
316 geometry_msgs::msg::PoseStamped & start)
318 if (goal->use_start) {
320 }
else if (!costmap_ros_->getRobotPose(start)) {
328 geometry_msgs::msg::PoseStamped & curr_start,
329 geometry_msgs::msg::PoseStamped & curr_goal)
331 if (!costmap_ros_->transformPoseToGlobalFrame(curr_start, curr_start) ||
332 !costmap_ros_->transformPoseToGlobalFrame(curr_goal, curr_goal))
342 const geometry_msgs::msg::PoseStamped & goal,
343 const nav_msgs::msg::Path & path,
344 const std::string & planner_id)
346 if (path.poses.empty()) {
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);
356 "Found valid path of size %zu to (%.2f, %.2f)",
357 path.poses.size(), goal.pose.position.x,
358 goal.pose.position.y);
365 std::lock_guard<std::mutex> lock_reinit(param_handler_->getMutex());
367 auto start_time = this->now();
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.");
375 geometry_msgs::msg::PoseStamped curr_start, curr_goal;
378 if (isServerInactive<ActionThroughPoses>(action_server_poses_) ||
379 isCancelRequested<ActionThroughPoses>(action_server_poses_))
386 getPreemptedGoalIfRequested<ActionThroughPoses>(action_server_poses_, goal);
388 if (goal->goals.goals.empty()) {
393 geometry_msgs::msg::PoseStamped start;
394 if (!getStartPose<ActionThroughPoses>(goal, start)) {
398 auto cancel_checker = [
this]() {
399 return action_server_poses_->is_cancel_requested();
403 for (
unsigned int i = 0; i != goal->goals.goals.size(); i++) {
410 curr_start = concat_path.poses.back();
411 curr_start.header = concat_path.header;
413 curr_goal = goal->goals.goals[i];
421 nav_msgs::msg::Path curr_path;
422 std::vector<geometry_msgs::msg::PoseStamped> viapoints;
424 curr_path =
getPlan(curr_start, curr_goal, viapoints, goal->planner_id, cancel_checker);
426 if (i == 0 || !params_->partial_plan_allowed) {
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.");
436 if (!validatePath<ActionThroughPoses>(curr_goal, curr_path, goal->planner_id)) {
440 if (i == 0 || !params_->partial_plan_allowed) {
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.");
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) {
458 concat_path.poses.insert(
459 concat_path.poses.end(), curr_path.poses.begin() + 1, curr_path.poses.end());
461 concat_path.header = curr_path.header;
463 if (i == goal->goals.goals.size() - 1) {
464 result->last_reached_index = ActionThroughPosesResult::ALL_GOALS;
466 result->last_reached_index = i;
471 result->path = concat_path;
474 auto cycle_duration = this->now() - start_time;
475 result->planning_time = cycle_duration;
477 if (params_->max_planner_duration && cycle_duration.seconds() > params_->max_planner_duration) {
480 "Planner loop missed its desired rate of %.4f Hz. Current loop rate is %.4f Hz"
482 1 / params_->max_planner_duration, 1 / cycle_duration.seconds(),
484 (
" Waited " + std::to_string(costmap_wait) +
"s for costmap update.").c_str() :
"");
487 action_server_poses_->succeeded_current(result);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
538 std::lock_guard<std::mutex> lock_reinit(param_handler_->getMutex());
540 auto start_time = this->now();
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.");
547 geometry_msgs::msg::PoseStamped start;
550 if (isServerInactive<ActionToPose>(action_server_pose_) ||
551 isCancelRequested<ActionToPose>(action_server_pose_))
558 getPreemptedGoalIfRequested<ActionToPose>(action_server_pose_, goal);
561 if (!getStartPose<ActionToPose>(goal, start)) {
566 geometry_msgs::msg::PoseStamped goal_pose = goal->goal;
571 auto cancel_checker = [
this]() {
572 return action_server_pose_->is_cancel_requested();
575 result->path =
getPlan(start, goal_pose, goal->viapoints, goal->planner_id, cancel_checker);
577 if (!validatePath<ActionThroughPoses>(goal_pose, result->path, goal->planner_id)) {
584 auto cycle_duration = this->now() - start_time;
585 result->planning_time = cycle_duration;
587 if (params_->max_planner_duration && cycle_duration.seconds() > params_->max_planner_duration) {
590 "Planner loop missed its desired rate of %.4f Hz. Current loop rate is %.4f Hz"
592 1 / params_->max_planner_duration, 1 / cycle_duration.seconds(),
594 (
" Waited " + std::to_string(costmap_wait) +
"s for costmap update.").c_str() :
"");
596 action_server_pose_->succeeded_current(result);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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)
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);
653 if (planners_.find(planner_id) != planners_.end()) {
654 return planners_[planner_id]->createPlan(start, goal, viapoints, cancel_checker);
656 if (planners_.size() == 1 && planner_id.empty()) {
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,
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());
672 return nav_msgs::msg::Path();
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));
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)
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
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)
700 << goal.pose.position.x <<
", " << goal.pose.position.y <<
")"
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)
706 <<
": \"" << ex.what() <<
"\"";
708 error_msg = ss.str();
709 RCLCPP_WARN(get_logger(),
"%s", error_msg.c_str());
714 #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.
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.