Loading...
Searching...
No Matches
BLITstar.cpp
1/*********************************************************************
2 * Software License Agreement (BSD License)
3 *
4 * Copyright (c) 2025, University of New Hampshire
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: Yi Wang, Eyal Weiss, Bingxian Mu, Oren Salzman
36
37#include <ompl/base/SpaceInformation.h>
38#include <ompl/base/objectives/PathLengthOptimizationObjective.h>
39#include <ompl/base/objectives/StateCostIntegralObjective.h>
40#include <ompl/base/objectives/MaximizeMinClearanceObjective.h>
41#include <ompl/base/spaces/RealVectorStateSpace.h>
42#include <ompl/base/spaces/RealVectorStateSpace.h>
43// For ompl::msg::setLogLevel
44#include "ompl/util/Console.h"
45#include "ompl/geometric/planners/lazyinformedtrees/BLITstar.h"
46
47#include <algorithm>
48#include <cmath>
49#include <string>
50
51#include <boost/range/adaptor/reversed.hpp>
52
53#include "ompl/base/objectives/PathLengthOptimizationObjective.h"
54#include "ompl/util/Console.h"
55
56using namespace std::string_literals;
57using namespace ompl::geometric::blitstar;
58namespace ob = ompl::base;
59using namespace std;
60namespace ompl
61{
62 namespace geometric
63 {
64 BLITstar::BLITstar(const ompl::base::SpaceInformationPtr &spaceInformation)
65 : ompl::base::Planner(spaceInformation, "BLITstar")
66 , detectionState_(spaceInformation->allocState())
67 , solutionCost_()
68 , graph_(solutionCost_)
69 , forwardVertexQueue_([this](const auto &lhs, const auto &rhs) { return isVertexBetter(lhs, rhs); })
70 , reverseVertexQueue_([this](const auto &lhs, const auto &rhs) { return isVertexBetter(lhs, rhs); })
71 , space_(spaceInformation->getStateSpace())
72 {
73 // Specify BLIT*'s planner specs.
74 specs_.recognizedGoal = base::GOAL_SAMPLEABLE_REGION;
75 specs_.multithreaded = false;
76 specs_.approximateSolutions = true;
77 specs_.optimizingPaths = true;
78 specs_.directed = true;
79 specs_.provingSolutionNonExistence = false;
80 specs_.canReportIntermediateSolutions = true;
81 spaceInformation_ = spaceInformation;
82 // Register the setting callbacks.
83 declareParam<bool>("use_k_nearest", this, &BLITstar::setUseKNearest, &BLITstar::getUseKNearest, "0,1");
84 declareParam<double>("rewire_factor", this, &BLITstar::setRewireFactor, &BLITstar::getRewireFactor,
85 "1.0:0.01:3.0");
86 declareParam<std::size_t>("samples_per_batch", this, &BLITstar::setBatchSize, &BLITstar::getBatchSize,
87 "1:1:1000");
88 declareParam<bool>("use_graph_pruning", this, &BLITstar::enablePruning, &BLITstar::isPruningEnabled, "0,1");
89 declareParam<std::size_t>("set_max_num_goals", this, &BLITstar::setMaxNumberOfGoals,
91
92 // Register the progress properties.
93 addPlannerProgressProperty("iterations INTEGER", [this]() { return std::to_string(numIterations_); });
94 addPlannerProgressProperty("best cost DOUBLE", [this]() { return std::to_string(solutionCost_.value()); });
95 addPlannerProgressProperty("state collision checks INTEGER",
96 [this]() { return std::to_string(graph_.getNumberOfStateCollisionChecks()); });
97 addPlannerProgressProperty("edge collision checks INTEGER",
98 [this]() { return std::to_string(numEdgeCollisionChecks_); });
99 addPlannerProgressProperty("nearest neighbour calls INTEGER",
100 [this]() { return std::to_string(graph_.getNumberOfNearestNeighborCalls()); });
101 }
102
104 {
105 si_->freeState(detectionState_);
106 }
107
109 {
110 // Call the base-class setup.
111 Planner::setup();
112
113 // Check that a problem definition has been set.
114 if (static_cast<bool>(Planner::pdef_))
115 {
116 // Default to path length optimization objective if none has been specified.
117 if (!pdef_->hasOptimizationObjective())
118 {
119 OMPL_WARN("%s: No optimization objective has been specified. Defaulting to path length.",
120 Planner::getName().c_str());
121 Planner::pdef_->setOptimizationObjective(
122 std::make_shared<ompl::base::PathLengthOptimizationObjective>(Planner::si_));
123 }
124
125 if (static_cast<bool>(pdef_->getGoal()))
126 {
127 // If we were given a goal, make sure its of appropriate type.
128 if (!(pdef_->getGoal()->hasType(ompl::base::GOAL_SAMPLEABLE_REGION)))
129 {
130 OMPL_ERROR("BLIT* is currently only implemented for goals that can be cast to "
131 "ompl::base::GOAL_SAMPLEABLE_GOAL_REGION.");
132 setup_ = false;
133 return;
134 }
135 }
136
137 // Pull the optimization objective through the problem definition.
138 objective_ = pdef_->getOptimizationObjective();
139
140 // Initialize the solution cost to be infinite.
141 solutionCost_ = objective_->infiniteCost();
142
143 iSolution_ = false;
144
145 C_curr = objective_->infiniteCost();
146 approximateSolutionCostToGoal_ = objective_->infiniteCost();
147
148 // Pull the motion validator through the space information.
149 motionValidator_ = si_->getMotionValidator();
150
151 // Setup a graph.
152 graph_.setup(si_, pdef_, &pis_);
153 }
154 else
155 {
156 // BLIT* can't be setup without a problem definition.
157 setup_ = false;
158 OMPL_WARN("BLIT*: Unable to setup without a problem definition.");
159 }
160 }
161
163 {
164 // Call the base planners validity check. This checks if the
165 // planner is setup if not then it calls setup().
167
168 // Ensure the planner is setup.
169 if (!setup_)
170 {
171 OMPL_ERROR("%s: The planner is not setup.", name_.c_str());
173 }
174
175 // Ensure the space is setup.
176 if (!si_->isSetup())
177 {
178 OMPL_ERROR("%s: The space information is not setup.", name_.c_str());
180 }
181
183 }
184
187 {
188 // If the graph currently does not have a start state, try to get one.
189 if (!graph_.hasAStartState())
190 {
191 graph_.updateStartAndGoalStates(terminationCondition, &pis_);
192
193 // If we could not get a start state, then there's nothing to solve.
194 if (!graph_.hasAStartState())
195 {
196 OMPL_WARN("%s: No solution can be found as no start states are available", name_.c_str());
198 }
199 }
200
201 // If the graph currently does not have a goal state, we wait until we get one.
202 if (!graph_.hasAGoalState())
203 {
204 graph_.updateStartAndGoalStates(terminationCondition, &pis_);
205
206 // If the graph still doesn't have a goal after waiting, then there's nothing to solve.
207 if (!graph_.hasAGoalState())
208 {
209 OMPL_WARN("%s: No solution can be found as no goal states are available", name_.c_str());
211 }
212 }
213
214 // Would it be worth implementing a 'setup' or 'checked' status type?
216 }
217
219 {
220 graph_.clear();
221 numIterations_ = 0u;
222 approximateSolutionCostToGoal_ = approximateSolutionCost_ = solutionCost_ = objective_->infiniteCost();
223 Planner::clear();
224 setup_ = false;
225 }
226
228 {
229 // Ensure that the planner and state space are setup before solving.
230 auto status = ensureSetup();
231
232 // Return early if the planner or state space are not setup.
234 {
235 return status;
236 }
237
238 // Ensure that the problem has start and goal states before solving.
239 status = ensureStartAndGoalStates(terminationCondition);
240
241 // Return early if the problem cannot be solved.
244 {
245 return status;
246 }
247 OMPL_INFORM("%s: Solving the given planning problem. The current best solution cost is %.4f", name_.c_str(),
248 solutionCost_.value());
249
250 // Iterate to solve the problem.
251 while (!terminationCondition && !objective_->isSatisfied(solutionCost_))
252 {
253 iterate(terminationCondition);
254 }
255 // Someone might call ProblemDefinition::clearSolutionPaths() between invocations of Planner::sovle(), in
256 // which case previously found solutions are not registered with the problem definition anymore.
257 status = updateSolution();
258
259 // Let the caller know the status.
260 informAboutPlannerStatus(status);
261 return status;
262 }
263
265 {
266 // base::PlannerDataVertex takes a raw pointer to a state. I want to guarantee, that the state lives as
267 // long as the program lives.
268 static std::set<std::shared_ptr<Vertex>,
269 std::function<bool(const std::shared_ptr<Vertex> &, const std::shared_ptr<Vertex> &)>>
270 liveStates([](const auto &lhs, const auto &rhs) { return lhs->getId() < rhs->getId(); });
271
272 // Fill the planner progress properties.
273 Planner::getPlannerData(data);
274
275 // Get the vertices.
276 auto vertices = graph_.getVertices();
277
278 // Add the vertices and edges.
279 for (const auto &vertex : vertices)
280 {
281 // Add the vertex to the live states.
282 liveStates.insert(vertex);
283
284 // Add the vertex as the right kind of vertex.
285 if (graph_.isStart(vertex))
286 {
287 data.addStartVertex(ompl::base::PlannerDataVertex(vertex->getState(), vertex->getId()));
288 }
289 else if (graph_.isGoal(vertex))
290 {
291 data.addGoalVertex(ompl::base::PlannerDataVertex(vertex->getState(), vertex->getId()));
292 }
293 else
294 {
295 data.addVertex(ompl::base::PlannerDataVertex(vertex->getState(), vertex->getId()));
296 }
297
298 // If it has a parent, add the corresponding edge.
299 if (vertex->hasForwardParent())
300 {
301 data.addEdge(ompl::base::PlannerDataVertex(vertex->getState(), vertex->getId()),
302 ompl::base::PlannerDataVertex(vertex->getForwardParent()->getState(),
303 vertex->getForwardParent()->getId()));
304 }
305 }
306 }
307
308 void BLITstar::setBatchSize(std::size_t batchSize)
309 {
310 batchSize_ = batchSize;
311 }
312
313 std::size_t BLITstar::getBatchSize() const
314 {
315 return batchSize_;
316 }
317
318 void BLITstar::setRewireFactor(double rewireFactor)
319 {
320 graph_.setRewireFactor(rewireFactor);
321 }
322
324 {
325 return graph_.getRewireFactor();
326 }
327
328 void BLITstar::enablePruning(bool prune)
329 {
330 isPruningEnabled_ = prune;
331 }
332
334 {
335 return isPruningEnabled_;
336 }
337
338 void BLITstar::setUseKNearest(bool useKNearest)
339 {
340 graph_.setUseKNearest(useKNearest);
341 }
342
344 {
345 return graph_.getUseKNearest();
346 }
347
348 void BLITstar::setMaxNumberOfGoals(unsigned int numberOfGoals)
349 {
350 graph_.setMaxNumberOfGoals(numberOfGoals);
351 }
352
353 unsigned int BLITstar::getMaxNumberOfGoals() const
354 {
355 return graph_.getMaxNumberOfGoals();
356 }
357
359 {
360 std::vector<blitstar::KeyVertexPair> reverseQueue;
361 reverseVertexQueue_.getContent(reverseQueue);
362 for (const auto &element : reverseQueue)
363 {
364 element.second->resetReverseVertexQueuePointer();
365 }
366 reverseVertexQueue_.clear();
367 }
368
369 void BLITstar::clearForwardVertexQueue()
370 {
371 std::vector<blitstar::KeyVertexPair> forwardQueue;
372 forwardVertexQueue_.getContent(forwardQueue);
373 for (const auto &element : forwardQueue)
374 {
375 element.second->resetForwardVertexQueuePointer();
376 }
377 forwardVertexQueue_.clear();
378 }
379 void BLITstar::informAboutNewSolution() const
380 {
381 OMPL_INFORM("%s (%u iterations): Found a new exact solution of cost %.4f. Sampled a total of %u states, %u "
382 "of which were valid samples (%.1f \%). Processed %u edges, %u of which were collision checked "
383 "(%.1f \%). The forward search tree has %u vertices, %u of which are start states. The reverse "
384 "search tree has %u vertices, %u of which are goal states.",
385 name_.c_str(), numIterations_, solutionCost_.value(), graph_.getNumberOfSampledStates(),
386 graph_.getNumberOfValidSamples(),
387 graph_.getNumberOfSampledStates() == 0u ?
388 0.0 :
389 100.0 * (static_cast<double>(graph_.getNumberOfValidSamples()) /
390 static_cast<double>(graph_.getNumberOfSampledStates())),
391 numProcessedEdges_, numEdgeCollisionChecks_,
392 numProcessedEdges_ == 0u ? 0.0 :
393 100.0 * (static_cast<float>(numEdgeCollisionChecks_) /
394 static_cast<float>(numProcessedEdges_)),
395 countNumVerticesInForwardTree(), graph_.getStartVertices().size(),
396 countNumVerticesInReverseTree(), graph_.getGoalVertices().size());
397 }
398
399 void BLITstar::informAboutPlannerStatus(ompl::base::PlannerStatus::StatusType status) const
400 {
401 switch (status)
402 {
404 {
405 OMPL_INFORM("%s (%u iterations): Found an exact solution of cost %.4f.", name_.c_str(),
406 numIterations_, solutionCost_.value());
407 break;
408 }
410 {
411 OMPL_INFORM("%s (%u iterations): Did not find an exact solution, but found an approximate "
412 "solution "
413 "of cost %.4f which is %.4f away from a goal (in cost space).",
414 name_.c_str(), numIterations_, approximateSolutionCost_.value(),
415 approximateSolutionCostToGoal_.value());
416 break;
417 }
419 {
420 if (trackApproximateSolutions_)
421 {
422 OMPL_INFORM("%s (%u iterations): Did not find any solution.", name_.c_str(), numIterations_);
423 }
424 else
425 {
426 OMPL_INFORM("%s (%u iterations): Did not find an exact solution, and tracking approximate "
427 "solutions is disabled.",
428 name_.c_str(), numIterations_);
429 }
430 break;
431 }
440 {
441 OMPL_INFORM("%s (%u iterations): Unable to solve the given planning problem.", name_.c_str(),
442 numIterations_);
443 }
444 }
445
447 "%s (%u iterations): Sampled a total of %u states, %u of which were valid samples (%.1f \%). "
448 "Processed %u edges, %u of which were collision checked (%.1f \%). The forward search tree "
449 "has %u vertices. The reverse search tree has %u vertices.",
450 name_.c_str(), numIterations_, graph_.getNumberOfSampledStates(), graph_.getNumberOfValidSamples(),
451 graph_.getNumberOfSampledStates() == 0u ?
452 0.0 :
453 100.0 * (static_cast<double>(graph_.getNumberOfValidSamples()) /
454 static_cast<double>(graph_.getNumberOfSampledStates())),
455 numProcessedEdges_, numEdgeCollisionChecks_,
456 numProcessedEdges_ == 0u ?
457 0.0 :
458 100.0 * (static_cast<float>(numEdgeCollisionChecks_) / static_cast<float>(numProcessedEdges_)),
459 countNumVerticesInForwardTree(), countNumVerticesInReverseTree());
460 }
461
462 std::size_t BLITstar::countNumVerticesInReverseTree() const
463 {
464 std::size_t numVerticesInReverseTree = 0u;
465 auto vertices = graph_.getVertices();
466 for (const auto &vertex : vertices)
467 {
468 if (graph_.isGoal(vertex) || vertex->hasReverseParent())
469 {
470 ++numVerticesInReverseTree;
471 }
472 }
473 return numVerticesInReverseTree;
474 }
475
476 std::size_t BLITstar::countNumVerticesInForwardTree() const
477 {
478 std::size_t numVerticesInForwardTree = 0u;
479 auto vertices = graph_.getVertices();
480 for (const auto &vertex : vertices)
481 {
482 if (graph_.isStart(vertex) || vertex->hasForwardParent())
483 {
484 ++numVerticesInForwardTree;
485 }
486 }
487 return numVerticesInForwardTree;
488 }
489
490 void BLITstar::insertStartVerticesInForWardVertexQueue()
491 {
492 for (const auto &start : graph_.getStartVertices())
493 {
494 // Set the cost to come from the goal to identity and the expanded cost to infinity.
495 start->setCostToComeFromStart(objective_->identityCost());
496 start->setCostToComeFromGoal(objective_->infiniteCost());
497 // Set the lower cost bound for start to go or to come
498 start->setLowerCostBoundToStart(objective_->identityCost());
499 start->setLowerCostBoundToGoal(lowerboundToGoal(start));
500
501 start->setStartVertex();
502 start->resetForwardExpanded();
503 start->setForwardId(forwardId_);
504 // Create an element for the queue.
505 blitstar::KeyVertexPair element({start->getLowerCostBoundToGoal(), objective_->identityCost()}, start);
506 // Insert the element into the queue and set the corresponding pointer.
507 auto forwardQueuePointer = forwardVertexQueue_.insert(element);
508 start->setForwardVertexQueuePointer(forwardQueuePointer);
509 }
510 }
511
513 {
514 for (const auto &goal : graph_.getGoalVertices())
515 {
516 // Set the cost to come from the goal to identity and the expanded cost to infinity.
517 goal->setCostToComeFromGoal(objective_->identityCost());
518 goal->setCostToComeFromStart(objective_->infiniteCost());
519 goal->setLowerCostBoundToGoal(objective_->identityCost());
520 goal->setLowerCostBoundToStart(lowerboundToStart(goal));
521 goal->resetReverseExpanded();
522 goal->setGoalVertex();
523 goal->setReverseId(reverseId_);
524 // Create an element for the queue.
525 blitstar::KeyVertexPair element({goal->getLowerCostBoundToStart(), objective_->identityCost()}, goal);
526 // Insert the element into the queue and set the corresponding pointer.
527 auto reverseQueuePointer = reverseVertexQueue_.insert(element);
528 goal->setReverseVertexQueuePointer(reverseQueuePointer);
529 }
530 }
531
533 {
534 if (betterThan(curMin_, minimalneighbor_))
535 {
536 minimalneighbor_ = curMin_;
537 bestNeighbor_ = neighbor;
538 }
539 }
540 void BLITstar::bestNeighbor(ompl::base::Cost costToCome, ompl::base::Cost costToGoal, size_t neighbor)
541 {
542 ompl::base::Cost f_value = objective_->combineCosts(costToCome, costToGoal);
543 lookingForBestNeighbor(f_value, neighbor);
544 }
545
546 void BLITstar::insertOrUpdateInReverseVertexQueue(const std::shared_ptr<blitstar::Vertex> &vertex,
547 ompl::base::Cost CostToCome, ompl::base::Cost CostToGoal,
548 bool couldMeet)
549 {
550 auto element = vertex->getReverseVertexQueuePointer();
551 // Update it if it is in
552 if (element)
553 {
554 element->data.first = computeEstimatedPathCosts(CostToCome, CostToGoal);
555 reverseVertexQueue_.update(element);
556 }
557 else // Insert the pointer into the queue
558 {
559 std::pair<std::array<ompl::base::Cost, 2u>, std::shared_ptr<Vertex>> element(
560 computeEstimatedPathCosts(CostToCome, CostToGoal), vertex);
561 // Insert the vertex into the queue, storing the corresponding pointer.
562 auto backwardQueuePointer = reverseVertexQueue_.insert(element);
563 vertex->setReverseVertexQueuePointer(backwardQueuePointer);
564 }
565 if (couldMeet)
566 bestNeighbor(CostToCome, CostToGoal, vertex->getId());
567 }
568
569 bool BLITstar::NeedMoreSamples()
570 {
571 return isVertexEmpty_ ? true : false;
572 }
573
574 bool BLITstar::PathValidity(std::shared_ptr<Vertex> &vertex)
575 {
576 bool found_validity = true;
577 forwardInvalid_ = false;
578 ForwardPathValidityChecking(vertex, found_validity);
579 reverseInvalid_ = false;
580 ReversePathValidityChecking(vertex, found_validity);
581 if (forwardInvalid_)
582 {
583 forwardId_++;
584 clearForwardVertexQueue();
585 insertStartVerticesInForWardVertexQueue();
586 }
587 if (reverseInvalid_)
588 {
589 reverseId_++;
592 }
593 return found_validity;
594 }
595 void BLITstar::ForwardPathValidityChecking(std::shared_ptr<Vertex> &vertex, bool &validity)
596 {
597 std::shared_ptr<Vertex> tar_ = vertex; // target vertex
598 std::vector<std::shared_ptr<Vertex>> reversePath;
599 while (!graph_.isStart(tar_))
600 {
601 std::shared_ptr<Vertex> src_ = tar_->getForwardParent(); // source vertex
602 reversePath.emplace_back(tar_);
603 bool valid_edge = false;
604 if (!tar_->hasForwardParent())
605 {
606 forwardInvalid_ = !(validity = false);
607 break;
608 }
609
610 if (!(valid_edge = CCD(make_pair(src_, tar_))))
611 {
612 forwardInvalid_ = !(validity = false);
613 resetForwardParentAndRemeberTheVertex(tar_, src_);
614 tar_->resetReverseEdgeParent();
615 }
616 else
617 {
618 src_->setReverseValidParent(tar_, tar_->getEdgeCostFromForwardParent());
619 }
620 tar_ = src_;
621 }
622 }
623
624 void BLITstar::ReversePathValidityChecking(std::shared_ptr<Vertex> &vertex, bool &validity)
625 {
626 std::shared_ptr<Vertex> tar_ = vertex;
627 std::vector<std::shared_ptr<Vertex>> reversePath;
628 while (!graph_.isGoal(tar_))
629 {
630 reversePath.emplace_back(tar_);
631 std::shared_ptr<Vertex> src_ = tar_->getReverseParent();
632 bool valid_edge = false;
633 if (!tar_->hasReverseParent())
634 {
635 reverseInvalid_ = !(validity = false);
636 break;
637 }
638 if (!(valid_edge = CCD(make_pair(tar_, src_))))
639 {
640 reverseInvalid_ = !(validity = false);
641 resetReverseParentAndRemeberTheVertex(tar_, src_);
642 tar_->resetForwardEdgeParent();
643 }
644 else
645 {
646 src_->setForwardValidParent(tar_, tar_->getEdgeCostFromReverseParent());
647 }
648 tar_ = src_;
649 }
650 }
651
652 void BLITstar::resetForwardParentInformation(const std::shared_ptr<blitstar::Vertex> &vertex)
653 {
654 vertex->resetForwardParent();
655 resetForwardValue(vertex);
656 }
657
658 void BLITstar::resetForwardValue(const std::shared_ptr<Vertex> &vertex)
659 {
660 vertex->resetForwardExpanded();
661 vertex->resetForwardVertexQueuePointer();
662 vertex->setCostToComeFromStart(objective_->infiniteCost());
663 }
664
665 void BLITstar::resetForwardParentAndRemeberTheVertex(const std::shared_ptr<Vertex> &child,
666 const std::shared_ptr<Vertex> &parent)
667 {
668 child->setNearObstacle();
669 parent->setNearObstacle();
671 child->setForwardInvalid();
672 parent->removeFromForwardChildren(child->getId());
673 }
674
675 void BLITstar::resetReverseParentInformation(const std::shared_ptr<blitstar::Vertex> &vertex)
676 {
677 vertex->resetReverseParent();
678 resetReverseValue(vertex);
679 }
680
681 void BLITstar::resetReverseValue(const std::shared_ptr<Vertex> &vertex)
682 {
683 vertex->resetReverseExpanded();
684 vertex->resetReverseVertexQueuePointer();
685 vertex->setCostToComeFromGoal(objective_->infiniteCost());
686 }
687
688 void BLITstar::resetReverseParentAndRemeberTheVertex(const std::shared_ptr<Vertex> &child,
689 const std::shared_ptr<Vertex> &parent)
690 {
691 child->setNearObstacle();
692 parent->setNearObstacle();
693 child->setReverseInvalid();
694 resetReverseParentInformation(child);
695 parent->removeFromReverseChildren(child->getId());
696 }
698 {
699 if (isVertexEmpty_)
700 {
701 return true;
702 }
703 if (!found_meeting_ && betterThan(fmin_, C_curr))
704 {
705 return false;
706 }
707
708 if (betterThan(C_curr, solutionCost_) && !PathValidity(V_meet.second))
709 {
710 C_curr = solutionCost_;
711 BestVertex_->resetMeet();
712 V_meet.second->resetMeet();
713 start_scratch_ = true;
714 return (found_meeting_ = false);
715 }
716 return (find_solution_ = true);
717 }
718
719 void BLITstar::iterate(const ompl::base::PlannerTerminationCondition &terminationCondition)
720 {
721 // Add new samples to the graph, respecting the termination condition, if needed.
722 if (NeedMoreSamples() && graph_.addSamples(batchSize_, terminationCondition, need_Prune_))
723 {
724 // Expanding start and goal vertices simultaneously
725 insertStartVerticesInForWardVertexQueue();
727
728 // count the scratch_
729 forwardId_ = reverseId_ = 0u;
730 isVertexEmpty_ = find_solution_ = false;
731 }
732 // Selecte a state with the minimal priority on both queues for expansion. */
733 bool forwardDirection_ = false;
734 if (!SelectExpandState(forwardDirection_))
735 {
736 return;
737 }
738
739 bool terminateSearch_ = terminateSearch();
740 if (start_scratch_)
741 {
742 return;
743 }
744 // If terminate function has not yet been triggered or either queue is empty, continuing the search in
745 // either direction
746 if (!terminateSearch_)
747 {
748 if (forwardDirection_)
749 {
750 BestVertex_->setForwardExpanded();
751 ForwardLazySearch(BestVertex_);
752 }
753 else
754 {
755 BestVertex_->setReverseExpanded();
756 ReverseLazySearch(BestVertex_);
757 }
758 }
759 else
760 {
761 if (find_solution_ && betterThan(C_curr, solutionCost_))
762 {
763 iSolution_ = true;
764 solutionCost_ = C_curr;
765 path_ = getPathToVertex(V_meet.second);
766 }
768 clearForwardVertexQueue();
769 need_Prune_ = !searchExhausted_;
770 found_meeting_ = !(isVertexEmpty_ = true);
771 }
772 // Keep track of the number of iterations.
773 ++numIterations_;
774 }
775
776 bool BLITstar::betterThan(const ompl::base::Cost &cost1, const ompl::base::Cost &cost2)
777 {
778 return cost1.value() + 0.000001 < cost2.value();
779 }
780
781 bool BLITstar::largerThan(const ompl::base::Cost &cost1, const ompl::base::Cost &cost2)
782 {
783 return cost1.value() > cost2.value() + 0.000001;
784 }
785
786 bool BLITstar::isVertexBetter(const blitstar::KeyVertexPair &lhs, const blitstar::KeyVertexPair &rhs) const
787 {
788 // it's a regular comparison of the keys.
789 return std::lexicographical_compare(lhs.first.cbegin(), lhs.first.cend(), rhs.first.cbegin(),
790 rhs.first.cend(), [this](const auto &a, const auto &b)
791 { return objective_->isCostBetterThan(a, b); });
792 }
793
794 void BLITstar::ForwardLazySearch(const std::shared_ptr<blitstar::Vertex> &vertex)
795 {
796 // Operation in forward search, with analogous operation in reverse search.
797 minimalneighbor_ = objective_->infiniteCost();
798 // Insert current children of this vertex into forward search tree.
799 for (const auto &child : vertex->getForwardChildren())
800 {
801 // Checks collision detection if the first valid solution has not been found or the promising edge is
802 // near obstacles.
803 if ((!iSolution_ || vertex->nearObstacle() || child->nearObstacle()) && !SCD(make_pair(vertex, child)))
804 {
805 resetForwardParentAndRemeberTheVertex(child, vertex);
806 continue;
807 }
808
809 // g_hat(x_v) = g(x_u) + c(x_u, x_v)
810 auto edgeCost = child->getEdgeCostFromForwardParent();
811 auto gValue = objective_->combineCosts(vertex->getCostToComeFromStart(), edgeCost);
812 if (iSolution_ && !child->hasReverseParent() &&
813 largerThan(objective_->combineCosts(gValue, gValue), solutionCost_))
814 {
815 continue;
816 }
817 // h_bar(x_v): lower cost bound to go such as Eculidean distance.
818 auto hValue = child->getLowerCostBoundToGoal();
819 child->setCostToComeFromStart(gValue);
820 gValue = child->getCostToComeFromStart();
821 // Refine heuristic value if needed.
822 updateForwardCost(child, gValue, hValue);
823 insertOrUpdateInForwardVertexQueue(child, gValue, hValue, vertex->meetVertex());
824 }
825
826 // Add a new promising vertex to the forward search tree.
827 bool Potential_collide_ = false;
828 for (const auto &neighbor : graph_.getNeighbors(vertex))
829 {
830 if (neighbor->getId() == vertex->getId())
831 continue;
832 if (iSolution_ && !vertex->nearObstacle() && neighbor->nearObstacle())
833 {
834 Potential_collide_ = true;
835 }
836
837 // Reset value metrics if the neighbor belongs to an obsolete branch of the forward search tree.
838 if (forwardInvalid_ && neighbor->getForwardId() != forwardId_)
839 {
840 neighbor->setCostToComeFromStart(objective_->infiniteCost());
841 neighbor->resetForwardExpanded();
842 }
843 if (!neighbor->isForwardExpanded())
844 {
845 // If x_u is the parent of x_v, it will not be added to the Q_F.
846 if (iSolution_ && vertex->hasReverseEdgeParent() &&
847 vertex->getReverseEdgeParent()->getId() == neighbor->getId())
848 {
849 continue;
850 }
851
852 if (neighbor->hasForwardParent() && neighbor->getForwardParent()->getId() == vertex->getId())
853 {
854 continue;
855 }
856
857 if (neighbor->isBlacklistedAsChild(vertex) || vertex->isBlacklistedAsChild(neighbor))
858 {
859 continue;
860 }
861 // Check whether start and goal are very close.
862 if (vertex->isStart() && neighbor->isGoal())
863 {
864 EvaluateValidityStartAndToGoal(neighbor, vertex);
865 continue;
866 }
867
868 // g_hat(x_v) = g_hat(x_u) + c_hat(x_u,x_v).
869 auto gValue = (neighbor->hasForwardParent()) ? neighbor->getCostToComeFromStart() :
870 objective_->infiniteCost();
871 auto edgeCost = objective_->motionCostHeuristic(neighbor->getState(), vertex->getState());
872 auto est_gValue = objective_->combineCosts(vertex->getCostToComeFromStart(), edgeCost);
873
874 // If g_F(x_v) > g_F(x_u) + c_hat(x_u,x_v), this neighbor is a promising vertex to provide a better
875 // solution.
876 if (betterThan(est_gValue, gValue) && !neighbor->isGoal())
877 {
878 neighbor->setForwardId(forwardId_);
879 bool fValid_ = false, rValid_ = false, supposeMeet = false;
880
881 // Use SCD (Sparse Collision Detection) to check edge validity before a solution is found.
882 if (!iSolution_ && !SCD(make_pair(vertex, neighbor)))
883 {
884 vertex->setNearObstacle();
885 neighbor->setNearObstacle();
886 neighbor->setForwardInvalid();
887 neighbor->resetForwardExpanded();
888 continue;
889 }
890 // Check whether this promising edge is near obstacles
891 bool startCollid_ = goalCloseToStart_ && vertex->isStart();
892 if (startCollid_ || Potential_collide_ || vertex->nearObstacle() || neighbor->nearObstacle())
893 {
894 if (!(fValid_ = SCD(make_pair(vertex, neighbor))) ||
895 (neighbor->forwardInvalid() && !CCD(make_pair(vertex, neighbor))))
896 {
897 neighbor->setNearObstacle();
898 vertex->setNearObstacle();
899 neighbor->setForwardInvalid();
900 neighbor->resetForwardExpanded();
901 continue;
902 }
903 if (startCollid_)
904 {
905 neighbor->setNearObstacle();
906 }
907 }
908
909 // Set x_u to be x_v's parent and update g_hat(x_v)
910 neighbor->setCostToComeFromStart(est_gValue);
911 neighbor->setForwardVertexParent(vertex, edgeCost);
912 vertex->addToForwardChildren(neighbor);
913 auto hValue = neighbor->getLowerCostBoundToGoal();
914 auto getReversepointer = neighbor->getReverseVertexQueuePointer();
915
916 bool onReverseTree = !(neighbor->hasReverseParent() && neighbor->getReverseId() != reverseId_);
917 if (!onReverseTree)
918 {
919 resetReverseValue(neighbor);
920 }
921
922 // Check if x_v already exists in reverse search tree. If so, operate associated updating
923 if (onReverseTree && (getReversepointer || (neighbor->hasReverseParent())))
924 {
925 keyEdgePair fEdge = make_pair(vertex, neighbor);
926 keyEdgePair rEdge = make_pair(neighbor, neighbor->getReverseParent());
927 if (!iSolution_)
928 {
929 if (!fValid_)
930 {
931 fValid_ = SCD(fEdge);
932 }
933 rValid_ = SCD(rEdge);
934 }
935
936 // If the promising edge could be valid and offer a better solution, operate associated
937 // updating
938 if ((fValid_ && rValid_) || iSolution_)
939 {
940 auto Totalcost =
941 objective_->combineCosts(est_gValue, neighbor->getCostToComeFromGoal());
942 lookingForBestNeighbor(Totalcost, neighbor->getId());
943 supposeMeet = true;
944 if (betterThan(Totalcost, C_curr) &&
945 (iSolution_ || ((fValid_ = CCD(fEdge)) && (rValid_ = CCD(rEdge))))) //
946 {
947 updateBestSolutionFoundSoFar(neighbor, Totalcost, est_gValue, hValue,
948 neighbor->getCostToComeFromGoal());
949 insertOrUpdateInForwardVertexQueue(neighbor, est_gValue, hValue,
950 vertex->meetVertex());
951 continue;
952 }
953 }
954
955 // Label states if the promising edge is invalid.
956 if (!iSolution_ && (!fValid_ || !rValid_))
957 {
958 neighbor->setNearObstacle();
959 auto backwardParent = neighbor->getReverseParent();
960 if (!fValid_)
961 {
962 resetForwardParentAndRemeberTheVertex(neighbor, vertex);
963 }
964 else
965 {
966 updateCostToGo(est_gValue, hValue, hValue, false);
967 insertOrUpdateInForwardVertexQueue(neighbor, est_gValue, hValue,
968 vertex->meetVertex());
969 }
970 if (!rValid_)
971 {
972 resetReverseParentAndRemeberTheVertex(neighbor, backwardParent);
973 }
974 continue;
975 }
976 }
977
978 // Refine the heuristic value on-the-fly and insert it into the forward queue
979 updateCostToGo(est_gValue, hValue, hValue, false);
980 if (iSolution_ && !neighbor->hasReverseParent() && largerThan(hValue, solutionCost_))
981 {
982 continue;
983 }
984 insertOrUpdateInForwardVertexQueue(neighbor, est_gValue, hValue,
985 vertex->meetVertex() && !supposeMeet);
986 }
987 }
988 }
989
990 // Check whether the current vertex is the meeting vertex.
991 found_meeting_ = (vertex->meetVertex() && betterThan(minimalneighbor_, C_curr)) ? true : false;
992
993 // Check if its valid reverse parent can offer a better solution
994 if (iSolution_ && vertex->hasReverseEdgeParent())
995 {
996 auto reverseParent = vertex->getReverseEdgeParent();
997 auto edgeCost = vertex->getValidReverseEdgeCost();
998 auto curValue = objective_->combineCosts(vertex->getCostToComeFromStart(), edgeCost);
999 auto hValue = reverseParent->getLowerCostBoundToGoal();
1000 if (betterThan(curValue, reverseParent->getCostToComeFromStart()))
1001 {
1002 reverseParent->setCostToComeFromStart(curValue);
1003 reverseParent->setForwardVertexParent(vertex, edgeCost);
1004 vertex->resetReverseEdgeParent();
1005 vertex->addToForwardChildren(reverseParent);
1006 }
1007 updateForwardCost(reverseParent, reverseParent->getCostToComeFromStart(), hValue);
1008 insertOrUpdateInForwardVertexQueue(reverseParent, reverseParent->getCostToComeFromStart(), hValue,
1009 vertex->meetVertex());
1010 }
1011 }
1012
1013 void BLITstar::updateBestSolutionFoundSoFar(const std::shared_ptr<Vertex> &vertex, ompl::base::Cost meetCost,
1014 ompl::base::Cost costToCome, ompl::base::Cost &costToGo,
1015 ompl::base::Cost costFromOriginal)
1016 {
1017 if (V_meet.second)
1018 {
1019 V_meet.second->resetMeet();
1020 }
1021 vertex->setMeet();
1022 V_meet = make_pair(C_curr = meetCost, vertex);
1023 updateCostToGo(costToCome, costToGo, costFromOriginal, true);
1024 }
1025 void BLITstar::updateCostToGo(ompl::base::Cost &costToCome, ompl::base::Cost &costToGo,
1026 ompl::base::Cost costFromOriginal, bool meetOnPath)
1027 {
1028 if (betterThan(costToGo, costToCome))
1029 {
1030 costToGo = costToCome;
1031 }
1032 if (meetOnPath && betterThan(costFromOriginal, costToGo))
1033 {
1034 costToGo = costFromOriginal;
1035 }
1036 }
1037
1038 void BLITstar::updateForwardCost(const std::shared_ptr<blitstar::Vertex> &vertex, ompl::base::Cost costToCome,
1039 ompl::base::Cost &costToGo)
1040 {
1041 vertex->resetForwardExpanded();
1042 vertex->setForwardId(forwardId_);
1043 if (vertex->hasReverseParent() && vertex->getReverseId() == reverseId_)
1044 {
1045 auto bettersolution_ =
1046 objective_->combineCosts(vertex->getCostToComeFromGoal(), vertex->getCostToComeFromStart());
1047 if (betterThan(bettersolution_, C_curr))
1048 {
1049 updateBestSolutionFoundSoFar(vertex, bettersolution_, costToCome, costToGo,
1050 vertex->getCostToComeFromGoal());
1051 }
1052 else
1053 {
1054 updateCostToGo(costToCome, costToGo, vertex->getCostToComeFromGoal(), true);
1055 }
1056 }
1057 else
1058 {
1059 updateCostToGo(costToCome, costToGo, costToGo, false);
1060 }
1061 }
1062
1063 void BLITstar::updateReverseCost(const std::shared_ptr<blitstar::Vertex> &vertex, ompl::base::Cost costToCome,
1064 ompl::base::Cost &costToGo)
1065 {
1066 vertex->resetReverseExpanded();
1067 vertex->setReverseId(reverseId_);
1068 if (vertex->hasForwardParent() && vertex->getForwardId() == forwardId_)
1069 {
1070 auto bettersolution_ =
1071 objective_->combineCosts(vertex->getCostToComeFromGoal(), vertex->getCostToComeFromStart());
1072 if (betterThan(bettersolution_, C_curr))
1073 {
1074 updateBestSolutionFoundSoFar(vertex, bettersolution_, costToCome, costToGo,
1075 vertex->getCostToComeFromStart());
1076 }
1077 else
1078 {
1079 updateCostToGo(costToCome, costToGo, vertex->getCostToComeFromStart(), true);
1080 }
1081 }
1082 else
1083 {
1084 updateCostToGo(costToCome, costToGo, costToGo, false);
1085 }
1086 }
1087
1088 void BLITstar::EvaluateValidityStartAndToGoal(const std::shared_ptr<Vertex> &start,
1089 const std::shared_ptr<Vertex> &goal)
1090 {
1091 goalCloseToStart_ = true;
1092 if (CCD(make_pair(start, goal)))
1093 {
1094 solutionCost_ = objective_->motionCost(start->getState(), goal->getState());
1095 start->setReverseVertexParent(goal, solutionCost_);
1096 goal->setForwardVertexParent(start, solutionCost_);
1097 }
1098 }
1099
1100 void BLITstar::ReverseLazySearch(const std::shared_ptr<blitstar::Vertex> &vertex)
1101 {
1102 minimalneighbor_ = objective_->infiniteCost();
1103 for (const auto &child : vertex->getReverseChildren())
1104 {
1105 if ((!iSolution_ || vertex->nearObstacle() || child->nearObstacle()) && !SCD(make_pair(child, vertex)))
1106 {
1107 resetReverseParentAndRemeberTheVertex(child, vertex);
1108 continue;
1109 }
1110 auto edgeCost = child->getEdgeCostFromReverseParent();
1111 auto gValue = objective_->combineCosts(vertex->getCostToComeFromGoal(), edgeCost);
1112 if (iSolution_ && !child->hasForwardParent() &&
1113 largerThan(objective_->combineCosts(gValue, gValue), solutionCost_))
1114 {
1115 continue;
1116 }
1117 auto hValue = child->getLowerCostBoundToStart();
1118 child->setCostToComeFromGoal(gValue);
1119 gValue = child->getCostToComeFromGoal();
1120 updateReverseCost(child, gValue, hValue);
1121 insertOrUpdateInReverseVertexQueue(child, gValue, hValue, vertex->meetVertex());
1122 }
1123 bool Potential_collide_ = false;
1124 for (const auto &neighbor : graph_.getNeighbors(vertex))
1125 {
1126 if (neighbor->getId() == vertex->getId())
1127 {
1128 continue;
1129 }
1130 if (iSolution_ && !vertex->nearObstacle() && neighbor->nearObstacle())
1131 {
1132 Potential_collide_ = true;
1133 }
1134 if (reverseInvalid_ && neighbor->getReverseId() != reverseId_)
1135 {
1136 neighbor->resetReverseExpanded();
1137 neighbor->setCostToComeFromGoal(objective_->infiniteCost());
1138 }
1139 if (!neighbor->isReverseExpanded())
1140 {
1141 if (iSolution_ && vertex->hasForwardEdgeParent() &&
1142 neighbor->getId() == vertex->getForwardEdgeParent()->getId())
1143 {
1144 continue;
1145 }
1146 if (vertex->isGoal() && neighbor->isStart())
1147 {
1148 EvaluateValidityStartAndToGoal(vertex, neighbor);
1149 continue;
1150 }
1151 if (neighbor->hasReverseParent() && neighbor->getReverseParent()->getId() == vertex->getId())
1152 {
1153 continue;
1154 }
1155
1156 if (neighbor->isBlacklistedAsChild(vertex) || vertex->isBlacklistedAsChild(neighbor))
1157 {
1158 continue;
1159 }
1160 auto gValue =
1161 neighbor->hasReverseParent() ? neighbor->getCostToComeFromGoal() : objective_->infiniteCost();
1162 auto edgeCost = objective_->motionCostHeuristic(neighbor->getState(), vertex->getState());
1163 auto est_gValue = objective_->combineCosts(vertex->getCostToComeFromGoal(), edgeCost);
1164 if (betterThan(est_gValue, gValue) && !neighbor->isStart())
1165 {
1166 neighbor->setReverseId(reverseId_);
1167 if (!iSolution_ && !SCD(make_pair(neighbor, vertex)))
1168 {
1169 vertex->setNearObstacle();
1170 neighbor->setNearObstacle();
1171 neighbor->setReverseInvalid();
1172 neighbor->resetReverseExpanded();
1173 continue;
1174 }
1175 bool fValid_ = false, rValid_ = false, supposeMeet = false;
1176 bool goalCollid_ = goalCloseToStart_ && vertex->isGoal();
1177 if (goalCollid_ || Potential_collide_ || vertex->nearObstacle() || neighbor->nearObstacle())
1178 {
1179 if (!(rValid_ = SCD(make_pair(neighbor, vertex))) ||
1180 (neighbor->reverseInvalid() && !CCD(make_pair(neighbor, vertex))))
1181 {
1182 neighbor->setNearObstacle();
1183 vertex->setNearObstacle();
1184 neighbor->setReverseInvalid();
1185 neighbor->resetReverseExpanded();
1186 continue;
1187 }
1188 if (goalCollid_)
1189 {
1190 neighbor->setNearObstacle();
1191 }
1192 }
1193 neighbor->setCostToComeFromGoal(est_gValue);
1194 neighbor->setReverseVertexParent(vertex, edgeCost);
1195 vertex->addToReverseChildren(neighbor);
1196 auto hValue = neighbor->getLowerCostBoundToStart();
1197 auto getForwardpointer = neighbor->getForwardVertexQueuePointer();
1198 bool onForwardTree = !(neighbor->hasForwardParent() && neighbor->getForwardId() != forwardId_);
1199 if (!onForwardTree)
1200 {
1201 resetForwardValue(neighbor);
1202 }
1203
1204 if (onForwardTree && (getForwardpointer || neighbor->hasForwardParent()))
1205 {
1206 keyEdgePair rEdge = make_pair(neighbor, vertex);
1207 keyEdgePair fEdge = make_pair(neighbor->getForwardParent(), neighbor);
1208 if (!iSolution_)
1209 {
1210 if (!rValid_)
1211 {
1212 rValid_ = SCD(rEdge);
1213 }
1214 fValid_ = SCD(fEdge);
1215 }
1216 if (iSolution_ || (fValid_ && rValid_))
1217 {
1218 auto Totalcost =
1219 objective_->combineCosts(est_gValue, neighbor->getCostToComeFromStart());
1220 supposeMeet = true;
1221 lookingForBestNeighbor(Totalcost, neighbor->getId());
1222 if (betterThan(Totalcost, C_curr) &&
1223 (iSolution_ || ((fValid_ = CCD(fEdge)) && (rValid_ = CCD(rEdge))))) //
1224 {
1225 updateBestSolutionFoundSoFar(neighbor, Totalcost, est_gValue, hValue,
1226 neighbor->getCostToComeFromStart());
1227 insertOrUpdateInReverseVertexQueue(neighbor, est_gValue, hValue,
1228 vertex->meetVertex());
1229 continue;
1230 }
1231 }
1232 if (!iSolution_ && (!fValid_ || !rValid_))
1233 {
1234 neighbor->setNearObstacle();
1235 auto forwardParent = neighbor->getForwardParent();
1236 if (!fValid_)
1237 {
1238 resetForwardParentAndRemeberTheVertex(neighbor, forwardParent);
1239 }
1240
1241 if (!rValid_)
1242 {
1243 resetReverseParentAndRemeberTheVertex(neighbor, vertex);
1244 }
1245 else
1246 {
1247 updateCostToGo(est_gValue, hValue, hValue, false);
1248 insertOrUpdateInReverseVertexQueue(neighbor, est_gValue, hValue,
1249 vertex->meetVertex());
1250 }
1251 continue;
1252 }
1253 }
1254 updateCostToGo(est_gValue, hValue, hValue, false);
1255 if (iSolution_ && !neighbor->hasForwardParent() && largerThan(hValue, solutionCost_))
1256 {
1257 continue;
1258 }
1259 insertOrUpdateInReverseVertexQueue(neighbor, est_gValue, hValue,
1260 vertex->meetVertex() && !supposeMeet);
1261 }
1262 }
1263 }
1264 found_meeting_ = (vertex->meetVertex() && betterThan(minimalneighbor_, C_curr)) ? true : false;
1265 if (iSolution_ && vertex->hasForwardEdgeParent())
1266 {
1267 auto forwardParent = vertex->getForwardEdgeParent();
1268 auto edgeCost = vertex->getValidForwardEdgeCost();
1269 auto curValue = objective_->combineCosts(vertex->getCostToComeFromGoal(), edgeCost);
1270 auto hValue = forwardParent->getLowerCostBoundToStart();
1271 if (betterThan(curValue, forwardParent->getCostToComeFromGoal()))
1272 {
1273 forwardParent->setCostToComeFromGoal(curValue);
1274 vertex->resetForwardEdgeParent();
1275 forwardParent->setReverseVertexParent(vertex, edgeCost);
1276 vertex->addToReverseChildren(forwardParent);
1277 }
1278 updateReverseCost(forwardParent, forwardParent->getCostToComeFromGoal(), hValue);
1279 insertOrUpdateInReverseVertexQueue(forwardParent, forwardParent->getCostToComeFromGoal(), hValue,
1280 vertex->meetVertex());
1281 }
1282 }
1283
1284 void BLITstar::insertOrUpdateInForwardVertexQueue(const std::shared_ptr<blitstar::Vertex> &vertex,
1285 ompl::base::Cost CostToCome, ompl::base::Cost CostToGoal,
1286 bool couldMeet)
1287 {
1288 // Get the pointer to the element in the queue
1289 auto element = vertex->getForwardVertexQueuePointer();
1290 // Update it if it is in
1291 if (element)
1292 {
1293 element->data.first = computeEstimatedPathCosts(CostToCome, CostToGoal);
1294 forwardVertexQueue_.update(element);
1295 }
1296 else // Insert the pointer into the queue
1297 {
1298 std::pair<std::array<ompl::base::Cost, 2u>, std::shared_ptr<Vertex>> element(
1299 computeEstimatedPathCosts(CostToCome, CostToGoal), vertex);
1300 // Insert the vertex into the queue, storing the corresponding pointer.
1301 auto forwardQueuePointer = forwardVertexQueue_.insert(element);
1302 vertex->setForwardVertexQueuePointer(forwardQueuePointer);
1303 }
1304 if (couldMeet)
1305 bestNeighbor(CostToCome, CostToGoal, vertex->getId());
1306 }
1307
1308 std::shared_ptr<ompl::geometric::PathGeometric>
1309 BLITstar::getPathToVertex(const std::shared_ptr<Vertex> &vertex) const
1310 {
1311 // Create the reverse path by following the parents in forward tree to the start from the meeting state.
1312 std::vector<std::shared_ptr<Vertex>> reversePath;
1313 auto current = vertex;
1314 while (!graph_.isStart(current))
1315 {
1316 reversePath.emplace_back(current);
1317 current = current->getForwardParent();
1318 }
1319 reversePath.emplace_back(current);
1320
1321 // Reverse the reverse path to get the forward path.
1322 auto path = std::make_shared<ompl::geometric::PathGeometric>(Planner::si_);
1323 for (const auto &vertex_ : boost::adaptors::reverse(reversePath))
1324 {
1325 path->append(vertex_->getState());
1326 }
1327 reversePath.clear();
1328 // Trace back the forward path by following the parents in reverse tree to goal from the meeting state.
1329 current = vertex;
1330 while (!graph_.isGoal(current))
1331 {
1332 reversePath.emplace_back(current);
1333 current = current->getReverseParent();
1334 }
1335 reversePath.emplace_back(current);
1336 for (const auto &vertex_ : reversePath)
1337 {
1338 if (vertex_->getId() != vertex->getId())
1339 {
1340 path->append(vertex_->getState());
1341 }
1342 }
1343 return path;
1344 }
1345
1346 std::array<ompl::base::Cost, 3u> BLITstar::computeEstimatedPathCosts(ompl::base::Cost CostToStart,
1347 ompl::base::Cost CostToGoal,
1348 ompl::base::Cost motionCost) const
1349 {
1350 // f = g(v) + c(v,w)+ h(w); g(x) = g(parent(x))+c(parent(x),x)
1351 return {objective_->combineCosts(CostToStart, CostToGoal), CostToStart, motionCost};
1352 }
1353
1354 std::array<ompl::base::Cost, 2u> BLITstar::computeEstimatedPathCosts(ompl::base::Cost CostToStart,
1355 ompl::base::Cost CostToGoal) const
1356 {
1357 // f = g(x) + h(x); g(x) = g(parent(x))+c(parent(x),x)
1358 return {objective_->combineCosts(CostToStart, CostToGoal), CostToStart};
1359 }
1360
1361 void BLITstar::updateExactSolution()
1362 {
1363 // Create a solution.
1364 ompl::base::PlannerSolution solution(path_);
1365 solution.setPlannerName(name_);
1366
1367 // Set the optimized flag.
1368 solution.setOptimized(objective_, solutionCost_, objective_->isSatisfied(solutionCost_));
1369
1370 // Let the problem definition know that a new solution exists.
1371 pdef_->addSolutionPath(solution);
1372
1373 // Let the user know about the new solution.
1374 informAboutNewSolution();
1375 }
1376
1377 ompl::base::PlannerStatus::StatusType BLITstar::updateSolution()
1378 {
1379 updateExactSolution();
1380
1381 if (objective_->isFinite(solutionCost_))
1382 {
1384 }
1385 else if (trackApproximateSolutions_)
1386 {
1388 }
1389 else
1390 {
1392 }
1393 }
1394
1395 ompl::base::Cost BLITstar::lowerboundToStart(const std::shared_ptr<Vertex> &vertex) const
1396 {
1397 const auto &start = graph_.getStartVertices()[0u];
1398 return objective_->motionCostHeuristic(vertex->getState(), start->getState());
1399 }
1400
1401 ompl::base::Cost BLITstar::lowerboundToGoal(const std::shared_ptr<Vertex> &vertex) const
1402 {
1403 const auto &goal = graph_.getGoalVertices()[0u];
1404 return objective_->motionCostHeuristic(vertex->getState(), goal->getState());
1405 }
1406
1407 bool BLITstar::SelectExpandState(bool &forwardDirection_) //
1408 {
1409 searchExhausted_ = start_scratch_ = found_meeting_ = false;
1410 if (found_meeting_)
1411 {
1412 return true;
1413 }
1414
1415 // Check if the search is exhausted in either direction
1416 if (forwardVertexQueue_.empty() || reverseVertexQueue_.empty())
1417 {
1418 C_curr = solutionCost_;
1419 return (isVertexEmpty_ = (searchExhausted_ = true));
1420 }
1421
1422 auto forwardVertex_ = forwardVertexQueue_.top()->data.second;
1423 auto backwardVertex_ = reverseVertexQueue_.top()->data.second;
1424 ForwardCost = forwardVertexQueue_.top()->data.first[0u];
1425 ReverseCost = reverseVertexQueue_.top()->data.first[0u];
1426
1427 // Check if the best vertex is on an obsolete search direction
1428 if (forwardInvalid_ && forwardVertex_->getForwardId() != forwardId_)
1429 {
1430 forwardVertexQueue_.pop();
1431 resetForwardValue(forwardVertex_);
1432 return false;
1433 }
1434
1435 if (reverseInvalid_ && backwardVertex_->getReverseId() != reverseId_)
1436 {
1437 reverseVertexQueue_.pop();
1438 resetReverseValue(backwardVertex_);
1439 return false;
1440 }
1441
1442 // Select the vertex with the lowest priority from both queues for expansion
1443 if (betterThan(ForwardCost, ReverseCost))
1444 {
1445 BestVertex_ = forwardVertexQueue_.top()->data.second;
1446 forwardVertexQueue_.pop();
1447 BestVertex_->resetForwardVertexQueuePointer();
1448 fmin_ = ForwardCost;
1449 if (BestVertex_->isForwardExpanded())
1450 {
1451 return false;
1452 }
1453 forwardDirection_ = true;
1454 }
1455 else
1456 {
1457 BestVertex_ = reverseVertexQueue_.top()->data.second;
1458 reverseVertexQueue_.pop();
1459 BestVertex_->resetReverseVertexQueuePointer();
1460 fmin_ = ReverseCost;
1461 if (BestVertex_->isReverseExpanded())
1462 {
1463 return false;
1464 }
1465 }
1466 return true;
1467 }
1468
1469 bool BLITstar::SCD(const keyEdgePair &edge)
1470 {
1471 return isValidAtResolution(edge, numSparseCollisionChecksCurrentLevel_, true);
1472 }
1473
1474 bool BLITstar::CCD(const keyEdgePair &edge)
1475 {
1476 return isValidAtResolution(edge, space_->validSegmentCount(edge.first->getState(), edge.second->getState()),
1477 iSolution_);
1478 }
1479
1480 bool BLITstar::isValidAtResolution(const keyEdgePair &edge, std::size_t numChecks, bool sparseCheck)
1481 {
1482 auto parent = edge.first;
1483 auto child = edge.second;
1484 // Check if the edge is whitelisted.
1485 if (parent->isWhitelistedAsChild(child))
1486 {
1487 return true;
1488 }
1489
1490 // If the edge is blacklisted.
1491 if (child->isBlacklistedAsChild(parent))
1492 {
1493 return false;
1494 }
1495
1496 // Get the segment count for the full resolution.
1497 const std::size_t fullSegmentCount = space_->validSegmentCount(parent->getState(), child->getState());
1498
1499 // The segment count is the number of checks on this level plus 1, capped by the full resolution segment
1500 // count.
1501 const auto segmentCount = std::min(numChecks + 1u, fullSegmentCount);
1502
1503 // Store the current check number.
1504 std::size_t currentCheck = 1u;
1505
1506 // Get the number of checks already performed on this edge.
1507 const std::size_t performedChecks = child->getIncomingCollisionCheckResolution(parent->getId());
1508 // Initialize the queue of positions to be tested.
1509 std::queue<std::pair<std::size_t, std::size_t>> indices;
1510 indices.emplace(1u, numChecks);
1511
1512 // Test states while there are states to be tested.
1513 while (!indices.empty())
1514 {
1515 // Get the current segment.
1516 const auto current = indices.front();
1517
1518 // Get the midpoint of the segment.
1519 auto mid = (current.first + current.second) / 2;
1520 // Only do the detection if we haven't tested this state on a previous level.
1521 if (currentCheck > performedChecks)
1522 {
1523 space_->interpolate(parent->getState(), child->getState(),
1524 static_cast<double>(mid) / static_cast<double>(segmentCount), detectionState_);
1525 if (!spaceInformation_->isValid(detectionState_))
1526 {
1527 // Blacklist the edge.
1528 parent->blacklistAsChild(child);
1529 child->blacklistAsChild(parent);
1530
1531 // Update the maximum sparse collision detection resolution if a new level is reached
1532 if (!sparseCheck && currentCheck > numSparseCollisionChecksCurrentLevel_)
1533 {
1534 numSparseCollisionChecksCurrentLevel_ = currentCheck + 1u;
1535 }
1536 return false;
1537 }
1538 }
1539
1540 // Remove the current segment from the queue.
1541 indices.pop();
1542
1543 // Create the first and second half of the split segment if necessary.
1544 if (current.first < mid)
1545 {
1546 indices.emplace(current.first, mid - 1u);
1547 }
1548
1549 if (current.second > mid)
1550 {
1551 indices.emplace(mid + 1u, current.second);
1552 }
1553
1554 // Increase the current check number.
1555 ++currentCheck;
1556 }
1557
1558 // Remember at what resolution this edge was already checked. We're assuming that the number of collision
1559 // checks is symmetric for each edge.
1560 parent->setIncomingCollisionCheckResolution(child->getId(), currentCheck - 1u);
1561 child->setIncomingCollisionCheckResolution(parent->getId(), currentCheck - 1u);
1562
1563 // Whitelist this edge if it was checked at full resolution.
1564 if (segmentCount == fullSegmentCount)
1565 {
1566 ++numCollisionCheckedEdges_;
1567 parent->whitelistAsChild(child);
1568 child->whitelistAsChild(parent);
1569 }
1570 return true;
1571 }
1572 } // namespace geometric
1573} // namespace ompl
Element * insert(const _T &data)
Add a new element.
Definition BinaryHeap.h:140
void update(Element *element)
Update an element in the heap.
Definition BinaryHeap.h:186
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
double value() const
The value of the cost.
Definition Cost.h:56
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
virtual unsigned int validSegmentCount(const State *state1, const State *state2) const
Count how many segments of the "longest valid length" fit on the motion from state1 to state2.
bool getUseKNearest() const
Get whether to use a k-nearest RGG connection model. If false, BLIT* uses an r-disc model.
Definition BLITstar.cpp:343
ompl::base::PlannerStatus solve(const ompl::base::PlannerTerminationCondition &terminationCondition) override
Solves a motion planning problem.
Definition BLITstar.cpp:227
void enablePruning(bool prune)
Set whether pruning is enabled or not.
Definition BLITstar.cpp:328
void setup() override
Additional setup that can only be done once a problem definition is set.
Definition BLITstar.cpp:108
void EvaluateValidityStartAndToGoal(const std::shared_ptr< blitstar::Vertex > &start, const std::shared_ptr< blitstar::Vertex > &goal)
Checking the collision detection between start and goal vertices.
void resetReverseValue(const std::shared_ptr< blitstar::Vertex > &vertex)
Reset a vertex's value.
Definition BLITstar.cpp:681
bool PathValidity(std::shared_ptr< blitstar::Vertex > &vertex)
Checking the validity of a path from each direction.
Definition BLITstar.cpp:574
bool isPruningEnabled() const
Get whether pruning is enabled or not.
Definition BLITstar.cpp:333
void ForwardLazySearch(const std::shared_ptr< blitstar::Vertex > &vertex)
Forward and Reverse Search.
Definition BLITstar.cpp:794
void updateReverseCost(const std::shared_ptr< blitstar::Vertex > &vertex, ompl::base::Cost costToCome, ompl::base::Cost &costToGo)
Refine heuristics on-the-fly.
void setUseKNearest(bool useKNearest)
Set whether to use a k-nearest RGG connection model. If false, BLIT* uses an r-disc model.
Definition BLITstar.cpp:338
void clear() override
Clears the algorithm's internal state.
Definition BLITstar.cpp:218
void resetForwardParentInformation(const std::shared_ptr< blitstar::Vertex > &vertex)
Reset parent vertex's information.
Definition BLITstar.cpp:652
bool SelectExpandState(bool &forward)
Select the vertex with minimal priority on both trees.
unsigned int getMaxNumberOfGoals() const
Get the maximum number of goals BLIT* will sample from sampleable goal regions.
Definition BLITstar.cpp:353
void insertGoalVerticesInReverseVertexQueue()
Insert start and goal vertices into the queues.
Definition BLITstar.cpp:512
bool isValidAtResolution(const blitstar::keyEdgePair &edge, std::size_t numChecks, bool sparseCheck)
Checking the collision detection.
ompl::base::PlannerStatus::StatusType ensureStartAndGoalStates(const ompl::base::PlannerTerminationCondition &terminationCondition)
Checks whether the problem is successfully setup.
Definition BLITstar.cpp:186
void lookingForBestNeighbor(ompl::base::Cost curMin_, size_t neighbor)
Look for a neighbor with the minimal priority.
Definition BLITstar.cpp:532
std::size_t getBatchSize() const
Get the batch size.
Definition BLITstar.cpp:313
void setBatchSize(std::size_t batchSize)
Set the batch size.
Definition BLITstar.cpp:308
BLITstar(const ompl::base::SpaceInformationPtr &spaceInformation)
Constructs a BLIT*.
Definition BLITstar.cpp:64
bool SCD(const blitstar::keyEdgePair &edge)
Above references inherit from BLIT*.
void clearReverseVertexQueue()
Empty the queues.
Definition BLITstar.cpp:358
double getRewireFactor() const
Get the rewire factor of the RGG graph.
Definition BLITstar.cpp:323
void getPlannerData(base::PlannerData &data) const override
Get the planner data.
Definition BLITstar.cpp:264
void setMaxNumberOfGoals(unsigned int numberOfGoals)
Set the maximum number of goals BLIT* will sample from sampleable goal regions.
Definition BLITstar.cpp:348
void setRewireFactor(double rewireFactor)
Set the rewire factor of the RGG graph.
Definition BLITstar.cpp:318
ompl::base::PlannerStatus::StatusType ensureSetup()
Checks whether the planner is successfully setup.
Definition BLITstar.cpp:162
void insertOrUpdateInForwardVertexQueue(const std::shared_ptr< blitstar::Vertex > &vertex, ompl::base::Cost CostToCome, ompl::base::Cost CostToGoal, bool couldMeet)
Inserts or updates a vertex in the reverse queue.
bool terminateSearch()
Ensuring meet-in-the-middle and optimality to terminate the current search.
Definition BLITstar.cpp:697
~BLITstar()
Destructs a BLIT*.
Definition BLITstar.cpp:103
void updateBestSolutionFoundSoFar(const std::shared_ptr< blitstar::Vertex > &vertex, ompl::base::Cost meetCost, ompl::base::Cost costToCome, ompl::base::Cost &costToGo, ompl::base::Cost costFromOri)
Improve the current solution.
#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...
@ 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.
STL namespace.
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.