RRTstar.cpp
1 /*********************************************************************
2 * Software License Agreement (BSD License)
3 *
4 * Copyright (c) 2011, 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 /* Authors: Alejandro Perez, Sertac Karaman, Ryan Luna, Luis G. Torres, Ioan Sucan, Javier V Gomez, Jonathan Gammell */
36 
37 #include "ompl/geometric/planners/rrt/RRTstar.h"
38 #include "ompl/base/goals/GoalSampleableRegion.h"
39 #include "ompl/tools/config/SelfConfig.h"
40 #include "ompl/base/objectives/PathLengthOptimizationObjective.h"
41 #include "ompl/base/Goal.h"
42 #include "ompl/base/goals/GoalState.h"
43 #include "ompl/util/GeometricEquations.h"
44 #include "ompl/base/samplers/InformedStateSampler.h"
45 #include "ompl/base/samplers/informed/RejectionInfSampler.h"
46 #include <algorithm>
47 #include <limits>
48 #include <boost/math/constants/constants.hpp>
49 #include <vector>
50 
51 ompl::geometric::RRTstar::RRTstar(const base::SpaceInformationPtr &si) :
52  base::Planner(si, "RRTstar"),
53  goalBias_(0.05),
54  maxDistance_(0.0),
55  useKNearest_(true),
56  rewireFactor_(1.1),
57  k_rrg_(0u),
58  r_rrg_(0.0),
59  delayCC_(true),
60  lastGoalMotion_(nullptr),
61  useTreePruning_(false),
62  pruneThreshold_(0.05),
63  usePrunedMeasure_(false),
64  useInformedSampling_(false),
65  useRejectionSampling_(false),
66  useNewStateRejection_(false),
67  useAdmissibleCostToCome_(true),
68  numSampleAttempts_ (100u),
69  bestCost_(std::numeric_limits<double>::quiet_NaN()),
70  prunedCost_(std::numeric_limits<double>::quiet_NaN()),
71  prunedMeasure_(0.0),
72  iterations_(0u)
73 {
75  specs_.optimizingPaths = true;
77 
78  Planner::declareParam<double>("range", this, &RRTstar::setRange, &RRTstar::getRange, "0.:1.:10000.");
79  Planner::declareParam<double>("goal_bias", this, &RRTstar::setGoalBias, &RRTstar::getGoalBias, "0.:.05:1.");
80  Planner::declareParam<double>("rewire_factor", this, &RRTstar::setRewireFactor, &RRTstar::getRewireFactor, "1.0:0.01:2.0");
81  Planner::declareParam<bool>("use_k_nearest", this, &RRTstar::setKNearest, &RRTstar::getKNearest, "0,1");
82  Planner::declareParam<bool>("delay_collision_checking", this, &RRTstar::setDelayCC, &RRTstar::getDelayCC, "0,1");
83  Planner::declareParam<bool>("tree_pruning", this, &RRTstar::setTreePruning, &RRTstar::getTreePruning, "0,1");
84  Planner::declareParam<double>("prune_threshold", this, &RRTstar::setPruneThreshold, &RRTstar::getPruneThreshold, "0.:.01:1.");
85  Planner::declareParam<bool>("pruned_measure", this, &RRTstar::setPrunedMeasure, &RRTstar::getPrunedMeasure, "0,1");
86  Planner::declareParam<bool>("informed_sampling", this, &RRTstar::setInformedSampling, &RRTstar::getInformedSampling, "0,1");
87  Planner::declareParam<bool>("sample_rejection", this, &RRTstar::setSampleRejection, &RRTstar::getSampleRejection, "0,1");
88  Planner::declareParam<bool>("new_state_rejection", this, &RRTstar::setNewStateRejection, &RRTstar::getNewStateRejection, "0,1");
89  Planner::declareParam<bool>("use_admissible_heuristic", this, &RRTstar::setAdmissibleCostToCome, &RRTstar::getAdmissibleCostToCome, "0,1");
90  Planner::declareParam<bool>("focus_search", this, &RRTstar::setFocusSearch, &RRTstar::getFocusSearch, "0,1");
91  Planner::declareParam<bool>("number_sampling_attempts", this, &RRTstar::setNumSamplingAttempts, &RRTstar::getNumSamplingAttempts, "10:10:100000");
92 
93  addPlannerProgressProperty("iterations INTEGER",
94  std::bind(&RRTstar::numIterationsProperty, this));
95  addPlannerProgressProperty("best cost REAL",
96  std::bind(&RRTstar::bestCostProperty, this));
97 }
98 
99 ompl::geometric::RRTstar::~RRTstar()
100 {
101  freeMemory();
102 }
103 
105 {
106  Planner::setup();
107  tools::SelfConfig sc(si_, getName());
108  sc.configurePlannerRange(maxDistance_);
109  if (!si_->getStateSpace()->hasSymmetricDistance() || !si_->getStateSpace()->hasSymmetricInterpolate())
110  {
111  OMPL_WARN("%s requires a state space with symmetric distance and symmetric interpolation.", getName().c_str());
112  }
113 
114  if (!nn_)
115  nn_.reset(tools::SelfConfig::getDefaultNearestNeighbors<Motion*>(this));
116  nn_->setDistanceFunction(std::bind(&RRTstar::distanceFunction, this, std::placeholders::_1, std::placeholders::_2));
117 
118  // Setup optimization objective
119  //
120  // If no optimization objective was specified, then default to
121  // optimizing path length as computed by the distance() function
122  // in the state space.
123  if (pdef_)
124  {
125  if (pdef_->hasOptimizationObjective())
126  opt_ = pdef_->getOptimizationObjective();
127  else
128  {
129  OMPL_INFORM("%s: No optimization objective specified. Defaulting to optimizing path length for the allowed planning time.", getName().c_str());
130  opt_.reset(new base::PathLengthOptimizationObjective(si_));
131 
132  // Store the new objective in the problem def'n
133  pdef_->setOptimizationObjective(opt_);
134  }
135  }
136  else
137  {
138  OMPL_INFORM("%s: problem definition is not set, deferring setup completion...", getName().c_str());
139  setup_ = false;
140  }
141 
142  // Get the measure of the entire space:
143  prunedMeasure_ = si_->getSpaceMeasure();
144 
145  // Calculate some constants:
146  calculateRewiringLowerBounds();
147 
148  // Set the bestCost_ and prunedCost_ as infinite
149  bestCost_ = opt_->infiniteCost();
150  prunedCost_ = opt_->infiniteCost();
151 }
152 
154 {
155  setup_ = false;
156  Planner::clear();
157  sampler_.reset();
158  infSampler_.reset();
159  freeMemory();
160  if (nn_)
161  nn_->clear();
162 
163  lastGoalMotion_ = nullptr;
164  goalMotions_.clear();
165  startMotions_.clear();
166 
167  iterations_ = 0;
168  bestCost_ = base::Cost(std::numeric_limits<double>::quiet_NaN());
169  prunedCost_ = base::Cost(std::numeric_limits<double>::quiet_NaN());
170  prunedMeasure_ = 0.0;
171 }
172 
174 {
175  checkValidity();
176  base::Goal *goal = pdef_->getGoal().get();
177  base::GoalSampleableRegion *goal_s = dynamic_cast<base::GoalSampleableRegion*>(goal);
178 
179  bool symCost = opt_->isSymmetric();
180 
181  // Check if there are more starts
182  if (pis_.haveMoreStartStates() == true)
183  {
184  // There are, add them
185  while (const base::State *st = pis_.nextStart())
186  {
187  Motion *motion = new Motion(si_);
188  si_->copyState(motion->state, st);
189  motion->cost = opt_->identityCost();
190  nn_->add(motion);
191  startMotions_.push_back(motion);
192  }
193 
194  // And assure that, if we're using an informed sampler, it's reset
195  infSampler_.reset();
196  }
197  // No else
198 
199  if (nn_->size() == 0)
200  {
201  OMPL_ERROR("%s: There are no valid initial states!", getName().c_str());
203  }
204 
205  //Allocate a sampler if necessary
206  if (!sampler_ && !infSampler_)
207  {
208  allocSampler();
209  }
210 
211  OMPL_INFORM("%s: Starting planning with %u states already in datastructure", getName().c_str(), nn_->size());
212 
213  if ((useTreePruning_ || useRejectionSampling_ || useInformedSampling_ || useNewStateRejection_) && !si_->getStateSpace()->isMetricSpace())
214  OMPL_WARN("%s: The state space (%s) is not metric and as a result the optimization objective may not satisfy the triangle inequality. "
215  "You may need to disable pruning or rejection."
216  , getName().c_str(), si_->getStateSpace()->getName().c_str());
217 
218  const base::ReportIntermediateSolutionFn intermediateSolutionCallback = pdef_->getIntermediateSolutionCallback();
219 
220  Motion *solution = lastGoalMotion_;
221 
222  Motion *approximation = nullptr;
223  double approximatedist = std::numeric_limits<double>::infinity();
224  bool sufficientlyShort = false;
225 
226  Motion *rmotion = new Motion(si_);
227  base::State *rstate = rmotion->state;
228  base::State *xstate = si_->allocState();
229 
230  std::vector<Motion*> nbh;
231 
232  std::vector<base::Cost> costs;
233  std::vector<base::Cost> incCosts;
234  std::vector<std::size_t> sortedCostIndices;
235 
236  std::vector<int> valid;
237  unsigned int rewireTest = 0;
238  unsigned int statesGenerated = 0;
239 
240  if (solution)
241  OMPL_INFORM("%s: Starting planning with existing solution of cost %.5f", getName().c_str(), solution->cost.value());
242 
243  if (useKNearest_)
244  OMPL_INFORM("%s: Initial k-nearest value of %u", getName().c_str(), (unsigned int)std::ceil(k_rrg_ * log((double)(nn_->size() + 1u))));
245  else
246  OMPL_INFORM("%s: Initial rewiring radius of %.2f", getName().c_str(), std::min(maxDistance_, r_rrg_*std::pow(log((double)(nn_->size() + 1u))/((double)(nn_->size() + 1u)), 1/(double)(si_->getStateDimension()))));
247 
248  // our functor for sorting nearest neighbors
249  CostIndexCompare compareFn(costs, *opt_);
250 
251  while (ptc == false)
252  {
253  iterations_++;
254 
255  // sample random state (with goal biasing)
256  // Goal samples are only sampled until maxSampleCount() goals are in the tree, to prohibit duplicate goal states.
257  if (goal_s && goalMotions_.size() < goal_s->maxSampleCount() && rng_.uniform01() < goalBias_ && goal_s->canSample())
258  goal_s->sampleGoal(rstate);
259  else
260  {
261  // Attempt to generate a sample, if we fail (e.g., too many rejection attempts), skip the remainder of this loop and return to try again
262  if (!sampleUniform(rstate))
263  continue;
264  }
265 
266  // find closest state in the tree
267  Motion *nmotion = nn_->nearest(rmotion);
268 
269  if (intermediateSolutionCallback && si_->equalStates(nmotion->state, rstate))
270  continue;
271 
272  base::State *dstate = rstate;
273 
274  // find state to add to the tree
275  double d = si_->distance(nmotion->state, rstate);
276  if (d > maxDistance_)
277  {
278  si_->getStateSpace()->interpolate(nmotion->state, rstate, maxDistance_ / d, xstate);
279  dstate = xstate;
280  }
281 
282  // Check if the motion between the nearest state and the state to add is valid
283  if (si_->checkMotion(nmotion->state, dstate))
284  {
285  // create a motion
286  Motion *motion = new Motion(si_);
287  si_->copyState(motion->state, dstate);
288  motion->parent = nmotion;
289  motion->incCost = opt_->motionCost(nmotion->state, motion->state);
290  motion->cost = opt_->combineCosts(nmotion->cost, motion->incCost);
291 
292  // Find nearby neighbors of the new motion
293  getNeighbors(motion, nbh);
294 
295  rewireTest += nbh.size();
296  ++statesGenerated;
297 
298  // cache for distance computations
299  //
300  // Our cost caches only increase in size, so they're only
301  // resized if they can't fit the current neighborhood
302  if (costs.size() < nbh.size())
303  {
304  costs.resize(nbh.size());
305  incCosts.resize(nbh.size());
306  sortedCostIndices.resize(nbh.size());
307  }
308 
309  // cache for motion validity (only useful in a symmetric space)
310  //
311  // Our validity caches only increase in size, so they're
312  // only resized if they can't fit the current neighborhood
313  if (valid.size() < nbh.size())
314  valid.resize(nbh.size());
315  std::fill(valid.begin(), valid.begin() + nbh.size(), 0);
316 
317  // Finding the nearest neighbor to connect to
318  // By default, neighborhood states are sorted by cost, and collision checking
319  // is performed in increasing order of cost
320  if (delayCC_)
321  {
322  // calculate all costs and distances
323  for (std::size_t i = 0 ; i < nbh.size(); ++i)
324  {
325  incCosts[i] = opt_->motionCost(nbh[i]->state, motion->state);
326  costs[i] = opt_->combineCosts(nbh[i]->cost, incCosts[i]);
327  }
328 
329  // sort the nodes
330  //
331  // we're using index-value pairs so that we can get at
332  // original, unsorted indices
333  for (std::size_t i = 0; i < nbh.size(); ++i)
334  sortedCostIndices[i] = i;
335  std::sort(sortedCostIndices.begin(), sortedCostIndices.begin() + nbh.size(),
336  compareFn);
337 
338  // collision check until a valid motion is found
339  //
340  // ASYMMETRIC CASE: it's possible that none of these
341  // neighbors are valid. This is fine, because motion
342  // already has a connection to the tree through
343  // nmotion (with populated cost fields!).
344  for (std::vector<std::size_t>::const_iterator i = sortedCostIndices.begin();
345  i != sortedCostIndices.begin() + nbh.size();
346  ++i)
347  {
348  if (nbh[*i] == nmotion || si_->checkMotion(nbh[*i]->state, motion->state))
349  {
350  motion->incCost = incCosts[*i];
351  motion->cost = costs[*i];
352  motion->parent = nbh[*i];
353  valid[*i] = 1;
354  break;
355  }
356  else valid[*i] = -1;
357  }
358  }
359  else // if not delayCC
360  {
361  motion->incCost = opt_->motionCost(nmotion->state, motion->state);
362  motion->cost = opt_->combineCosts(nmotion->cost, motion->incCost);
363  // find which one we connect the new state to
364  for (std::size_t i = 0 ; i < nbh.size(); ++i)
365  {
366  if (nbh[i] != nmotion)
367  {
368  incCosts[i] = opt_->motionCost(nbh[i]->state, motion->state);
369  costs[i] = opt_->combineCosts(nbh[i]->cost, incCosts[i]);
370  if (opt_->isCostBetterThan(costs[i], motion->cost))
371  {
372  if (si_->checkMotion(nbh[i]->state, motion->state))
373  {
374  motion->incCost = incCosts[i];
375  motion->cost = costs[i];
376  motion->parent = nbh[i];
377  valid[i] = 1;
378  }
379  else valid[i] = -1;
380  }
381  }
382  else
383  {
384  incCosts[i] = motion->incCost;
385  costs[i] = motion->cost;
386  valid[i] = 1;
387  }
388  }
389  }
390 
391  if (useNewStateRejection_)
392  {
393  if (opt_->isCostBetterThan(solutionHeuristic(motion), bestCost_))
394  {
395  nn_->add(motion);
396  motion->parent->children.push_back(motion);
397  }
398  else // If the new motion does not improve the best cost it is ignored.
399  {
400  si_->freeState(motion->state);
401  delete motion;
402  continue;
403  }
404  }
405  else
406  {
407  // add motion to the tree
408  nn_->add(motion);
409  motion->parent->children.push_back(motion);
410  }
411 
412  bool checkForSolution = false;
413  for (std::size_t i = 0; i < nbh.size(); ++i)
414  {
415  if (nbh[i] != motion->parent)
416  {
417  base::Cost nbhIncCost;
418  if (symCost)
419  nbhIncCost = incCosts[i];
420  else
421  nbhIncCost = opt_->motionCost(motion->state, nbh[i]->state);
422  base::Cost nbhNewCost = opt_->combineCosts(motion->cost, nbhIncCost);
423  if (opt_->isCostBetterThan(nbhNewCost, nbh[i]->cost))
424  {
425  bool motionValid;
426  if (valid[i] == 0)
427  {
428  motionValid = si_->checkMotion(motion->state, nbh[i]->state);
429  }
430  else
431  {
432  motionValid = (valid[i] == 1);
433  }
434 
435  if (motionValid)
436  {
437  // Remove this node from its parent list
438  removeFromParent (nbh[i]);
439 
440  // Add this node to the new parent
441  nbh[i]->parent = motion;
442  nbh[i]->incCost = nbhIncCost;
443  nbh[i]->cost = nbhNewCost;
444  nbh[i]->parent->children.push_back(nbh[i]);
445 
446  // Update the costs of the node's children
447  updateChildCosts(nbh[i]);
448 
449  checkForSolution = true;
450  }
451  }
452  }
453  }
454 
455  // Add the new motion to the goalMotion_ list, if it satisfies the goal
456  double distanceFromGoal;
457  if (goal->isSatisfied(motion->state, &distanceFromGoal))
458  {
459  goalMotions_.push_back(motion);
460  checkForSolution = true;
461  }
462 
463  // Checking for solution or iterative improvement
464  if (checkForSolution)
465  {
466  bool updatedSolution = false;
467  for (size_t i = 0; i < goalMotions_.size(); ++i)
468  {
469  if (opt_->isCostBetterThan(goalMotions_[i]->cost, bestCost_))
470  {
471  if (opt_->isFinite(bestCost_) == false)
472  {
473  OMPL_INFORM("%s: Found an initial solution with a cost of %.2f in %u iterations (%u vertices in the graph)", getName().c_str(), goalMotions_[i]->cost.value(), iterations_, nn_->size());
474  }
475  bestCost_ = goalMotions_[i]->cost;
476  updatedSolution = true;
477  }
478 
479  sufficientlyShort = opt_->isSatisfied(goalMotions_[i]->cost);
480  if (sufficientlyShort)
481  {
482  solution = goalMotions_[i];
483  break;
484  }
485  else if (!solution ||
486  opt_->isCostBetterThan(goalMotions_[i]->cost,solution->cost))
487  {
488  solution = goalMotions_[i];
489  updatedSolution = true;
490  }
491  }
492 
493  if (updatedSolution)
494  {
495  if (useTreePruning_)
496  {
497  pruneTree(bestCost_);
498  }
499 
500  if (intermediateSolutionCallback)
501  {
502  std::vector<const base::State *> spath;
503  Motion *intermediate_solution = solution->parent; // Do not include goal state to simplify code.
504 
505  //Push back until we find the start, but not the start itself
506  while (intermediate_solution->parent != nullptr)
507  {
508  spath.push_back(intermediate_solution->state);
509  intermediate_solution = intermediate_solution->parent;
510  }
511 
512  intermediateSolutionCallback(this, spath, bestCost_);
513  }
514  }
515  }
516 
517  // Checking for approximate solution (closest state found to the goal)
518  if (goalMotions_.size() == 0 && distanceFromGoal < approximatedist)
519  {
520  approximation = motion;
521  approximatedist = distanceFromGoal;
522  }
523  }
524 
525  // terminate if a sufficient solution is found
526  if (solution && sufficientlyShort)
527  break;
528  }
529 
530  bool approximate = (solution == nullptr);
531  bool addedSolution = false;
532  if (approximate)
533  solution = approximation;
534  else
535  lastGoalMotion_ = solution;
536 
537  if (solution != nullptr)
538  {
539  ptc.terminate();
540  // construct the solution path
541  std::vector<Motion*> mpath;
542  while (solution != nullptr)
543  {
544  mpath.push_back(solution);
545  solution = solution->parent;
546  }
547 
548  // set the solution path
549  PathGeometric *geoPath = new PathGeometric(si_);
550  for (int i = mpath.size() - 1 ; i >= 0 ; --i)
551  geoPath->append(mpath[i]->state);
552 
553  base::PathPtr path(geoPath);
554  // Add the solution path.
555  base::PlannerSolution psol(path);
556  psol.setPlannerName(getName());
557  if (approximate)
558  psol.setApproximate(approximatedist);
559  // Does the solution satisfy the optimization objective?
560  psol.setOptimized(opt_, bestCost_, sufficientlyShort);
561  pdef_->addSolutionPath(psol);
562 
563  addedSolution = true;
564  }
565 
566  si_->freeState(xstate);
567  if (rmotion->state)
568  si_->freeState(rmotion->state);
569  delete rmotion;
570 
571  OMPL_INFORM("%s: Created %u new states. Checked %u rewire options. %u goal states in tree. Final solution cost %.3f", getName().c_str(), statesGenerated, rewireTest, goalMotions_.size(), bestCost_.value());
572 
573  return base::PlannerStatus(addedSolution, approximate);
574 }
575 
576 void ompl::geometric::RRTstar::getNeighbors(Motion *motion, std::vector<Motion*> &nbh) const
577 {
578  double cardDbl = static_cast<double>(nn_->size() + 1u);
579  if (useKNearest_)
580  {
581  //- k-nearest RRT*
582  unsigned int k = std::ceil(k_rrg_ * log(cardDbl));
583  nn_->nearestK(motion, k, nbh);
584  }
585  else
586  {
587  double r = std::min(maxDistance_, r_rrg_ * std::pow(log(cardDbl) / cardDbl, 1 / static_cast<double>(si_->getStateDimension())));
588  nn_->nearestR(motion, r, nbh);
589  }
590 }
591 
593 {
594  for (std::vector<Motion*>::iterator it = m->parent->children.begin ();
595  it != m->parent->children.end (); ++it)
596  {
597  if (*it == m)
598  {
599  m->parent->children.erase(it);
600  break;
601  }
602  }
603 }
604 
606 {
607  for (std::size_t i = 0; i < m->children.size(); ++i)
608  {
609  m->children[i]->cost = opt_->combineCosts(m->cost, m->children[i]->incCost);
610  updateChildCosts(m->children[i]);
611  }
612 }
613 
615 {
616  if (nn_)
617  {
618  std::vector<Motion*> motions;
619  nn_->list(motions);
620  for (std::size_t i = 0 ; i < motions.size() ; ++i)
621  {
622  if (motions[i]->state)
623  si_->freeState(motions[i]->state);
624  delete motions[i];
625  }
626  }
627 }
628 
630 {
631  Planner::getPlannerData(data);
632 
633  std::vector<Motion*> motions;
634  if (nn_)
635  nn_->list(motions);
636 
637  if (lastGoalMotion_)
638  data.addGoalVertex(base::PlannerDataVertex(lastGoalMotion_->state));
639 
640  for (std::size_t i = 0 ; i < motions.size() ; ++i)
641  {
642  if (motions[i]->parent == nullptr)
643  data.addStartVertex(base::PlannerDataVertex(motions[i]->state));
644  else
645  data.addEdge(base::PlannerDataVertex(motions[i]->parent->state),
646  base::PlannerDataVertex(motions[i]->state));
647  }
648 }
649 
651 {
652  // Variable
653  // The percent improvement (expressed as a [0,1] fraction) in cost
654  double fracBetter;
655  // The number pruned
656  int numPruned = 0;
657 
658  if (opt_->isFinite(prunedCost_))
659  {
660  fracBetter = std::abs((pruneTreeCost.value() - prunedCost_.value())/prunedCost_.value());
661  }
662  else
663  {
664  fracBetter = 1.0;
665  }
666 
667  if (fracBetter > pruneThreshold_)
668  {
669  // We are only pruning motions if they, AND all descendents, have a estimated cost greater than pruneTreeCost
670  // The easiest way to do this is to find leaves that should be pruned and ascend up their ancestry until a motion is found that is kept.
671  // To avoid making an intermediate copy of the NN structure, we process the tree by descending down from the start(s).
672  // In the first pass, all Motions with a cost below pruneTreeCost, or Motion's with children with costs below pruneTreeCost are added to the replacement NN structure,
673  // while all other Motions are stored as either a 'leaf' or 'chain' Motion. After all the leaves are disconnected and deleted, we check
674  // if any of the the chain Motions are now leaves, and repeat that process until done.
675  // This avoids (1) copying the NN structure into an intermediate variable and (2) the use of the expensive NN::remove() method.
676 
677  // Variable
678  // The queue of Motions to process:
679  std::queue<Motion*, std::deque<Motion*> > motionQueue;
680  // The list of leaves to prune
681  std::queue<Motion*, std::deque<Motion*> > leavesToPrune;
682  // The list of chain vertices to recheck after pruning
683  std::list<Motion*> chainsToRecheck;
684 
685  //Clear the NN structure:
686  nn_->clear();
687 
688  // Put all the starts into the NN structure and their children into the queue:
689  // We do this so that start states are never pruned.
690  for (unsigned int i = 0u; i < startMotions_.size(); ++i)
691  {
692  // Add to the NN
693  nn_->add(startMotions_.at(i));
694 
695  // Add their children to the queue:
696  addChildrenToList(&motionQueue, startMotions_.at(i));
697  }
698 
699  while (motionQueue.empty() == false)
700  {
701  // Test, can the current motion ever provide a better solution?
702  if (keepCondition(motionQueue.front(), pruneTreeCost))
703  {
704  // Yes it can, so it definitely won't be pruned
705  // Add it back into the NN structure
706  nn_->add(motionQueue.front());
707 
708  //Add it's children to the queue
709  addChildrenToList(&motionQueue, motionQueue.front());
710  }
711  else
712  {
713  // No it can't, but does it have children?
714  if (motionQueue.front()->children.empty() == false)
715  {
716  // Yes it does.
717  // We can minimize the number of intermediate chain motions if we check their children
718  // If any of them won't be pruned, then this motion won't either. This intuitively seems
719  // like a nice balance between following the descendents forever.
720 
721  // Variable
722  // Whether the children are definitely to be kept.
723  bool keepAChild = false;
724 
725  // Find if any child is definitely not being pruned.
726  for (unsigned int i = 0u; keepAChild == false && i < motionQueue.front()->children.size(); ++i)
727  {
728  // Test if the child can ever provide a better solution
729  keepAChild = keepCondition(motionQueue.front()->children.at(i), pruneTreeCost);
730  }
731 
732  // Are we *definitely* keeping any of the children?
733  if (keepAChild)
734  {
735  // Yes, we are, so we are not pruning this motion
736  // Add it back into the NN structure.
737  nn_->add(motionQueue.front());
738  }
739  else
740  {
741  // No, we aren't. This doesn't mean we won't though
742  // Move this Motion to the temporary list
743  chainsToRecheck.push_back(motionQueue.front());
744  }
745 
746  // Either way. add it's children to the queue
747  addChildrenToList(&motionQueue, motionQueue.front());
748  }
749  else
750  {
751  // No, so we will be pruning this motion:
752  leavesToPrune.push(motionQueue.front());
753  }
754  }
755 
756  // Pop the iterator, std::list::erase returns the next iterator
757  motionQueue.pop();
758  }
759 
760  // We now have a list of Motions to definitely remove, and a list of Motions to recheck
761  // Iteratively check the two lists until there is nothing to to remove
762  while (leavesToPrune.empty() == false)
763  {
764  // First empty the leave-to-prune
765  while (leavesToPrune.empty() == false)
766  {
767  // Remove the leaf from its parent
768  removeFromParent(leavesToPrune.front());
769 
770  // Erase the actual motion
771  // First free the state
772  si_->freeState(leavesToPrune.front()->state);
773 
774  // then delete the pointer
775  delete leavesToPrune.front();
776 
777  // And finally remove it from the list, erase returns the next iterator
778  leavesToPrune.pop();
779 
780  // Update our counter
781  ++numPruned;
782  }
783 
784  // Now, we need to go through the list of chain vertices and see if any are now leaves
785  std::list<Motion*>::iterator mIter = chainsToRecheck.begin();
786  while (mIter != chainsToRecheck.end())
787  {
788  // Is the Motion a leaf?
789  if ((*mIter)->children.empty() == true)
790  {
791  // It is, add to the removal queue
792  leavesToPrune.push(*mIter);
793 
794  // Remove from this queue, getting the next
795  mIter = chainsToRecheck.erase(mIter);
796  }
797  else
798  {
799  // Is isn't, skip to the next
800  ++mIter;
801  }
802  }
803  }
804 
805  // Now finally add back any vertices left in chainsToReheck.
806  // These are chain vertices that have descendents that we want to keep
807  for (std::list<Motion*>::const_iterator mIter = chainsToRecheck.begin(); mIter != chainsToRecheck.end(); ++mIter)
808  {
809  // Add the motion back to the NN struct:
810  nn_->add(*mIter);
811  }
812 
813  // All done pruning.
814  // Update the cost at which we've pruned:
815  prunedCost_ = pruneTreeCost;
816 
817  // And if we're using the pruned measure, the measure to which we've pruned
818  if (usePrunedMeasure_)
819  {
820  prunedMeasure_ = infSampler_->getInformedMeasure(prunedCost_);
821 
822  if (useKNearest_ == false)
823  {
824  calculateRewiringLowerBounds();
825  }
826  }
827  //No else, prunedMeasure_ is the si_ measure by default.
828  }
829 
830  return numPruned;
831 }
832 
833 void ompl::geometric::RRTstar::addChildrenToList(std::queue<Motion*, std::deque<Motion*> > *motionList, Motion* motion)
834 {
835  for (unsigned int j = 0u; j < motion->children.size(); ++j)
836  {
837  motionList->push(motion->children.at(j));
838  }
839 }
840 
841 bool ompl::geometric::RRTstar::keepCondition(const Motion* motion, const base::Cost& threshold) const
842 {
843  // We keep if the cost-to-come-heuristic of motion is <= threshold, by checking
844  // if (!threshold < heuristic), as if b is not better than a, then a is better than, or equal to, b
845  return !opt_->isCostBetterThan(threshold, solutionHeuristic(motion));
846 }
847 
849 {
850  base::Cost costToCome;
851  if (useAdmissibleCostToCome_)
852  {
853  // Start with infinite cost
854  costToCome = opt_->infiniteCost();
855 
856  //Find the min from each start
857  for (unsigned int i = 0u; i < startMotions_.size(); ++i)
858  {
859  costToCome = opt_->betterCost(costToCome, opt_->motionCost(startMotions_.at(i)->state, motion->state)); // lower-bounding cost from the start to the state
860  }
861  }
862  else
863  {
864  costToCome = motion->cost; // current cost from the state to the goal
865  }
866 
867  const base::Cost costToGo = opt_->costToGo(motion->state, pdef_->getGoal().get()); // lower-bounding cost from the state to the goal
868  return opt_->combineCosts(costToCome, costToGo); // add the two costs
869 }
870 
872 {
873  if (static_cast<bool>(opt_) == true)
874  {
875  if (opt_->hasCostToGoHeuristic() == false)
876  {
877  OMPL_INFORM("%s: No cost-to-go heuristic set. Informed techniques will not work well.", getName().c_str());
878  }
879  }
880 
881  // If we just disabled tree pruning, but we wee using prunedMeasure, we need to disable that as it required myself
882  if (prune == false && getPrunedMeasure() == true)
883  {
884  setPrunedMeasure(false);
885  }
886 
887  // Store
888  useTreePruning_ = prune;
889 }
890 
892 {
893  if (static_cast<bool>(opt_) == true)
894  {
895  if (opt_->hasCostToGoHeuristic() == false)
896  {
897  OMPL_INFORM("%s: No cost-to-go heuristic set. Informed techniques will not work well.", getName().c_str());
898  }
899  }
900 
901  // This option only works with informed sampling
902  if (informedMeasure == true && (useInformedSampling_ == false || useTreePruning_ == false))
903  {
904  OMPL_ERROR("%s: InformedMeasure requires InformedSampling and TreePruning.", getName().c_str());
905  }
906 
907  // Check if we're changed and update parameters if we have:
908  if (informedMeasure != usePrunedMeasure_)
909  {
910  // Store the setting
911  usePrunedMeasure_ = informedMeasure;
912 
913  // Update the prunedMeasure_ appropriately, if it has been configured.
914  if (setup_ == true)
915  {
916  if (usePrunedMeasure_)
917  {
918  prunedMeasure_ = infSampler_->getInformedMeasure(prunedCost_);
919  }
920  else
921  {
922  prunedMeasure_ = si_->getSpaceMeasure();
923  }
924  }
925 
926  // And either way, update the rewiring radius if necessary
927  if (useKNearest_ == false)
928  {
929  calculateRewiringLowerBounds();
930  }
931  }
932 }
933 
935 {
936  if (static_cast<bool>(opt_) == true)
937  {
938  if (opt_->hasCostToGoHeuristic() == false)
939  {
940  OMPL_INFORM("%s: No cost-to-go heuristic set. Informed techniques will not work well.", getName().c_str());
941  }
942  }
943 
944  // This option is mutually exclusive with setSampleRejection, assert that:
945  if (informedSampling == true && useRejectionSampling_ == true)
946  {
947  OMPL_ERROR("%s: InformedSampling and SampleRejection are mutually exclusive options.", getName().c_str());
948  }
949 
950  // If we just disabled tree pruning, but we are using prunedMeasure, we need to disable that as it required myself
951  if (informedSampling == false && getPrunedMeasure() == true)
952  {
953  setPrunedMeasure(false);
954  }
955 
956  // Check if we're changing the setting of informed sampling. If we are, we will need to create a new sampler, which we only want to do if one is already allocated.
957  if (informedSampling != useInformedSampling_)
958  {
959  //If we're disabled informedSampling, and prunedMeasure is enabled, we need to disable that
960  if (informedSampling == false && usePrunedMeasure_ == true)
961  {
962  setPrunedMeasure(false);
963  }
964 
965  // Store the value
966  useInformedSampling_ = informedSampling;
967 
968  // If we currently have a sampler, we need to make a new one
969  if (sampler_ || infSampler_)
970  {
971  // Reset the samplers
972  sampler_.reset();
973  infSampler_.reset();
974 
975  // Create the sampler
976  allocSampler();
977  }
978  }
979 }
980 
982 {
983  if (static_cast<bool>(opt_) == true)
984  {
985  if (opt_->hasCostToGoHeuristic() == false)
986  {
987  OMPL_INFORM("%s: No cost-to-go heuristic set. Informed techniques will not work well.", getName().c_str());
988  }
989  }
990 
991  // This option is mutually exclusive with setSampleRejection, assert that:
992  if (reject == true && useInformedSampling_ == true)
993  {
994  OMPL_ERROR("%s: InformedSampling and SampleRejection are mutually exclusive options.", getName().c_str());
995  }
996 
997  // Check if we're changing the setting of rejection sampling. If we are, we will need to create a new sampler, which we only want to do if one is already allocated.
998  if (reject != useRejectionSampling_)
999  {
1000  // Store the setting
1001  useRejectionSampling_ = reject;
1002 
1003  // If we currently have a sampler, we need to make a new one
1004  if (sampler_ || infSampler_)
1005  {
1006  // Reset the samplers
1007  sampler_.reset();
1008  infSampler_.reset();
1009 
1010  // Create the sampler
1011  allocSampler();
1012  }
1013  }
1014 }
1015 
1017 {
1018  // Allocate the appropriate type of sampler.
1019  if (useInformedSampling_)
1020  {
1021  // We are using informed sampling, this can end-up reverting to rejection sampling in some cases
1022  OMPL_INFORM("%s: Using informed sampling.", getName().c_str());
1023  infSampler_ = opt_->allocInformedStateSampler(pdef_, numSampleAttempts_);
1024  }
1025  else if (useRejectionSampling_)
1026  {
1027  // We are explicitly using rejection sampling.
1028  OMPL_INFORM("%s: Using rejection sampling.", getName().c_str());
1029  infSampler_ = std::make_shared<base::RejectionInfSampler>(pdef_, numSampleAttempts_);
1030  }
1031  else
1032  {
1033  // We are using a regular sampler
1034  sampler_ = si_->allocStateSampler();
1035  }
1036 }
1037 
1039 {
1040  // Use the appropriate sampler
1041  if (useInformedSampling_ || useRejectionSampling_)
1042  {
1043  // Attempt the focused sampler and return the result.
1044  // If bestCost is changing a lot by small amounts, this could
1045  // be prunedCost_ to reduce the number of times the informed sampling
1046  // transforms are recalculated.
1047  return infSampler_->sampleUniform(statePtr, bestCost_);
1048  }
1049  else
1050  {
1051  // Simply return a state from the regular sampler
1052  sampler_->sampleUniform(statePtr);
1053 
1054  // Always true
1055  return true;
1056  }
1057 }
1058 
1060 {
1061  double dimDbl = static_cast<double>(si_->getStateDimension());
1062 
1063  // k_rrg > e+e/d. K-nearest RRT*
1064  k_rrg_ = rewireFactor_ * (boost::math::constants::e<double>() + (boost::math::constants::e<double>() / dimDbl));
1065 
1066  // r_rrg > 2*(1+1/d)^(1/d)*(measure/ballvolume)^(1/d)
1067  // If we're not using the informed measure, prunedMeasure_ will be set to si_->getSpaceMeasure();
1068  r_rrg_ = rewireFactor_ * 2.0 * std::pow((1.0 + 1.0/dimDbl) * (prunedMeasure_ / unitNBallMeasure(si_->getStateDimension())), 1.0 / dimDbl);
1069 }
bool getNewStateRejection() const
Get the state of the new-state rejection option.
Definition: RRTstar.h:234
bool approximateSolutions
Flag indicating whether the planner is able to compute approximate solutions.
Definition: Planner.h:212
bool getTreePruning() const
Get the state of the pruning option.
Definition: RRTstar.h:179
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:392
Object containing planner generated vertex and edge data. It is assumed that all vertices are unique...
Definition: PlannerData.h:163
void setFocusSearch(const bool focus)
A meta parameter to focusing the search to improving the current solution. This is the parameter set ...
Definition: RRTstar.h:256
void addChildrenToList(std::queue< Motion *, std::deque< Motion * > > *motionList, Motion *motion)
Add the children of a vertex to the given list.
Definition: RRTstar.cpp:833
void setApproximate(double difference)
Specify that the solution is approximate and set the difference to the goal.
virtual void clear()
Clear all internal datastructures. Planner settings are not affected. Subsequent calls to solve() wil...
Definition: RRTstar.cpp:153
void getNeighbors(Motion *motion, std::vector< Motion * > &nbh) const
Gets the neighbours of a given motion, using either k-nearest of radius as appropriate.
Definition: RRTstar.cpp:576
void setRewireFactor(double rewireFactor)
Set the rewiring scale factor, s, such that r_rrg = s r_rrg* (or k_rrg = s k_rrg*) ...
Definition: RRTstar.h:132
void log(const char *file, int line, LogLevel level, const char *m,...)
Root level logging function. This should not be invoked directly, but rather used via a logging macro...
Definition: Console.cpp:120
void setDelayCC(bool delayCC)
Option that delays collision checking procedures. When it is enabled, all neighbors are sorted by cos...
Definition: RRTstar.h:158
void setInformedSampling(bool informedSampling)
Use direct sampling of the heuristic for the generation of random samples (e.g., x_rand). If a direct sampling method is not defined for the objective, rejection sampling will be used by default.
Definition: RRTstar.cpp:934
bool getSampleRejection() const
Get the state of the sample rejection option.
Definition: RRTstar.h:222
Representation of a solution to a planning problem.
void updateChildCosts(Motion *m)
Updates the cost of the children of this node if the cost up to this node has changed.
Definition: RRTstar.cpp:605
double getGoalBias() const
Get the goal bias the planner is using.
Definition: RRTstar.h:110
void setPrunedMeasure(bool informedMeasure)
Use the measure of the pruned subproblem instead of the measure of the entire problem domain (if such...
Definition: RRTstar.cpp:891
unsigned int addGoalVertex(const PlannerDataVertex &v)
Adds the given vertex to the graph data, and marks it as a start vertex. The vertex index is returned...
Abstract definition of goals.
Definition: Goal.h:62
base::Cost cost
The cost up to this motion.
Definition: RRTstar.h:328
Encapsulate a termination condition for a motion planner. Planners will call operator() to decide whe...
std::function< void(const Planner *, const std::vector< const base::State * > &, const Cost)> ReportIntermediateSolutionFn
When a planner has an intermediate solution (e.g., optimizing planners), a function with this signatu...
void setRange(double distance)
Set the range the planner is supposed to use.
Definition: RRTstar.h:120
bool canReportIntermediateSolutions
Flag indicating whether the planner is able to report the computation of intermediate paths...
Definition: Planner.h:226
void append(const base::State *state)
Append state to the end of this path. The memory for state is copied.
void setPruneThreshold(const double pp)
Set the fractional change in solution cost necessary for pruning to occur, i.e., prune if the new sol...
Definition: RRTstar.h:187
void setNewStateRejection(const bool reject)
Controls whether heuristic rejection is used on new states before connection (e.g., x_new = steer(x_nearest, x_rand))
Definition: RRTstar.h:228
virtual void setup()
Perform extra configuration steps, if needed. This call will also issue a call to ompl::base::SpaceIn...
Definition: RRTstar.cpp:104
void setKNearest(bool useKNearest)
Use a k-nearest search for rewiring instead of a r-disc search.
Definition: RRTstar.h:271
Representation of a motion.
Definition: RRTstar.h:307
virtual void sampleGoal(State *st) const =0
Sample a state in the goal region.
double getRange() const
Get the range the planner is using.
Definition: RRTstar.h:126
base::State * state
The state contained by the motion.
Definition: RRTstar.h:322
void freeMemory()
Free the memory allocated by this planner.
Definition: RRTstar.cpp:614
double unitNBallMeasure(unsigned int N)
The Lebesgue measure (i.e., "volume") of an n-dimensional ball with a unit radius.
bool keepCondition(const Motion *motion, const base::Cost &threshold) const
Check whether the given motion passes the specified cost threshold, meaning it will be kept during pr...
Definition: RRTstar.cpp:841
void setNumSamplingAttempts(unsigned int numAttempts)
Set the number of attempts to make while performing rejection or informed sampling.
Definition: RRTstar.h:283
bool getAdmissibleCostToCome() const
Get the admissibility of the pruning and new-state rejection heuristic.
Definition: RRTstar.h:246
Base class for a vertex in the PlannerData structure. All derived classes must implement the clone an...
Definition: PlannerData.h:59
Invalid start state or no start state specified.
Definition: PlannerStatus.h:56
Abstract definition of a goal region that can be sampled.
unsigned int getNumSamplingAttempts() const
Get the number of attempts to make while performing rejection or informed sampling.
Definition: RRTstar.h:289
bool getKNearest() const
Get the state of using a k-nearest search for rewiring.
Definition: RRTstar.h:277
virtual unsigned int maxSampleCount() const =0
Return the maximum number of samples that can be asked for before repeating.
void setAdmissibleCostToCome(const bool admissible)
Controls whether pruning and new-state rejection uses an admissible cost-to-come estimate or not...
Definition: RRTstar.h:240
#define OMPL_ERROR(fmt,...)
Log a formatted error string.
Definition: Console.h:64
virtual void getPlannerData(base::PlannerData &data) const
Get information about the current run of the motion planner. Repeated calls to this function will upd...
Definition: RRTstar.cpp:629
bool getPrunedMeasure() const
Get the state of using the pruned measure.
Definition: RRTstar.h:203
double value() const
The value of the cost.
Definition: Cost.h:54
int pruneTree(const base::Cost &pruneTreeCost)
Prunes all those states which estimated total cost is higher than pruneTreeCost. Returns the number o...
Definition: RRTstar.cpp:650
double getPruneThreshold() const
Get the current prune states percentage threshold parameter.
Definition: RRTstar.h:193
A class to store the exit status of Planner::solve()
Definition: PlannerStatus.h:48
virtual bool addEdge(unsigned int v1, unsigned int v2, const PlannerDataEdge &edge=PlannerDataEdge(), Cost weight=Cost(1.0))
Adds a directed edge between the given vertex indexes. An optional edge structure and weight can be s...
bool canSample() const
Return true if maxSampleCount() > 0, since in this case samples can certainly be produced.
base::Cost solutionHeuristic(const Motion *motion) const
Computes the solution cost heuristically as the cost to come from start to the motion plus the cost t...
Definition: RRTstar.cpp:848
An optimization objective which corresponds to optimizing path length.
unsigned int addStartVertex(const PlannerDataVertex &v)
Adds the given vertex to the graph data, and marks it as a start vertex. The vertex index is returned...
Definition of an abstract state.
Definition: State.h:50
virtual bool isSatisfied(const State *st) const =0
Return true if the state satisfies the goal constraints.
#define OMPL_WARN(fmt,...)
Log a formatted warning string.
Definition: Console.h:66
std::vector< Motion * > children
The set of motions descending from the current motion.
Definition: RRTstar.h:334
PlannerSpecs specs_
The specifications of the planner (its capabilities)
Definition: Planner.h:410
void setOptimized(const OptimizationObjectivePtr &opt, Cost cost, bool meetsObjective)
Set the optimization objective used to optimize this solution, the cost of the solution and whether i...
bool getFocusSearch() const
Get the state of search focusing.
Definition: RRTstar.h:265
void configurePlannerRange(double &range)
Compute what a good length for motion segments is.
Definition: SelfConfig.cpp:230
void setSampleRejection(const bool reject)
Controls whether heuristic rejection is used on samples (e.g., x_rand)
Definition: RRTstar.cpp:981
Motion * parent
The parent motion in the exploration tree.
Definition: RRTstar.h:325
This class contains methods that automatically configure various parameters for motion planning...
Definition: SelfConfig.h:60
bool sampleUniform(base::State *statePtr)
Generate a sample.
Definition: RRTstar.cpp:1038
void allocSampler()
Create the samplers.
Definition: RRTstar.cpp:1016
bool optimizingPaths
Flag indicating whether the planner attempts to optimize the path and reduce its length until the max...
Definition: Planner.h:216
void calculateRewiringLowerBounds()
Calculate the k_RRG* and r_RRG* terms.
Definition: RRTstar.cpp:1059
virtual base::PlannerStatus solve(const base::PlannerTerminationCondition &ptc)
Function that can solve the motion planning problem. This function can be called multiple times on th...
Definition: RRTstar.cpp:173
void removeFromParent(Motion *m)
Removes the given motion from the parent's child list.
Definition: RRTstar.cpp:592
void setTreePruning(const bool prune)
Controls whether the tree is pruned during the search. This pruning removes a vertex if and only if i...
Definition: RRTstar.cpp:871
void setGoalBias(double goalBias)
Set the goal bias.
Definition: RRTstar.h:104
Definition of a geometric path.
Definition: PathGeometric.h:60
void terminate() const
Notify that the condition for termination should become true, regardless of what eval() returns...
bool getDelayCC() const
Get the state of the delayed collision checking option.
Definition: RRTstar.h:164
void setPlannerName(const std::string &name)
Set the name of the planner used to compute this solution.
Definition of a cost value. Can represent the cost of a motion or the cost of a state.
Definition: Cost.h:47
base::Cost incCost
The incremental cost of this motion's parent to this motion (this is stored to save distance computat...
Definition: RRTstar.h:331
double getRewireFactor() const
Set the rewiring scale factor, s, such that r_rrg = s r_rrg* > r_rrg* (or k_rrg = s k_rrg* > k_rrg*...
Definition: RRTstar.h:139
A shared pointer wrapper for ompl::base::Path.
bool getInformedSampling() const
Get the state direct heuristic sampling.
Definition: RRTstar.h:213
double distanceFunction(const Motion *a, const Motion *b) const
Compute distance between motions (actually distance between contained states)
Definition: RRTstar.h:362
#define OMPL_INFORM(fmt,...)
Log a formatted information string.
Definition: Console.h:68