15 #include "nav2_waypoint_follower/waypoint_follower.hpp"
24 #include "nav2_ros_common/rate.hpp"
26 namespace nav2_waypoint_follower
29 using rcl_interfaces::msg::ParameterType;
30 using std::placeholders::_1;
33 : nav2::LifecycleNode(
"waypoint_follower",
"", options),
34 waypoint_task_executor_loader_(
"nav2_core",
35 "nav2_core::WaypointTaskExecutor")
37 RCLCPP_INFO(get_logger(),
"Creating");
47 RCLCPP_INFO(get_logger(),
"Configuring");
51 param_handler_ = std::make_unique<ParameterHandler>(
53 params_ = param_handler_->getParams();
55 callback_group_ = create_callback_group(
56 rclcpp::CallbackGroupType::MutuallyExclusive,
58 callback_group_executor_.add_callback_group(callback_group_, get_node_base_interface());
60 nav_to_pose_client_ = create_action_client<ClientT>(
61 "navigate_to_pose", callback_group_);
63 xyz_action_server_ = create_action_server<ActionT>(
64 "follow_waypoints", std::bind(
67 std::bind(&WaypointFollower::goalReceived<ActionT>,
this, std::placeholders::_1),
68 nullptr, std::chrono::milliseconds(
71 from_ll_to_map_client_ = node->create_client<robot_localization::srv::FromLL>(
75 gps_action_server_ = create_action_server<ActionTGPS>(
76 "follow_gps_waypoints",
80 std::bind(&WaypointFollower::goalReceived<ActionTGPS>,
this, std::placeholders::_1),
81 nullptr, std::chrono::milliseconds(
85 waypoint_task_executor_ = waypoint_task_executor_loader_.createUniqueInstance(
86 params_->waypoint_task_executor_type);
88 get_logger(),
"Created waypoint_task_executor : %s of type %s",
89 params_->waypoint_task_executor_id.c_str(), params_->waypoint_task_executor_type.c_str());
90 waypoint_task_executor_->initialize(node, params_->waypoint_task_executor_id);
91 }
catch (
const std::exception & e) {
94 "Failed to create waypoint_task_executor. Exception: %s", e.what());
96 return nav2::CallbackReturn::FAILURE;
99 return nav2::CallbackReturn::SUCCESS;
105 RCLCPP_INFO(get_logger(),
"Activating");
107 xyz_action_server_->activate();
108 gps_action_server_->activate();
113 return nav2::CallbackReturn::SUCCESS;
119 RCLCPP_INFO(get_logger(),
"Deactivating");
121 xyz_action_server_->deactivate();
122 gps_action_server_->deactivate();
126 return nav2::CallbackReturn::SUCCESS;
132 RCLCPP_INFO(get_logger(),
"Cleaning up");
134 xyz_action_server_.reset();
135 nav_to_pose_client_.reset();
136 gps_action_server_.reset();
137 from_ll_to_map_client_.reset();
139 return nav2::CallbackReturn::SUCCESS;
145 RCLCPP_INFO(get_logger(),
"Shutting down");
146 return nav2::CallbackReturn::SUCCESS;
152 if constexpr (std::is_same_v<T, ActionTGPS>) {
153 if (goal->gps_poses.empty()) {
155 get_logger(),
"Empty vector of GPS waypoints passed to waypoint following action.");
159 if (goal->poses.empty()) {
161 get_logger(),
"Empty vector of waypoints passed to waypoint following action.");
170 const T & action_server)
172 std::vector<geometry_msgs::msg::PoseStamped> poses;
173 const auto current_goal = action_server->get_current_goal();
176 RCLCPP_ERROR(get_logger(),
"No current action goal found!");
181 if constexpr (std::is_same<T, ActionServer::SharedPtr>::value) {
183 poses = current_goal->poses;
187 current_goal->gps_poses);
192 template<
typename T,
typename V,
typename Z>
194 const T & action_server,
198 auto goal = action_server->get_current_goal();
201 unsigned int current_loop_no = 0;
202 auto no_of_loops = goal->number_of_loops;
204 std::vector<geometry_msgs::msg::PoseStamped> poses;
205 poses = getLatestGoalPoses<T>(action_server);
207 if (!action_server || !action_server->is_server_active()) {
208 RCLCPP_DEBUG(get_logger(),
"Action server inactive. Stopping.");
213 get_logger(),
"Received follow waypoint request with %i waypoints.",
214 static_cast<int>(poses.size()));
219 nav2_msgs::action::FollowWaypoints::Result::NO_VALID_WAYPOINTS;
221 "Empty vector of waypoints, probably due to conversion failure.";
222 RCLCPP_ERROR(get_logger(),
"%s", result->error_msg.c_str());
223 action_server->terminate_current(result);
230 uint32_t goal_index = goal->goal_index;
231 bool new_goal =
true;
233 while (rclcpp::ok()) {
235 if (action_server->is_cancel_requested()) {
236 auto cancel_future = nav_to_pose_client_->async_cancel_all_goals();
237 callback_group_executor_.spin_until_future_complete(cancel_future);
239 callback_group_executor_.spin_some();
240 action_server->terminate_all();
245 if (action_server->is_preempt_requested()) {
246 RCLCPP_INFO(get_logger(),
"Preempting the goal pose.");
247 goal = action_server->accept_pending_goal();
248 poses = getLatestGoalPoses<T>(action_server);
251 nav2_msgs::action::FollowWaypoints::Result::NO_VALID_WAYPOINTS;
253 "Empty vector of Waypoints passed to waypoint following logic. "
254 "Nothing to execute, returning with failure!";
255 RCLCPP_ERROR(get_logger(),
"%s", result->error_msg.c_str());
256 action_server->terminate_current(result);
259 goal_index = goal->goal_index;
260 no_of_loops = goal->number_of_loops;
262 result->missed_waypoints.clear();
269 ClientT::Goal client_goal;
270 client_goal.pose = poses[goal_index];
271 client_goal.pose.header.stamp = this->now();
273 auto send_goal_options = nav2::ActionClient<ClientT>::SendGoalOptions();
274 send_goal_options.result_callback = std::bind(
276 std::placeholders::_1);
277 send_goal_options.goal_response_callback = std::bind(
279 this, std::placeholders::_1);
281 future_goal_handle_ =
282 nav_to_pose_client_->async_send_goal(client_goal, send_goal_options);
283 current_goal_status_ = {ActionStatus::PROCESSING, 0,
""};
286 feedback->current_waypoint = goal_index;
287 action_server->publish_feedback(feedback);
290 current_goal_status_.status == ActionStatus::FAILED ||
291 current_goal_status_.status == ActionStatus::UNKNOWN)
293 nav2_msgs::msg::WaypointStatus missedWaypoint;
294 missedWaypoint.waypoint_status = nav2_msgs::msg::WaypointStatus::FAILED;
295 missedWaypoint.waypoint_index = goal_index;
296 missedWaypoint.waypoint_pose = poses[goal_index];
297 missedWaypoint.error_code = current_goal_status_.error_code;
298 missedWaypoint.error_msg = current_goal_status_.error_msg;
299 result->missed_waypoints.push_back(missedWaypoint);
301 if (params_->stop_on_failure) {
303 nav2_msgs::action::FollowWaypoints::Result::STOP_ON_MISSED_WAYPOINT;
305 "Failed to process waypoint " + std::to_string(goal_index) +
306 " in waypoint list and stop on failure is enabled."
307 " Terminating action.";
308 RCLCPP_WARN(get_logger(),
"%s", result->error_msg.c_str());
309 action_server->terminate_current(result);
310 current_goal_status_.error_code = 0;
311 current_goal_status_.error_msg =
"";
315 get_logger(),
"Failed to process waypoint %i,"
316 " moving to next.", goal_index);
318 }
else if (current_goal_status_.status == ActionStatus::SUCCEEDED) {
320 get_logger(),
"Succeeded processing waypoint %i, processing waypoint task execution",
322 bool is_task_executed = waypoint_task_executor_->processAtWaypoint(
323 poses[goal_index], goal_index);
325 get_logger(),
"Task execution at waypoint %i %s", goal_index,
326 is_task_executed ?
"succeeded" :
"failed!");
328 if (!is_task_executed) {
329 nav2_msgs::msg::WaypointStatus missedWaypoint;
330 missedWaypoint.waypoint_status = nav2_msgs::msg::WaypointStatus::FAILED;
331 missedWaypoint.waypoint_index = goal_index;
332 missedWaypoint.waypoint_pose = poses[goal_index];
333 missedWaypoint.error_code =
334 nav2_msgs::action::FollowWaypoints::Result::TASK_EXECUTOR_FAILED;
335 missedWaypoint.error_msg =
"Task execution failed";
336 result->missed_waypoints.push_back(missedWaypoint);
339 if (!is_task_executed && params_->stop_on_failure) {
341 nav2_msgs::action::FollowWaypoints::Result::TASK_EXECUTOR_FAILED;
343 "Failed to execute task at waypoint " + std::to_string(goal_index) +
344 " stop on failure is enabled. Terminating action.";
345 RCLCPP_WARN(get_logger(),
"%s", result->error_msg.c_str());
346 action_server->terminate_current(result);
347 current_goal_status_.error_code = 0;
348 current_goal_status_.error_msg =
"";
352 get_logger(),
"Handled task execution on waypoint %i,"
353 " moving to next.", goal_index);
357 if (current_goal_status_.status != ActionStatus::PROCESSING) {
361 if (goal_index >= poses.size()) {
362 if (current_loop_no == no_of_loops) {
364 get_logger(),
"Completed all %zu waypoints requested.",
366 action_server->succeeded_current(result);
367 current_goal_status_.error_code = 0;
368 current_goal_status_.error_msg =
"";
372 get_logger(),
"Starting a new loop, current loop count is %i",
379 callback_group_executor_.spin_some();
386 auto feedback = std::make_shared<ActionT::Feedback>();
387 auto result = std::make_shared<ActionT::Result>();
390 ActionT::Feedback::SharedPtr,
391 ActionT::Result::SharedPtr>(
398 auto feedback = std::make_shared<ActionTGPS::Feedback>();
399 auto result = std::make_shared<ActionTGPS::Result>();
402 ActionTGPS::Feedback::SharedPtr,
403 ActionTGPS::Result::SharedPtr>(
410 const rclcpp_action::ClientGoalHandle<ClientT>::WrappedResult & result)
412 if (result.goal_id != future_goal_handle_.get()->get_goal_id()) {
415 "Goal IDs do not match for the current goal handle and received result."
416 "Ignoring likely due to receiving result for an old goal.");
420 switch (result.code) {
421 case rclcpp_action::ResultCode::SUCCEEDED:
422 current_goal_status_.status = ActionStatus::SUCCEEDED;
424 case rclcpp_action::ResultCode::ABORTED:
425 current_goal_status_.status = ActionStatus::FAILED;
426 current_goal_status_.error_code = result.result->error_code;
427 current_goal_status_.error_msg = result.result->error_msg;
429 case rclcpp_action::ResultCode::CANCELED:
430 current_goal_status_.status = ActionStatus::FAILED;
433 current_goal_status_.status = ActionStatus::UNKNOWN;
434 current_goal_status_.error_code = nav2_msgs::action::FollowWaypoints::Result::UNKNOWN;
435 current_goal_status_.error_msg =
"Received an UNKNOWN result code from navigation action!";
436 RCLCPP_ERROR(get_logger(),
"%s", current_goal_status_.error_msg.c_str());
443 const rclcpp_action::ClientGoalHandle<ClientT>::SharedPtr & goal)
446 current_goal_status_.status = ActionStatus::FAILED;
447 current_goal_status_.error_code = nav2_msgs::action::FollowWaypoints::Result::UNKNOWN;
448 current_goal_status_.error_msg =
449 "navigate_to_pose action client failed to send goal to server.";
450 RCLCPP_ERROR(get_logger(),
"%s", current_goal_status_.error_msg.c_str());
454 std::vector<geometry_msgs::msg::PoseStamped>
456 const std::vector<geographic_msgs::msg::GeoPose> & gps_poses)
459 this->get_logger(),
"Converting GPS waypoints to %s Frame..",
460 params_->global_frame_id.c_str());
462 std::vector<geometry_msgs::msg::PoseStamped> poses_in_map_frame_vector;
463 int waypoint_index = 0;
464 for (
auto && curr_geopose : gps_poses) {
465 auto request = std::make_shared<robot_localization::srv::FromLL::Request>();
466 auto response = std::make_shared<robot_localization::srv::FromLL::Response>();
467 request->ll_point.latitude = curr_geopose.position.latitude;
468 request->ll_point.longitude = curr_geopose.position.longitude;
469 request->ll_point.altitude = curr_geopose.position.altitude;
472 if (!from_ll_to_map_client_->
invoke(request, response)) {
475 "fromLL service of robot_localization could not convert %i th GPS waypoint to"
476 "%s frame, going to skip this point!"
477 "Make sure you have run navsat_transform_node of robot_localization",
478 waypoint_index, params_->global_frame_id.c_str());
479 if (params_->stop_on_failure) {
482 "Conversion of %i th GPS waypoint to"
483 "%s frame failed and stop_on_failure is set to true"
484 "Not going to execute any of waypoints, exiting with failure!",
485 waypoint_index, params_->global_frame_id.c_str());
486 return std::vector<geometry_msgs::msg::PoseStamped>();
490 geometry_msgs::msg::PoseStamped curr_pose_map_frame;
491 curr_pose_map_frame.header.frame_id = params_->global_frame_id;
492 curr_pose_map_frame.header.stamp = this->now();
493 curr_pose_map_frame.pose.position = response->map_point;
494 curr_pose_map_frame.pose.orientation = curr_geopose.orientation;
495 poses_in_map_frame_vector.push_back(curr_pose_map_frame);
501 "Converted all %i GPS waypoint to %s frame",
502 static_cast<int>(poses_in_map_frame_vector.size()), params_->global_frame_id.c_str());
503 return poses_in_map_frame_vector;
508 #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.
A sim-time-aware rate for Nav2 loops.
ResponseType::SharedPtr invoke(typename RequestType::SharedPtr &request, const std::chrono::nanoseconds timeout=std::chrono::nanoseconds(-1), const std::chrono::nanoseconds wait_for_service_timeout=std::chrono::seconds(10))
Invoke the service and block until completed or timed out.
bool wait_for_service(const std::chrono::nanoseconds timeout=std::chrono::nanoseconds::max())
Block until a service is available or timeout.
An action server that uses behavior tree for navigating a robot to its goal position.
nav2::CallbackReturn on_deactivate(const rclcpp_lifecycle::State &state) override
Deactivates action server.
bool goalReceived(std::shared_ptr< const typename T::Goal > goal)
Goal received callbacks to validate a new goal before acceptance. Rejects goals with empty waypoint l...
nav2::CallbackReturn on_shutdown(const rclcpp_lifecycle::State &state) override
Called when in shutdown state.
void resultCallback(const rclcpp_action::ClientGoalHandle< ClientT >::WrappedResult &result)
Action client result callback.
~WaypointFollower()
A destructor for nav2_waypoint_follower::WaypointFollower class.
WaypointFollower(const rclcpp::NodeOptions &options=rclcpp::NodeOptions())
A constructor for nav2_waypoint_follower::WaypointFollower class.
void goalResponseCallback(const rclcpp_action::ClientGoalHandle< ClientT >::SharedPtr &goal)
Action client goal response callback.
nav2::CallbackReturn on_cleanup(const rclcpp_lifecycle::State &state) override
Resets member variables.
nav2::CallbackReturn on_configure(const rclcpp_lifecycle::State &state) override
Configures member variables.
void followGPSWaypointsCallback()
send robot through each of GPS point , which are converted to map frame first then using a client to ...
std::vector< geometry_msgs::msg::PoseStamped > convertGPSPosesToMapPoses(const std::vector< geographic_msgs::msg::GeoPose > &gps_poses)
given some gps_poses, converts them to map frame using robot_localization's service fromLL....
void followWaypointsHandler(const T &action_server, const V &feedback, const Z &result)
Templated function to perform internal logic behind waypoint following, Both GPS and non GPS waypoint...
void followWaypointsCallback()
Action server callbacks.
std::vector< geometry_msgs::msg::PoseStamped > getLatestGoalPoses(const T &action_server)
get the latest poses on the action server goal. If they are GPS poses, convert them to the global car...
nav2::CallbackReturn on_activate(const rclcpp_lifecycle::State &state) override
Activates action server.