move_base_action.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2018, Magazino GmbH, Sebastian Pütz, Jorge Santos Simón
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above
12  * copyright notice, this list of conditions and the following
13  * disclaimer in the documentation and/or other materials provided
14  * with the distribution.
15  *
16  * 3. Neither the name of the copyright holder nor the names of its
17  * contributors may be used to endorse or promote products derived
18  * from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31  * POSSIBILITY OF SUCH DAMAGE.
32  *
33  * move_base_action.cpp
34  *
35  * authors:
36  * Sebastian Pütz <spuetz@uni-osnabrueck.de>
37  * Jorge Santos Simón <santos@magazino.eu>
38  *
39  */
40 
42 
43 #include "mbf_abstract_nav/MoveBaseFlexConfig.h"
45 
46 namespace mbf_abstract_nav
47 {
48 
49 MoveBaseAction::MoveBaseAction(const std::string &name,
50  const RobotInformation &robot_info,
51  const std::vector<std::string> &behaviors)
52  : name_(name), robot_info_(robot_info), private_nh_("~"),
53  action_client_exe_path_(private_nh_, "exe_path"),
54  action_client_get_path_(private_nh_, "get_path"),
55  action_client_recovery_(private_nh_, "recovery"),
56  oscillation_timeout_(0),
57  oscillation_distance_(0),
58  recovery_enabled_(true),
59  behaviors_(behaviors),
60  action_state_(NONE),
61  recovery_trigger_(NONE),
62  replanning_(false),
63  replanning_rate_(1.0)
64 {
65 }
66 
68 {
69 }
70 
72  mbf_abstract_nav::MoveBaseFlexConfig &config, uint32_t level)
73 {
74  if (config.planner_frequency > 0.0)
75  {
76  boost::lock_guard<boost::mutex> guard(replanning_mtx_);
77  if (!replanning_)
78  {
79  replanning_ = true;
80  if (action_state_ == EXE_PATH &&
83  {
84  // exe_path is running and user has enabled replanning
85  ROS_INFO_STREAM_NAMED("move_base", "Planner frequency set to " << config.planner_frequency
86  << ": start replanning, using the \"get_path\" action!");
89  boost::bind(&MoveBaseAction::actionGetPathReplanningDone, this, _1, _2)
90  );
91  }
92  }
93  replanning_rate_ = ros::Rate(config.planner_frequency);
94  }
95  else
96  replanning_ = false;
97  oscillation_timeout_ = ros::Duration(config.oscillation_timeout);
98  oscillation_distance_ = config.oscillation_distance;
99  recovery_enabled_ = config.recovery_enabled;
100 }
101 
103 {
105 
107  {
109  }
110 
112  {
114  }
115 
117  {
119  }
120 }
121 
123 {
125 
126  goal_handle.setAccepted();
127 
128  goal_handle_ = goal_handle;
129 
130  ROS_DEBUG_STREAM_NAMED("move_base", "Start action " << "move_base");
131 
132  const mbf_msgs::MoveBaseGoal& goal = *(goal_handle.getGoal().get());
133 
134  mbf_msgs::MoveBaseResult move_base_result;
135 
136  get_path_goal_.target_pose = goal.target_pose;
137  get_path_goal_.use_start_pose = false; // use the robot pose
138  get_path_goal_.planner = goal.planner;
139  exe_path_goal_.controller = goal.controller;
140 
141  ros::Duration connection_timeout(1.0);
142 
144 
145  // start recovering with the first behavior, use the recovery behaviors from the action request, if specified,
146  // otherwise, use all loaded behaviors.
147 
148  recovery_behaviors_ = goal.recovery_behaviors.empty() ? behaviors_ : goal.recovery_behaviors;
150 
151  geometry_msgs::PoseStamped robot_pose;
152  // get the current robot pose only at the beginning, as exe_path will keep updating it as we move
153  if (!robot_info_.getRobotPose(robot_pose))
154  {
155  ROS_ERROR_STREAM_NAMED("move_base", "Could not get the current robot pose!");
156  move_base_result.message = "Could not get the current robot pose!";
157  move_base_result.outcome = mbf_msgs::MoveBaseResult::TF_ERROR;
158  goal_handle.setAborted(move_base_result, move_base_result.message);
159  return;
160  }
161 
162  // wait for server connections
163  if (!action_client_get_path_.waitForServer(connection_timeout) ||
164  !action_client_exe_path_.waitForServer(connection_timeout) ||
165  !action_client_recovery_.waitForServer(connection_timeout))
166  {
167  ROS_ERROR_STREAM_NAMED("move_base", "Could not connect to one or more of move_base_flex actions:"
168  "\"get_path\" , \"exe_path\", \"recovery \"!");
169  move_base_result.outcome = mbf_msgs::MoveBaseResult::INTERNAL_ERROR;
170  move_base_result.message = "Could not connect to the move_base_flex actions!";
171  goal_handle.setAborted(move_base_result, move_base_result.message);
172  return;
173  }
174 
175  // call get_path action server to get a first plan
178  boost::bind(&MoveBaseAction::actionGetPathDone, this, _1, _2));
179 }
180 
182 {
183  ROS_DEBUG_STREAM_NAMED("move_base", "The \"exe_path\" action is active.");
184 }
185 
187  const mbf_msgs::ExePathFeedbackConstPtr &feedback)
188 {
189  move_base_feedback_.outcome = feedback->outcome;
190  move_base_feedback_.message = feedback->message;
191  move_base_feedback_.angle_to_goal = feedback->angle_to_goal;
192  move_base_feedback_.dist_to_goal = feedback->dist_to_goal;
193  move_base_feedback_.current_pose = feedback->current_pose;
194  move_base_feedback_.last_cmd_vel = feedback->last_cmd_vel;
195  robot_pose_ = feedback->current_pose;
197 
198  // we create a navigation-level oscillation detection using exe_path action's feedback,
199  // as the later doesn't handle oscillations created by quickly failing repeated plans
200 
201  // if oscillation detection is enabled by osciallation_timeout != 0
203  {
204  // check if oscillating
205  // moved more than the minimum oscillation distance
207  {
210 
212  {
213  ROS_INFO_NAMED("move_base", "Recovered from robot oscillation: restart recovery behaviors");
216  }
217  }
219  {
220  std::stringstream oscillation_msgs;
221  oscillation_msgs << "Robot is oscillating for " << ((ros::Time::now() - last_oscillation_reset_).toSec()) << "s!";
222  ROS_WARN_STREAM_NAMED("exe_path", oscillation_msgs.str());
224 
225  if (attemptRecovery())
226  {
228  }
229  else
230  {
231  mbf_msgs::MoveBaseResult move_base_result;
232  move_base_result.outcome = OSCILLATING;
234  move_base_result.message = oscillation_msgs.str() + " No recovery behaviors for the move_base action are defined!";
235  else
236  move_base_result.message = oscillation_msgs.str() + " Recovery is disabled for the move_base action! use the param \"enable_recovery\"";
237  move_base_result.final_pose = robot_pose_;
238  move_base_result.angle_to_goal = move_base_feedback_.angle_to_goal;
239  move_base_result.dist_to_goal = move_base_feedback_.dist_to_goal;
240  goal_handle_.setAborted(move_base_result, move_base_result.message);
241  }
242  }
243  }
244 }
245 
248  const mbf_msgs::GetPathResultConstPtr &result_ptr)
249 {
251 
252  const mbf_msgs::GetPathResult &result = *(result_ptr.get());
253  const mbf_msgs::MoveBaseGoal& goal = *(goal_handle_.getGoal().get());
254  mbf_msgs::MoveBaseResult move_base_result;
255  switch (state.state_)
256  {
258  ROS_FATAL_STREAM_NAMED("move_base", "get_path PENDING state not implemented, this should not be reachable!");
259  break;
260 
262  ROS_DEBUG_STREAM_NAMED("move_base", "Action \""
263  << "move_base\" received a path from \""
264  << "get_path\": " << state.getText());
265 
266  exe_path_goal_.path = result.path;
267  ROS_DEBUG_STREAM_NAMED("move_base", "Action \""
268  << "move_base\" sends the path to \""
269  << "exe_path\".");
270 
272  {
273  ROS_WARN_NAMED("move_base", "Recovered from planner failure: restart recovery behaviors");
276  }
277 
280  boost::bind(&MoveBaseAction::actionExePathDone, this, _1, _2),
281  boost::bind(&MoveBaseAction::actionExePathActive, this),
282  boost::bind(&MoveBaseAction::actionExePathFeedback, this, _1));
284  break;
285 
287 
288  if (attemptRecovery())
289  {
291  }
292  else
293  {
294  // copy result from get_path action
295  move_base_result.outcome = result.outcome;
296  move_base_result.message = result.message;
297  move_base_result.dist_to_goal = static_cast<float>(mbf_utility::distance(robot_pose_, goal.target_pose));
298  move_base_result.angle_to_goal = static_cast<float>(mbf_utility::angle(robot_pose_, goal.target_pose));
299  move_base_result.final_pose = robot_pose_;
300 
301  ROS_WARN_STREAM_NAMED("move_base", "Abort the execution of the planner: " << result.message);
302  goal_handle_.setAborted(move_base_result, state.getText());
303  }
304  break;
305 
307  // the get_path action has been preempted.
308 
309  // copy result from get_path action
310  move_base_result.outcome = result.outcome;
311  move_base_result.message = result.message;
312  move_base_result.dist_to_goal = static_cast<float>(mbf_utility::distance(robot_pose_, goal.target_pose));
313  move_base_result.angle_to_goal = static_cast<float>(mbf_utility::angle(robot_pose_, goal.target_pose));
314  move_base_result.final_pose = robot_pose_;
315  goal_handle_.setCanceled(move_base_result, state.getText());
316  break;
317 
320  ROS_FATAL_STREAM_NAMED("move_base", "The states RECALLED and REJECTED are not implemented in the SimpleActionServer!");
322  break;
323 
325  ROS_FATAL_STREAM_NAMED("move_base", "Connection lost to the action \"get_path\"!");
327  break;
328 
329  default:
330  ROS_FATAL_STREAM_NAMED("move_base", "Reached unknown action server state!");
332  break;
333  }
334 
335  // start replanning if enabled (can be disabled by dynamic reconfigure) and if we started following a path
337  return;
338 
339  // we reset the replan clock (we can have been stopped for a while) and make a fist sleep, so we don't replan
340  // just after start moving
341  boost::lock_guard<boost::mutex> guard(replanning_mtx_);
344  if (!replanning_ || action_state_ != EXE_PATH ||
347  return; // another chance to stop replannings after waiting for replanning period
348  ROS_INFO_STREAM_NAMED("move_base", "Start replanning, using the \"get_path\" action!");
351  boost::bind(&MoveBaseAction::actionGetPathReplanningDone, this, _1, _2)
352  );
353 }
354 
357  const mbf_msgs::ExePathResultConstPtr &result_ptr)
358 {
360 
361  ROS_DEBUG_STREAM_NAMED("move_base", "Action \"exe_path\" finished.");
362 
363  const mbf_msgs::ExePathResult& result = *(result_ptr.get());
364  const mbf_msgs::MoveBaseGoal& goal = *(goal_handle_.getGoal().get());
365  mbf_msgs::MoveBaseResult move_base_result;
366 
367  // copy result from get_path action
368  move_base_result.outcome = result.outcome;
369  move_base_result.message = result.message;
370  move_base_result.dist_to_goal = result.dist_to_goal;
371  move_base_result.angle_to_goal = result.angle_to_goal;
372  move_base_result.final_pose = result.final_pose;
373 
374  ROS_DEBUG_STREAM_NAMED("exe_path", "Current state:" << state.toString());
375 
376  switch (state.state_)
377  {
379  move_base_result.outcome = mbf_msgs::MoveBaseResult::SUCCESS;
380  move_base_result.message = "Action \"move_base\" succeeded!";
381  ROS_INFO_STREAM_NAMED("move_base", move_base_result.message);
382  goal_handle_.setSucceeded(move_base_result, move_base_result.message);
384  break;
385 
387  switch (result.outcome)
388  {
389  case mbf_msgs::ExePathResult::INVALID_PATH:
390  case mbf_msgs::ExePathResult::TF_ERROR:
391  case mbf_msgs::ExePathResult::NOT_INITIALIZED:
392  case mbf_msgs::ExePathResult::INVALID_PLUGIN:
393  case mbf_msgs::ExePathResult::INTERNAL_ERROR:
394  // none of these errors is recoverable
395  goal_handle_.setAborted(move_base_result, state.getText());
396  break;
397 
398  default:
399  // all the rest are, so we start calling the recovery behaviors in sequence
400 
401  if (attemptRecovery())
402  {
404  }
405  else
406  {
407  ROS_WARN_STREAM_NAMED("move_base", "Abort the execution of the controller: " << result.message);
408  goal_handle_.setAborted(move_base_result, state.getText());
409  }
410  break;
411  }
412  break;
413 
415  // action was preempted successfully!
416  ROS_DEBUG_STREAM_NAMED("move_base", "The action \""
417  << "exe_path" << "\" was preempted successfully!");
418  // TODO
419  break;
420 
422  ROS_DEBUG_STREAM_NAMED("move_base", "The action \""
423  << "exe_path" << "\" was recalled!");
424  // TODO
425  break;
426 
428  ROS_DEBUG_STREAM_NAMED("move_base", "The action \""
429  << "exe_path" << "\" was rejected!");
430  // TODO
431  break;
432 
434  // TODO
435  break;
436 
437  default:
438  ROS_FATAL_STREAM_NAMED("move_base",
439  "Reached unreachable case! Unknown SimpleActionServer state!");
441  break;
442  }
443 }
444 
446 {
447  if (!recovery_enabled_)
448  {
449  ROS_WARN_STREAM_NAMED("move_base", "Recovery behaviors are disabled!");
450  return false;
451  }
452 
454  {
455  if (recovery_behaviors_.empty())
456  {
457  ROS_WARN_STREAM_NAMED("move_base", "No Recovery Behaviors loaded!");
458  }
459  else
460  {
461  ROS_WARN_STREAM_NAMED("move_base", "Executed all available recovery behaviors!");
462  }
463  return false;
464  }
465 
467  ROS_DEBUG_STREAM_NAMED("move_base", "Start recovery behavior\""
468  << *current_recovery_behavior_ <<"\".");
471  boost::bind(&MoveBaseAction::actionRecoveryDone, this, _1, _2)
472  );
474  return true;
475 }
476 
479  const mbf_msgs::RecoveryResultConstPtr &result_ptr)
480 {
481  action_state_ = FAILED; // unless recovery succeeds or gets canceled...
482 
483  const mbf_msgs::RecoveryResult& result = *(result_ptr.get());
484  const mbf_msgs::MoveBaseGoal& goal = *(goal_handle_.getGoal().get());
485  mbf_msgs::MoveBaseResult move_base_result;
486 
487  // copy result from get_path action
488  move_base_result.outcome = result.outcome;
489  move_base_result.message = result.message;
490  move_base_result.dist_to_goal = static_cast<float>(mbf_utility::distance(robot_pose_, goal.target_pose));
491  move_base_result.angle_to_goal = static_cast<float>(mbf_utility::angle(robot_pose_, goal.target_pose));
492  move_base_result.final_pose = robot_pose_;
493 
494  switch (state.state_)
495  {
497  ROS_DEBUG_STREAM_NAMED("move_base", "Recovery behavior aborted!");
498  ROS_DEBUG_STREAM_NAMED("move_base", "The recovery behavior \""
499  << *current_recovery_behavior_ << "\" failed. ");
500  ROS_DEBUG_STREAM("Recovery behavior message: " << result.message
501  << ", outcome: " << result.outcome);
502 
503  current_recovery_behavior_++; // use next behavior;
505  {
506  ROS_DEBUG_STREAM_NAMED("move_base",
507  "All recovery behaviors failed. Abort recovering and abort the move_base action");
508  goal_handle_.setAborted(move_base_result, "All recovery behaviors failed.");
509  }
510  else
511  {
513 
514  ROS_INFO_STREAM_NAMED("move_base", "Run the next recovery behavior\""
515  << *current_recovery_behavior_ << "\".");
518  boost::bind(&MoveBaseAction::actionRecoveryDone, this, _1, _2)
519  );
520  }
521  break;
523  //go to planning state
524  ROS_DEBUG_STREAM_NAMED("move_base", "Execution of the recovery behavior \""
525  << *current_recovery_behavior_ << "\" succeeded!");
526  ROS_DEBUG_STREAM_NAMED("move_base",
527  "Try planning again and increment the current recovery behavior in the list.");
529  current_recovery_behavior_++; // use next behavior, the next time;
532  boost::bind(&MoveBaseAction::actionGetPathDone, this, _1, _2)
533  );
534  break;
536  ROS_INFO_STREAM_NAMED("move_base",
537  "The recovery action has been preempted!");
538  if(action_state_ == CANCELED)
540  break;
541 
543  ROS_INFO_STREAM_NAMED("move_base",
544  "The recovery action has been recalled!");
545  if(action_state_ == CANCELED)
547  break;
548 
550  ROS_FATAL_STREAM_NAMED("move_base",
551  "The recovery action has been rejected!");
553  break;
555  ROS_FATAL_STREAM_NAMED("move_base",
556  "The recovery action has lost the connection to the server!");
558  break;
559  default:
560  ROS_FATAL_STREAM_NAMED("move_base",
561  "Reached unreachable case! Unknown state!");
563  break;
564  }
565 }
566 
569  const mbf_msgs::GetPathResultConstPtr &result)
570 {
572  return; // replan only while following a path and if replanning is enabled (can be disabled by dynamic reconfigure)
573 
575  {
576  ROS_DEBUG_STREAM_NAMED("move_base", "Replanning succeeded; sending a goal to \"exe_path\" with the new plan");
577  exe_path_goal_.path = result->path;
578  mbf_msgs::ExePathGoal goal(exe_path_goal_);
580  goal,
581  boost::bind(&MoveBaseAction::actionExePathDone, this, _1, _2),
582  boost::bind(&MoveBaseAction::actionExePathActive, this),
583  boost::bind(&MoveBaseAction::actionExePathFeedback, this, _1));
584  }
585 
586  replanning_mtx_.lock();
588  replanning_mtx_.unlock();
589 
591  return; // another chance to stop replannings after waiting for replanning period
592 
593  ROS_DEBUG_STREAM_NAMED("move_base", "Next replanning cycle, using the \"get_path\" action!");
596  boost::bind(&MoveBaseAction::actionGetPathReplanningDone, this, _1, _2)); // replanning
597 }
598 
599 
600 } /* namespace mbf_abstract_nav */
601 
void publishFeedback(const Feedback &feedback)
double angle(const geometry_msgs::PoseStamped &pose1, const geometry_msgs::PoseStamped &pose2)
std::vector< std::string > recovery_behaviors_
#define ROS_INFO_NAMED(name,...)
bool waitForServer(const ros::Duration &timeout=ros::Duration(0, 0)) const
mbf_msgs::GetPathGoal get_path_goal_
mbf_msgs::MoveBaseFeedback move_base_feedback_
#define ROS_DEBUG_STREAM_NAMED(name, args)
geometry_msgs::PoseStamped last_oscillation_pose_
#define ROS_ERROR_STREAM_NAMED(name, args)
#define ROS_WARN_NAMED(name,...)
void actionExePathDone(const actionlib::SimpleClientGoalState &state, const mbf_msgs::ExePathResultConstPtr &result)
void setRejected(const Result &result=Result(), const std::string &text=std::string(""))
MoveBaseAction(const std::string &name, const RobotInformation &robot_info, const std::vector< std::string > &controllers)
boost::shared_ptr< const Goal > getGoal() const
#define ROS_INFO_STREAM_NAMED(name, args)
bool getRobotPose(geometry_msgs::PoseStamped &robot_pose) const
void setCanceled(const Result &result=Result(), const std::string &text=std::string(""))
std::string toString() const
ActionClientGetPath action_client_get_path_
Action client used by the move_base action.
void setAccepted(const std::string &text=std::string(""))
void reset()
void reconfigure(mbf_abstract_nav::MoveBaseFlexConfig &config, uint32_t level)
void setSucceeded(const Result &result=Result(), const std::string &text=std::string(""))
const std::vector< std::string > & behaviors_
#define ROS_FATAL_STREAM_NAMED(name, args)
mbf_msgs::RecoveryGoal recovery_goal_
void actionRecoveryDone(const actionlib::SimpleClientGoalState &state, const mbf_msgs::RecoveryResultConstPtr &result)
std::string getText() const
void actionGetPathDone(const actionlib::SimpleClientGoalState &state, const mbf_msgs::GetPathResultConstPtr &result)
#define ROS_DEBUG_STREAM(args)
bool sleep()
mbf_msgs::ExePathGoal exe_path_goal_
double distance(const geometry_msgs::PoseStamped &pose1, const geometry_msgs::PoseStamped &pose2)
MoveBaseActionState recovery_trigger_
void setAborted(const Result &result=Result(), const std::string &text=std::string(""))
void sendGoal(const Goal &goal, SimpleDoneCallback done_cb=SimpleDoneCallback(), SimpleActiveCallback active_cb=SimpleActiveCallback(), SimpleFeedbackCallback feedback_cb=SimpleFeedbackCallback())
static Time now()
std::vector< std::string >::iterator current_recovery_behavior_
geometry_msgs::PoseStamped robot_pose_
SimpleClientGoalState getState() const
ActionClientRecovery action_client_recovery_
Action client used by the move_base action.
void actionGetPathReplanningDone(const actionlib::SimpleClientGoalState &state, const mbf_msgs::GetPathResultConstPtr &result)
void actionExePathFeedback(const mbf_msgs::ExePathFeedbackConstPtr &feedback)
ActionClientExePath action_client_exe_path_
Action client used by the move_base action.
void start(GoalHandle &goal_handle)
#define ROS_WARN_STREAM_NAMED(name, args)


mbf_abstract_nav
Author(s): Sebastian Pütz
autogenerated on Tue Jun 18 2019 19:34:34