BFMT.cpp
1 
2 #include <boost/math/constants/constants.hpp>
3 #include <boost/math/distributions/binomial.hpp>
4 #include <ompl/datastructures/BinaryHeap.h>
5 #include <ompl/tools/config/SelfConfig.h>
6 
7 #include <ompl/datastructures/NearestNeighborsGNAT.h>
8 #include <ompl/base/objectives/PathLengthOptimizationObjective.h>
9 #include <ompl/geometric/planners/fmt/BFMT.h>
10 
11 #include <fstream>
12 #include <ompl/base/spaces/RealVectorStateSpace.h>
13 
14 namespace ompl {
15 namespace geometric {
16 
17 BFMT::BFMT(const base::SpaceInformationPtr &si)
18  : base::Planner(si, "BFMT")
19  , numSamples_(1000)
20  , radiusMultiplier_(1.0)
21  , freeSpaceVolume_(si_->getStateSpace()->getMeasure()) // An upper bound on the free space volume is the total space volume; the free fraction is estimated in sampleFree
22  , collisionChecks_(0)
23  , nearestK_(true)
24  , NNr_(0)
25  , NNk_(0)
26  , tree_(FWD)
27  , exploration_(SWAP_EVERY_TIME)
28  , termination_(OPTIMALITY)
29  , precomputeNN_(false)
30  , heuristics_(true)
31  , cacheCC_(true)
32  , extendedFMT_(true)
33 {
35  specs_.directed = false;
36 
37  ompl::base::Planner::declareParam<unsigned int>("num_samples", this, &BFMT::setNumSamples, &BFMT::getNumSamples, "10:10:1000000");
38  ompl::base::Planner::declareParam<double>("radius_multiplier", this, &BFMT::setRadiusMultiplier, &BFMT::getRadiusMultiplier, "0.1:0.05:50.");
39  ompl::base::Planner::declareParam<bool>("nearest_k", this, &BFMT::setNearestK, &BFMT::getNearestK, "0,1");
40  ompl::base::Planner::declareParam<bool>("balanced", this, &BFMT::setExploration, &BFMT::getExploration, "0,1");
41  ompl::base::Planner::declareParam<bool>("optimality", this, &BFMT::setTermination, &BFMT::getTermination, "0,1");
42  ompl::base::Planner::declareParam<bool>("heuristics", this, &BFMT::setHeuristics, &BFMT::getHeuristics, "0,1");
43  ompl::base::Planner::declareParam<bool>("cache_cc", this, &BFMT::setCacheCC, &BFMT::getCacheCC, "0,1");
44  ompl::base::Planner::declareParam<bool>("extended_fmt", this, &BFMT::setExtendedFMT, &BFMT::getExtendedFMT, "0,1");
45 }
46 
47 ompl::geometric::BFMT::~BFMT()
48 {
49  freeMemory();
50 }
51 
52 void BFMT::setup(void)
53 {
54  if (pdef_)
55  {
56  /* Setup the optimization objective. If no optimization objective was
57  specified, then default to optimizing path length as computed by the
58  distance() function in the state space */
59  if (pdef_->hasOptimizationObjective())
60  opt_ = pdef_->getOptimizationObjective();
61  else
62  {
63  OMPL_INFORM("%s: No optimization objective specified. Defaulting to optimizing path length.", getName().c_str());
65  // Store the new objective in the problem def'n
66  pdef_->setOptimizationObjective(opt_);
67  }
68  Open_[0].getComparisonOperator().opt_ = opt_.get();
69  Open_[0].getComparisonOperator().heuristics_ = heuristics_;
70  Open_[1].getComparisonOperator().opt_ = opt_.get();
71  Open_[1].getComparisonOperator().heuristics_ = heuristics_;
72 
73  if (!nn_)
74  nn_.reset(tools::SelfConfig::getDefaultNearestNeighbors<BiDirMotion*>(this));
75  nn_->setDistanceFunction(std::bind(&BFMT::distanceFunction, this,
76  std::placeholders::_1, std::placeholders::_2));
77 
78  if (nearestK_ && !nn_->reportsSortedResults())
79  {
80  OMPL_WARN("%s: NearestNeighbors datastructure does not return sorted solutions. Nearest K strategy disabled.", getName().c_str());
81  nearestK_ = false;
82  }
83  }
84  else
85  {
86  OMPL_INFORM("%s: problem definition is not set, deferring setup completion...", getName().c_str());
87  setup_ = false;
88  }
89 }
90 
92 {
93  if (nn_)
94  {
95  BiDirMotionPtrs motions;
96  nn_->list(motions);
97  for (unsigned int i = 0 ; i < motions.size() ; ++i)
98  {
99  si_->freeState(motions[i]->getState());
100  delete motions[i];
101  }
102  }
103 }
104 
106 {
107  Planner::clear();
108  sampler_.reset();
109  freeMemory();
110  if (nn_)
111  nn_->clear();
112  Open_[FWD].clear();
113  Open_[REV].clear();
114  Open_elements[FWD].clear();
115  Open_elements[REV].clear();
116  neighborhoods_.clear();
117  collisionChecks_ = 0;
118 }
119 
121 {
123  BiDirMotionPtrs motions;
124  nn_->list(motions);
125 
126  int numStartNodes = 0;
127  int numGoalNodes = 0;
128  int numEdges = 0;
129  int numFwdEdges = 0;
130  int numRevEdges = 0;
131 
132  int fwd_tree_tag = 1;
133  int rev_tree_tag = 2;
134 
135  for (unsigned int k = 0; k < motions.size(); ++k)
136  {
137  BiDirMotion* motion = motions[k];
138  bool inFwdTree = (motion->currentSet_[FWD] != BiDirMotion::SET_UNVISITED);
139 
140  // For samples added to the fwd tree, add incoming edges (from fwd tree parent)
141  if (inFwdTree)
142  {
143  if (motion->parent_[FWD] == NULL)
144  {
145  // Motion is a forward tree root node
146  ++numStartNodes;
147  } else
148  {
149  bool success = data.addEdge(
150  base::PlannerDataVertex(motion->parent_[FWD]->getState(), fwd_tree_tag),
151  base::PlannerDataVertex(motion->getState(), fwd_tree_tag));
152  if (success)
153  {
154  ++numFwdEdges;
155  ++numEdges;
156  }
157  }
158  }
159  }
160 
161  // The edges in the goal tree are reversed so that they are in the same direction as start tree
162  for (unsigned int k = 0; k < motions.size(); ++k)
163  {
164  BiDirMotion* motion = motions[k];
165  bool inRevTree = (motion->currentSet_[REV] != BiDirMotion::SET_UNVISITED);
166 
167  // For samples added to a tree, add incoming edges (from fwd tree parent)
168  if (inRevTree)
169  {
170  if (motion->parent_[REV] == NULL)
171  {
172  // Motion is a reverse tree root node
173  ++numGoalNodes;
174  } else {
175  bool success = data.addEdge(
176  base::PlannerDataVertex(motion->getState(), rev_tree_tag),
177  base::PlannerDataVertex(motion->parent_[REV]->getState(), rev_tree_tag));
178  if (success)
179  {
180  ++numRevEdges;
181  ++numEdges;
182  }
183  }
184  }
185  }
186 }
187 
189  BiDirMotion* m)
190 {
191  // Check if neighborhood has already been saved
192  if (neighborhoods_.find(m) == neighborhoods_.end())
193  {
194  BiDirMotionPtrs neighborhood;
195  if (nearestK_)
196  nn_->nearestK(m, NNk_, neighborhood);
197  else
198  nn_->nearestR(m, NNr_, neighborhood);
199 
200  if (!neighborhood.empty())
201  {
202  // Save the neighborhood but skip the first element (m)
203  neighborhoods_[m] = std::vector<BiDirMotion*>(neighborhood.size()-1, 0);
204  std::copy(neighborhood.begin()+1, neighborhood.end(), neighborhoods_[m].begin());
205  }
206  else
207  {
208  // Save an empty neighborhood
209  neighborhoods_[m] = std::vector<BiDirMotion*>(0);
210  }
211  }
212 }
213 
216 {
217  unsigned int nodeCount = 0;
218  unsigned int sampleAttempts = 0;
219  BiDirMotion *motion = new BiDirMotion(si_, &tree_);
220 
221  // Sample numSamples_ number of nodes from the free configuration space
222  while (nodeCount < numSamples_ && !ptc)
223  {
224  sampler_->sampleUniform(motion->getState());
225  sampleAttempts++;
226  if (si_->isValid(motion->getState()))
227  { // collision checking
228  ++nodeCount;
229  nn->add(motion);
230  motion = new BiDirMotion(si_, &tree_);
231  }
232  }
233  si_->freeState(motion->getState());
234  delete motion;
235 
236  // 95% confidence limit for an upper bound for the true free space volume
237  freeSpaceVolume_ = boost::math::binomial_distribution<>::find_upper_bound_on_p(sampleAttempts, nodeCount, 0.05) * si_->getStateSpace()->getMeasure();
238 }
239 
240 double BFMT::calculateUnitBallVolume(const unsigned int dimension) const {
241  if (dimension == 0)
242  return 1.0;
243  else if (dimension == 1)
244  return 2.0;
245  return 2.0 * boost::math::constants::pi<double>() / dimension
246  * calculateUnitBallVolume(dimension-2);
247 }
248 
249 double BFMT::calculateRadius(const unsigned int dimension, const unsigned int n) const {
250 
251  double a = 1.0 / (double)dimension;
252  double unitBallVolume = calculateUnitBallVolume(dimension);
253 
254  return radiusMultiplier_ * 2.0 * std::pow(a, a) * std::pow(freeSpaceVolume_ / unitBallVolume, a) * std::pow(log((double)n) / (double)n, a);
255 }
256 
258 {
259  checkValidity();
260  if (!sampler_)
261  {
262  sampler_ = si_->allocStateSampler();
263  }
264  goal_s = dynamic_cast<base::GoalSampleableRegion*>(pdef_->getGoal().get());
265 }
266 
268 {
270  initializeProblem(goal_s);
271  if (!goal_s)
272  {
273  OMPL_ERROR("%s: Unknown type of goal", getName().c_str());
275  }
276 
277  useFwdTree();
278 
279  // Add start states to Unvisitedfwd and Openfwd
280  bool valid_initMotion = false;
281  BiDirMotion *initMotion;
282  while (const base::State *st = pis_.nextStart())
283  {
284  initMotion = new BiDirMotion(si_, &tree_);
285  si_->copyState(initMotion->getState(), st);
286 
287  initMotion->currentSet_[REV] = BiDirMotion::SET_UNVISITED;
288  nn_->add(initMotion); // S <-- {x_init}
289  if (si_->isValid(initMotion->getState()))
290  {
291  // Take the first valid initial state as the forward tree root
292  Open_elements[FWD][initMotion] = Open_[FWD].insert(initMotion);
293  initMotion->currentSet_[FWD] = BiDirMotion::SET_OPEN;
294  initMotion->cost_[FWD] = opt_->initialCost(initMotion->getState());
295  valid_initMotion = true;
296  heurGoalState_[1] = initMotion->getState();
297  }
298  }
299 
300  if (!initMotion || !valid_initMotion)
301  {
302  OMPL_ERROR("Start state undefined or invalid.");
304  }
305 
306  // Sample N free states in configuration state_
307  sampleFree(nn_, ptc); // S <-- SAMPLEFREE(N)
308  OMPL_INFORM("%s: Starting planning with %u states already in datastructure", getName().c_str(), nn_->size());
309 
310  // Calculate the nearest neighbor search radius
311  if (nearestK_)
312  {
313  NNk_ = std::ceil(std::pow(2.0 * radiusMultiplier_, (double)si_->getStateDimension()) *
314  (boost::math::constants::e<double>() / (double)si_->getStateDimension()) *
315  log((double)nn_->size()));
316  OMPL_DEBUG("Using nearest-neighbors k of %d", NNk_);
317  }
318  else
319  {
320  NNr_ = calculateRadius(si_->getStateDimension(), nn_->size());
321  OMPL_DEBUG("Using radius of %f", NNr_);
322  }
323 
324  // Add goal states to Unvisitedrev and Openrev
325  bool valid_goalMotion = false;
326  BiDirMotion *goalMotion;
327  while (const base::State *st = pis_.nextGoal())
328  {
329  goalMotion = new BiDirMotion(si_, &tree_);
330  si_->copyState(goalMotion->getState(), st);
331 
332  goalMotion->currentSet_[FWD] = BiDirMotion::SET_UNVISITED;
333  nn_->add(goalMotion); // S <-- {x_goal}
334  if (si_->isValid(goalMotion->getState()))
335  {
336  // Take the first valid goal state as the reverse tree root
337  Open_elements[REV][goalMotion] = Open_[REV].insert(goalMotion);
338  goalMotion->currentSet_[REV] = BiDirMotion::SET_OPEN;
339  goalMotion->cost_[REV] = opt_->terminalCost(goalMotion->getState());
340  valid_goalMotion = true;
341  heurGoalState_[0] = goalMotion->getState();
342  }
343  }
344 
345  if (!goalMotion || !valid_goalMotion)
346  {
347  OMPL_ERROR("Goal state undefined or invalid.");
349  }
350 
351  useRevTree();
352 
353  // Plan a path
354  BiDirMotion *connection_point = NULL;
355  bool earlyFailure = true;
356 
357  if (initMotion != NULL && goalMotion != NULL)
358  {
359  earlyFailure = plan(initMotion, goalMotion, connection_point, ptc);
360  }
361  else
362  {
363  OMPL_ERROR("Initial/goal state(s) are undefined!");
364  }
365 
366  if (earlyFailure)
367  {
368  return base::PlannerStatus(false,false);
369  }
370 
371  // Save the best path (through z)
372  if (!ptc)
373  {
374  base::Cost fwd_cost, rev_cost, connection_cost;
375 
376  // Construct the solution path
377  useFwdTree();
378  BiDirMotionPtrs path_fwd;
379  tracePath(connection_point, path_fwd);
380  fwd_cost = connection_point->getCost();
381 
382  useRevTree();
383  BiDirMotionPtrs path_rev;
384  tracePath(connection_point, path_rev);
385  rev_cost = connection_point->getCost();
386 
387  // ASSUMES FROM THIS POINT THAT z = path_fwd[0] = path_rev[0]
388  // Remove the first element, z, in the traced reverse path
389  // (the same as the first element in the traced forward path)
390  if (path_rev.size() > 1)
391  {
392  connection_cost = base::Cost(rev_cost.value() - path_rev[1]->getCost().value());
393  path_rev.erase(path_rev.begin());
394  }
395  else if (path_fwd.size() > 1)
396  {
397  connection_cost = base::Cost(fwd_cost.value() - path_fwd[1]->getCost().value());
398  path_fwd.erase(path_fwd.begin());
399  }
400  else
401  {
402  OMPL_ERROR("Solution path traced incorrectly or otherwise constructed improperly \
403  through forward/reverse trees (both paths are one node in length, each).");
404  }
405 
406  // Adjust costs/parents in reverse tree nodes as cost/direction from forward tree root
407  useFwdTree();
408  path_rev[0]->setCost(base::Cost(path_fwd[0]->getCost().value() + connection_cost.value()));
409  path_rev[0]->setParent(path_fwd[0]);
410  for (unsigned int i = 1; i < path_rev.size(); ++i)
411  {
412  path_rev[i]->setCost(base::Cost(fwd_cost.value() + (rev_cost.value() - path_rev[i]->getCost().value())));
413  path_rev[i]->setParent(path_rev[i-1]);
414  }
415 
416  BiDirMotionPtrs mpath;
417  std::reverse(path_rev.begin(), path_rev.end());
418  mpath.reserve(path_fwd.size() + path_rev.size()); // preallocate memory
419  mpath.insert(mpath.end(), path_rev.begin(), path_rev.end());
420  mpath.insert(mpath.end(), path_fwd.begin(), path_fwd.end());
421 
422  // Set the solution path
423  PathGeometric *path = new PathGeometric(si_);
424  for (int i = mpath.size() - 1 ; i >= 0 ; --i)
425  {
426  path->append(mpath[i]->getState());
427  }
428 
429  static const bool approximate = false;
430  static const double cost_difference_from_goal = 0.0;
431  pdef_->addSolutionPath(base::PathPtr(path), approximate, cost_difference_from_goal, getName());
432 
433  OMPL_DEBUG("Total path cost: %f\n", fwd_cost.value() + rev_cost.value());
434  return base::PlannerStatus(true, false);
435 
436  }
437  else
438  {
439  // Planner terminated without accomplishing goal
440  return base::PlannerStatus(false, false);
441  }
442 }
443 
444 
445 void BFMT::expandTreeFromNode(BiDirMotion *&z, BiDirMotion *&connection_point)
446 {
447  // Define Opennew and set it to NULL
448  BiDirMotionPtrs Open_new;
449 
450  // Define Znear as all unexplored nodes in the neighborhood around z
451  BiDirMotionPtrs zNear;
452  const BiDirMotionPtrs &zNeighborhood = neighborhoods_[z];
453 
454  for (unsigned int i = 0; i < zNeighborhood.size(); ++i)
455  {
456  if (zNeighborhood[i]->getCurrentSet() == BiDirMotion::SET_UNVISITED)
457  {
458  zNear.push_back(zNeighborhood[i]);
459  }
460  }
461 
462  // For each node x in Znear
463  for (unsigned int i = 0; i < zNear.size(); ++i)
464  {
465  BiDirMotion *x = zNear.at(i);
466  if (!precomputeNN_)
467  saveNeighborhood(nn_, x); // nearest neighbors
468 
469  // Define Xnear as all frontier nodes in the neighborhood around the unexplored node x
470  BiDirMotionPtrs xNear;
471  const BiDirMotionPtrs &xNeighborhood = neighborhoods_[x];
472  for (unsigned int j = 0; j < xNeighborhood.size(); ++j)
473  {
474  if (xNeighborhood[j]->getCurrentSet() == BiDirMotion::SET_OPEN)
475  {
476  xNear.push_back(xNeighborhood[j]);
477  }
478  }
479  // Find the node in Xnear with minimum cost-to-come in the current tree
480  BiDirMotion* xMin = NULL;
481  double cMin = std::numeric_limits<double>::infinity();
482  for (unsigned int j = 0; j < xNear.size(); ++j)
483  {
484  // check if node costs are smaller than minimum
485  double cNew = xNear.at(j)->getCost().value() + distanceFunction(xNear.at(j), x);
486 
487  if (cNew < cMin)
488  {
489  xMin = xNear.at(j);
490  cMin = cNew;
491  }
492  }
493 
494  // xMin was found
495  if (xMin != NULL)
496  {
497  bool collision_free = false;
498  if (cacheCC_)
499  {
500  if (!xMin->alreadyCC(x))
501  {
502  collision_free = si_->checkMotion(xMin->getState(), x->getState());
504  // Due to FMT3* design, it is only necessary to save unsuccesful
505  // connection attemps because of collision
506  if (!collision_free)
507  xMin->addCC(x);
508  }
509  }
510  else
511  {
513  collision_free = si_->checkMotion(xMin->getState(), x->getState());
514  }
515 
516  if (collision_free)
517  { // motion between yMin and x is obstacle free
518  // add edge from xMin to x
519  x->setParent(xMin);
520  x->setCost(base::Cost(cMin));
521  xMin->getChildren().push_back(x);
522 
523  if (heuristics_)
524  x->setHeuristicCost(opt_->motionCostHeuristic(x->getState(), heurGoalState_[tree_]));
525 
526  // check if new node x is in the other tree; if so, save result
527  if (x->getOtherSet() != BiDirMotion::SET_UNVISITED)
528  {
529  if (connection_point == NULL)
530  {
531  connection_point = x;
532  if (termination_ == FEASIBILITY)
533  {
534  break;
535  }
536  }
537  else
538  {
539  if ((connection_point->cost_[FWD].value() + connection_point->cost_[REV].value())
540  > (x->cost_[FWD].value() + x->cost_[REV].value()))
541  {
542  connection_point = x;
543  }
544  }
545  }
546 
547  Open_new.push_back(x); // add x to Open_new
548  x->setCurrentSet(BiDirMotion::SET_CLOSED); // remove x from Unvisited
549  }
550  }
551  } // End "for x in Znear"
552 
553  // Remove motion z from binary heap and map
554  BiDirMotionBinHeap::Element* zElement = Open_elements[tree_][z];
555  Open_[tree_].remove(zElement);
556  Open_elements[tree_].erase(z);
557  z->setCurrentSet(BiDirMotion::SET_CLOSED);
558 
559  // add nodes in Open_new to Open
560  for (unsigned int i = 0; i < Open_new.size(); i++)
561  {
562  Open_elements[tree_][Open_new.at(i)] = Open_[tree_].insert(Open_new.at(i));
563  Open_new.at(i)->setCurrentSet(BiDirMotion::SET_OPEN);
564  }
565 }
566 
567 
568 bool BFMT::plan(BiDirMotion *x_init, BiDirMotion *x_goal,
569  BiDirMotion *&connection_point, const base::PlannerTerminationCondition& ptc)
570 {
571  // If pre-computation, find neighborhoods for all N sample nodes plus initial
572  // and goal state(s). Otherwise compute the neighborhoods of the initial and
573  // goal states separately and compute the others as needed.
574  BiDirMotionPtrs sampleNodes;
575  nn_->list(sampleNodes);
578  if (precomputeNN_)
579  {
580  for (unsigned int i = 0; i < sampleNodes.size(); i++)
581  {
582  saveNeighborhood(nn_, sampleNodes[i]); // nearest neighbors
583  }
584  }
585  else
586  {
587  saveNeighborhood(nn_, x_init); // nearest neighbors
588  saveNeighborhood(nn_, x_goal); // nearest neighbors
589  }
590 
591  // Copy nodes in the sample set to Unvisitedfwd. Overwrite the label of the initial
592  // node with set Open for the forward tree, since it starts in set Openfwd.
593  useFwdTree();
594  for (unsigned int i = 0; i < sampleNodes.size(); i++)
595  {
596  sampleNodes[i]->setCurrentSet(BiDirMotion::SET_UNVISITED);
597  }
598  x_init->setCurrentSet(BiDirMotion::SET_OPEN);
599 
600  // Copy nodes in the sample set to Unvisitedrev. Overwrite the label of the goal
601  // node with set Open for the reverse tree, since it starts in set Openrev.
602  useRevTree();
603  for (unsigned int i = 0; i < sampleNodes.size(); i++)
604  {
605  sampleNodes[i]->setCurrentSet(BiDirMotion::SET_UNVISITED);
606  }
607  x_goal->setCurrentSet(BiDirMotion::SET_OPEN);
608 
609  // Expand the trees until reaching the termination condition
610  bool earlyFailure = false;
611  bool success = false;
612 
613  useFwdTree();
614  BiDirMotion *z = x_init;
615 
616  while (!success)
617  {
618  expandTreeFromNode(z, connection_point);
619 
620  // Check if the algorithm should terminate. Possibly redefines connection_point.
621  if (termination(z, connection_point, ptc))
622  success = true;
623  else
624  {
625  if (Open_[tree_].empty()) // If this heap is empty...
626  {
627  if (!extendedFMT_) // ... eFMT not enabled...
628  {
629  if (Open_[(tree_+1) % 2].empty()) // ... and this one, failure.
630  {
631  OMPL_INFORM("Both Open are empty before path was found --> no feasible path exists");
632  earlyFailure = true;
633  return earlyFailure;
634  }
635  }
636  else // However, if eFMT is enabled, run it.
637  insertNewSampleInOpen(ptc);
638  }
639 
640  // This function will be always reached with at least one state in one heap.
641  // However, if ptc terminates, we should skip this.
642  if (!ptc)
643  chooseTreeAndExpansionNode(z);
644  else
645  return true;
646  }
647  }
648  earlyFailure = false;
649  return earlyFailure;
650 }
651 
653 {
654  // Sample and connect samples to tree only if there is
655  // a possibility to connect to unvisited nodes.
656  std::vector<BiDirMotion*> nbh;
657  std::vector<base::Cost> costs;
658  std::vector<base::Cost> incCosts;
659  std::vector<std::size_t> sortedCostIndices;
660 
661  // our functor for sorting nearest neighbors
662  CostIndexCompare compareFn(costs, *opt_);
663 
664  BiDirMotion *m = new BiDirMotion(si_, &tree_);
665  while (!ptc && Open_[tree_].empty()) //&& oneSample)
666  {
667  // Get new sample and check whether it is valid.
668  sampler_->sampleUniform(m->getState());
669  if (!si_->isValid(m->getState()))
670  continue;
671 
672  // Get neighbours of the new sample.
673  std::vector<BiDirMotion*> yNear;
674  if (nearestK_)
675  nn_->nearestK(m, NNk_, nbh);
676  else
677  nn_->nearestR(m, NNr_, nbh);
678 
679  yNear.reserve(nbh.size());
680  for (std::size_t j = 0; j < nbh.size(); ++j)
681  {
682  if (nbh[j]->getCurrentSet() == BiDirMotion::SET_CLOSED)
683  {
684  if (nearestK_)
685  {
686  // Only include neighbors that are mutually k-nearest
687  // Relies on NN datastructure returning k-nearest in sorted order
688  const base::Cost connCost = opt_->motionCost(nbh[j]->getState(), m->getState());
689  const base::Cost worstCost = opt_->motionCost(neighborhoods_[nbh[j]].back()->getState(), nbh[j]->getState());
690 
691  if (opt_->isCostBetterThan(worstCost, connCost))
692  continue;
693  else
694  yNear.push_back(nbh[j]);
695  }
696  else
697  yNear.push_back(nbh[j]);
698  }
699  }
700 
701  // Sample again if the new sample does not connect to the tree.
702  if (yNear.empty())
703  continue;
704 
705  // cache for distance computations
706  //
707  // Our cost caches only increase in size, so they're only
708  // resized if they can't fit the current neighborhood
709  if (costs.size() < yNear.size())
710  {
711  costs.resize(yNear.size());
712  incCosts.resize(yNear.size());
713  sortedCostIndices.resize(yNear.size());
714  }
715 
716  // Finding the nearest neighbor to connect to
717  // By default, neighborhood states are sorted by cost, and collision checking
718  // is performed in increasing order of cost
719  //
720  // calculate all costs and distances
721  for (std::size_t i = 0 ; i < yNear.size(); ++i)
722  {
723  incCosts[i] = opt_->motionCost(yNear[i]->getState(), m->getState());
724  costs[i] = opt_->combineCosts(yNear[i]->getCost(), incCosts[i]);
725  }
726 
727  // sort the nodes
728  //
729  // we're using index-value pairs so that we can get at
730  // original, unsorted indices
731  for (std::size_t i = 0; i < yNear.size(); ++i)
732  sortedCostIndices[i] = i;
733  std::sort(sortedCostIndices.begin(), sortedCostIndices.begin() + yNear.size(),
734  compareFn);
735 
736  // collision check until a valid motion is found
737  for (std::vector<std::size_t>::const_iterator i = sortedCostIndices.begin();
738  i != sortedCostIndices.begin() + yNear.size();
739  ++i)
740  {
742  if (si_->checkMotion(yNear[*i]->getState(), m->getState()))
743  {
744  const base::Cost incCost = opt_->motionCost(yNear[*i]->getState(), m->getState());
745  m->setParent(yNear[*i]);
746  yNear[*i]->getChildren().push_back(m);
747  m->setCost(opt_->combineCosts(yNear[*i]->getCost(), incCost));
748  m->setHeuristicCost(opt_->motionCostHeuristic(m->getState(), heurGoalState_[tree_]));
749  m->setCurrentSet(BiDirMotion::SET_OPEN);
750  Open_elements[tree_][m] = Open_[tree_].insert(m);
751 
752  nn_->add(m);
753  saveNeighborhood(nn_, m);
754  updateNeighborhood(m, nbh);
755 
756  break;
757  }
758  }
759  } // While Open_[tree_] empty
760 }
761 
763 {
764  bool terminate = false;
765  switch (termination_)
766  {
767  case FEASIBILITY:
768  // Test if a connection point was found during tree expansion
769  return (connection_point != NULL || ptc);
770  break;
771 
772  case OPTIMALITY:
773  // Test if z is in SET_CLOSED (interior) of other tree
774  if (ptc)
775  terminate = true;
776  else if (z->getOtherSet() == BiDirMotion::SET_CLOSED)
777  terminate = true;
778 
779  break;
780  };
781  return terminate;
782 }
783 
784 // Choose exploration tree and node z to expand
786 {
787  switch (exploration_)
788  {
789  case SWAP_EVERY_TIME:
790  if (Open_[(tree_+1) % 2].empty())
791  z = Open_[tree_].top()->data; // Continue expanding the current tree (not empty by exit condition in plan())
792  else
793  {
794  z = Open_[(tree_+1) % 2].top()->data; // Take top of opposite tree heap as new z
795  swapTrees(); // Swap to the opposite tree
796  }
797  break;
798 
799  case CHOOSE_SMALLEST_Z:
800  BiDirMotion *z1, *z2;
801  if (Open_[(tree_+1) % 2].empty())
802  z = Open_[tree_].top()->data; // Continue expanding the current tree (not empty by exit condition in plan())
803  else if (Open_[tree_].empty())
804  {
805  z = Open_[(tree_+1) % 2].top()->data; // Take top of opposite tree heap as new z
806  swapTrees(); // Swap to the opposite tree
807  } else {
808  z1 = Open_[tree_].top()->data;
809  z2 = Open_[(tree_+1) % 2].top()->data;
810 
811  if (z1->getCost().value() < z2->getOtherCost().value())
812  z = z1;
813  else
814  {
815  z = z2;
816  swapTrees();
817  }
818  }
819  break;
820  };
821 }
822 
823 // Trace a path of nodes along a tree towards the root (forward or reverse)
824 void BFMT::tracePath(BiDirMotion *z, BiDirMotionPtrs& path)
825 {
826  BiDirMotion* solution = z;
827 
828  while (solution != NULL)
829  {
830  path.push_back(solution);
831  solution = solution->getParent();
832  }
833 }
834 
836 {
837  tree_ = (TreeType) ((((int) tree_) + 1) % 2);
838 }
839 
840 void BFMT::updateNeighborhood(BiDirMotion *m, const std::vector<BiDirMotion *> nbh)
841 {
842  // Neighborhoods are only updated if the new motion is within bounds (k nearest or within r).
843  for (std::size_t i = 0; i < nbh.size(); ++i)
844  {
845  // If CLOSED, that neighborhood won't be used again.
846  // Else, if neighhboorhod already exists, we have to insert the node in
847  // the corresponding place of the neighborhood of the neighbor of m.
848  if (nbh[i]->getCurrentSet() == BiDirMotion::SET_CLOSED)
849  continue;
850  else
851  {
852  auto it = neighborhoods_.find(nbh[i]);
853  if (it != neighborhoods_.end())
854  {
855  if (!it->second.size())
856  continue;
857 
858  const base::Cost connCost = opt_->motionCost(nbh[i]->getState(), m->getState());
859  const base::Cost worstCost = opt_->motionCost(it->second.back()->getState(), nbh[i]->getState());
860 
861  if (opt_->isCostBetterThan(worstCost, connCost))
862  continue;
863  else
864  {
865  // insert the neighbor in the vector in the correct order
866  std::vector<BiDirMotion*> &nbhToUpdate = it->second;
867  for (std::size_t j = 0; j < nbhToUpdate.size(); ++j)
868  {
869  // If connection to the new state is better than the current neighbor tested, insert.
870  const base::Cost cost = opt_->motionCost(nbh[i]->getState(), nbhToUpdate[j]->getState());
871  if (opt_->isCostBetterThan(connCost, cost))
872  {
873  nbhToUpdate.insert(nbhToUpdate.begin()+j, m);
874  break;
875  }
876  }
877  }
878  }
879  }
880  }
881 }
882 
883 } // End "geometric" namespace
884 } // End "ompl" namespace
bool nearestK_
Flag to activate the K nearest neighbors strategy.
Definition: FMT.h:448
bool cacheCC_
Flag to activate the collision check caching.
Definition: FMT.h:451
bool approximateSolutions
Flag indicating whether the planner is able to compute approximate solutions.
Definition: Planner.h:212
virtual Cost initialCost(const State *s) const
Returns a cost value corresponding to starting at a state s. No optimal planners currently support th...
Object containing planner generated vertex and edge data. It is assumed that all vertices are unique...
Definition: PlannerData.h:163
void insertNewSampleInOpen(const base::PlannerTerminationCondition &ptc)
Extended FMT strategy: inserts a new motion in open if the heap is empty.
Definition: BFMT.cpp:652
void sampleFree(const ompl::base::PlannerTerminationCondition &ptc)
Sample a state from the free configuration space and save it into the nearest neighbors data structur...
Definition: FMT.cpp:213
bool getTermination() const
Returns the termination strategy.
Definition: BFMT.h:238
unsigned int numSamples_
The number of samples to use when planning.
Definition: FMT.h:442
void setCurrentSet(SetType set)
Set the current set of the motion.
Definition: BFMT.h:366
void saveNeighborhood(std::shared_ptr< NearestNeighbors< BiDirMotion * > > nn, BiDirMotion *m)
Save the neighbors within a neighborhood of a given state. The strategy used (nearestK or nearestR de...
Definition: BFMT.cpp:188
double calculateRadius(unsigned int dimension, unsigned int n) const
Calculate the radius to use for nearest neighbor searches, using the bound given in L...
Definition: BFMT.cpp:249
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
SetType getOtherSet(void) const
Get set of this motion in the inactive tree.
Definition: BFMT.h:378
void setTermination(bool optimality)
Sets the termination strategy: optimality true finishes when the best possible path is found...
Definition: BFMT.h:229
void clear()
Clear the heap.
Definition: BinaryHeap.h:106
base::StateSamplerPtr sampler_
State sampler.
Definition: FMT.h:480
double distanceFunction(const Motion *a, const Motion *b) const
Compute the distance between two motions as the cost between their contained states. Note that for computationally intensive cost functions, the cost between motions should be stored to avoid duplicate calculations.
Definition: FMT.h:373
base::Cost getCost(void) const
Set the state associated with the motion.
Definition: BFMT.h:323
void initializeProblem(base::GoalSampleableRegion *&goal_s)
Carries out some planner checks.
Definition: BFMT.cpp:257
double NNr_
Radius employed in the nearestR strategy.
Definition: FMT.h:457
const State * nextGoal(const PlannerTerminationCondition &ptc)
Return the next valid goal state or nullptr if no more valid goal states are available. Because sampling of goal states may also produce invalid goals, this function takes an argument that specifies whether a termination condition has been reached. If the termination condition evaluates to true the function terminates even if no valid goal has been found.
Definition: Planner.cpp:271
bool extendedFMT_
Add new samples if the tree was not able to find a solution.
Definition: FMT.h:492
void remove(Element *element)
Remove a specific element.
Definition: BinaryHeap.h:127
MotionBinHeap Open_
A binary heap for storing explored motions in cost-to-come sorted order. The motions in Open have bee...
Definition: FMT.h:435
double freeSpaceVolume_
The volume of the free configuration space, computed as an upper bound with 95% confidence.
Definition: FMT.h:464
Encapsulate a termination condition for a motion planner. Planners will call operator() to decide whe...
void setCacheCC(bool ccc)
Sets the collision check caching to save calls to the collision checker with slightly memory usage as...
Definition: BFMT.h:174
std::map< Motion *, std::vector< Motion * > > neighborhoods_
A map linking a motion to all of the motions within a distance r of that motion.
Definition: FMT.h:439
void setNearestK(bool nearestK)
If nearestK is true, FMT will be run using the Knearest strategy.
Definition: BFMT.h:123
unsigned int collisionChecks_
Number of collision checks performed by the algorithm.
Definition: FMT.h:445
base::Cost getOtherCost(void) const
Get cost of this motion in the inactive tree.
Definition: BFMT.h:329
bool termination(BiDirMotion *&z, BiDirMotion *&connection_point, const base::PlannerTerminationCondition &ptc)
Checks if the termination condition is met.
Definition: BFMT.cpp:762
void setParent(BiDirMotion *parent)
Set the parent of the motion.
Definition: BFMT.h:342
void freeMemory()
Free the memory allocated by this planner.
Definition: FMT.cpp:121
bool getNearestK() const
Get the state of the nearestK strategy.
Definition: BFMT.h:129
void append(const base::State *state)
Append state to the end of this path. The memory for state is copied.
BiDirMotionPtrs getChildren(void) const
Get the children of the motion.
Definition: BFMT.h:360
ProblemDefinitionPtr pdef_
The user set problem definition.
Definition: Planner.h:401
bool directed
Flag indicating whether the planner is able to account for the fact that the validity of a motion fro...
Definition: Planner.h:220
base::Cost cost_[2]
The cost of this motion.
Definition: BFMT.h:314
bool expandTreeFromNode(Motion **z)
Complete one iteration of the main loop of the FMT* algorithm: Find K nearest nodes in set Unvisited ...
Definition: FMT.cpp:501
virtual Cost motionCost(const State *s1, const State *s2) const =0
Get the cost that corresponds to the motion segment between s1 and s2.
void setExtendedFMT(bool e)
Activates the extended FMT*: adding new samples if planner does not finish successfully.
Definition: BFMT.h:199
SetType currentSet_[2]
Current set in which the motion is included.
Definition: BFMT.h:308
void freeMemory()
Free the memory allocated by this planner.
Definition: BFMT.cpp:91
base::State * getState() const
Get the state associated with the motion.
Definition: BFMT.h:401
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
bool setup_
Flag indicating whether setup() has been called.
Definition: Planner.h:419
void tracePath(BiDirMotion *z, BiDirMotionPtrs &path)
Trace the path along a tree towards the root (forward or reverse)
Definition: BFMT.cpp:824
bool alreadyCC(BiDirMotion *m)
Returns true if the connection to m has been already tested and failed because of a collision...
Definition: BFMT.h:407
Abstract definition of a goal region that can be sampled.
Main namespace. Contains everything in this library.
Definition: Cost.h:42
unsigned int NNk_
K used in the nearestK strategy.
Definition: FMT.h:460
virtual void setup(void)
Perform extra configuration steps, if needed. This call will also issue a call to ompl::base::SpaceIn...
Definition: BFMT.cpp:52
bool getExtendedFMT() const
Returns true if the extended FMT* is activated.
Definition: BFMT.h:205
void addCC(BiDirMotion *m)
Caches a failed collision check to m.
Definition: BFMT.h:415
bool plan(BiDirMotion *x_init, BiDirMotion *x_goal, BiDirMotion *&z, const base::PlannerTerminationCondition &ptc)
Executes the actual planning algorithm, swapping and expanding the trees.
Definition: BFMT.cpp:568
The goal is of a type that a planner does not recognize.
Definition: PlannerStatus.h:60
Element * top() const
Return the top element. nullptr for an empty heap.
Definition: BinaryHeap.h:115
#define OMPL_ERROR(fmt,...)
Log a formatted error string.
Definition: Console.h:64
bool getCacheCC() const
Get the state of the collision check caching.
Definition: BFMT.h:180
virtual Cost motionCostHeuristic(const State *s1, const State *s2) const
Defines an admissible estimate on the optimal cost on the motion between states s1 and s2...
void sampleFree(std::shared_ptr< NearestNeighbors< BiDirMotion * > > nn, const base::PlannerTerminationCondition &ptc)
Sample a state from the free configuration space and save it into the nearest neighbors data structur...
Definition: BFMT.cpp:214
BiDirMotion * parent_[2]
The parent motion in the exploration tree.
Definition: BFMT.h:302
double value() const
The value of the cost.
Definition: Cost.h:54
void saveNeighborhood(Motion *m)
Save the neighbors within a neighborhood of a given state. The strategy used (nearestK or nearestR de...
Definition: FMT.cpp:170
virtual bool isCostBetterThan(Cost c1, Cost c2) const
Check whether the the cost c1 is considered better than the cost c2. By default, this returns true if...
void swapTrees()
Change the active tree.
Definition: BFMT.cpp:835
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...
unsigned int getNumSamples() const
Get the number of states that the planner will sample.
Definition: BFMT.h:117
void expandTreeFromNode(BiDirMotion *&z, BiDirMotion *&connection_point)
Complete one iteration of the main loop of the BFMT* algorithm: Find K nearest nodes in set Unvisited...
Definition: BFMT.cpp:445
void setRadiusMultiplier(const double radiusMultiplier)
The planner searches for neighbors of a node within a cost r, where r is the value described for BFMT...
Definition: BFMT.h:141
virtual void clear()
Clear all internal datastructures. Planner settings are not affected. Subsequent calls to solve() wil...
Definition: BFMT.cpp:105
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: BFMT.cpp:267
An optimization objective which corresponds to optimizing path length.
void updateNeighborhood(BiDirMotion *m, const std::vector< BiDirMotion * > nbh)
For a motion m, updates the stored neighborhoods of all its neighbors by by inserting m (maintaining ...
Definition: BFMT.cpp:840
Definition of an abstract state.
Definition: State.h:50
virtual void checkValidity()
Check to see if the planner is in a working state (setup has been called, a goal was set...
Definition: Planner.cpp:100
#define OMPL_WARN(fmt,...)
Log a formatted warning string.
Definition: Console.h:66
PlannerInputStates pis_
Utility class to extract valid input states.
Definition: Planner.h:404
Representation of a bidirectional motion.
Definition: BFMT.h:257
void setHeuristicCost(const base::Cost h)
Set the cost to go heuristic cost.
Definition: BFMT.h:421
Abstract representation of a container that can perform nearest neighbors queries.
double getRadiusMultiplier() const
Get the multiplier used for the nearest neighbors search radius.
Definition: BFMT.h:150
TreeType
Tree identifier.
Definition: BFMT.h:86
PlannerSpecs specs_
The specifications of the planner (its capabilities)
Definition: Planner.h:410
const State * nextStart()
Return the next valid start state or nullptr if no more valid start states are available.
Definition: Planner.cpp:230
#define OMPL_DEBUG(fmt,...)
Log a formatted debugging string.
Definition: Console.h:70
void chooseTreeAndExpansionNode(BiDirMotion *&z)
Chooses and expand a tree according to the exploration strategy.
Definition: BFMT.cpp:785
void setCost(base::Cost cost)
Set the cost of the motion.
Definition: BFMT.h:336
bool getHeuristics() const
Returns true if the heap is ordered taking into account cost to go heuristics.
Definition: BFMT.h:193
double distanceFunction(const BiDirMotion *a, const BiDirMotion *b) const
Compute the distance between two motions as the cost between their contained states. Note that for computationally intensive cost functions, the cost between motions should be stored to avoid duplicate calculations.
Definition: BFMT.h:471
LessThan & getComparisonOperator()
Return a reference to the comparison operator.
Definition: BinaryHeap.h:230
virtual void getPlannerData(PlannerData &data) const
Get information about the current run of the motion planner. Repeated calls to this function will upd...
Definition: Planner.cpp:118
double calculateRadius(unsigned int dimension, unsigned int n) const
Calculate the radius to use for nearest neighbor searches, using the bound given in L...
Definition: FMT.cpp:205
std::shared_ptr< NearestNeighbors< Motion * > > nn_
A nearest-neighbor datastructure containing the set of all motions.
Definition: FMT.h:477
double radiusMultiplier_
This planner uses a nearest neighbor search radius proportional to the lower bound for optimality der...
Definition: FMT.h:474
void updateNeighborhood(Motion *m, const std::vector< Motion * > nbh)
For a motion m, updates the stored neighborhoods of all its neighbors by by inserting m (maintaining ...
Definition: FMT.cpp:640
Definition of a geometric path.
Definition: PathGeometric.h:60
void setExploration(bool balanced)
Sets exploration strategy: balanced true expands one tree every iteration. False will select the tree...
Definition: BFMT.h:212
virtual Cost combineCosts(Cost c1, Cost c2) const
Get the cost that corresponds to combining the costs c1 and c2. Default implementation defines this c...
void setHeuristics(bool h)
Activates the cost to go heuristics when ordering the heap.
Definition: BFMT.h:186
SpaceInformationPtr si_
The space information for which planning is done.
Definition: Planner.h:398
BiDirMotion * getParent(void) const
Get the parent of the motion.
Definition: BFMT.h:348
virtual Cost terminalCost(const State *s) const
Returns a cost value corresponding to a path ending at a state s. No optimal planners currently suppo...
bool getExploration() const
Returns the exploration strategy.
Definition: BFMT.h:221
double calculateUnitBallVolume(const unsigned int dimension) const
Compute the volume of the unit ball in a given dimension.
Definition: FMT.cpp:195
bool heuristics_
Flag to activate the cost to go heuristics.
Definition: FMT.h:454
Definition of a cost value. Can represent the cost of a motion or the cost of a state.
Definition: Cost.h:47
double calculateUnitBallVolume(const unsigned int dimension) const
Compute the volume of the unit ball in a given dimension.
Definition: BFMT.cpp:240
const std::string & getName() const
Get the name of the planner.
Definition: Planner.cpp:55
Element * insert(const _T &data)
Add a new element.
Definition: BinaryHeap.h:135
void setNumSamples(const unsigned int numSamples)
Set the number of states that the planner should sample. The planner will sample this number of state...
Definition: BFMT.h:111
A shared pointer wrapper for ompl::base::Path.
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: BFMT.cpp:120
#define OMPL_INFORM(fmt,...)
Log a formatted information string.
Definition: Console.h:68