Loading...
Searching...
No Matches
TRRTstar.cpp
1/*********************************************************************
2 * Software License Agreement (BSD License)
3 *
4 * Copyright (c) 2025, Joris Chomarat
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 copyright holder 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: Joris Chomarat */
36
37#include "ompl/geometric/planners/rrt/TRRTstar.h"
38#include <algorithm>
39#include <boost/math/constants/constants.hpp>
40#include <limits>
41#include <vector>
42#include "ompl/base/Goal.h"
43#include "ompl/base/goals/GoalSampleableRegion.h"
44#include "ompl/base/goals/GoalState.h"
45#include "ompl/base/objectives/MechanicalWorkOptimizationObjective.h"
46#include "ompl/tools/config/SelfConfig.h"
47#include "ompl/util/GeometricEquations.h"
48
49ompl::geometric::TRRTstar::TRRTstar(const base::SpaceInformationPtr &si) : base::Planner(si, "TRRTstar")
50{
51 specs_.approximateSolutions = true;
52 specs_.optimizingPaths = true;
53 specs_.canReportIntermediateSolutions = true;
54
55 Planner::declareParam<double>("range", this, &TRRTstar::setRange, &TRRTstar::getRange, "0.:1.:10000.");
56 Planner::declareParam<double>("goal_bias", this, &TRRTstar::setGoalBias, &TRRTstar::getGoalBias, "0.:.05:1.");
57 Planner::declareParam<double>("rewire_factor", this, &TRRTstar::setRewireFactor, &TRRTstar::getRewireFactor,
58 "1.0:0.01:2.0");
59 Planner::declareParam<bool>("use_k_nearest", this, &TRRTstar::setKNearest, &TRRTstar::getKNearest, "0,1");
60 Planner::declareParam<bool>("delay_collision_checking", this, &TRRTstar::setDelayCC, &TRRTstar::getDelayCC, "0,1");
61 Planner::declareParam<bool>("tree_pruning", this, &TRRTstar::setTreePruning, &TRRTstar::getTreePruning, "0,1");
62 Planner::declareParam<double>("prune_threshold", this, &TRRTstar::setPruneThreshold, &TRRTstar::getPruneThreshold,
63 "0.:.01:1.");
64 Planner::declareParam<bool>("use_admissible_heuristic", this, &TRRTstar::setAdmissibleCostToCome,
65 &TRRTstar::getAdmissibleCostToCome, "0,1");
66 Planner::declareParam<unsigned int>("number_sampling_attempts", this, &TRRTstar::setNumSamplingAttempts,
67 &TRRTstar::getNumSamplingAttempts, "10:10:100000");
68
69 addPlannerProgressProperty("iterations INTEGER", [this] { return numIterationsProperty(); });
70 addPlannerProgressProperty("best cost REAL", [this] { return bestCostProperty(); });
71
72 // TRRTstar-specific
73 Planner::declareParam<double>("temp_change_factor", this, &TRRTstar::setTempChangeFactor,
74 &TRRTstar::getTempChangeFactor, "0.:.1:1.");
75 Planner::declareParam<double>("init_temperature", this, &TRRTstar::setInitTemperature,
76 &TRRTstar::getInitTemperature);
77 Planner::declareParam<double>("cost_threshold", this, &TRRTstar::setCostThreshold, &TRRTstar::getCostThreshold);
78}
79
80ompl::geometric::TRRTstar::~TRRTstar()
81{
82 freeMemory();
83}
84
86{
87 Planner::setup();
90 if (!si_->getStateSpace()->hasSymmetricDistance() || !si_->getStateSpace()->hasSymmetricInterpolate())
91 {
92 OMPL_WARN("%s requires a state space with symmetric distance and symmetric interpolation.", getName().c_str());
93 }
94
95 if (!nn_)
97 nn_->setDistanceFunction([this](const Motion *a, const Motion *b) { return distanceFunction(a, b); });
98
99 // Setup optimization objective
100 //
101 // If no optimization objective was specified, then default to
102 // optimizing mechanical work.
103 if (pdef_)
104 {
105 if (pdef_->hasOptimizationObjective())
106 opt_ = pdef_->getOptimizationObjective();
107 else
108 {
109 OMPL_INFORM("%s: No optimization objective specified. Defaulting to optimizing mechanical work "
110 "minimization.",
111 getName().c_str());
112 opt_ = std::make_shared<base::MechanicalWorkOptimizationObjective>(si_);
113
114 // Store the new objective in the problem def'n
115 pdef_->setOptimizationObjective(opt_);
116 }
117 }
118 else
119 {
120 OMPL_INFORM("%s: problem definition is not set, deferring setup completion...", getName().c_str());
121 setup_ = false;
122 }
123
124 // Get the measure of the entire space:
125 prunedMeasure_ = si_->getSpaceMeasure();
126
127 // Calculate some constants:
129
130 // Setup TRRT specific variables ---------------------------------------------------------
132 bestCost_ = worstCost_ = prunedCost_ = opt_->infiniteCost();
133}
134
136{
137 setup_ = false;
138 Planner::clear();
139 sampler_.reset();
140 freeMemory();
141 if (nn_)
142 nn_->clear();
143
144 bestGoalMotion_ = nullptr;
145 goalMotions_.clear();
146 startMotions_.clear();
147
148 iterations_ = 0;
149 bestCost_ = base::Cost(std::numeric_limits<double>::quiet_NaN());
150 prunedCost_ = base::Cost(std::numeric_limits<double>::quiet_NaN());
151 prunedMeasure_ = 0.0;
152
153 // Clear TRRT specific variables ---------------------------------------------------------
155}
156
158{
159 // Basic error checking
161
162 // Goal information
163 base::Goal *goal = pdef_->getGoal().get();
164 auto *goal_s = dynamic_cast<base::GoalSampleableRegion *>(goal);
165
166 if (goal_s == nullptr)
167 {
168 OMPL_ERROR("%s: Unknown type of goal", getName().c_str());
170 }
171
172 if (!goal_s->couldSample())
173 {
174 OMPL_ERROR("%s: Insufficient states in sampleable goal region", getName().c_str());
176 }
177
178 // Check if there are more starts
179 if (pis_.haveMoreStartStates() == true)
180 {
181 // There are, add them
182 while (const base::State *st = pis_.nextStart())
183 {
184 auto *motion = new Motion(si_);
185 si_->copyState(motion->state, st);
186 motion->cost = opt_->stateCost(motion->state);
187 nn_->add(motion);
188 startMotions_.push_back(motion);
189 }
190 }
191 // No else
192
193 if (nn_->size() == 0)
194 {
195 OMPL_ERROR("%s: There are no valid initial states!", getName().c_str());
197 }
198
199 // Allocate a sampler if necessary
200 if (!sampler_)
201 {
202 allocSampler();
203 }
204
205 OMPL_INFORM("%s: Started planning with %u states. Seeking a solution better than %.5f.", getName().c_str(),
206 nn_->size(), opt_->getCostThreshold().value());
207
208 if ((useTreePruning_) && !si_->getStateSpace()->isMetricSpace())
209 OMPL_WARN("%s: The state space (%s) is not metric and as a result the optimization objective may not satisfy "
210 "the triangle inequality. "
211 "You may need to disable pruning.",
212 getName().c_str(), si_->getStateSpace()->getName().c_str());
213
214 const base::ReportIntermediateSolutionFn intermediateSolutionCallback = pdef_->getIntermediateSolutionCallback();
215
216 Motion *approxGoalMotion = nullptr;
217 double approxDist = std::numeric_limits<double>::infinity();
218
219 auto *rmotion = new Motion(si_);
220 base::State *rstate = rmotion->state;
221 base::State *xstate = si_->allocState();
222
223 std::vector<Motion *> nbh;
224
225 std::vector<base::Cost> costs;
226 std::vector<base::Cost> incCosts;
227 std::vector<std::size_t> sortedCostIndices;
228
229 std::vector<int> valid;
230 unsigned int rewireTest = 0;
231 unsigned int statesGenerated = 0;
232
233 if (bestGoalMotion_)
234 OMPL_INFORM("%s: Starting planning with existing solution of cost %.5f", getName().c_str(), bestCost_.value());
235
236 if (useKNearest_)
237 OMPL_INFORM("%s: Initial k-nearest value of %u", getName().c_str(),
238 (unsigned int)std::ceil(k_rrt_ * log((double)(nn_->size() + 1u))));
239 else
241 "%s: Initial rewiring radius of %.2f", getName().c_str(),
242 std::min(maxDistance_, r_rrt_ * std::pow(log((double)(nn_->size() + 1u)) / ((double)(nn_->size() + 1u)),
243 1 / (double)(si_->getStateDimension()))));
244
245 // our functor for sorting nearest neighbors
246 CostIndexCompare compareFn(costs, *opt_);
247
248 while (ptc == false)
249 {
250 iterations_++;
251
252 // I.
253
254 // sample random state (with goal biasing)
255 // Goal samples are only sampled until maxSampleCount() goals are in the tree, to prohibit duplicate goal
256 // states.
257 if (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
262 // loop and return to try again
263 if (!sampleUniform(rstate))
264 continue; // try a new sample
265 }
266
267 // II.
268
269 // find closest state in the tree
270 Motion *nmotion = nn_->nearest(rmotion);
271
272 if (intermediateSolutionCallback && si_->equalStates(nmotion->state, rstate))
273 continue; // try a new sample
274
275 // III.
276
277 base::State *dstate = rstate;
278
279 // find state to add to the tree
280 double d = si_->distance(nmotion->state, rstate);
281 if (d > maxDistance_)
282 {
283 si_->getStateSpace()->interpolate(nmotion->state, rstate, maxDistance_ / d, xstate);
284 dstate = xstate;
285 }
286 else // Random state is close enough
287 dstate = rstate;
288
289 // IV.
290
291 // Check if the motion between the nearest state and the state to add is valid
292 if (si_->checkMotion(nmotion->state, dstate))
293 {
294 base::Cost incCost = opt_->motionCost(nmotion->state, dstate);
295
296 // Only add this motion to the tree if the transition test accepts it
297 if (transitionTest(incCost))
298 {
299 // V.
300
301 // create a motion
302 auto *motion = new Motion(si_);
303 si_->copyState(motion->state, dstate);
304 motion->parent = nmotion;
305 motion->incCost = incCost;
306 motion->cost = opt_->combineCosts(nmotion->cost, motion->incCost);
307
308 // Find nearby neighbors of the new motion
309 getNeighbors(motion, nbh);
310
311 rewireTest += nbh.size();
312 ++statesGenerated;
313
314 // cache for distance computations
315 //
316 // Our cost caches only increase in size, so they're only
317 // resized if they can't fit the current neighborhood
318 if (costs.size() < nbh.size())
319 {
320 costs.resize(nbh.size());
321 incCosts.resize(nbh.size());
322 sortedCostIndices.resize(nbh.size());
323 }
324
325 // cache for motion validity (only useful in a symmetric space)
326 //
327 // Our validity caches only increase in size, so they're
328 // only resized if they can't fit the current neighborhood
329 if (valid.size() < nbh.size())
330 valid.resize(nbh.size());
331 std::fill(valid.begin(), valid.begin() + nbh.size(), 0);
332
333 // Finding the nearest neighbor to connect to
334 // By default, neighborhood states are sorted by cost, and collision checking
335 // is performed in increasing order of cost
336 if (delayCC_)
337 {
338 // calculate all costs and distances
339 for (std::size_t i = 0; i < nbh.size(); ++i)
340 {
341 incCosts[i] = opt_->motionCost(nbh[i]->state, motion->state);
342 costs[i] = opt_->combineCosts(nbh[i]->cost, incCosts[i]);
343 }
344
345 // sort the nodes
346 //
347 // we're using index-value pairs so that we can get at
348 // original, unsorted indices
349 for (std::size_t i = 0; i < nbh.size(); ++i)
350 sortedCostIndices[i] = i;
351 std::sort(sortedCostIndices.begin(), sortedCostIndices.begin() + nbh.size(), compareFn);
352
353 // collision check until a valid motion is found
354 //
355 // ASYMMETRIC CASE: it's possible that none of these
356 // neighbors are valid. This is fine, because motion
357 // already has a connection to the tree through
358 // nmotion (with populated cost fields!).
359 for (std::vector<std::size_t>::const_iterator i = sortedCostIndices.begin();
360 i != sortedCostIndices.begin() + nbh.size(); ++i)
361 {
362 if (nbh[*i] == nmotion ||
363 ((!useKNearest_ || si_->distance(nbh[*i]->state, motion->state) < maxDistance_) &&
364 si_->checkMotion(nbh[*i]->state, motion->state)))
365 {
366 motion->incCost = incCosts[*i];
367 motion->cost = costs[*i];
368 motion->parent = nbh[*i];
369 valid[*i] = 1;
370 break;
371 }
372 else
373 valid[*i] = -1;
374 }
375 }
376 else // if not delayCC
377 {
378 motion->incCost = opt_->motionCost(nmotion->state, motion->state);
379 motion->cost = opt_->combineCosts(nmotion->cost, motion->incCost);
380 // find which one we connect the new state to
381 for (std::size_t i = 0; i < nbh.size(); ++i)
382 {
383 if (nbh[i] != nmotion)
384 {
385 incCosts[i] = opt_->motionCost(nbh[i]->state, motion->state);
386 costs[i] = opt_->combineCosts(nbh[i]->cost, incCosts[i]);
387 if (opt_->isCostBetterThan(costs[i], motion->cost))
388 {
389 if ((!useKNearest_ || si_->distance(nbh[i]->state, motion->state) < maxDistance_) &&
390 si_->checkMotion(nbh[i]->state, motion->state))
391 {
392 motion->incCost = incCosts[i];
393 motion->cost = costs[i];
394 motion->parent = nbh[i];
395 valid[i] = 1;
396 }
397 else
398 valid[i] = -1;
399 }
400 }
401 else
402 {
403 incCosts[i] = motion->incCost;
404 costs[i] = motion->cost;
405 valid[i] = 1;
406 }
407 }
408 }
409
410 // add motion to the tree
411 nn_->add(motion);
412 motion->parent->children.push_back(motion);
413
414 bool checkForSolution = false;
415 for (std::size_t i = 0; i < nbh.size(); ++i)
416 {
417 if (nbh[i] != motion->parent)
418 {
419 base::Cost nbhIncCost;
420 nbhIncCost = opt_->motionCost(motion->state, nbh[i]->state);
421 base::Cost nbhNewCost = opt_->combineCosts(motion->cost, nbhIncCost);
422 if (opt_->isCostBetterThan(nbhNewCost, nbh[i]->cost))
423 {
424 bool motionValid;
425 if (valid[i] == 0)
426 {
427 motionValid =
428 (!useKNearest_ || si_->distance(nbh[i]->state, motion->state) < maxDistance_) &&
429 si_->checkMotion(motion->state, nbh[i]->state);
430 }
431 else
432 {
433 motionValid = (valid[i] == 1);
434 }
435
436 if (motionValid)
437 {
438 // Remove this node from its parent list
439 removeFromParent(nbh[i]);
440
441 // Add this node to the new parent
442 nbh[i]->parent = motion;
443 nbh[i]->incCost = nbhIncCost;
444 nbh[i]->cost = nbhNewCost;
445 nbh[i]->parent->children.push_back(nbh[i]);
446
447 // Update the costs of the node's children
448 updateChildCosts(nbh[i]);
449
450 checkForSolution = true;
451 }
452 }
453 }
454 }
455
456 // Add the new motion to the goalMotion_ list, if it satisfies the goal
457 double distanceFromGoal;
458 if (goal->isSatisfied(motion->state, &distanceFromGoal))
459 {
460 motion->inGoal = true;
461 goalMotions_.push_back(motion);
462 checkForSolution = true;
463 }
464
465 // Checking for solution or iterative improvement
466 if (checkForSolution)
467 {
468 bool updatedSolution = false;
469 if (!bestGoalMotion_ && !goalMotions_.empty())
470 {
471 // We have found our first solution, store it as the best. We only add one
472 // vertex at a time, so there can only be one goal vertex at this moment.
475 updatedSolution = true;
476
477 OMPL_INFORM("%s: Found an initial solution with a cost of %.2f in %u iterations (%u "
478 "vertices in the graph)",
479 getName().c_str(), bestCost_.value(), iterations_, nn_->size());
480 }
481 else
482 {
483 // We already have a solution, iterate through the list of goal vertices
484 // and see if there's any improvement.
485 for (auto &goalMotion : goalMotions_)
486 {
487 // Is this goal motion better than the (current) best?
488 if (opt_->isCostBetterThan(goalMotion->cost, bestCost_))
489 {
490 bestGoalMotion_ = goalMotion;
492 updatedSolution = true;
493
494 // Check if it satisfies the optimization objective, if it does, break the for loop
495 if (opt_->isSatisfied(bestCost_))
496 {
497 break;
498 }
499 }
500 if (opt_->isCostBetterThan(worstCost_, goalMotion->cost))
501 {
502 worstCost_ = goalMotion->cost;
503 }
504 }
505 }
506
507 if (updatedSolution)
508 {
509 if (useTreePruning_)
510 {
512 }
513
514 if (intermediateSolutionCallback)
515 {
516 std::vector<const base::State *> spath;
517 Motion *intermediate_solution =
518 bestGoalMotion_->parent; // Do not include goal state to simplify code.
519
520 // Push back until we find the start, but not the start itself
521 while (intermediate_solution->parent != nullptr)
522 {
523 spath.push_back(intermediate_solution->state);
524 intermediate_solution = intermediate_solution->parent;
525 }
526
527 intermediateSolutionCallback(this, spath, bestCost_);
528 }
529 }
530 }
531
532 // Checking for approximate solution (closest state found to the goal)
533 if (goalMotions_.size() == 0 && distanceFromGoal < approxDist)
534 {
535 approxGoalMotion = motion;
536 approxDist = distanceFromGoal;
537 }
538 }
539 }
540
541 // terminate if a sufficient solution is found
542 if (bestGoalMotion_ && opt_->isSatisfied(bestCost_))
543 break;
544 // else: try a new sample
545 }
546
547 // Add our solution (if it exists)
548 Motion *newSolution = nullptr;
549 if (bestGoalMotion_)
550 {
551 // We have an exact solution
552 newSolution = bestGoalMotion_;
553 }
554 else if (approxGoalMotion)
555 {
556 // We don't have a solution, but we do have an approximate solution
557 newSolution = approxGoalMotion;
558 }
559 // No else, we have nothing
560
561 // Add what we found
562 if (newSolution)
563 {
564 ptc.terminate();
565 // construct the solution path
566 std::vector<Motion *> mpath;
567 Motion *iterMotion = newSolution;
568 while (iterMotion != nullptr)
569 {
570 mpath.push_back(iterMotion);
571 iterMotion = iterMotion->parent;
572 }
573
574 // set the solution path
575 auto path(std::make_shared<PathGeometric>(si_));
576 for (int i = mpath.size() - 1; i >= 0; --i)
577 path->append(mpath[i]->state);
578
579 // Add the solution path.
580 base::PlannerSolution psol(path);
581 psol.setPlannerName(getName());
582
583 // If we don't have a goal motion, the solution is approximate
584 if (!bestGoalMotion_)
585 psol.setApproximate(approxDist);
586
587 // Does the solution satisfy the optimization objective?
588 psol.setOptimized(opt_, newSolution->cost, opt_->isSatisfied(bestCost_));
589 pdef_->addSolutionPath(psol);
590 }
591 // No else, we have nothing
592
593 si_->freeState(xstate);
594 if (rmotion->state)
595 si_->freeState(rmotion->state);
596 delete rmotion;
597
598 OMPL_INFORM("%s: Created %u new states. Checked %u rewire options. %u goal states in tree. Final solution cost "
599 "%.3f",
600 getName().c_str(), statesGenerated, rewireTest, goalMotions_.size(), bestCost_.value());
601
602 // We've added a solution if newSolution == true, and it is an approximate solution if bestGoalMotion_ == false
603 return {newSolution != nullptr, bestGoalMotion_ == nullptr};
604}
605
606void ompl::geometric::TRRTstar::getNeighbors(Motion *motion, std::vector<Motion *> &nbh) const
607{
608 auto cardDbl = static_cast<double>(nn_->size() + 1u);
609 if (useKNearest_)
610 {
611 //- k-nearest RRT*
612 unsigned int k = std::ceil(k_rrt_ * log(cardDbl));
613 nn_->nearestK(motion, k, nbh);
614 }
615 else
616 {
617 double r = std::min(
618 maxDistance_, r_rrt_ * std::pow(log(cardDbl) / cardDbl, 1 / static_cast<double>(si_->getStateDimension())));
619 nn_->nearestR(motion, r, nbh);
620 }
621}
622
624{
625 for (auto it = m->parent->children.begin(); it != m->parent->children.end(); ++it)
626 {
627 if (*it == m)
628 {
629 m->parent->children.erase(it);
630 break;
631 }
632 }
633}
634
636{
637 for (std::size_t i = 0; i < m->children.size(); ++i)
638 {
639 m->children[i]->cost = opt_->combineCosts(m->cost, m->children[i]->incCost);
641 }
642}
643
645{
646 if (nn_)
647 {
648 std::vector<Motion *> motions;
649 nn_->list(motions);
650 for (auto &motion : motions)
651 {
652 if (motion->state)
653 si_->freeState(motion->state);
654 delete motion;
655 }
656 }
657}
658
660{
661 Planner::getPlannerData(data);
662
663 std::vector<Motion *> motions;
664 if (nn_)
665 nn_->list(motions);
666
667 if (bestGoalMotion_)
669
670 for (auto &motion : motions)
671 {
672 if (motion->parent == nullptr)
673 data.addStartVertex(base::PlannerDataVertex(motion->state));
674 else
675 data.addEdge(base::PlannerDataVertex(motion->parent->state), base::PlannerDataVertex(motion->state));
676 }
677}
678
680{
681 // Variable
682 // The percent improvement (expressed as a [0,1] fraction) in cost
683 double fracBetter;
684 // The number pruned
685 int numPruned = 0;
686 if (opt_->isFinite(prunedCost_))
687 {
688 fracBetter = std::abs((pruneTreeCost.value() - prunedCost_.value()) / prunedCost_.value());
689 }
690 else
691 {
692 fracBetter = 1.0;
693 }
694
695 if (fracBetter > pruneThreshold_)
696 {
697 // We are only pruning motions if they, AND all descendents, have a estimated cost greater than pruneTreeCost
698 // The easiest way to do this is to find leaves that should be pruned and ascend up their ancestry until a
699 // motion is found that is kept.
700 // To avoid making an intermediate copy of the NN structure, we process the tree by descending down from the
701 // start(s).
702 // In the first pass, all Motions with a cost below pruneTreeCost, or Motion's with children with costs below
703 // pruneTreeCost are added to the replacement NN structure,
704 // while all other Motions are stored as either a 'leaf' or 'chain' Motion. After all the leaves are
705 // disconnected and deleted, we check
706 // if any of the the chain Motions are now leaves, and repeat that process until done.
707 // This avoids (1) copying the NN structure into an intermediate variable and (2) the use of the expensive
708 // NN::remove() method.
709
710 // Variable
711 // The queue of Motions to process:
712 std::queue<Motion *, std::deque<Motion *>> motionQueue;
713 // The list of leaves to prune
714 std::queue<Motion *, std::deque<Motion *>> leavesToPrune;
715 // The list of chain vertices to recheck after pruning
716 std::list<Motion *> chainsToRecheck;
717
718 // Clear the NN structure:
719 nn_->clear();
720
721 // Put all the starts into the NN structure and their children into the queue:
722 // We do this so that start states are never pruned.
723 for (auto &startMotion : startMotions_)
724 {
725 // Add to the NN
726 nn_->add(startMotion);
727
728 // Add their children to the queue:
729 addChildrenToList(&motionQueue, startMotion);
730 }
731
732 while (motionQueue.empty() == false)
733 {
734 // Test, can the current motion ever provide a better solution?
735 if (keepCondition(motionQueue.front(), pruneTreeCost))
736 {
737 // Yes it can, so it definitely won't be pruned
738 // Add it back into the NN structure
739 nn_->add(motionQueue.front());
740
741 // Add it's children to the queue
742 addChildrenToList(&motionQueue, motionQueue.front());
743 }
744 else
745 {
746 // No it can't, but does it have children?
747 if (motionQueue.front()->children.empty() == false)
748 {
749 // Yes it does.
750 // We can minimize the number of intermediate chain motions if we check their children
751 // If any of them won't be pruned, then this motion won't either. This intuitively seems
752 // like a nice balance between following the descendents forever.
753
754 // Variable
755 // Whether the children are definitely to be kept.
756 bool keepAChild = false;
757
758 // Find if any child is definitely not being pruned.
759 for (unsigned int i = 0u; keepAChild == false && i < motionQueue.front()->children.size(); ++i)
760 {
761 // Test if the child can ever provide a better solution
762 keepAChild = keepCondition(motionQueue.front()->children.at(i), pruneTreeCost);
763 }
764
765 // Are we *definitely* keeping any of the children?
766 if (keepAChild)
767 {
768 // Yes, we are, so we are not pruning this motion
769 // Add it back into the NN structure.
770 nn_->add(motionQueue.front());
771 }
772 else
773 {
774 // No, we aren't. This doesn't mean we won't though
775 // Move this Motion to the temporary list
776 chainsToRecheck.push_back(motionQueue.front());
777 }
778
779 // Either way. add it's children to the queue
780 addChildrenToList(&motionQueue, motionQueue.front());
781 }
782 else
783 {
784 // No, so we will be pruning this motion:
785 leavesToPrune.push(motionQueue.front());
786 }
787 }
788
789 // Pop the iterator, std::list::erase returns the next iterator
790 motionQueue.pop();
791 }
792
793 // We now have a list of Motions to definitely remove, and a list of Motions to recheck
794 // Iteratively check the two lists until there is nothing to to remove
795 while (leavesToPrune.empty() == false)
796 {
797 // First empty the current leaves-to-prune
798 while (leavesToPrune.empty() == false)
799 {
800 // If this leaf is a goal, remove it from the goal set
801 if (leavesToPrune.front()->inGoal == true)
802 {
803 // Warn if pruning the _best_ goal
804 if (leavesToPrune.front() == bestGoalMotion_)
805 {
806 OMPL_ERROR("%s: Pruning the best goal.", getName().c_str());
807 }
808 // Remove it
809 goalMotions_.erase(std::remove(goalMotions_.begin(), goalMotions_.end(), leavesToPrune.front()),
810 goalMotions_.end());
811 }
812
813 // Remove the leaf from its parent
814 removeFromParent(leavesToPrune.front());
815
816 // Erase the actual motion
817 // First free the state
818 si_->freeState(leavesToPrune.front()->state);
819
820 // then delete the pointer
821 delete leavesToPrune.front();
822
823 // And finally remove it from the list, erase returns the next iterator
824 leavesToPrune.pop();
825
826 // Update our counter
827 ++numPruned;
828 }
829
830 // Now, we need to go through the list of chain vertices and see if any are now leaves
831 auto mIter = chainsToRecheck.begin();
832 while (mIter != chainsToRecheck.end())
833 {
834 // Is the Motion a leaf?
835 if ((*mIter)->children.empty() == true)
836 {
837 // It is, add to the removal queue
838 leavesToPrune.push(*mIter);
839
840 // Remove from this queue, getting the next
841 mIter = chainsToRecheck.erase(mIter);
842 }
843 else
844 {
845 // Is isn't, skip to the next
846 ++mIter;
847 }
848 }
849 }
850
851 // Now finally add back any vertices left in chainsToReheck.
852 // These are chain vertices that have descendents that we want to keep
853 for (const auto &r : chainsToRecheck)
854 // Add the motion back to the NN struct:
855 nn_->add(r);
856
857 // All done pruning.
858 // Update the cost at which we've pruned:
859 prunedCost_ = pruneTreeCost;
860 }
861
862 return numPruned;
863}
864
865void ompl::geometric::TRRTstar::addChildrenToList(std::queue<Motion *, std::deque<Motion *>> *motionList,
866 Motion *motion)
867{
868 for (auto &child : motion->children)
869 {
870 motionList->push(child);
871 }
872}
873
874bool ompl::geometric::TRRTstar::keepCondition(const Motion *motion, const base::Cost &threshold) const
875{
876 // We keep if the cost-to-come-heuristic of motion is <= threshold, by checking
877 // if !(threshold < heuristic), as if b is not better than a, then a is better than, or equal to, b
878 if (bestGoalMotion_ && motion == bestGoalMotion_)
879 {
880 // If the threshold is the theoretical minimum, the bestGoalMotion_ will sometimes fail the test due to floating
881 // point precision. Avoid that.
882 return true;
883 }
884
885 return !opt_->isCostBetterThan(threshold, solutionHeuristic(motion));
886}
887
889{
890 base::Cost costToCome;
892 {
893 // Start with infinite cost
894 costToCome = opt_->infiniteCost();
895
896 // Find the min from each start
897 for (auto &startMotion : startMotions_)
898 {
899 costToCome = opt_->betterCost(costToCome, opt_->motionCost(startMotion->state,
900 motion->state)); // lower-bounding cost from the
901 // start to the state
902 }
903 }
904 else
905 {
906 costToCome = motion->cost; // current cost from the state to the goal
907 }
908
909 const base::Cost costToGo =
910 opt_->costToGo(motion->state, pdef_->getGoal().get()); // lower-bounding cost from the state to the goal
911 return opt_->combineCosts(costToCome, costToGo); // add the two costs
912}
913
915{
916 if (static_cast<bool>(opt_) == true)
917 {
918 if (opt_->hasCostToGoHeuristic() == false)
919 {
920 OMPL_INFORM("%s: No cost-to-go heuristic set. Informed techniques will not work well.", getName().c_str());
921 }
922 }
923
924 // Store
925 useTreePruning_ = prune;
926}
927
929{
930 // We are using a regular sampler
931 sampler_ = si_->allocStateSampler();
932}
933
935{
936 // Simply return a state from the regular sampler
937 sampler_->sampleUniform(statePtr);
938
939 // Always true
940 return true;
941}
942
944{
945 const auto dimDbl = static_cast<double>(si_->getStateDimension());
946
947 // k_rrt > 2^(d + 1) * e * (1 + 1 / d). K-nearest RRT*
948 k_rrt_ = rewireFactor_ * (std::pow(2, dimDbl + 1) * boost::math::constants::e<double>() * (1.0 + 1.0 / dimDbl));
949
950 // r_rrt > (2*(1+1/d))^(1/d)*(measure/ballvolume)^(1/d)
951 // If we're not using the informed measure, prunedMeasure_ will be set to si_->getSpaceMeasure();
953 std::pow(2 * (1.0 + 1.0 / dimDbl) * (prunedMeasure_ / unitNBallMeasure(si_->getStateDimension())),
954 1.0 / dimDbl);
955}
956
958{
959 // Disallow any cost that is not better than the cost threshold
960 if (!opt_->isCostBetterThan(motionCost, costThreshold_))
961 return false;
962
963 // Always accept if the cost is near or below zero
964 if (motionCost.value() < 1e-4)
965 return true;
966
967 double dCost = motionCost.value();
968 double transitionProbability = exp(-dCost / temp_);
969 if (transitionProbability > 0.5)
970 {
971 double costRange = worstCost_.value() - bestCost_.value();
972 if (fabs(costRange) > 1e-4) // Do not divide by zero
973 // Successful transition test. Decrease the temperature slightly
974 temp_ /= exp(dCost / (0.1 * costRange));
975
976 return true;
977 }
978
979 // The transition failed. Increase the temperature (slightly)
981 return false;
982}
Definition of a cost value. Can represent the cost of a motion or the cost of a state.
Definition Cost.h:48
double value() const
The value of the cost.
Definition Cost.h:56
Abstract definition of a goal region that can be sampled.
Abstract definition of goals.
Definition Goal.h:63
virtual bool isSatisfied(const State *st) const =0
Return true if the state satisfies the goal constraints.
Base class for a vertex in the PlannerData structure. All derived classes must implement the clone an...
Definition PlannerData.h:59
Object containing planner generated vertex and edge data. It is assumed that all vertices are unique,...
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...
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...
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...
Encapsulate a termination condition for a motion planner. Planners will call operator() to decide whe...
void terminate() const
Notify that the condition for termination should become true, regardless of what eval() returns....
PlannerInputStates pis_
Utility class to extract valid input states.
Definition Planner.h:407
ProblemDefinitionPtr pdef_
The user set problem definition.
Definition Planner.h:404
const std::string & getName() const
Get the name of the planner.
Definition Planner.cpp:56
SpaceInformationPtr si_
The space information for which planning is done.
Definition Planner.h:401
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:106
bool setup_
Flag indicating whether setup() has been called.
Definition Planner.h:424
Definition of an abstract state.
Definition State.h:50
Representation of a motion.
Definition TRRTstar.h:313
base::State * state
The state contained by the motion.
Definition TRRTstar.h:324
base::Cost incCost
The incremental cost of this motion's parent to this motion (this is stored to save distance computat...
Definition TRRTstar.h:337
base::Cost cost
The cost up to this motion.
Definition TRRTstar.h:333
Motion * parent
The parent motion in the exploration tree.
Definition TRRTstar.h:327
std::vector< Motion * > children
The set of motions descending from the current motion.
Definition TRRTstar.h:340
base::Cost worstCost_
The least desirable (e.g., maximum) cost value in the search tree.
Definition TRRTstar.h:496
bool useKNearest_
Option to use k-nearest search for rewiring.
Definition TRRTstar.h:429
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 TRRTstar.cpp:888
base::Cost bestCost_
Best cost found so far by algorithm.
Definition TRRTstar.h:472
void getPlannerData(base::PlannerData &data) const override
Get information about the current run of the motion planner. Repeated calls to this function will upd...
Definition TRRTstar.cpp:659
std::shared_ptr< NearestNeighbors< Motion * > > nn_
A nearest-neighbors datastructure containing the tree of motions.
Definition TRRTstar.h:416
double r_rrt_
A constant for r-disc rewiring calculations.
Definition TRRTstar.h:439
base::OptimizationObjectivePtr opt_
Objective we're optimizing.
Definition TRRTstar.h:445
void calculateRewiringLowerBounds()
Calculate the k_RRG* and r_RRG* terms.
Definition TRRTstar.cpp:943
bool delayCC_
Option to delay and reduce collision checking within iterations.
Definition TRRTstar.h:442
bool useTreePruning_
The status of the tree pruning option.
Definition TRRTstar.h:454
double pruneThreshold_
The tree is pruned when the change in solution cost is greater than this fraction.
Definition TRRTstar.h:457
bool sampleUniform(base::State *statePtr)
Generate a sample.
Definition TRRTstar.cpp:934
std::vector< Motion * > startMotions_
Stores the start states as Motions.
Definition TRRTstar.h:469
void allocSampler()
Create the samplers.
Definition TRRTstar.cpp:928
double goalBias_
The fraction of time the goal is picked as the state to expand towards (if such a state is available)...
Definition TRRTstar.h:420
std::vector< Motion * > goalMotions_
A list of states in the tree that satisfy the goal condition.
Definition TRRTstar.h:451
bool useAdmissibleCostToCome_
The admissibility of the new-state rejection heuristic.
Definition TRRTstar.h:460
RNG rng_
The random number generator.
Definition TRRTstar.h:426
void setup() override
Perform extra configuration steps, if needed. This call will also issue a call to ompl::base::SpaceIn...
Definition TRRTstar.cpp:85
unsigned int iterations_
Number of iterations the algorithm performed.
Definition TRRTstar.h:482
void removeFromParent(Motion *m)
Removes the given motion from the parent's child list.
Definition TRRTstar.cpp:623
double maxDistance_
The maximum length of a motion to be added to a tree.
Definition TRRTstar.h:423
double rewireFactor_
The rewiring factor, s, so that r_rrt = s \times r_rrt* > r_rrt* (or k_rrt = s \times k_rrt* > k_rrt*...
Definition TRRTstar.h:433
base::StateSamplerPtr sampler_
State sampler.
Definition TRRTstar.h:410
void freeMemory()
Free the memory allocated by this planner.
Definition TRRTstar.cpp:644
double prunedMeasure_
The measure of the problem when we pruned it (if this isn't in use, it will be set to si_->getSpaceMe...
Definition TRRTstar.h:479
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 TRRTstar.cpp:606
Motion * bestGoalMotion_
The best goal motion.
Definition TRRTstar.h:448
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 TRRTstar.cpp:874
double k_rrt_
A constant for k-nearest rewiring calculations.
Definition TRRTstar.h:436
void clear() override
Clear all internal datastructures. Planner settings are not affected. Subsequent calls to solve() wil...
Definition TRRTstar.cpp:135
void addChildrenToList(std::queue< Motion *, std::deque< Motion * > > *motionList, Motion *motion)
Add the children of a vertex to the given list.
Definition TRRTstar.cpp:865
int pruneTree(const base::Cost &pruneTreeCost)
Prunes all those states which estimated total cost is higher than pruneTreeCost. Returns the number o...
Definition TRRTstar.cpp:679
double temp_
Temperature parameter used to control the difficulty level of transition tests. Low temperatures limi...
Definition TRRTstar.h:493
base::Cost costThreshold_
All motion costs must be better than this cost (default is infinity).
Definition TRRTstar.h:499
double distanceFunction(const Motion *a, const Motion *b) const
Compute distance between motions (actually distance between contained states).
Definition TRRTstar.h:368
void updateChildCosts(Motion *m)
Updates the cost of the children of this node if the cost up to this node has changed.
Definition TRRTstar.cpp:635
void setTreePruning(bool prune)
Controls whether the tree is pruned during the search. This pruning removes a vertex if and only if i...
Definition TRRTstar.cpp:914
base::Cost prunedCost_
The cost at which the graph was last pruned.
Definition TRRTstar.h:475
base::PlannerStatus solve(const base::PlannerTerminationCondition &ptc) override
Function that can solve the motion planning problem. This function can be called multiple times on th...
Definition TRRTstar.cpp:157
double initTemperature_
The initial value of temp_.
Definition TRRTstar.h:506
double tempChangeFactor_
The value of the expression exp^T_rate. The temperature is increased by this factor whenever the tran...
Definition TRRTstar.h:503
bool transitionTest(const base::Cost &motionCost)
Filter irrelevant configuration regarding the search of low-cost paths before inserting into tree.
Definition TRRTstar.cpp:957
This class contains methods that automatically configure various parameters for motion planning....
Definition SelfConfig.h:59
static NearestNeighbors< _T > * getDefaultNearestNeighbors(const base::Planner *planner)
Select a default nearest neighbor datastructure for the given space.
Definition SelfConfig.h:105
void configurePlannerRange(double &range)
Compute what a good length for motion segments is.
#define OMPL_INFORM(fmt,...)
Log a formatted information string.
Definition Console.h:68
#define OMPL_ERROR(fmt,...)
Log a formatted error string.
Definition Console.h:64
#define OMPL_WARN(fmt,...)
Log a formatted warning string.
Definition Console.h:66
This namespace contains sampling based planning routines shared by both planning under geometric cons...
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...
double unitNBallMeasure(unsigned int N)
The Lebesgue measure (i.e., "volume") of an n-dimensional ball with a unit radius.
Representation of a solution to a planning problem.
void setApproximate(double difference)
Specify that the solution is approximate and set the difference to the goal.
void setPlannerName(const std::string &name)
Set the name of the planner used to compute this solution.
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...
A class to store the exit status of Planner::solve().
@ INVALID_START
Invalid start state or no start state specified.
@ INVALID_GOAL
Invalid goal state.
@ UNRECOGNIZED_GOAL_TYPE
The goal is of a type that a planner does not recognize.