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 
47  const std::string &name,
48  const mbf_abstract_core::AbstractPlanner::Ptr &planner_ptr,
49  const MoveBaseFlexConfig &config) :
51  planner_(planner_ptr), state_(INITIALIZED), planning_(false),
52  has_new_start_(false), has_new_goal_(false)
53 {
54  ros::NodeHandle private_nh("~");
55 
56  // non-dynamically reconfigurable parameters
57  private_nh.param("robot_frame", robot_frame_, std::string("base_footprint"));
58  private_nh.param("map_frame", global_frame_, std::string("map"));
59 
60  // dynamically reconfigurable parameters
61  reconfigure(config);
62 }
63 
65 {
66 }
67 
68 
70 {
71  boost::lock_guard<boost::mutex> guard(plan_mtx_);
72  // copy plan and costs to output
73  // if the planner plugin do not compute costs compute costs by discrete path length
74  if(cost_ == 0 && !plan_.empty())
75  {
76  ROS_DEBUG_STREAM("Compute costs by discrete path length!");
77  double cost = 0;
78 
79  geometry_msgs::PoseStamped prev_pose = plan_.front();
80  for(std::vector<geometry_msgs::PoseStamped>::const_iterator iter = plan_.begin() + 1; iter != plan_.end(); ++iter)
81  {
82  cost += mbf_utility::distance(prev_pose, *iter);
83  prev_pose = *iter;
84  }
85  return cost;
86  }
87  return cost_;
88 }
89 
90 void AbstractPlannerExecution::reconfigure(const MoveBaseFlexConfig &config)
91 {
92  boost::lock_guard<boost::mutex> guard(configuration_mutex_);
93 
94  max_retries_ = config.planner_max_retries;
95  frequency_ = config.planner_frequency;
96 
97  // Timeout granted to the global planner. We keep calling it up to this time or up to max_retries times
98  // If it doesn't return within time, the navigator will cancel it and abort the corresponding action
99  patience_ = ros::Duration(config.planner_patience);
100 }
101 
102 
104 {
105  boost::lock_guard<boost::mutex> guard(state_mtx_);
106  return state_;
107 }
108 
110 {
111  boost::lock_guard<boost::mutex> guard(state_mtx_);
112  state_ = state;
113 
114  // some states are quiet, most aren't
115  if(signalling)
116  condition_.notify_all();
117 }
118 
119 
121 {
122  boost::lock_guard<boost::mutex> guard(plan_mtx_);
123  return last_valid_plan_time_;
124 }
125 
126 
128 {
130 }
131 
132 
133 std::vector<geometry_msgs::PoseStamped> AbstractPlannerExecution::getPlan() const
134 {
135  boost::lock_guard<boost::mutex> guard(plan_mtx_);
136  // copy plan and costs to output
137  return plan_;
138 }
139 
140 
141 void AbstractPlannerExecution::setNewGoal(const geometry_msgs::PoseStamped &goal, double tolerance)
142 {
143  boost::lock_guard<boost::mutex> guard(goal_start_mtx_);
144  goal_ = goal;
145  tolerance_ = tolerance;
146  has_new_goal_ = true;
147 }
148 
149 
150 void AbstractPlannerExecution::setNewStart(const geometry_msgs::PoseStamped &start)
151 {
152  boost::lock_guard<boost::mutex> guard(goal_start_mtx_);
153  start_ = start;
154  has_new_start_ = true;
155 }
156 
157 
158 void AbstractPlannerExecution::setNewStartAndGoal(const geometry_msgs::PoseStamped &start,
159  const geometry_msgs::PoseStamped &goal,
160  double tolerance)
161 {
162  boost::lock_guard<boost::mutex> guard(goal_start_mtx_);
163  start_ = start;
164  goal_ = goal;
165  tolerance_ = tolerance;
166  has_new_start_ = true;
167  has_new_goal_ = true;
168 }
169 
170 
171 bool AbstractPlannerExecution::start(const geometry_msgs::PoseStamped &start,
172  const geometry_msgs::PoseStamped &goal,
173  double tolerance)
174 {
175  if (planning_)
176  {
177  return false;
178  }
179  boost::lock_guard<boost::mutex> guard(planning_mtx_);
180  planning_ = true;
181  start_ = start;
182  goal_ = goal;
183  tolerance_ = tolerance;
184 
185  const geometry_msgs::Point& s = start.pose.position;
186  const geometry_msgs::Point& g = goal.pose.position;
187 
188  ROS_DEBUG_STREAM("Start planning from the start pose: (" << s.x << ", " << s.y << ", " << s.z << ")"
189  << " to the goal pose: ("<< g.x << ", " << g.y << ", " << g.z << ")");
190 
192 }
193 
194 
196 {
197  cancel_ = true; // force cancel immediately, as the call to cancel in the planner can take a while
198 
199  // returns false if cancel is not implemented or rejected by the planner (will run until completion)
200  if (!planner_->cancel())
201  {
202  ROS_WARN_STREAM("Cancel planning failed or is not supported by the plugin. "
203  << "Wait until the current planning finished!");
204 
205  return false;
206  }
207  return true;
208 }
209 
210 uint32_t AbstractPlannerExecution::makePlan(const geometry_msgs::PoseStamped &start,
211  const geometry_msgs::PoseStamped &goal,
212  double tolerance,
213  std::vector<geometry_msgs::PoseStamped> &plan,
214  double &cost,
215  std::string &message)
216 {
217  return planner_->makePlan(start, goal, tolerance, plan, cost, message);
218 }
219 
221 {
222  setState(STARTED, false);
223  boost::lock_guard<boost::mutex> guard(planning_mtx_);
224  int retries = 0;
225  geometry_msgs::PoseStamped current_start = start_;
226  geometry_msgs::PoseStamped current_goal = goal_;
227  double current_tolerance = tolerance_;
228 
229  bool success = false;
230  bool make_plan = false;
231  bool exceeded = false;
232 
235 
236  try
237  {
238  while (planning_ && ros::ok())
239  {
240  boost::chrono::thread_clock::time_point start_time = boost::chrono::thread_clock::now();
241 
242  // call the planner
243  std::vector<geometry_msgs::PoseStamped> plan;
244  double cost;
245 
246  // lock goal start mutex
247  goal_start_mtx_.lock();
248  if (has_new_start_)
249  {
250  has_new_start_ = false;
251  current_start = start_;
252  ROS_INFO_STREAM("A new start pose is available. Planning with the new start pose!");
253  exceeded = false;
254  const geometry_msgs::Point& s = start_.pose.position;
255  ROS_INFO_STREAM("New planning start pose: (" << s.x << ", " << s.y << ", " << s.z << ")");
256  }
257  if (has_new_goal_)
258  {
259  has_new_goal_ = false;
260  current_goal = goal_;
261  current_tolerance = tolerance_;
262  ROS_INFO_STREAM("A new goal pose is available. Planning with the new goal pose and the tolerance: "
263  << current_tolerance);
264  exceeded = false;
265  const geometry_msgs::Point& g = goal_.pose.position;
266  ROS_INFO_STREAM("New goal pose: (" << g.x << ", " << g.y << ", " << g.z << ")");
267  }
268 
269  make_plan = !(success || exceeded) || has_new_start_ || has_new_goal_;
270 
271  // unlock goal
272  goal_start_mtx_.unlock();
273  setState(PLANNING, false);
274  if (make_plan)
275  {
276  outcome_ = makePlan(current_start, current_goal, current_tolerance, plan, cost, message_);
277  success = outcome_ < 10;
278 
279  boost::lock_guard<boost::mutex> guard(configuration_mutex_);
280 
281  if (cancel_ && !isPatienceExceeded())
282  {
283  ROS_INFO_STREAM("The planner \"" << name_ << "\" has been canceled!"); // but not due to patience exceeded
284  planning_ = false;
285  setState(CANCELED, true);
286  }
287  else if (success)
288  {
289  ROS_DEBUG_STREAM("Successfully found a plan.");
290  exceeded = false;
291  planning_ = false;
292 
293  plan_mtx_.lock();
294  plan_ = plan;
295  // todo compute the cost once!
296  cost_ = cost;
298  plan_mtx_.unlock();
299  setState(FOUND_PLAN, true);
300  }
301  else if (max_retries_ >= 0 && ++retries > max_retries_)
302  {
303  ROS_INFO_STREAM("Planning reached max retries! (" << max_retries_ << ")");
304  exceeded = true;
305  planning_ = false;
306  setState(MAX_RETRIES, true);
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!" : ""));
316  exceeded = true;
317  planning_ = false;
318  setState(PAT_EXCEEDED, true);
319  }
320  else if (max_retries_ == 0 && patience_.isZero())
321  {
322  ROS_INFO_STREAM("Planning could not find a plan!");
323  exceeded = true;
324  planning_ = false;
325  setState(NO_PLAN_FOUND, true);
326  }
327  else
328  {
329  exceeded = false;
330  ROS_DEBUG_STREAM("Planning could not find a plan! Trying again...");
331  }
332  }
333  else if (cancel_)
334  {
335  ROS_INFO_STREAM("The global planner has been canceled!");
336  planning_ = false;
337  setState(CANCELED, true);
338  }
339  } // while (planning_ && ros::ok())
340  }
341  catch (const boost::thread_interrupted &ex)
342  {
343  // Planner thread interrupted; probably we have exceeded planner patience
344  ROS_WARN_STREAM("Planner thread interrupted!");
345  planning_ = false;
346  setState(STOPPED, true);
347  }
348  catch (...)
349  {
350  ROS_FATAL_STREAM("Unknown error occurred: " << boost::current_exception_diagnostic_information());
351  setState(INTERNAL_ERROR, true);
352  }
353 }
354 
355 } /* namespace mbf_abstract_nav */
356 
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
std::vector< geometry_msgs::PoseStamped > getPlan() const
Returns a new plan, if one is available.
geometry_msgs::PoseStamped goal_
the current goal pose used for planning
void setState(PlanningState state, bool signalling)
Sets the internal state, thread communication safe.
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
std::string name_
the plugin name; not the plugin type!
No plan has been found (MAX_RETRIES and PAT_EXCEEDED are 0).
PlanningState state_
current internal state
PlanningState getState() const
Returns the current internal state.
bool isPatienceExceeded() const
Checks whether the patience was exceeded.
boost::mutex configuration_mutex_
dynamic reconfigure mutex for a thread safe communication
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.
#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...
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)
double getCost() const
Gets computed costs.
AbstractPlannerExecution(const std::string &name, const mbf_abstract_core::AbstractPlanner::Ptr &planner_ptr, const MoveBaseFlexConfig &config)
Constructor.
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 robot trajectory
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
ros::Time getLastValidPlanTime() const
Returns the last time a valid plan was available.


mbf_abstract_nav
Author(s): Sebastian Pütz
autogenerated on Fri Nov 6 2020 03:56:24