Loading...
Searching...
No Matches
AITstar.cpp
1/*********************************************************************
2 * Software License Agreement (BSD License)
3 *
4 * Copyright (c) 2019, University of Oxford
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 names of the copyright holders 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: Marlin Strub
36
37#include "ompl/geometric/planners/informedtrees/AITstar.h"
38
39#include <algorithm>
40#include <cmath>
41#include <string>
42
43#include <boost/range/adaptor/reversed.hpp>
44
45#include "ompl/base/objectives/PathLengthOptimizationObjective.h"
46#include "ompl/util/Console.h"
47
48using namespace std::string_literals;
49using namespace ompl::geometric::aitstar;
50
51namespace ompl
52{
53 namespace geometric
54 {
55 AITstar::AITstar(const ompl::base::SpaceInformationPtr &spaceInformation)
56 : ompl::base::Planner(spaceInformation, "AITstar")
57 , solutionCost_()
58 , graph_(solutionCost_)
59 , forwardQueue_([this](const auto &lhs, const auto &rhs) { return isEdgeBetter(lhs, rhs); })
60 , reverseQueue_([this](const auto &lhs, const auto &rhs) { return isVertexBetter(lhs, rhs); })
61 {
62 // Specify AIT*'s planner specs.
63 specs_.recognizedGoal = base::GOAL_SAMPLEABLE_REGION;
64 specs_.multithreaded = false;
65 specs_.approximateSolutions = true;
66 specs_.optimizingPaths = true;
67 specs_.directed = true;
68 specs_.provingSolutionNonExistence = false;
69 specs_.canReportIntermediateSolutions = true;
70
71 // Register the setting callbacks.
72 declareParam<bool>("use_k_nearest", this, &AITstar::setUseKNearest, &AITstar::getUseKNearest, "0,1");
73 declareParam<double>("rewire_factor", this, &AITstar::setRewireFactor, &AITstar::getRewireFactor,
74 "1.0:0.01:3.0");
75 declareParam<std::size_t>("samples_per_batch", this, &AITstar::setBatchSize, &AITstar::getBatchSize,
76 "1:1:1000");
77 declareParam<bool>("use_graph_pruning", this, &AITstar::enablePruning, &AITstar::isPruningEnabled, "0,1");
78 declareParam<bool>("find_approximate_solutions", this, &AITstar::trackApproximateSolutions,
80 declareParam<std::size_t>("set_max_num_goals", this, &AITstar::setMaxNumberOfGoals,
81 &AITstar::getMaxNumberOfGoals, "1:1:1000");
82
83 // Register the progress properties.
84 addPlannerProgressProperty("iterations INTEGER", [this]() { return std::to_string(numIterations_); });
85 addPlannerProgressProperty("best cost DOUBLE", [this]() { return std::to_string(solutionCost_.value()); });
86 addPlannerProgressProperty("state collision checks INTEGER",
87 [this]() { return std::to_string(graph_.getNumberOfStateCollisionChecks()); });
88 addPlannerProgressProperty("edge collision checks INTEGER",
89 [this]() { return std::to_string(numEdgeCollisionChecks_); });
90 addPlannerProgressProperty("nearest neighbour calls INTEGER",
91 [this]() { return std::to_string(graph_.getNumberOfNearestNeighborCalls()); });
92 }
93
95 {
96 // Call the base-class setup.
97 Planner::setup();
98
99 // Check that a problem definition has been set.
100 if (static_cast<bool>(Planner::pdef_))
101 {
102 // Default to path length optimization objective if none has been specified.
103 if (!pdef_->hasOptimizationObjective())
104 {
105 OMPL_WARN("%s: No optimization objective has been specified. Defaulting to path length.",
106 Planner::getName().c_str());
107 Planner::pdef_->setOptimizationObjective(
108 std::make_shared<ompl::base::PathLengthOptimizationObjective>(Planner::si_));
109 }
110
111 if (static_cast<bool>(pdef_->getGoal()))
112 {
113 // If we were given a goal, make sure its of appropriate type.
114 if (!(pdef_->getGoal()->hasType(ompl::base::GOAL_SAMPLEABLE_REGION)))
115 {
116 OMPL_ERROR("AIT* is currently only implemented for goals that can be cast to "
117 "ompl::base::GOAL_SAMPLEABLE_GOAL_REGION.");
118 setup_ = false;
119 return;
120 }
121 }
122
123 // Pull the optimization objective through the problem definition.
124 objective_ = pdef_->getOptimizationObjective();
125
126 // Initialize the solution cost to be infinite.
127 solutionCost_ = objective_->infiniteCost();
128 approximateSolutionCost_ = objective_->infiniteCost();
129 approximateSolutionCostToGoal_ = objective_->infiniteCost();
130
131 // Pull the motion validator through the space information.
132 motionValidator_ = si_->getMotionValidator();
133
134 // Setup a graph.
135 graph_.setup(si_, pdef_, &pis_);
136 }
137 else
138 {
139 // AIT* can't be setup without a problem definition.
140 setup_ = false;
141 OMPL_WARN("AIT*: Unable to setup without a problem definition.");
142 }
143 }
144
146 {
147 // Call the base planners validity check. This checks if the
148 // planner is setup if not then it calls setup().
150
151 // Ensure the planner is setup.
152 if (!setup_)
153 {
154 OMPL_ERROR("%s: The planner is not setup.", name_.c_str());
156 }
157
158 // Ensure the space is setup.
159 if (!si_->isSetup())
160 {
161 OMPL_ERROR("%s: The space information is not setup.", name_.c_str());
163 }
164
166 }
167
170 {
171 // If the graph currently does not have a start state, try to get one.
172 if (!graph_.hasAStartState())
173 {
174 graph_.updateStartAndGoalStates(terminationCondition, &pis_);
175
176 // If we could not get a start state, then there's nothing to solve.
177 if (!graph_.hasAStartState())
178 {
179 OMPL_WARN("%s: No solution can be found as no start states are available", name_.c_str());
181 }
182 }
183
184 // If the graph currently does not have a goal state, we wait until we get one.
185 if (!graph_.hasAGoalState())
186 {
187 graph_.updateStartAndGoalStates(terminationCondition, &pis_);
188
189 // If the graph still doesn't have a goal after waiting, then there's nothing to solve.
190 if (!graph_.hasAGoalState())
191 {
192 OMPL_WARN("%s: No solution can be found as no goal states are available", name_.c_str());
194 }
195 }
196
197 // Would it be worth implementing a 'setup' or 'checked' status type?
199 }
200
202 {
203 graph_.clear();
204 forwardQueue_.clear();
205 reverseQueue_.clear();
206 if (objective_)
207 {
208 solutionCost_ = objective_->infiniteCost();
209 approximateSolutionCost_ = objective_->infiniteCost();
210 approximateSolutionCostToGoal_ = objective_->infiniteCost();
211 }
212 numIterations_ = 0u;
213 numInconsistentOrUnconnectedTargets_ = 0u;
214 Planner::clear();
215 setup_ = false;
216 }
217
219 {
220 // Ensure that the planner and state space are setup before solving.
221 auto status = ensureSetup();
222
223 // Return early if the planner or state space are not setup.
225 {
226 return status;
227 }
228
229 // Ensure that the problem has start and goal states before solving.
230 status = ensureStartAndGoalStates(terminationCondition);
231
232 // Return early if the problem cannot be solved.
235 {
236 return status;
237 }
238
239 OMPL_INFORM("%s: Solving the given planning problem. The current best solution cost is %.4f", name_.c_str(),
240 solutionCost_.value());
241
242 // Iterate to solve the problem.
243 while (!terminationCondition && !objective_->isSatisfied(solutionCost_))
244 {
245 iterate(terminationCondition);
246 }
247
248 // Someone might call ProblemDefinition::clearSolutionPaths() between invocations of Planner::sovle(), in
249 // which case previously found solutions are not registered with the problem definition anymore.
250 status = updateSolution();
251
252 // Let the caller know the status.
253 informAboutPlannerStatus(status);
254 return status;
255 }
256
258 {
259 return solutionCost_;
260 }
261
263 {
264 // base::PlannerDataVertex takes a raw pointer to a state. I want to guarantee, that the state lives as
265 // long as the program lives.
266 static std::set<std::shared_ptr<Vertex>,
267 std::function<bool(const std::shared_ptr<Vertex> &, const std::shared_ptr<Vertex> &)>>
268 liveStates([](const auto &lhs, const auto &rhs) { return lhs->getId() < rhs->getId(); });
269
270 // Fill the planner progress properties.
271 Planner::getPlannerData(data);
272
273 // Get the vertices.
274 auto vertices = graph_.getVertices();
275
276 // Add the vertices and edges.
277 for (const auto &vertex : vertices)
278 {
279 // Add the vertex to the live states.
280 liveStates.insert(vertex);
281
282 // Add the vertex as the right kind of vertex.
283 if (graph_.isStart(vertex))
284 {
285 data.addStartVertex(ompl::base::PlannerDataVertex(vertex->getState(), vertex->getId()));
286 }
287 else if (graph_.isGoal(vertex))
288 {
289 data.addGoalVertex(ompl::base::PlannerDataVertex(vertex->getState(), vertex->getId()));
290 }
291 else
292 {
293 data.addVertex(ompl::base::PlannerDataVertex(vertex->getState(), vertex->getId()));
294 }
295
296 // If it has a parent, add the corresponding edge.
297 if (vertex->hasForwardParent())
298 {
299 data.addEdge(ompl::base::PlannerDataVertex(vertex->getState(), vertex->getId()),
300 ompl::base::PlannerDataVertex(vertex->getForwardParent()->getState(),
301 vertex->getForwardParent()->getId()));
302 }
303 }
304 }
305
306 void AITstar::setBatchSize(std::size_t batchSize)
307 {
308 batchSize_ = batchSize;
309 }
310
311 std::size_t AITstar::getBatchSize() const
312 {
313 return batchSize_;
314 }
315
316 void AITstar::setRewireFactor(double rewireFactor)
317 {
318 graph_.setRewireFactor(rewireFactor);
319 }
320
322 {
323 return graph_.getRewireFactor();
324 }
325
327 {
328 trackApproximateSolutions_ = track;
329 if (!trackApproximateSolutions_)
330 {
331 if (static_cast<bool>(objective_))
332 {
333 approximateSolutionCost_ = objective_->infiniteCost();
334 approximateSolutionCostToGoal_ = objective_->infiniteCost();
335 }
336 }
337 }
338
340 {
341 return trackApproximateSolutions_;
342 }
343
344 void AITstar::enablePruning(bool prune)
345 {
346 isPruningEnabled_ = prune;
347 }
348
350 {
351 return isPruningEnabled_;
352 }
353
354 void AITstar::setUseKNearest(bool useKNearest)
355 {
356 graph_.setUseKNearest(useKNearest);
357 }
358
360 {
361 return graph_.getUseKNearest();
362 }
363
364 void AITstar::setMaxNumberOfGoals(unsigned int numberOfGoals)
365 {
366 graph_.setMaxNumberOfGoals(numberOfGoals);
367 }
368
369 unsigned int AITstar::getMaxNumberOfGoals() const
370 {
371 return graph_.getMaxNumberOfGoals();
372 }
373
374 void AITstar::rebuildForwardQueue()
375 {
376 // Get all edges from the queue.
377 std::vector<Edge> edges;
378 forwardQueue_.getContent(edges);
379
380 // Rebuilding the queue invalidates the incoming and outgoing lookup.
381 for (const auto &edge : edges)
382 {
383 edge.getChild()->resetForwardQueueIncomingLookup();
384 edge.getParent()->resetForwardQueueOutgoingLookup();
385 }
386
387 // Clear the queue.
388 forwardQueue_.clear();
389 numInconsistentOrUnconnectedTargets_ = 0u;
390
391 // Insert all edges into the queue if they connect vertices that have been processed, otherwise store
392 // them in the cache of edges that are to be inserted.
393 for (auto &edge : edges)
394 {
395 insertOrUpdateInForwardQueue(
396 Edge(edge.getParent(), edge.getChild(), computeSortKey(edge.getParent(), edge.getChild())));
397 }
398 }
399
400 void AITstar::clearForwardQueue()
401 {
402 std::vector<Edge> forwardQueue;
403 forwardQueue_.getContent(forwardQueue);
404 for (const auto &element : forwardQueue)
405 {
406 element.getChild()->resetForwardQueueIncomingLookup();
407 element.getParent()->resetForwardQueueOutgoingLookup();
408 }
409 forwardQueue_.clear();
410 numInconsistentOrUnconnectedTargets_ = 0u;
411 }
412
413 void AITstar::rebuildReverseQueue()
414 {
415 // Rebuilding the reverse queue invalidates the reverse queue pointers.
416 std::vector<aitstar::KeyVertexPair> content;
417 reverseQueue_.getContent(content);
418 for (auto &element : content)
419 {
420 element.second->resetReverseQueuePointer();
421 }
422 reverseQueue_.clear();
423
424 for (auto &vertex : content)
425 {
426 // Compute the sort key for the vertex queue.
427 std::pair<std::array<ompl::base::Cost, 2u>, std::shared_ptr<Vertex>> element(
428 computeSortKey(vertex.second), vertex.second);
429 auto reverseQueuePointer = reverseQueue_.insert(element);
430 element.second->setReverseQueuePointer(reverseQueuePointer);
431 }
432 }
433
434 void AITstar::clearReverseQueue()
435 {
436 std::vector<aitstar::KeyVertexPair> reverseQueue;
437 reverseQueue_.getContent(reverseQueue);
438 for (const auto &element : reverseQueue)
439 {
440 element.second->resetReverseQueuePointer();
441 }
442 reverseQueue_.clear();
443 }
444
445 void AITstar::informAboutNewSolution() const
446 {
447 OMPL_INFORM("%s (%u iterations): Found a new exact solution of cost %.4f. Sampled a total of %u states, %u "
448 "of which were valid samples (%.1f \%). Processed %u edges, %u of which were collision checked "
449 "(%.1f \%). The forward search tree has %u vertices, %u of which are start states. The reverse "
450 "search tree has %u vertices, %u of which are goal states.",
451 name_.c_str(), numIterations_, solutionCost_.value(), graph_.getNumberOfSampledStates(),
452 graph_.getNumberOfValidSamples(),
453 graph_.getNumberOfSampledStates() == 0u ?
454 0.0 :
455 100.0 * (static_cast<double>(graph_.getNumberOfValidSamples()) /
456 static_cast<double>(graph_.getNumberOfSampledStates())),
457 numProcessedEdges_, numEdgeCollisionChecks_,
458 numProcessedEdges_ == 0u ? 0.0 :
459 100.0 * (static_cast<float>(numEdgeCollisionChecks_) /
460 static_cast<float>(numProcessedEdges_)),
461 countNumVerticesInForwardTree(), graph_.getStartVertices().size(),
462 countNumVerticesInReverseTree(), graph_.getGoalVertices().size());
463 }
464
465 void AITstar::informAboutPlannerStatus(ompl::base::PlannerStatus::StatusType status) const
466 {
467 switch (status)
468 {
470 {
471 OMPL_INFORM("%s (%u iterations): Found an exact solution of cost %.4f.", name_.c_str(),
472 numIterations_, solutionCost_.value());
473 break;
474 }
476 {
477 OMPL_INFORM("%s (%u iterations): Did not find an exact solution, but found an approximate "
478 "solution "
479 "of cost %.4f which is %.4f away from a goal (in cost space).",
480 name_.c_str(), numIterations_, approximateSolutionCost_.value(),
481 approximateSolutionCostToGoal_.value());
482 break;
483 }
485 {
486 if (trackApproximateSolutions_)
487 {
488 OMPL_INFORM("%s (%u iterations): Did not find any solution.", name_.c_str(), numIterations_);
489 }
490 else
491 {
492 OMPL_INFORM("%s (%u iterations): Did not find an exact solution, and tracking approximate "
493 "solutions is disabled.",
494 name_.c_str(), numIterations_);
495 }
496 break;
497 }
506 {
507 OMPL_INFORM("%s (%u iterations): Unable to solve the given planning problem.", name_.c_str(),
508 numIterations_);
509 }
510 }
511
513 "%s (%u iterations): Sampled a total of %u states, %u of which were valid samples (%.1f \%). "
514 "Processed %u edges, %u of which were collision checked (%.1f \%). The forward search tree "
515 "has %u vertices. The reverse search tree has %u vertices.",
516 name_.c_str(), numIterations_, graph_.getNumberOfSampledStates(), graph_.getNumberOfValidSamples(),
517 graph_.getNumberOfSampledStates() == 0u ?
518 0.0 :
519 100.0 * (static_cast<double>(graph_.getNumberOfValidSamples()) /
520 static_cast<double>(graph_.getNumberOfSampledStates())),
521 numProcessedEdges_, numEdgeCollisionChecks_,
522 numProcessedEdges_ == 0u ?
523 0.0 :
524 100.0 * (static_cast<float>(numEdgeCollisionChecks_) / static_cast<float>(numProcessedEdges_)),
525 countNumVerticesInForwardTree(), countNumVerticesInReverseTree());
526 }
527
528 void AITstar::insertGoalVerticesInReverseQueue()
529 {
530 for (const auto &goal : graph_.getGoalVertices())
531 {
532 // Set the cost to come from the goal to identity and the expanded cost to infinity.
533 goal->setExpandedCostToComeFromGoal(objective_->infiniteCost());
534 goal->setCostToComeFromGoal(objective_->identityCost());
535
536 // Create an element for the queue.
537 aitstar::KeyVertexPair element({computeCostToGoToStartHeuristic(goal), objective_->identityCost()},
538 goal);
539
540 // Insert the element into the queue and set the corresponding pointer.
541 auto reverseQueuePointer = reverseQueue_.insert(element);
542 goal->setReverseQueuePointer(reverseQueuePointer);
543 }
544 }
545
546 void AITstar::expandStartVerticesIntoForwardQueue()
547 {
548 for (const auto &start : graph_.getStartVertices())
549 {
550 start->setCostToComeFromStart(objective_->identityCost());
551 insertOrUpdateInForwardQueue(getOutgoingEdges(start));
552 }
553 }
554
555 bool AITstar::continueReverseSearch() const
556 {
557 // Never continue the reverse search if the reverse of forward queue is empty.
558 if (reverseQueue_.empty() || forwardQueue_.empty())
559 {
560 return false;
561 }
562
563 // Get references to the best edge and vertex in the queues.
564 const auto &bestEdge = forwardQueue_.top()->data;
565 const auto &bestVertex = reverseQueue_.top()->data;
566
567 // The reverse search must be continued if the best edge has an inconsistent child state or if the best
568 // vertex can potentially lead to a better solution than the best edge.
569 return !((bestEdge.getChild()->isConsistent() &&
570 objective_->isCostBetterThan(bestEdge.getSortKey()[0u], bestVertex.first[0u])) ||
571 numInconsistentOrUnconnectedTargets_ == 0u);
572 }
573
574 bool AITstar::continueForwardSearch()
575 {
576 // Never continue the forward search if its queue is empty.
577 if (forwardQueue_.empty())
578 {
579 return false;
580 }
581
582 // If the best edge in the forward queue has a potential total solution cost of infinity, the forward
583 // search does not need to be continued. This can happen if the reverse search did not reach any target
584 // state of the edges in the forward queue.
585 const auto &bestEdgeCost = forwardQueue_.top()->data.getSortKey()[0u];
586 if (!objective_->isFinite(bestEdgeCost))
587 {
588 return false;
589 }
590
591 // The forward search can be stopped once the resolution optimal solution has been found.
592 return objective_->isCostBetterThan(bestEdgeCost, solutionCost_);
593 }
594
595 std::vector<Edge> AITstar::getEdgesInQueue() const
596 {
597 std::vector<Edge> edges;
598 forwardQueue_.getContent(edges);
599 return edges;
600 }
601
602 std::vector<std::shared_ptr<Vertex>> AITstar::getVerticesInQueue() const
603 {
604 // Get the content from the queue.
605 std::vector<std::pair<std::array<ompl::base::Cost, 2u>, std::shared_ptr<Vertex>>> content;
606 reverseQueue_.getContent(content);
607
608 // Return the vertices.
609 std::vector<std::shared_ptr<Vertex>> vertices;
610 for (const auto &pair : content)
611 {
612 vertices.emplace_back(pair.second);
613 }
614 return vertices;
615 }
616
618 {
619 if (!forwardQueue_.empty())
620 {
621 return forwardQueue_.top()->data;
622 }
623
624 return {};
625 }
626
627 std::shared_ptr<Vertex> AITstar::getNextVertexInQueue() const
628 {
629 if (!reverseQueue_.empty())
630 {
631 return reverseQueue_.top()->data.second;
632 }
633
634 return {};
635 }
636
637 std::vector<std::shared_ptr<Vertex>> AITstar::getVerticesInReverseSearchTree() const
638 {
639 // Get all vertices from the graph.
640 auto vertices = graph_.getVertices();
641
642 // Erase the vertices that are not in the reverse search tree.
643 vertices.erase(std::remove_if(vertices.begin(), vertices.end(),
644 [this](const std::shared_ptr<Vertex> &vertex)
645 { return !graph_.isGoal(vertex) && !vertex->hasReverseParent(); }),
646 vertices.end());
647 return vertices;
648 }
649
650 void AITstar::iterate(const ompl::base::PlannerTerminationCondition &terminationCondition)
651 {
652 // If this is the first time solve is called, populate the queues.
653 if (numIterations_ == 0u)
654 {
655 insertGoalVerticesInReverseQueue();
656 expandStartVerticesIntoForwardQueue();
657 }
658
659 // Keep track of the number of iterations.
660 ++numIterations_;
661
662 // If the reverse search needs to be continued, do that now.
663 if (continueReverseSearch())
664 {
665 iterateReverseSearch();
666 } // If the reverse search is suspended, check whether the forward search needs to be continued.
667 else if (continueForwardSearch())
668 {
669 iterateForwardSearch();
670 } // If neither the forward search nor the reverse search needs to be continued, add more samples.
671 else
672 {
673 // Add new samples to the graph, respecting the termination condition.
674 if (graph_.addSamples(batchSize_, terminationCondition))
675 {
676 // Remove useless samples from the graph.
677 if (isPruningEnabled_)
678 {
679 graph_.prune();
680 }
681
682 // Clear the reverse search tree.
683 for (auto &goal : graph_.getGoalVertices())
684 {
685 invalidateCostToComeFromGoalOfReverseBranch(goal);
686 }
687
688 // Add new start and goal states if necessary.
689 if (pis_.haveMoreStartStates() || pis_.haveMoreGoalStates())
690 {
691 graph_.updateStartAndGoalStates(ompl::base::plannerAlwaysTerminatingCondition(), &pis_);
692 }
693
694 // Reinitialize the queues.
695 clearReverseQueue();
696 clearForwardQueue();
697 insertGoalVerticesInReverseQueue();
698 expandStartVerticesIntoForwardQueue();
699 }
700 }
701 }
702
703 void AITstar::iterateForwardSearch()
704 {
705 assert(!forwardQueue_.empty());
706
707 // Get the most promising edge.
708 auto parent = forwardQueue_.top()->data.getParent();
709 auto child = forwardQueue_.top()->data.getChild();
710 child->removeFromForwardQueueIncomingLookup(forwardQueue_.top());
711 parent->removeFromForwardQueueOutgoingLookup(forwardQueue_.top());
712 forwardQueue_.pop();
713
714 // Ensure that the child is consistent and the parent isn't the goal.
715 assert(child->isConsistent());
716 assert(!graph_.isGoal(parent));
717
718 // This counts as processing an edge.
719 ++numProcessedEdges_;
720
721 // If this edge is already in the forward tree, it's a freeby.
722 if (child->hasForwardParent() && child->getForwardParent()->getId() == parent->getId())
723 {
724 insertOrUpdateInForwardQueue(getOutgoingEdges(child));
725 return;
726 } // Check if this edge can possibly improve the current search tree.
727 else if (objective_->isCostBetterThan(objective_->combineCosts(parent->getCostToComeFromStart(),
728 objective_->motionCostHeuristic(
729 parent->getState(), child->getState())),
730 child->getCostToComeFromStart()))
731 {
732 // The edge can possibly improve the solution and the path to the child. Let's check it for
733 // collision.
734 if (parent->isWhitelistedAsChild(child) ||
735 motionValidator_->checkMotion(parent->getState(), child->getState()))
736 {
737 // Remember that this is a good edge.
738 if (!parent->isWhitelistedAsChild(child))
739 {
740 parent->whitelistAsChild(child);
741 numEdgeCollisionChecks_++;
742 }
743
744 // Compute the edge cost.
745 const auto edgeCost = objective_->motionCost(parent->getState(), child->getState());
746
747 // Check if the edge can improve the cost to come to the child.
748 if (objective_->isCostBetterThan(
749 objective_->combineCosts(parent->getCostToComeFromStart(), edgeCost),
750 child->getCostToComeFromStart()))
751 {
752 // Rewire the child.
753 child->setForwardParent(parent, edgeCost);
754
755 // Add it to the children of the parent.
756 parent->addToForwardChildren(child);
757
758 // Share the good news with the whole branch.
759 child->updateCostOfForwardBranch();
760
761 // Check if the solution can benefit from this.
762 updateSolution(child);
763
764 // Insert the child's outgoing edges into the queue.
765 insertOrUpdateInForwardQueue(getOutgoingEdges(child));
766 }
767 }
768 else // This edge is in collision
769 {
770 // The edge should be blacklisted in both directions.
771 parent->blacklistAsChild(child);
772 child->blacklistAsChild(parent);
773
774 // Repair the reverse search if this edge was in the reverse search tree.
775 if (parent->hasReverseParent() && parent->getReverseParent()->getId() == child->getId())
776 {
777 // The parent was connected to the child through an invalid edge, so we need to invalidate
778 // the branch of the reverse search tree starting from the parent.
779 invalidateCostToComeFromGoalOfReverseBranch(parent);
780 }
781 }
782 }
783 }
784
785 void AITstar::iterateReverseSearch()
786 {
787 assert(!reverseQueue_.empty());
788
789 // Get the most promising vertex and remove it from the queue.
790 auto vertex = reverseQueue_.top()->data.second;
791 reverseQueue_.pop();
792 vertex->resetReverseQueuePointer();
793
794 // The open queue should not contain consistent vertices.
795 assert(!vertex->isConsistent());
796
797 // Check if the vertex is underconsistent. g[s] < v[s].
798 if (objective_->isCostBetterThan(vertex->getCostToComeFromGoal(), vertex->getExpandedCostToComeFromGoal()))
799 {
800 // Make the vertex consistent and update the vertex.
801 vertex->setExpandedCostToComeFromGoal(vertex->getCostToComeFromGoal());
802 updateReverseSearchNeighbors(vertex);
803
804 // Update the number of inconsistent targets in the forward queue.
805 numInconsistentOrUnconnectedTargets_ -= vertex->getForwardQueueIncomingLookup().size();
806 }
807 else
808 {
809 // Make the vertex overconsistent.
810 vertex->setExpandedCostToComeFromGoal(objective_->infiniteCost());
811
812 // Update the vertex and its neighbors.
813 updateReverseSearchVertex(vertex);
814 updateReverseSearchNeighbors(vertex);
815 }
816 }
817
818 bool AITstar::isEdgeBetter(const Edge &lhs, const Edge &rhs) const
819 {
820 return std::lexicographical_compare(
821 lhs.getSortKey().cbegin(), lhs.getSortKey().cend(), rhs.getSortKey().cbegin(), rhs.getSortKey().cend(),
822 [this](const auto &a, const auto &b) { return objective_->isCostBetterThan(a, b); });
823 }
824
825 bool AITstar::isVertexBetter(const aitstar::KeyVertexPair &lhs, const aitstar::KeyVertexPair &rhs) const
826 {
827 // If the costs of two vertices are equal then we prioritize inconsistent vertices that are targets of
828 // edges in the forward queue.
829 if (objective_->isCostEquivalentTo(lhs.first[0u], rhs.first[0u]) &&
830 objective_->isCostEquivalentTo(lhs.first[1u], rhs.first[1u]))
831 {
832 return !lhs.second->getForwardQueueIncomingLookup().empty() && !lhs.second->isConsistent();
833 }
834 else
835 {
836 // Otherwise it's a regular lexicographical comparison of the keys.
837 return std::lexicographical_compare(lhs.first.cbegin(), lhs.first.cend(), rhs.first.cbegin(),
838 rhs.first.cend(), [this](const auto &a, const auto &b)
839 { return objective_->isCostBetterThan(a, b); });
840 }
841 }
842
843 void AITstar::updateReverseSearchVertex(const std::shared_ptr<Vertex> &vertex)
844 {
845 // If the vertex is a goal, there's no updating to do.
846 if (graph_.isGoal(vertex))
847 {
848 assert(objective_->isCostEquivalentTo(vertex->getCostToComeFromGoal(), objective_->identityCost()));
849 return;
850 }
851
852 // Get the best parent for this vertex.
853 auto bestParent = vertex->getReverseParent();
854 auto bestCost = vertex->hasReverseParent() ? vertex->getCostToComeFromGoal() : objective_->infiniteCost();
855
856 // Check all neighbors as defined by the RGG.
857 for (const auto &neighbor : graph_.getNeighbors(vertex))
858 {
859 if (neighbor->getId() != vertex->getId() && !neighbor->isBlacklistedAsChild(vertex) &&
860 !vertex->isBlacklistedAsChild(neighbor))
861 {
862 auto edgeCost = objective_->motionCostHeuristic(neighbor->getState(), vertex->getState());
863 auto parentCost = objective_->combineCosts(neighbor->getExpandedCostToComeFromGoal(), edgeCost);
864 if (objective_->isCostBetterThan(parentCost, bestCost))
865 {
866 bestParent = neighbor;
867 bestCost = parentCost;
868 }
869 }
870 }
871
872 // Check all children this vertex holds in the forward search.
873 for (const auto &forwardChild : vertex->getForwardChildren())
874 {
875 auto edgeCost = objective_->motionCostHeuristic(forwardChild->getState(), vertex->getState());
876 auto parentCost = objective_->combineCosts(forwardChild->getExpandedCostToComeFromGoal(), edgeCost);
877
878 if (objective_->isCostBetterThan(parentCost, bestCost))
879 {
880 bestParent = forwardChild;
881 bestCost = parentCost;
882 }
883 }
884
885 // Check the parent of this vertex in the forward search.
886 if (vertex->hasForwardParent())
887 {
888 auto forwardParent = vertex->getForwardParent();
889 auto edgeCost = objective_->motionCostHeuristic(forwardParent->getState(), vertex->getState());
890 auto parentCost = objective_->combineCosts(forwardParent->getExpandedCostToComeFromGoal(), edgeCost);
891
892 if (objective_->isCostBetterThan(parentCost, bestCost))
893 {
894 bestParent = forwardParent;
895 bestCost = parentCost;
896 }
897 }
898
899 // Set the best cost as the cost-to-come from the goal.
900 vertex->setCostToComeFromGoal(bestCost);
901
902 // What happens next depends on whether the vertex is disconnected or not.
903 if (objective_->isFinite(bestCost))
904 {
905 // The vertex is connected. Update the reverse parent.
906 vertex->setReverseParent(bestParent);
907 bestParent->addToReverseChildren(vertex);
908 }
909 else
910 {
911 // This vertex is now orphaned. Reset the reverse parent if the vertex had one.
912 if (vertex->hasReverseParent())
913 {
914 vertex->getReverseParent()->removeFromReverseChildren(vertex->getId());
915 vertex->resetReverseParent();
916 }
917 }
918
919 // If this has made the vertex inconsistent, insert or update it in the open queue.
920 if (!vertex->isConsistent())
921 {
922 insertOrUpdateInReverseQueue(vertex);
923 }
924 else // Remove this vertex from the queue if it is in the queue if it is consistent.
925 {
926 auto reverseQueuePointer = vertex->getReverseQueuePointer();
927 if (reverseQueuePointer)
928 {
929 reverseQueue_.remove(reverseQueuePointer);
930 vertex->resetReverseQueuePointer();
931 }
932 }
933
934 // This vertex now has a changed cost-to-come in the reverse search. All edges in the forward queue that
935 // have this vertex as a target must be updated. This cannot be delayed, as whether the reverse search
936 // can be suspended depends on the best edge in the forward queue.
937 for (const auto &element : vertex->getForwardQueueIncomingLookup())
938 {
939 auto &edge = element->data;
940 edge.setSortKey(computeSortKey(edge.getParent(), edge.getChild()));
941 forwardQueue_.update(element);
942 }
943 }
944
945 void AITstar::updateReverseSearchNeighbors(const std::shared_ptr<Vertex> &vertex)
946 {
947 // Start with the reverse search children, because if this vertex becomes the parent of a neighbor, that
948 // neighbor would be updated again as part of the reverse children.
949 for (const auto &child : vertex->getReverseChildren())
950 {
951 updateReverseSearchVertex(child);
952 }
953
954 // We can now process the neighbors.
955 for (const auto &neighbor : graph_.getNeighbors(vertex))
956 {
957 if (neighbor->getId() != vertex->getId() && !neighbor->isBlacklistedAsChild(vertex) &&
958 !vertex->isBlacklistedAsChild(neighbor))
959 {
960 updateReverseSearchVertex(neighbor);
961 }
962 }
963
964 // We also need to update the forward search children.
965 for (const auto &child : vertex->getForwardChildren())
966 {
967 updateReverseSearchVertex(child);
968 }
969
970 // We also need to update the forward search parent if it exists.
971 if (vertex->hasForwardParent())
972 {
973 updateReverseSearchVertex(vertex->getForwardParent());
974 }
975 }
976
977 void AITstar::insertOrUpdateInReverseQueue(const std::shared_ptr<Vertex> &vertex)
978 {
979 // Get the pointer to the element in the queue.
980 auto element = vertex->getReverseQueuePointer();
981
982 // Update it if it is in the queue.
983 if (element)
984 {
985 element->data.first = computeSortKey(vertex);
986 reverseQueue_.update(element);
987 }
988 else // Insert it into the queue otherwise.
989 {
990 // Compute the sort key for the vertex queue.
991 std::pair<std::array<ompl::base::Cost, 2u>, std::shared_ptr<Vertex>> element(computeSortKey(vertex),
992 vertex);
993
994 // Insert the vertex into the queue, storing the corresponding pointer.
995 auto reverseQueuePointer = reverseQueue_.insert(element);
996 vertex->setReverseQueuePointer(reverseQueuePointer);
997 }
998 }
999
1000 void AITstar::insertOrUpdateInForwardQueue(const Edge &edge)
1001 {
1002 // Check if the edge is already in the queue and can be updated.
1003 const auto lookup = edge.getChild()->getForwardQueueIncomingLookup();
1004 const auto it = std::find_if(lookup.begin(), lookup.end(), [&edge](const auto element)
1005 { return element->data.getParent()->getId() == edge.getParent()->getId(); });
1006
1007 if (it != lookup.end())
1008 {
1009 // We used the incoming lookup of the child. Assert that it is already in the outgoing lookup of the
1010 // parent.
1011 assert(std::find_if(edge.getParent()->getForwardQueueOutgoingLookup().begin(),
1012 edge.getParent()->getForwardQueueOutgoingLookup().end(), [&edge](const auto element)
1013 { return element->data.getChild()->getId() == edge.getChild()->getId(); }) !=
1015
1016 // This edge exists in the queue. If the new sort key is better than the old, we update it.
1017 if (isEdgeBetter(edge, (*it)->data))
1018 {
1019 (*it)->data.setSortKey(edge.getSortKey());
1020 forwardQueue_.update(*it);
1021 }
1022 }
1023 else
1024 {
1025 auto element = forwardQueue_.insert(edge);
1028
1029 // Incement the counter if the target is inconsistent.
1030 if (!edge.getChild()->isConsistent() || !objective_->isFinite(edge.getChild()->getCostToComeFromGoal()))
1031 {
1032 ++numInconsistentOrUnconnectedTargets_;
1033 }
1034 }
1035 }
1036
1037 void AITstar::insertOrUpdateInForwardQueue(const std::vector<Edge> &edges)
1038 {
1039 for (const auto &edge : edges)
1040 {
1041 insertOrUpdateInForwardQueue(edge);
1042 }
1043 }
1044
1045 std::shared_ptr<ompl::geometric::PathGeometric>
1046 AITstar::getPathToVertex(const std::shared_ptr<Vertex> &vertex) const
1047 {
1048 // Create the reverse path by following the parents to the start.
1049 std::vector<std::shared_ptr<Vertex>> reversePath;
1050 auto current = vertex;
1051 while (!graph_.isStart(current))
1052 {
1053 reversePath.emplace_back(current);
1054 current = current->getForwardParent();
1055 }
1056 reversePath.emplace_back(current);
1057
1058 // Reverse the reverse path to get the forward path.
1059 auto path = std::make_shared<ompl::geometric::PathGeometric>(Planner::si_);
1060 for (const auto &vertex : boost::adaptors::reverse(reversePath))
1061 {
1062 path->append(vertex->getState());
1063 }
1064
1065 return path;
1066 }
1067
1068 std::array<ompl::base::Cost, 3u> AITstar::computeSortKey(const std::shared_ptr<Vertex> &parent,
1069 const std::shared_ptr<Vertex> &child) const
1070 {
1071 // Compute the sort key [g_T(start) + c_hat(start, neighbor) + h_hat(neighbor), g_T(start) +
1072 // c_hat(start, neighbor), g_T(start)].
1073 ompl::base::Cost edgeCostHeuristic = objective_->motionCostHeuristic(parent->getState(), child->getState());
1074 return {
1075 objective_->combineCosts(objective_->combineCosts(parent->getCostToComeFromStart(), edgeCostHeuristic),
1076 child->getCostToGoToGoal()),
1077 objective_->combineCosts(edgeCostHeuristic, child->getCostToGoToGoal()),
1078 parent->getCostToComeFromStart()};
1079 }
1080
1081 std::array<ompl::base::Cost, 2u> AITstar::computeSortKey(const std::shared_ptr<Vertex> &vertex) const
1082 {
1083 // LPA* sort key is [min(g(x), v(x)) + h(x); min(g(x), v(x))].
1084 return {objective_->combineCosts(objective_->betterCost(vertex->getCostToComeFromGoal(),
1085 vertex->getExpandedCostToComeFromGoal()),
1086 computeCostToGoToStartHeuristic(vertex)),
1087 objective_->betterCost(vertex->getCostToComeFromGoal(), vertex->getExpandedCostToComeFromGoal())};
1088 }
1089
1090 std::vector<Edge> AITstar::getOutgoingEdges(const std::shared_ptr<Vertex> &vertex) const
1091 {
1092 // Prepare the return variable.
1093 std::vector<Edge> outgoingEdges;
1094
1095 // Insert the edges to the current children.
1096 for (const auto &child : vertex->getForwardChildren())
1097 {
1098 outgoingEdges.emplace_back(vertex, child, computeSortKey(vertex, child));
1099 }
1100
1101 // Insert the edges to the current neighbors.
1102 for (const auto &neighbor : graph_.getNeighbors(vertex))
1103 {
1104 // We do not want self loops.
1105 if (vertex->getId() == neighbor->getId())
1106 {
1107 continue;
1108 }
1109
1110 // If the neighbor is the reverse parent, it will explicitly be added later.
1111 if (vertex->hasReverseParent() && neighbor->getId() == vertex->getReverseParent()->getId())
1112 {
1113 continue;
1114 }
1115
1116 // We do not want blacklisted edges.
1117 if (neighbor->isBlacklistedAsChild(vertex) || vertex->isBlacklistedAsChild(neighbor))
1118 {
1119 continue;
1120 }
1121
1122 // If none of the above tests caught on, we can insert the edge.
1123 outgoingEdges.emplace_back(vertex, neighbor, computeSortKey(vertex, neighbor));
1124 }
1125
1126 // Insert the edge to the reverse search parent.
1127 if (vertex->hasReverseParent())
1128 {
1129 const auto &reverseParent = vertex->getReverseParent();
1130 outgoingEdges.emplace_back(vertex, reverseParent, computeSortKey(vertex, reverseParent));
1131 }
1132
1133 return outgoingEdges;
1134 }
1135
1136 void AITstar::updateExactSolution()
1137 {
1138 // Check if any of the goals have a cost to come less than the current solution cost.
1139 for (const auto &goal : graph_.getGoalVertices())
1140 {
1141 // We need to check whether the cost is better, or whether someone has removed the exact solution
1142 // from the problem definition.
1143 if (objective_->isCostBetterThan(goal->getCostToComeFromStart(), solutionCost_) ||
1144 (!pdef_->hasExactSolution() && objective_->isFinite(goal->getCostToComeFromStart())))
1145 {
1146 // Remember the incumbent cost.
1147 solutionCost_ = goal->getCostToComeFromStart();
1148
1149 // Create a solution.
1150 ompl::base::PlannerSolution solution(getPathToVertex(goal));
1151 solution.setPlannerName(name_);
1152
1153 // Set the optimized flag.
1154 solution.setOptimized(objective_, solutionCost_, objective_->isSatisfied(solutionCost_));
1155
1156 // Let the problem definition know that a new solution exists.
1157 pdef_->addSolutionPath(solution);
1158
1159 // If enabled, pass the intermediate solution back through the callback:
1160 if (static_cast<bool>(pdef_->getIntermediateSolutionCallback()))
1161 {
1162 const auto &path = solution.path_->as<ompl::geometric::PathGeometric>()->getStates();
1163 // the callback requires a vector with const elements
1164 std::vector<const base::State *> const_path(path.begin(), path.end());
1165 pdef_->getIntermediateSolutionCallback()(this, const_path, solutionCost_);
1166 }
1167
1168 // Let the user know about the new solution.
1169 informAboutNewSolution();
1170 }
1171 }
1172 }
1173
1174 void AITstar::updateApproximateSolution()
1175 {
1176 for (auto &start : graph_.getStartVertices())
1177 {
1178 start->callOnForwardBranch([this](const auto &vertex) -> void { updateApproximateSolution(vertex); });
1179 }
1180 }
1181
1182 void AITstar::updateApproximateSolution(const std::shared_ptr<Vertex> &vertex)
1183 {
1184 assert(trackApproximateSolutions_);
1185 if (vertex->hasForwardParent() || graph_.isStart(vertex))
1186 {
1187 auto costToGoal = computeCostToGoToGoal(vertex);
1188
1189 // We need to check whether this is better than the current approximate solution or whether someone
1190 // has removed all approximate solutions from the problem definition.
1191 if (objective_->isCostBetterThan(costToGoal, approximateSolutionCostToGoal_) ||
1192 !pdef_->hasApproximateSolution())
1193 {
1194 // Remember the incumbent approximate cost.
1195 approximateSolutionCost_ = vertex->getCostToComeFromStart();
1196 approximateSolutionCostToGoal_ = costToGoal;
1197 ompl::base::PlannerSolution solution(getPathToVertex(vertex));
1198 solution.setPlannerName(name_);
1199
1200 // Set the approximate flag.
1201 solution.setApproximate(costToGoal.value());
1202
1203 // This solution is approximate and can not satisfy the objective.
1204 solution.setOptimized(objective_, approximateSolutionCost_, false);
1205
1206 // Let the problem definition know that a new solution exists.
1207 pdef_->addSolutionPath(solution);
1208 }
1209 }
1210 };
1211
1212 ompl::base::PlannerStatus::StatusType AITstar::updateSolution()
1213 {
1214 updateExactSolution();
1215 if (objective_->isFinite(solutionCost_))
1216 {
1218 }
1219 else if (trackApproximateSolutions_)
1220 {
1221 updateApproximateSolution();
1223 }
1224 else
1225 {
1227 }
1228 }
1229
1230 ompl::base::PlannerStatus::StatusType AITstar::updateSolution(const std::shared_ptr<Vertex> &vertex)
1231 {
1232 updateExactSolution();
1233 if (objective_->isFinite(solutionCost_))
1234 {
1236 }
1237 else if (trackApproximateSolutions_)
1238 {
1239 updateApproximateSolution(vertex);
1241 }
1242 else
1243 {
1245 }
1246 }
1247
1248 ompl::base::Cost AITstar::computeCostToGoToStartHeuristic(const std::shared_ptr<Vertex> &vertex) const
1249 {
1250 // We need to loop over all start vertices and see which is the closest one.
1251 ompl::base::Cost bestCost = objective_->infiniteCost();
1252 for (const auto &start : graph_.getStartVertices())
1253 {
1254 bestCost = objective_->betterCost(
1255 bestCost, objective_->motionCostHeuristic(vertex->getState(), start->getState()));
1256 }
1257 return bestCost;
1258 }
1259
1260 ompl::base::Cost AITstar::computeCostToGoToGoalHeuristic(const std::shared_ptr<Vertex> &vertex) const
1261 {
1262 // We need to loop over all goal vertices and see which is the closest one.
1263 ompl::base::Cost bestCost = objective_->infiniteCost();
1264 for (const auto &goal : graph_.getGoalVertices())
1265 {
1266 bestCost = objective_->betterCost(
1267 bestCost, objective_->motionCostHeuristic(vertex->getState(), goal->getState()));
1268 }
1269 return bestCost;
1270 }
1271
1272 ompl::base::Cost AITstar::computeCostToGoToGoal(const std::shared_ptr<Vertex> &vertex) const
1273 {
1274 // We need to loop over all goal vertices and see which is the closest one.
1275 ompl::base::Cost bestCost = objective_->infiniteCost();
1276 for (const auto &goal : graph_.getGoalVertices())
1277 {
1278 bestCost =
1279 objective_->betterCost(bestCost, objective_->motionCost(vertex->getState(), goal->getState()));
1280 }
1281 return bestCost;
1282 }
1283
1284 ompl::base::Cost AITstar::computeBestCostToComeFromGoalOfAnyStart() const
1285 {
1286 // We need to loop over all start vertices and see which is the closest one.
1287 ompl::base::Cost bestCost = objective_->infiniteCost();
1288 for (const auto &start : graph_.getStartVertices())
1289 {
1290 bestCost = objective_->betterCost(bestCost, start->getCostToComeFromGoal());
1291 }
1292 return bestCost;
1293 }
1294
1295 std::size_t AITstar::countNumVerticesInForwardTree() const
1296 {
1297 std::size_t numVerticesInForwardTree = 0u;
1298 auto vertices = graph_.getVertices();
1299 for (const auto &vertex : vertices)
1300 {
1301 if (graph_.isStart(vertex) || vertex->hasForwardParent())
1302 {
1303 ++numVerticesInForwardTree;
1304 }
1305 }
1306 return numVerticesInForwardTree;
1307 }
1308
1309 std::size_t AITstar::countNumVerticesInReverseTree() const
1310 {
1311 std::size_t numVerticesInReverseTree = 0u;
1312 auto vertices = graph_.getVertices();
1313 for (const auto &vertex : vertices)
1314 {
1315 if (graph_.isGoal(vertex) || vertex->hasReverseParent())
1316 {
1317 ++numVerticesInReverseTree;
1318 }
1319 }
1320 return numVerticesInReverseTree;
1321 }
1322
1323 void AITstar::invalidateCostToComeFromGoalOfReverseBranch(const std::shared_ptr<Vertex> &vertex)
1324 {
1325 // If this vertex is consistent before invalidation, then all incoming edges now have targets that are
1326 // inconsistent.
1327 if (vertex->isConsistent())
1328 {
1329 numInconsistentOrUnconnectedTargets_ += vertex->getForwardQueueIncomingLookup().size();
1330 }
1331
1332 // Reset the cost to come from the goal and the reverse parent unless the vertex is itself a goal.
1333 if (!graph_.isGoal(vertex))
1334 {
1335 // Reset the cost to come from the goal.
1336 vertex->resetCostToComeFromGoal();
1337
1338 // Reset the reverse parent.
1339 vertex->getReverseParent()->removeFromReverseChildren(vertex->getId());
1340 vertex->resetReverseParent();
1341 }
1342
1343 // Reset the expanded cost to come from goal.
1344 vertex->resetExpandedCostToComeFromGoal();
1345
1346 // Update all affected edges in the forward queue.
1347 for (const auto &edge : vertex->getForwardQueueIncomingLookup())
1348 {
1349 edge->data.setSortKey(computeSortKey(edge->data.getParent(), edge->data.getChild()));
1350 forwardQueue_.update(edge);
1351 }
1352
1353 // Remove this vertex from the reverse search queue if it is in it.
1354 auto reverseQueuePointer = vertex->getReverseQueuePointer();
1355 if (reverseQueuePointer)
1356 {
1357 reverseQueue_.remove(reverseQueuePointer);
1358 vertex->resetReverseQueuePointer();
1359 }
1360
1361 // Update the cost of all reverse children.
1362 for (const auto &child : vertex->getReverseChildren())
1363 {
1364 invalidateCostToComeFromGoalOfReverseBranch(child);
1365 }
1366
1367 // Update the reverse search vertex to ensure that this vertex is placed in open if necessary.
1368 updateReverseSearchVertex(vertex);
1369 }
1370
1371 } // namespace geometric
1372} // namespace ompl
void getContent(std::vector< _T > &content) const
Get the data stored in this heap.
Definition BinaryHeap.h:207
void clear()
Clear the heap.
Definition BinaryHeap.h:112
Definition of a cost value. Can represent the cost of a motion or the cost of a state.
Definition Cost.h:48
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...
unsigned int addVertex(const PlannerDataVertex &st)
Adds the given vertex to the graph data. The vertex index is returned. Duplicates are not added....
Encapsulate a termination condition for a motion planner. Planners will call operator() to decide whe...
PlannerInputStates pis_
Utility class to extract valid input states.
Definition Planner.h:407
ProblemDefinitionPtr pdef_
The user set problem definition.
Definition Planner.h:404
std::string name_
The name of this planner.
Definition Planner.h:410
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
std::shared_ptr< aitstar::Vertex > getNextVertexInQueue() const
Get the next vertex in the queue.
Definition AITstar.cpp:627
void enablePruning(bool prune)
Set whether pruning is enabled or not.
Definition AITstar.cpp:344
void setMaxNumberOfGoals(unsigned int numberOfGoals)
Set the maximum number of goals AIT* will sample from sampleable goal regions.
Definition AITstar.cpp:364
double getRewireFactor() const
Get the rewire factor of the RGG graph.
Definition AITstar.cpp:321
bool isPruningEnabled() const
Get whether pruning is enabled or not.
Definition AITstar.cpp:349
void setRewireFactor(double rewireFactor)
Set the rewire factor of the RGG graph.
Definition AITstar.cpp:316
void setBatchSize(std::size_t batchSize)
Set the batch size.
Definition AITstar.cpp:306
ompl::base::PlannerStatus::StatusType ensureStartAndGoalStates(const ompl::base::PlannerTerminationCondition &terminationCondition)
Checks whether the problem is successfully setup.
Definition AITstar.cpp:169
ompl::base::Cost bestCost() const
Get the cost of the incumbent solution.
Definition AITstar.cpp:257
unsigned int getMaxNumberOfGoals() const
Get the maximum number of goals AIT* will sample from sampleable goal regions.
Definition AITstar.cpp:369
std::vector< std::shared_ptr< aitstar::Vertex > > getVerticesInQueue() const
Get the vertex queue.
Definition AITstar.cpp:602
AITstar(const ompl::base::SpaceInformationPtr &spaceInformation)
Constructs a AIT*.
Definition AITstar.cpp:55
bool getUseKNearest() const
Get whether to use a k-nearest RGG connection model. If false, AIT* uses an r-disc model.
Definition AITstar.cpp:359
aitstar::Edge getNextEdgeInQueue() const
Get the next edge in the queue.
Definition AITstar.cpp:617
void getPlannerData(base::PlannerData &data) const override
Get the planner data.
Definition AITstar.cpp:262
void setup() override
Additional setup that can only be done once a problem definition is set.
Definition AITstar.cpp:94
ompl::base::PlannerStatus solve(const ompl::base::PlannerTerminationCondition &terminationCondition) override
Solves a motion planning problem.
Definition AITstar.cpp:218
ompl::base::PlannerStatus::StatusType ensureSetup()
Checks whether the planner is successfully setup.
Definition AITstar.cpp:145
std::vector< std::shared_ptr< aitstar::Vertex > > getVerticesInReverseSearchTree() const
Get the vertices in the reverse search tree.
Definition AITstar.cpp:637
void trackApproximateSolutions(bool track)
Set whether to track approximate solutions.
Definition AITstar.cpp:326
void setUseKNearest(bool useKNearest)
Set whether to use a k-nearest RGG connection model. If false, AIT* uses an r-disc model.
Definition AITstar.cpp:354
void clear() override
Clears the algorithm's internal state.
Definition AITstar.cpp:201
std::vector< aitstar::Edge > getEdgesInQueue() const
Get the edge queue.
Definition AITstar.cpp:595
std::size_t getBatchSize() const
Get the batch size.
Definition AITstar.cpp:311
bool areApproximateSolutionsTracked() const
Get whether approximate solutions are tracked.
Definition AITstar.cpp:339
std::shared_ptr< Vertex > getChild() const
Returns the child in this edge.
Definition Edge.cpp:65
const std::array< ompl::base::Cost, 3u > & getSortKey() const
Returns the sort key associated with this edge.
Definition Edge.cpp:70
void setSortKey(const std::array< ompl::base::Cost, 3u > &key)
Sets the sort key associated with this edge.
Definition Edge.cpp:75
std::shared_ptr< Vertex > getParent() const
Returns the parent in this edge.
Definition Edge.cpp:60
std::vector< EdgeQueue::Element * > getForwardQueueIncomingLookup() const
Returns the forward queue incoming lookup of this vertex.
Definition Vertex.cpp:511
void addToForwardQueueOutgoingLookup(typename EdgeQueue::Element *pointer)
Adds an element to the forward queue outgoing lookup.
Definition Vertex.cpp:502
std::vector< EdgeQueue::Element * > getForwardQueueOutgoingLookup() const
Returns the forward queue outgoing lookup of this vertex.
Definition Vertex.cpp:518
bool isConsistent() const
Returns whether the vertex is consistent, i.e., whether its cost-to-come is equal to the cost-to-come...
Definition Vertex.cpp:459
ompl::base::Cost getCostToComeFromGoal() const
Returns the cost to come to this vertex from the goal.
Definition Vertex.cpp:134
void addToForwardQueueIncomingLookup(typename EdgeQueue::Element *pointer)
Adds an element to the forward queue incoming lookup.
Definition Vertex.cpp:495
#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...
PlannerTerminationCondition plannerAlwaysTerminatingCondition()
Simple termination condition that always returns true. The termination condition will always be met.
@ GOAL_SAMPLEABLE_REGION
This bit is set if casting to sampleable goal regions (ompl::base::GoalSampleableRegion) is possible.
Definition GoalTypes.h:56
This namespace contains code that is specific to planning under geometric constraints.
Main namespace. Contains everything in this library.
A class to store the exit status of Planner::solve().
StatusType
The possible values of the status returned by a planner.
@ ABORT
The planner did not find a solution for some other reason.
@ INVALID_START
Invalid start state or no start state specified.
@ EXACT_SOLUTION
The planner found an exact solution.
@ INVALID_GOAL
Invalid goal state.
@ UNRECOGNIZED_GOAL_TYPE
The goal is of a type that a planner does not recognize.
@ APPROXIMATE_SOLUTION
The planner found an approximate solution.
@ TIMEOUT
The planner failed to find a solution.
@ UNKNOWN
Uninitialized status.
@ TYPE_COUNT
The number of possible status values.
@ INFEASIBLE
The planner decided that problem is infeasible.
@ CRASH
The planner crashed.