AnytimePathShortening.cpp
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2014, Rice University
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above
14  * copyright notice, this list of conditions and the following
15  * disclaimer in the documentation and/or other materials provided
16  * with the distribution.
17  * * Neither the name of the Rice University nor the names of its
18  * contributors may be used to endorse or promote products derived
19  * from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *********************************************************************/
34 
35 /* Author: Ryan Luna */
36 
37 #include <algorithm> // for std::min
38 #include <boost/algorithm/string.hpp>
39 
40 #include "ompl/geometric/planners/AnytimePathShortening.h"
41 #include "ompl/geometric/planners/fmt/BFMT.h"
42 #include "ompl/geometric/planners/est/BiEST.h"
43 #include "ompl/geometric/planners/rrt/BiTRRT.h"
44 #include "ompl/geometric/planners/informedtrees/BITstar.h"
45 #include "ompl/geometric/planners/kpiece/BKPIECE1.h"
46 #include "ompl/geometric/planners/est/EST.h"
47 #include "ompl/geometric/planners/fmt/FMT.h"
48 #include "ompl/geometric/planners/kpiece/KPIECE1.h"
49 #include "ompl/geometric/planners/rrt/LazyLBTRRT.h"
50 #include "ompl/geometric/planners/prm/LazyPRM.h"
51 #include "ompl/geometric/planners/prm/LazyPRMstar.h"
52 #include "ompl/geometric/planners/rrt/LazyRRT.h"
53 #include "ompl/geometric/planners/kpiece/LBKPIECE1.h"
54 #include "ompl/geometric/planners/rrt/LBTRRT.h"
55 #include "ompl/geometric/planners/pdst/PDST.h"
56 #include "ompl/geometric/planners/prm/PRM.h"
57 #include "ompl/geometric/planners/prm/PRMstar.h"
58 #include "ompl/geometric/planners/est/ProjEST.h"
59 #include "ompl/geometric/planners/rrt/RRT.h"
60 #include "ompl/geometric/planners/rrt/RRTConnect.h"
61 #include "ompl/geometric/planners/rrt/RRTstar.h"
62 #include "ompl/geometric/planners/rrt/RRTXstatic.h"
63 #include "ompl/geometric/planners/sbl/SBL.h"
64 #include "ompl/geometric/planners/prm/SPARS.h"
65 #include "ompl/geometric/planners/prm/SPARStwo.h"
66 #include "ompl/geometric/planners/sst/SST.h"
67 #include "ompl/geometric/planners/stride/STRIDE.h"
68 #include "ompl/geometric/planners/rrt/TRRT.h"
69 #include "ompl/geometric/PathHybridization.h"
70 #include "ompl/geometric/PathSimplifier.h"
71 #include "ompl/tools/config/SelfConfig.h"
72 #include "ompl/base/objectives/PathLengthOptimizationObjective.h"
73 #include "ompl/util/String.h"
74 
76  : ompl::base::Planner(si, "APS"), defaultNumPlanners_(std::max(1u, std::thread::hardware_concurrency()))
77 {
79  specs_.multithreaded = true;
80  specs_.optimizingPaths = true;
81 
82  Planner::declareParam<bool>("shortcut", this, &AnytimePathShortening::setShortcut,
84  Planner::declareParam<bool>("hybridize", this, &AnytimePathShortening::setHybridize,
86  Planner::declareParam<unsigned int>("max_hybrid_paths", this, &AnytimePathShortening::setMaxHybridizationPath,
88  Planner::declareParam<unsigned int>("num_planners", this, &AnytimePathShortening::setDefaultNumPlanners,
90  Planner::declareParam<std::string>("planners", this, &AnytimePathShortening::setPlanners,
92 
93  addPlannerProgressProperty("best cost REAL", [this] { return getBestCost(); });
94 }
95 
97 
99 {
100  if (planner && planner->getSpaceInformation().get() != si_.get())
101  {
102  OMPL_ERROR("NOT adding planner %s: SpaceInformation instances are different", planner->getName().c_str());
103  return;
104  }
105 
106  // Ensure all planners are unique instances
107  for (auto &i : planners_)
108  {
109  if (planner.get() == i.get())
110  {
111  OMPL_ERROR("NOT adding planner %s: Planner instances MUST be unique", planner->getName().c_str());
112  return;
113  }
114  }
115 
116  planners_.push_back(planner);
117 }
118 
119 void ompl::geometric::AnytimePathShortening::addPath(const geometric::PathGeometricPtr &path, base::Planner *planner)
120 {
121  const base::OptimizationObjectivePtr &opt(pdef_->getOptimizationObjective());
122  base::Cost pathCost = path->cost(opt);
123  std::lock_guard<std::mutex> _(lock_);
124  if (opt->isCostBetterThan(pathCost, bestCost_))
125  {
126  bestCost_ = pathCost;
127  pdef_->addSolutionPath(path, false, 0.0, planner->getName());
128  }
129  else if (planner != this)
130  pdef_->addSolutionPath(path, false, 0.0, planner->getName());
131 }
132 
135 {
136  std::vector<std::thread> threads;
137  geometric::PathHybridization phybrid(si_);
138 
139  base::OptimizationObjectivePtr opt = pdef_->getOptimizationObjective();
140  if (!opt)
141  {
142  OMPL_INFORM("%s: No optimization objective specified. Defaulting to optimizing path length for the allowed "
143  "planning time.",
144  getName().c_str());
145  opt = std::make_shared<base::PathLengthOptimizationObjective>(si_);
146  pdef_->setOptimizationObjective(opt);
147  }
148 
149  // Disable output from the motion planners, except for errors
150  msg::LogLevel currentLogLevel = msg::getLogLevel();
151  msg::setLogLevel(std::max(msg::LOG_ERROR, currentLogLevel));
152 
153  // Clear any previous planning data for the set of planners
154  clear();
155  // Spawn a thread for each planner. This will shortcut the best path after solving.
156  for (auto &planner : planners_)
157  threads.emplace_back([this, planner, &ptc] { return threadSolve(planner.get(), ptc); });
158 
160  geometric::PathGeometric *sln = nullptr, *prevLastPath = nullptr;
161  bestCost_ = opt->infiniteCost();
162  std::size_t prevSolCount = 0;
163  while (!ptc)
164  {
165  // We have found a solution that is good enough
166  if (opt->isSatisfied(bestCost_))
167  {
168  ptc.terminate();
169  break;
170  }
171 
172  // Hybridize the set of paths computed. Add the new hybrid path to the mix.
173  unsigned int solCount = pdef_->getSolutionCount();
174  if (hybridize_ && !ptc && solCount > 1)
175  {
176  const std::vector<base::PlannerSolution> &paths = pdef_->getSolutions();
177  std::size_t numPaths = std::min(solCount, maxHybridPaths_);
178  geometric::PathGeometric *lastPath = static_cast<PathGeometric *>(paths[numPaths - 1].path_.get());
179  // check if new solution paths have been added to top numPaths paths
180  if (lastPath != prevLastPath || (prevSolCount < solCount && solCount <= maxHybridPaths_))
181  {
182  for (size_t j = 0; j < numPaths && !ptc; ++j)
183  phybrid.recordPath(std::static_pointer_cast<PathGeometric>(paths[j].path_), false);
184 
185  phybrid.computeHybridPath();
186  sln = phybrid.getHybridPath().get();
187  prevLastPath = lastPath;
188  }
189  else
190  sln = static_cast<PathGeometric *>(pdef_->getSolutionPath().get());
191  prevSolCount = solCount;
192  }
193  else if (solCount > 0)
194  sln = static_cast<PathGeometric *>(pdef_->getSolutionPath().get());
195 
196  if (sln)
197  {
198  auto pathCopy(std::make_shared<geometric::PathGeometric>(*sln));
199  if (shortcut_) // Shortcut the path
200  if (!ps.simplify(*pathCopy, ptc, true))
201  // revert if shortcutting failed
202  pathCopy = std::make_shared<geometric::PathGeometric>(*sln);
203  addPath(pathCopy, this);
204  }
205  if (hybridize_ && phybrid.pathCount() >= maxHybridPaths_)
206  phybrid.clear();
207  }
208 
209  for (auto &thread : threads)
210  thread.join();
211 
212  msg::setLogLevel(currentLogLevel);
214  if (invalidStartStateCount_ > 0)
216  else if (invalidGoalCount_ > 0)
218  return (pdef_->getSolutionCount() > 0) ? base::PlannerStatus::EXACT_SOLUTION : status;
219 }
220 
223 {
224  // create a local clone of the problem definition where we keep at most one solution
225  auto pdef = pdef_->clone();
227 
228  planner->setProblemDefinition(pdef);
229  while (!ptc)
230  {
231  const base::PlannerStatus &status = planner->solve(ptc);
233  {
234  geometric::PathGeometric *sln = static_cast<geometric::PathGeometric *>(pdef->getSolutionPath().get());
235  auto pathCopy(std::make_shared<geometric::PathGeometric>(*sln));
236  if (shortcut_) // Shortcut the path
237  ps.partialShortcutPath(*pathCopy);
238  addPath(pathCopy, planner);
239  }
240  else if ((status == base::PlannerStatus::INVALID_START)
241  || (status == base::PlannerStatus::INVALID_GOAL)
243  {
244  // there is not point in trying again with these error types that will repeat.
245  {
246  std::lock_guard<std::mutex> _(invalidStartOrGoalLock_);
248  {
249  ++invalidStartStateCount_;
250  }
251  else if (status == base::PlannerStatus::INVALID_GOAL)
252  {
253  ++invalidGoalCount_;
254  }
255  }
256  planner->clear();
257  pdef->clearSolutionPaths();
258  break;
259  }
260 
261  planner->clear();
262  pdef->clearSolutionPaths();
263  }
264 }
265 
267 {
268  Planner::clear();
269  for (auto &planner : planners_)
270  planner->clear();
271  bestCost_ = base::Cost(std::numeric_limits<double>::quiet_NaN());
272  invalidStartStateCount_ = 0;
273  invalidGoalCount_ = 0;
274 }
275 
277 {
278  if (planners_.empty())
279  return;
280 
281  OMPL_WARN("Returning planner data for planner #0");
282  getPlannerData(data, 0);
283 }
284 
286 {
287  if (planners_.size() < idx)
288  return;
289  planners_[idx]->getPlannerData(data);
290 }
291 
293 {
294  Planner::setup();
295 
296  if (planners_.empty())
297  {
298  planners_.reserve(defaultNumPlanners_);
299  for (unsigned int i = 0; i < defaultNumPlanners_; ++i)
300  {
301  planners_.push_back(tools::SelfConfig::getDefaultPlanner(pdef_->getGoal()));
302  planners_.back()->setProblemDefinition(pdef_);
303  }
304  OMPL_INFORM("%s: No planners specified; using %u instances of %s", getName().c_str(), planners_.size(),
305  planners_[0]->getName().c_str());
306  }
307 
308  for (auto &planner : planners_)
309  planner->setup();
310 }
311 
313 {
314  for (auto &planner : planners_)
315  planner->checkValidity();
316 }
317 
319 {
320  return planners_.size();
321 }
322 
324 {
325  assert(idx < planners_.size());
326  return planners_[idx];
327 }
328 
330 {
331  return shortcut_;
332 }
333 
335 {
336  shortcut_ = shortcut;
337 }
338 
340 {
341  return hybridize_;
342 }
343 
345 {
346  hybridize_ = hybridize;
347 }
348 
350 {
351  return maxHybridPaths_;
352 }
353 
355 {
356  maxHybridPaths_ = maxPathCount;
357 }
358 
360 {
361  defaultNumPlanners_ = numPlanners;
362 }
363 
365 {
366  return defaultNumPlanners_;
367 }
368 
370 {
371  return ompl::toString(bestCost_.value());
372 }
373 
374 void ompl::geometric::AnytimePathShortening::setPlanners(const std::string &plannerList)
375 {
376  std::vector<std::string> plannerStrings;
377 
378  boost::split(plannerStrings, plannerList, boost::is_any_of(","));
379 
380  planners_.clear();
381  for (const auto &plannerString : plannerStrings)
382  {
383  std::vector<std::string> plannerAndParams;
384  boost::split(plannerAndParams, plannerString, boost::is_any_of("[ ]"));
385  const std::string &plannerName = plannerAndParams[0];
386 
387  if (plannerName == "BFMT")
388  planners_.push_back(std::make_shared<geometric::BFMT>(si_));
389  else if (plannerName == "BiEST")
390  planners_.push_back(std::make_shared<geometric::BiEST>(si_));
391  else if (plannerName == "BiTRRT")
392  planners_.push_back(std::make_shared<geometric::BiTRRT>(si_));
393  else if (plannerName == "BITstar")
394  planners_.push_back(std::make_shared<geometric::BITstar>(si_));
395  else if (plannerName == "BKPIECE")
396  planners_.push_back(std::make_shared<geometric::BKPIECE1>(si_));
397  else if (plannerName == "EST")
398  planners_.push_back(std::make_shared<geometric::EST>(si_));
399  else if (plannerName == "FMT")
400  planners_.push_back(std::make_shared<geometric::FMT>(si_));
401  else if (plannerName == "KPIECE")
402  planners_.push_back(std::make_shared<geometric::KPIECE1>(si_));
403  else if (plannerName == "LazyLBTRRT")
404  planners_.push_back(std::make_shared<geometric::LazyLBTRRT>(si_));
405  else if (plannerName == "LazyPRM")
406  planners_.push_back(std::make_shared<geometric::LazyPRM>(si_));
407  else if (plannerName == "LazyPRMstar")
408  planners_.push_back(std::make_shared<geometric::LazyPRMstar>(si_));
409  else if (plannerName == "LazyRRT")
410  planners_.push_back(std::make_shared<geometric::LazyRRT>(si_));
411  else if (plannerName == "LBKPIECE")
412  planners_.push_back(std::make_shared<geometric::LBKPIECE1>(si_));
413  else if (plannerName == "LBTRRT")
414  planners_.push_back(std::make_shared<geometric::LBTRRT>(si_));
415  else if (plannerName == "PDST")
416  planners_.push_back(std::make_shared<geometric::PDST>(si_));
417  else if (plannerName == "PRM")
418  planners_.push_back(std::make_shared<geometric::PRM>(si_));
419  else if (plannerName == "PRMstar")
420  planners_.push_back(std::make_shared<geometric::PRMstar>(si_));
421  else if (plannerName == "ProjEST")
422  planners_.push_back(std::make_shared<geometric::ProjEST>(si_));
423  else if (plannerName == "RRT")
424  planners_.push_back(std::make_shared<geometric::RRT>(si_));
425  else if (plannerName == "RRTConnect")
426  planners_.push_back(std::make_shared<geometric::RRTConnect>(si_));
427  else if (plannerName == "RRTstar")
428  planners_.push_back(std::make_shared<geometric::RRTstar>(si_));
429  else if (plannerName == "RRTXstatic")
430  planners_.push_back(std::make_shared<geometric::RRTXstatic>(si_));
431  else if (plannerName == "SBL")
432  planners_.push_back(std::make_shared<geometric::SBL>(si_));
433  else if (plannerName == "SPARS")
434  planners_.push_back(std::make_shared<geometric::SPARS>(si_));
435  else if (plannerName == "SPARStwo")
436  planners_.push_back(std::make_shared<geometric::SPARStwo>(si_));
437  else if (plannerName == "SST")
438  planners_.push_back(std::make_shared<geometric::SST>(si_));
439  else if (plannerName == "STRIDE")
440  planners_.push_back(std::make_shared<geometric::STRIDE>(si_));
441  else if (plannerName == "TRRT")
442  planners_.push_back(std::make_shared<geometric::TRRT>(si_));
443  else
444  OMPL_ERROR("Unknown planner name: %s", plannerName.c_str());
445 
446  std::vector<std::string> paramValue;
447  for (auto it = plannerAndParams.begin() + 1; it != plannerAndParams.end(); ++it)
448  if (!it->empty())
449  {
450  boost::split(paramValue, *it, boost::is_any_of("="));
451  planners_.back()->params().setParam(paramValue[0], paramValue[1]);
452  }
453  }
454 }
455 
457 {
458  std::stringstream ss;
459  for (unsigned int i = 0; i < planners_.size(); ++i)
460  {
461  if (i > 0)
462  ss << ',';
463  ss << planners_[i]->getName();
464 
465  std::map<std::string, std::string> params;
466  planners_[i]->params().getParams(params);
467  if (params.size() > 0)
468  {
469  ss << '[';
470  for (auto it = params.begin(); it != params.end(); ++it)
471  {
472  if (it != params.begin())
473  ss << ' ';
474  ss << it->first << '=' << it->second;
475  }
476  ss << ']';
477  }
478  }
479  return ss.str();
480 }
481 
483 {
484  Planner::printSettings(out);
485  out << "Settings for planner instances in AnytimePathShortening instance:\n";
486  for (const auto &planner : planners_)
487  {
488  out << "* ";
489  planner->printSettings(out);
490  }
491 }
Base class for a planner.
Definition: Planner.h:279
@ UNRECOGNIZED_GOAL_TYPE
The goal is of a type that a planner does not recognize.
unsigned int maxHybridizationPaths() const
Return the maximum number of paths that will be hybridized.
std::string getPlanners() const
Get a string representation of the planners and their parameters in the format of setPlanners.
A shared pointer wrapper for ompl::base::SpaceInformation.
bool multithreaded
Flag indicating whether multiple threads are used in the computation of the planner.
Definition: Planner.h:256
unsigned int recordPath(const geometric::PathGeometricPtr &pp, bool matchAcrossGaps)
Add a path to the hybridization. If matchAcrossGaps is true, more possible edge connections are evalu...
void getPlannerData(base::PlannerData &data) const override
Get information about the most recent run of the motion planner.
void clear()
Clear all the stored paths.
bool isShortcutting() const
Return whether the anytime planner will perform shortcutting on paths.
void addPlannerProgressProperty(const std::string &progressPropertyName, const PlannerProgressProperty &prop)
Add a planner progress property called progressPropertyName with a property querying function prop to...
Definition: Planner.h:467
bool partialShortcutPath(PathGeometric &path, unsigned int maxSteps=0, unsigned int maxEmptySteps=0, double rangeRatio=0.33, double snapToVertex=0.005)
Given a path, attempt to shorten it while maintaining its validity. This is an iterative process that...
std::size_t pathCount() const
Get the number of paths that are currently considered as part of the hybridization.
This class contains routines that attempt to simplify geometric paths.
const geometric::PathGeometricPtr & getHybridPath() const
Get the currently computed hybrid path. computeHybridPath() needs to have been called before.
Definition of a cost value. Can represent the cost of a motion or the cost of a state.
Definition: Cost.h:111
bool simplify(PathGeometric &path, double maxTime, bool atLeastOnce=true)
Run simplification algorithms on the path for at most maxTime seconds, and at least once if atLeastOn...
#define OMPL_INFORM(fmt,...)
Log a formatted information string.
Definition: Console.h:68
base::PlannerPtr getPlanner(unsigned int idx) const
Retrieve a pointer to the ith planner instance.
unsigned int getNumPlanners() const
Retrieve the number of planners added.
Definition of a geometric path.
Definition: PathGeometric.h:97
A shared pointer wrapper for ompl::base::Planner.
Object containing planner generated vertex and edge data. It is assumed that all vertices are unique,...
Definition: PlannerData.h:238
void setup() override
Perform any necessary configuration steps. This method also invokes ompl::base::SpaceInformation::set...
Given multiple geometric paths, attempt to combine them in order to obtain a shorter solution.
Encapsulate a termination condition for a motion planner. Planners will call operator() to decide whe...
void computeHybridPath()
Run Dijkstra's algorithm to find out the lowest-cost path among the mixed ones.
PlannerSpecs specs_
The specifications of the planner (its capabilities)
Definition: Planner.h:486
void setLogLevel(LogLevel level)
Set the minimum level of logging data to output. Messages with lower logging levels will not be recor...
Definition: Console.cpp:136
@ INVALID_GOAL
Invalid goal state.
LogLevel
The set of priorities for message logging.
Definition: Console.h:84
AnytimePathShortening(const base::SpaceInformationPtr &si)
Constructor requires the space information to plan in.
bool optimizingPaths
Flag indicating whether the planner attempts to optimize the path and reduce its length until the max...
Definition: Planner.h:263
bool isHybridizing() const
Return whether the anytime planner will extract a hybrid path from the set of solution paths.
virtual void threadSolve(base::Planner *planner, const base::PlannerTerminationCondition &ptc)
The function that the planning threads execute when solving a motion planning problem.
A class to store the exit status of Planner::solve()
void setDefaultNumPlanners(unsigned int numPlanners)
Set default number of planners to use if none are specified.
void addPath(const geometric::PathGeometricPtr &path, base::Planner *planner)
add a path to set of solutions
void printSettings(std::ostream &out) const override
Print settings of this planner as well as those of the planner instances it contains.
void setMaxHybridizationPath(unsigned int maxPathCount)
Set the maximum number of paths that will be hybridized.
void setHybridize(bool hybridize)
Enable/disable path hybridization on the set of solution paths.
#define OMPL_WARN(fmt,...)
Log a formatted warning string.
Definition: Console.h:66
unsigned int getDefaultNumPlanners() const
Get default number of planners used if none are specified.
@ EXACT_SOLUTION
The planner found an exact solution.
@ UNKNOWN
Uninitialized status.
StatusType
The possible values of the status returned by a planner.
std::string getBestCost() const
Return best cost found so far by algorithm.
void clear() override
Clear all internal planning datastructures. Planner settings are not affected. Subsequent calls to so...
LogLevel getLogLevel()
Retrieve the current level of logging data. Messages with lower logging levels will not be recorded.
Definition: Console.cpp:142
#define OMPL_ERROR(fmt,...)
Log a formatted error string.
Definition: Console.h:64
void setPlanners(const std::string &plannerList)
Set the list of planners to use.
void terminate() const
Notify that the condition for termination should become true, regardless of what eval() returns....
bool approximateSolutions
Flag indicating whether the planner is able to compute approximate solutions.
Definition: Planner.h:259
~AnytimePathShortening() override
Destructor.
static base::PlannerPtr getDefaultPlanner(const base::GoalPtr &goal)
Given a goal specification, decide on a planner for that goal.
Definition: SelfConfig.cpp:243
void addPlanner(base::PlannerPtr &planner)
Adds the given planner to the set of planners used to compute candidate paths.
@ INVALID_START
Invalid start state or no start state specified.
base::PlannerStatus solve(const base::PlannerTerminationCondition &ptc) override
Method that solves the motion planning problem. This method terminates under just two conditions,...
std::string toString(float val)
convert float to string using classic "C" locale semantics
Definition: String.cpp:82
Main namespace. Contains everything in this library.
Definition: AppBase.h:21
void setShortcut(bool shortcut)
Enable/disable shortcutting on paths.
void checkValidity() override
Check to see if the planners are in a working state (setup has been called, a goal was set,...