abstract_planner_execution.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  * abstract_planner_execution.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 namespace mbf_abstract_nav
44 {
45 
46 
49  const MoveBaseFlexConfig &config,
50  boost::function<void()> setup_fn,
51  boost::function<void()> cleanup_fn) :
52  AbstractExecutionBase(name, setup_fn, cleanup_fn),
53  planner_(planner_ptr), state_(INITIALIZED), planning_(false),
54  has_new_start_(false), has_new_goal_(false)
55  {
56  ros::NodeHandle private_nh("~");
57 
58  // non-dynamically reconfigurable parameters
59  private_nh.param("robot_frame", robot_frame_, std::string("base_footprint"));
60  private_nh.param("map_frame", global_frame_, std::string("map"));
61 
62  // dynamically reconfigurable parameters
63  reconfigure(config);
64  }
65 
67  {
68  }
69 
70 
72  {
73  boost::lock_guard<boost::mutex> guard(plan_mtx_);
74  // copy plan and costs to output
75  // if the planner plugin do not compute costs compute costs by discrete path length
76  if(cost_ == 0 && !plan_.empty())
77  {
78  ROS_DEBUG_STREAM("Compute costs by discrete path length!");
79  double cost = 0;
80 
81  geometry_msgs::PoseStamped prev_pose = plan_.front();
82  for(std::vector<geometry_msgs::PoseStamped>::iterator iter = plan_.begin() + 1; iter != plan_.end(); ++iter)
83  {
84  cost += mbf_utility::distance(prev_pose, *iter);
85  prev_pose = *iter;
86  }
87  return cost;
88  }
89  return cost_;
90  }
91 
92  void AbstractPlannerExecution::reconfigure(const MoveBaseFlexConfig &config)
93  {
94  boost::lock_guard<boost::mutex> guard(configuration_mutex_);
95 
96  max_retries_ = config.planner_max_retries;
97  frequency_ = config.planner_frequency;
98 
99  // Timeout granted to the global planner. We keep calling it up to this time or up to max_retries times
100  // If it doesn't return within time, the navigator will cancel it and abort the corresponding action
101  patience_ = ros::Duration(config.planner_patience);
102  }
103 
104 
106  {
107  boost::lock_guard<boost::mutex> guard(state_mtx_);
108  return state_;
109  }
110 
112  {
113  boost::lock_guard<boost::mutex> guard(state_mtx_);
114  state_ = state;
115  }
116 
117 
119  {
120  boost::lock_guard<boost::mutex> guard(plan_mtx_);
121  return last_valid_plan_time_;
122  }
123 
124 
126  {
128  }
129 
130 
131  std::vector<geometry_msgs::PoseStamped> AbstractPlannerExecution::getPlan()
132  {
133  boost::lock_guard<boost::mutex> guard(plan_mtx_);
134  // copy plan and costs to output
135  return plan_;
136  }
137 
138 
139  void AbstractPlannerExecution::setNewGoal(const geometry_msgs::PoseStamped &goal, double tolerance)
140  {
141  boost::lock_guard<boost::mutex> guard(goal_start_mtx_);
142  goal_ = goal;
143  tolerance_ = tolerance;
144  has_new_goal_ = true;
145  }
146 
147 
148  void AbstractPlannerExecution::setNewStart(const geometry_msgs::PoseStamped &start)
149  {
150  boost::lock_guard<boost::mutex> guard(goal_start_mtx_);
151  start_ = start;
152  has_new_start_ = true;
153  }
154 
155 
156  void AbstractPlannerExecution::setNewStartAndGoal(const geometry_msgs::PoseStamped &start,
157  const geometry_msgs::PoseStamped &goal,
158  double tolerance)
159  {
160  boost::lock_guard<boost::mutex> guard(goal_start_mtx_);
161  start_ = start;
162  goal_ = goal;
163  tolerance_ = tolerance;
164  has_new_start_ = true;
165  has_new_goal_ = true;
166  }
167 
168 
169  bool AbstractPlannerExecution::start(const geometry_msgs::PoseStamped &start,
170  const geometry_msgs::PoseStamped &goal,
171  double tolerance)
172  {
173  if (planning_)
174  {
175  return false;
176  }
177  boost::lock_guard<boost::mutex> guard(planning_mtx_);
178  planning_ = true;
179  start_ = start;
180  goal_ = goal;
181  tolerance_ = tolerance;
182 
183  geometry_msgs::Point s = start.pose.position;
184  geometry_msgs::Point g = goal.pose.position;
185 
186  ROS_DEBUG_STREAM("Start planning from the start pose: (" << s.x << ", " << s.y << ", " << s.z << ")"
187  << " to the goal pose: ("<< g.x << ", " << g.y << ", " << g.z << ")");
188 
190  }
191 
192 
194  {
195  cancel_ = true; // force cancel immediately, as the call to cancel in the planner can take a while
196 
197  // returns false if cancel is not implemented or rejected by the planner (will run until completion)
198  if(!planner_->cancel())
199  {
200  ROS_WARN_STREAM("Cancel planning failed or is not supported by the plugin. "
201  << "Wait until the current planning finished!");
202 
203  return false;
204  }
205  return true;
206  }
207 
208  uint32_t AbstractPlannerExecution::makePlan(const geometry_msgs::PoseStamped &start,
209  const geometry_msgs::PoseStamped &goal,
210  double tolerance,
211  std::vector<geometry_msgs::PoseStamped> &plan,
212  double &cost,
213  std::string &message)
214  {
215  return planner_->makePlan(start, goal, tolerance, plan, cost, message);
216  }
217 
219  {
220  boost::lock_guard<boost::mutex> guard(planning_mtx_);
221  int retries = 0;
222  geometry_msgs::PoseStamped current_start = start_;
223  geometry_msgs::PoseStamped current_goal = goal_;
224  double current_tolerance = tolerance_;
225 
226  bool success = false;
227  bool make_plan = false;
228  bool exceeded = false;
229 
232 
233  try
234  {
235  while (planning_ && ros::ok())
236  {
237  boost::chrono::thread_clock::time_point start_time = boost::chrono::thread_clock::now();
238 
239  // call the planner
240  std::vector<geometry_msgs::PoseStamped> plan;
241  double cost;
242 
243  // lock goal start mutex
244  goal_start_mtx_.lock();
245  if (has_new_start_)
246  {
247  has_new_start_ = false;
248  current_start = start_;
249  ROS_INFO_STREAM("A new start pose is available. Planning with the new start pose!");
250  exceeded = false;
251  geometry_msgs::Point s = start_.pose.position;
252  ROS_INFO_STREAM("New planning start pose: (" << s.x << ", " << s.y << ", " << s.z << ")");
253  }
254  if (has_new_goal_)
255  {
256  has_new_goal_ = false;
257  current_goal = goal_;
258  current_tolerance = tolerance_;
259  ROS_INFO_STREAM("A new goal pose is available. Planning with the new goal pose and the tolerance: "
260  << current_tolerance);
261  exceeded = false;
262  geometry_msgs::Point g = goal_.pose.position;
263  ROS_INFO_STREAM("New goal pose: (" << g.x << ", " << g.y << ", " << g.z << ")");
264  }
265 
266  make_plan = !(success || exceeded) || has_new_start_ || has_new_goal_;
267 
268  // unlock goal
269  goal_start_mtx_.unlock();
271  if (make_plan)
272  {
273  outcome_ = makePlan(current_start, current_goal, current_tolerance, plan, cost, message_);
274  success = outcome_ < 10;
275 
276  boost::lock_guard<boost::mutex> guard(configuration_mutex_);
277 
278  if (cancel_ && !isPatienceExceeded())
279  {
281  ROS_INFO_STREAM("The global planner has been canceled!"); // but not due to patience exceeded
282  planning_ = false;
284  }
285  else if (success)
286  {
287  ROS_DEBUG_STREAM("Successfully found a plan.");
288  exceeded = false;
289  planning_ = false;
290 
291  plan_mtx_.lock();
292  plan_ = plan;
293  cost_ = cost;
295  plan_mtx_.unlock();
297 
298  condition_.notify_all(); // notify observer
299  }
300  else if (max_retries_ >= 0 && ++retries > max_retries_)
301  {
302  ROS_INFO_STREAM("Planning reached max retries! (" << max_retries_ << ")");
304  exceeded = true;
305  planning_ = false;
306  condition_.notify_all(); // notify observer
307  }
308  else if (isPatienceExceeded())
309  {
310  // Patience exceeded is handled at two levels: here to stop retrying planning when max_retries is
311  // disabled, and on the navigation server when the planner doesn't return for more that patience seconds.
312  // In the second case, the navigation server has tried to cancel planning (possibly without success, as
313  // old nav_core-based planners do not support canceling), and we add here the fact to the log for info
314  ROS_INFO_STREAM("Planning patience (" << patience_.toSec() << "s) has been exceeded"
315  << (cancel_ ? "; planner canceled!" : ""));
317  exceeded = true;
318  planning_ = false;
319  condition_.notify_all(); // notify observer
320  }
321  else if (max_retries_ == 0 && patience_.isZero())
322  {
323  ROS_INFO_STREAM("Planning could not find a plan!");
324  exceeded = true;
326  condition_.notify_all(); // notify observer
327  planning_ = false;
328  }
329  else
330  {
331  exceeded = false;
332  ROS_DEBUG_STREAM("Planning could not find a plan! Trying again...");
333  }
334  }
335  else if (cancel_)
336  {
337  ROS_INFO_STREAM("The global planner has been canceled!");
339  planning_ = false;
341  }
342  } // while (planning_ && ros::ok())
343  }
344  catch (const boost::thread_interrupted &ex)
345  {
346  // Planner thread interrupted; probably we have exceeded planner patience
347  ROS_WARN_STREAM("Planner thread interrupted!");
348  setState(STOPPED);
349  condition_.notify_all(); // notify observer
350  planning_ = false;
351  }
352  catch (...)
353  {
354  ROS_FATAL_STREAM("Unknown error occurred: " << boost::current_exception_diagnostic_information());
356  }
357  }
358 
359 } /* namespace mbf_abstract_nav */
360 
ros::Time last_valid_plan_time_
the last time a valid plan has been computed.
boost::condition_variable condition_
condition variable to wake up control thread
void notify_all() BOOST_NOEXCEPT
PlanningState getState()
Returns the current internal state.
geometry_msgs::PoseStamped goal_
the current goal pose used for planning
bool has_new_start_
true, if a new start pose has been set, until it is used.
virtual uint32_t makePlan(const geometry_msgs::PoseStamped &start, const geometry_msgs::PoseStamped &goal, double tolerance, std::vector< geometry_msgs::PoseStamped > &plan, double &cost, std::string &message)
calls the planner plugin to make a plan from the start pose to the goal pose with the given tolerance...
geometry_msgs::PoseStamped start_
the current start pose used for planning
double tolerance_
optional goal tolerance, in meters
XmlRpcServer s
bool isPatienceExceeded()
Checks whether the patience was exceeded.
No plan has been found (MAX_RETRIES and PAT_EXCEEDED are 0).
PlanningState state_
current internal state
void setState(PlanningState state)
Sets the internal state, thread communication safe.
boost::mutex configuration_mutex_
dynamic reconfigure mutex for a thread safe communication
AbstractPlannerExecution(const std::string name, const mbf_abstract_core::AbstractPlanner::Ptr planner_ptr, const MoveBaseFlexConfig &config, boost::function< void()> setup_fn, boost::function< void()> cleanup_fn)
Constructor.
virtual void run()
The main run method, a thread will execute this method. It contains the main planner execution loop...
bool planning_
main cycle variable of the execution loop
bool cancel_
flag for canceling controlling
std::string message_
the last received plugin execution message
Exceeded the maximum number of retries without a valid command.
ros::Time getLastValidPlanTime()
Returns the last time a valid plan was available.
#define ROS_FATAL_STREAM(args)
bool param(const std::string &param_name, T &param_val, const T &default_val) const
ros::Time last_call_start_time_
the last call start time, updated each cycle.
ROSCPP_DECL bool ok()
uint32_t outcome_
the last received plugin execution outcome
boost::mutex state_mtx_
mutex to handle safe thread communication for the current state
void setNewStart(const geometry_msgs::PoseStamped &start)
Sets a new start pose for the planner execution.
virtual bool cancel()
Cancel the planner execution. This calls the cancel method of the planner plugin. This could be usefu...
Exceeded the patience time without a valid command.
void setNewGoal(const geometry_msgs::PoseStamped &goal, double tolerance)
Sets a new goal pose for the planner execution.
double frequency_
planning cycle frequency (used only when running full navigation; we store here for grouping paramete...
std::vector< geometry_msgs::PoseStamped > getPlan()
Returns a new plan, if one is available.
boost::mutex plan_mtx_
mutex to handle safe thread communication for the plan and plan-costs
#define ROS_WARN_STREAM(args)
#define ROS_DEBUG_STREAM(args)
std::string global_frame_
the global frame in which the planner needs to plan
boost::mutex goal_start_mtx_
mutex to handle safe thread communication for the goal and start pose.
double distance(const geometry_msgs::PoseStamped &pose1, const geometry_msgs::PoseStamped &pose2)
std::vector< geometry_msgs::PoseStamped > plan_
current global plan
#define ROS_INFO_STREAM(args)
void setNewStartAndGoal(const geometry_msgs::PoseStamped &start, const geometry_msgs::PoseStamped &goal, double tolerance)
Sets a new star and goal pose for the planner execution.
bool has_new_goal_
true, if a new goal pose has been set, until it is used.
static Time now()
mbf_abstract_core::AbstractPlanner::Ptr planner_
the local planer to calculate the velocity command
boost::mutex planning_mtx_
mutex to handle safe thread communication for the planning_ flag.
void reconfigure(const MoveBaseFlexConfig &config)
Is called by the server thread to reconfigure the controller execution, if a user uses dynamic reconf...
std::string robot_frame_
robot frame used for computing the current robot pose
ros::Duration patience_
planning patience duration time


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