Loading...
Searching...
No Matches
AnytimePathShortening.cpp
1/*********************************************************************
2 * Software License Agreement (BSD License)
3 *
4 * Copyright (c) 2014, Rice University
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * * Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
17 * * Neither the name of the Rice University nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 *********************************************************************/
34
35/* Author: Ryan Luna */
36
37#include <algorithm> // for std::min
38#include <boost/algorithm/string.hpp>
39
40#include "ompl/geometric/planners/AnytimePathShortening.h"
41#include "ompl/geometric/planners/fmt/BFMT.h"
42#include "ompl/geometric/planners/est/BiEST.h"
43#include "ompl/geometric/planners/rrt/BiTRRT.h"
44#include "ompl/geometric/planners/informedtrees/BITstar.h"
45#include "ompl/geometric/planners/kpiece/BKPIECE1.h"
46#include "ompl/geometric/planners/est/EST.h"
47#include "ompl/geometric/planners/fmt/FMT.h"
48#include "ompl/geometric/planners/kpiece/KPIECE1.h"
49#include "ompl/geometric/planners/rrt/LazyLBTRRT.h"
50#include "ompl/geometric/planners/prm/LazyPRM.h"
51#include "ompl/geometric/planners/prm/LazyPRMstar.h"
52#include "ompl/geometric/planners/rrt/LazyRRT.h"
53#include "ompl/geometric/planners/kpiece/LBKPIECE1.h"
54#include "ompl/geometric/planners/rrt/LBTRRT.h"
55#include "ompl/geometric/planners/pdst/PDST.h"
56#include "ompl/geometric/planners/prm/PRM.h"
57#include "ompl/geometric/planners/prm/PRMstar.h"
58#include "ompl/geometric/planners/est/ProjEST.h"
59#include "ompl/geometric/planners/rrt/RRT.h"
60#include "ompl/geometric/planners/rrt/RRTConnect.h"
61#include "ompl/geometric/planners/rrt/RRTstar.h"
62#include "ompl/geometric/planners/rrt/RRTXstatic.h"
63#include "ompl/geometric/planners/sbl/SBL.h"
64#include "ompl/geometric/planners/prm/SPARS.h"
65#include "ompl/geometric/planners/prm/SPARStwo.h"
66#include "ompl/geometric/planners/sst/SST.h"
67#include "ompl/geometric/planners/stride/STRIDE.h"
68#include "ompl/geometric/planners/rrt/TRRT.h"
69#include "ompl/geometric/PathHybridization.h"
70#include "ompl/geometric/PathSimplifier.h"
71#include "ompl/tools/config/SelfConfig.h"
72#include "ompl/base/objectives/PathLengthOptimizationObjective.h"
73#include "ompl/util/String.h"
74
75ompl::geometric::AnytimePathShortening::AnytimePathShortening(const ompl::base::SpaceInformationPtr &si)
76 : ompl::base::Planner(si, "APS"), defaultNumPlanners_(std::max(1u, std::thread::hardware_concurrency()))
77{
78 specs_.approximateSolutions = true;
79 specs_.multithreaded = true;
80 specs_.optimizingPaths = true;
81
82 Planner::declareParam<bool>("shortcut", this, &AnytimePathShortening::setShortcut,
84 Planner::declareParam<bool>("hybridize", this, &AnytimePathShortening::setHybridize,
86 Planner::declareParam<unsigned int>("max_hybrid_paths", this, &AnytimePathShortening::setMaxHybridizationPath,
88 Planner::declareParam<unsigned int>("num_planners", this, &AnytimePathShortening::setDefaultNumPlanners,
90 Planner::declareParam<std::string>("planners", this, &AnytimePathShortening::setPlanners,
92
93 addPlannerProgressProperty("best cost REAL", [this] { return getBestCost(); });
94}
95
97
99{
100 if (planner && planner->getSpaceInformation().get() != si_.get())
101 {
102 OMPL_ERROR("NOT adding planner %s: SpaceInformation instances are different", planner->getName().c_str());
103 return;
104 }
105
106 // Ensure all planners are unique instances
107 for (auto &i : planners_)
108 {
109 if (planner.get() == i.get())
110 {
111 OMPL_ERROR("NOT adding planner %s: Planner instances MUST be unique", planner->getName().c_str());
112 return;
113 }
114 }
115
116 planners_.push_back(planner);
117}
118
119void ompl::geometric::AnytimePathShortening::addPath(const geometric::PathGeometricPtr &path, base::Planner *planner)
120{
121 const base::OptimizationObjectivePtr &opt(pdef_->getOptimizationObjective());
122 base::Cost pathCost = path->cost(opt);
123 std::lock_guard<std::mutex> _(lock_);
124 if (opt->isCostBetterThan(pathCost, bestCost_))
125 {
126 bestCost_ = pathCost;
127 pdef_->addSolutionPath(path, false, 0.0, planner->getName());
128 }
129 else if (planner != this)
130 pdef_->addSolutionPath(path, false, 0.0, planner->getName());
131}
132
135{
136 std::vector<std::thread> threads;
138
139 base::OptimizationObjectivePtr opt = pdef_->getOptimizationObjective();
140 if (!opt)
141 {
142 OMPL_INFORM("%s: No optimization objective specified. Defaulting to optimizing path length for the allowed "
143 "planning time.",
144 getName().c_str());
145 opt = std::make_shared<base::PathLengthOptimizationObjective>(si_);
146 pdef_->setOptimizationObjective(opt);
147 }
148
149 // Disable output from the motion planners, except for errors
150 msg::LogLevel currentLogLevel = msg::getLogLevel();
151 msg::setLogLevel(std::max(msg::LOG_ERROR, currentLogLevel));
152
153 // Clear any previous planning data for the set of planners
154 clear();
155 // Spawn a thread for each planner. This will shortcut the best path after solving.
156 for (auto &planner : planners_)
157 threads.emplace_back([this, planner, &ptc] { return threadSolve(planner.get(), ptc); });
158
160 geometric::PathGeometric *sln = nullptr, *prevLastPath = nullptr;
161 bestCost_ = opt->infiniteCost();
162 std::size_t prevSolCount = 0;
163 while (!ptc)
164 {
165 // We have found a solution that is good enough
166 if (opt->isSatisfied(bestCost_))
167 {
168 ptc.terminate();
169 break;
170 }
171
172 // Hybridize the set of paths computed. Add the new hybrid path to the mix.
173 unsigned int solCount = pdef_->getSolutionCount();
174 if (hybridize_ && !ptc && solCount > 1)
175 {
176 const std::vector<base::PlannerSolution> &paths = pdef_->getSolutions();
177 std::size_t numPaths = std::min(solCount, maxHybridPaths_);
178 geometric::PathGeometric *lastPath = static_cast<PathGeometric *>(paths[numPaths - 1].path_.get());
179 // check if new solution paths have been added to top numPaths paths
180 if (lastPath != prevLastPath || (prevSolCount < solCount && solCount <= maxHybridPaths_))
181 {
182 for (size_t j = 0; j < numPaths && !ptc; ++j)
183 phybrid.recordPath(std::static_pointer_cast<PathGeometric>(paths[j].path_), false);
184
185 phybrid.computeHybridPath();
186 sln = phybrid.getHybridPath().get();
187 prevLastPath = lastPath;
188 }
189 else
190 sln = static_cast<PathGeometric *>(pdef_->getSolutionPath().get());
191 prevSolCount = solCount;
192 }
193 else if (solCount > 0)
194 sln = static_cast<PathGeometric *>(pdef_->getSolutionPath().get());
195
196 if (sln)
197 {
198 auto pathCopy(std::make_shared<geometric::PathGeometric>(*sln));
199 if (shortcut_) // Shortcut the path
200 if (!ps.simplify(*pathCopy, ptc, true))
201 // revert if shortcutting failed
202 pathCopy = std::make_shared<geometric::PathGeometric>(*sln);
203 addPath(pathCopy, this);
204 }
205 if (hybridize_ && phybrid.pathCount() >= maxHybridPaths_)
206 phybrid.clear();
207 }
208
209 for (auto &thread : threads)
210 thread.join();
211
212 msg::setLogLevel(currentLogLevel);
216 else if (invalidGoalCount_ > 0)
218 return (pdef_->getSolutionCount() > 0) ? base::PlannerStatus::EXACT_SOLUTION : status;
219}
220
223{
224 // create a local clone of the problem definition where we keep at most one solution
225 auto pdef = pdef_->clone();
227
228 planner->setProblemDefinition(pdef);
229 while (!ptc)
230 {
231 const base::PlannerStatus &status = planner->solve(ptc);
233 {
234 geometric::PathGeometric *sln = static_cast<geometric::PathGeometric *>(pdef->getSolutionPath().get());
235 auto pathCopy(std::make_shared<geometric::PathGeometric>(*sln));
236 if (shortcut_) // Shortcut the path
237 ps.partialShortcutPath(*pathCopy);
238 addPath(pathCopy, planner);
239 }
240 else if ((status == base::PlannerStatus::INVALID_START) || (status == base::PlannerStatus::INVALID_GOAL) ||
242 {
243 // there is not point in trying again with these error types that will repeat.
244 {
245 std::lock_guard<std::mutex> _(invalidStartOrGoalLock_);
247 {
249 }
250 else if (status == base::PlannerStatus::INVALID_GOAL)
251 {
253 }
254 }
255 planner->clear();
256 pdef->clearSolutionPaths();
257 break;
258 }
259
260 planner->clear();
261 pdef->clearSolutionPaths();
262 }
263}
264
266{
267 Planner::clear();
268 for (auto &planner : planners_)
269 planner->clear();
270 bestCost_ = base::Cost(std::numeric_limits<double>::quiet_NaN());
273}
274
276{
277 if (planners_.empty())
278 return;
279
280 OMPL_WARN("Returning planner data for planner #0");
281 getPlannerData(data, 0);
282}
283
285{
286 if (planners_.size() < idx)
287 return;
288 planners_[idx]->getPlannerData(data);
289}
290
292{
293 Planner::setup();
294
295 if (planners_.empty())
296 {
298 for (unsigned int i = 0; i < defaultNumPlanners_; ++i)
299 {
301 planners_.back()->setProblemDefinition(pdef_);
302 }
303 OMPL_INFORM("%s: No planners specified; using %u instances of %s", getName().c_str(), planners_.size(),
304 planners_[0]->getName().c_str());
305 }
306
307 for (auto &planner : planners_)
308 planner->setup();
309}
310
312{
313 for (auto &planner : planners_)
314 planner->checkValidity();
315}
316
318{
319 return planners_.size();
320}
321
322ompl::base::PlannerPtr ompl::geometric::AnytimePathShortening::getPlanner(unsigned int idx) const
323{
324 assert(idx < planners_.size());
325 return planners_[idx];
326}
327
332
334{
335 shortcut_ = shortcut;
336}
337
342
344{
345 hybridize_ = hybridize;
346}
347
352
354{
355 maxHybridPaths_ = maxPathCount;
356}
357
359{
360 defaultNumPlanners_ = numPlanners;
361}
362
367
369{
370 return ompl::toString(bestCost_.value());
371}
372
373void ompl::geometric::AnytimePathShortening::setPlanners(const std::string &plannerList)
374{
375 std::vector<std::string> plannerStrings;
376
377 boost::split(plannerStrings, plannerList, boost::is_any_of(","));
378
379 planners_.clear();
380 for (const auto &plannerString : plannerStrings)
381 {
382 std::vector<std::string> plannerAndParams;
383 boost::split(plannerAndParams, plannerString, boost::is_any_of("[ ]"));
384 const std::string &plannerName = plannerAndParams[0];
385
386 if (plannerName == "BFMT")
387 planners_.push_back(std::make_shared<geometric::BFMT>(si_));
388 else if (plannerName == "BiEST")
389 planners_.push_back(std::make_shared<geometric::BiEST>(si_));
390 else if (plannerName == "BiTRRT")
391 planners_.push_back(std::make_shared<geometric::BiTRRT>(si_));
392 else if (plannerName == "BITstar")
393 planners_.push_back(std::make_shared<geometric::BITstar>(si_));
394 else if (plannerName == "BKPIECE")
395 planners_.push_back(std::make_shared<geometric::BKPIECE1>(si_));
396 else if (plannerName == "EST")
397 planners_.push_back(std::make_shared<geometric::EST>(si_));
398 else if (plannerName == "FMT")
399 planners_.push_back(std::make_shared<geometric::FMT>(si_));
400 else if (plannerName == "KPIECE")
401 planners_.push_back(std::make_shared<geometric::KPIECE1>(si_));
402 else if (plannerName == "LazyLBTRRT")
403 planners_.push_back(std::make_shared<geometric::LazyLBTRRT>(si_));
404 else if (plannerName == "LazyPRM")
405 planners_.push_back(std::make_shared<geometric::LazyPRM>(si_));
406 else if (plannerName == "LazyPRMstar")
407 planners_.push_back(std::make_shared<geometric::LazyPRMstar>(si_));
408 else if (plannerName == "LazyRRT")
409 planners_.push_back(std::make_shared<geometric::LazyRRT>(si_));
410 else if (plannerName == "LBKPIECE")
411 planners_.push_back(std::make_shared<geometric::LBKPIECE1>(si_));
412 else if (plannerName == "LBTRRT")
413 planners_.push_back(std::make_shared<geometric::LBTRRT>(si_));
414 else if (plannerName == "PDST")
415 planners_.push_back(std::make_shared<geometric::PDST>(si_));
416 else if (plannerName == "PRM")
417 planners_.push_back(std::make_shared<geometric::PRM>(si_));
418 else if (plannerName == "PRMstar")
419 planners_.push_back(std::make_shared<geometric::PRMstar>(si_));
420 else if (plannerName == "ProjEST")
421 planners_.push_back(std::make_shared<geometric::ProjEST>(si_));
422 else if (plannerName == "RRT")
423 planners_.push_back(std::make_shared<geometric::RRT>(si_));
424 else if (plannerName == "RRTConnect")
425 planners_.push_back(std::make_shared<geometric::RRTConnect>(si_));
426 else if (plannerName == "RRTstar")
427 planners_.push_back(std::make_shared<geometric::RRTstar>(si_));
428 else if (plannerName == "RRTXstatic")
429 planners_.push_back(std::make_shared<geometric::RRTXstatic>(si_));
430 else if (plannerName == "SBL")
431 planners_.push_back(std::make_shared<geometric::SBL>(si_));
432 else if (plannerName == "SPARS")
433 planners_.push_back(std::make_shared<geometric::SPARS>(si_));
434 else if (plannerName == "SPARStwo")
435 planners_.push_back(std::make_shared<geometric::SPARStwo>(si_));
436 else if (plannerName == "SST")
437 planners_.push_back(std::make_shared<geometric::SST>(si_));
438 else if (plannerName == "STRIDE")
439 planners_.push_back(std::make_shared<geometric::STRIDE>(si_));
440 else if (plannerName == "TRRT")
441 planners_.push_back(std::make_shared<geometric::TRRT>(si_));
442 else
443 OMPL_ERROR("Unknown planner name: %s", plannerName.c_str());
444
445 std::vector<std::string> paramValue;
446 for (auto it = plannerAndParams.begin() + 1; it != plannerAndParams.end(); ++it)
447 if (!it->empty())
448 {
449 boost::split(paramValue, *it, boost::is_any_of("="));
450 planners_.back()->params().setParam(paramValue[0], paramValue[1]);
451 }
452 }
453}
454
456{
457 std::stringstream ss;
458 for (unsigned int i = 0; i < planners_.size(); ++i)
459 {
460 if (i > 0)
461 ss << ',';
462 ss << planners_[i]->getName();
463
464 std::map<std::string, std::string> params;
465 planners_[i]->params().getParams(params);
466 if (params.size() > 0)
467 {
468 ss << '[';
469 for (auto it = params.begin(); it != params.end(); ++it)
470 {
471 if (it != params.begin())
472 ss << ' ';
473 ss << it->first << '=' << it->second;
474 }
475 ss << ']';
476 }
477 }
478 return ss.str();
479}
480
482{
483 Planner::printSettings(out);
484 out << "Settings for planner instances in AnytimePathShortening instance:\n";
485 for (const auto &planner : planners_)
486 {
487 out << "* ";
488 planner->printSettings(out);
489 }
490}
Definition of a cost value. Can represent the cost of a motion or the cost of a state.
Definition Cost.h:48
Object containing planner generated vertex and edge data. It is assumed that all vertices are unique,...
Encapsulate a termination condition for a motion planner. Planners will call operator() to decide whe...
void terminate() const
Notify that the condition for termination should become true, regardless of what eval() returns....
Base class for a planner.
Definition Planner.h:216
ParamSet & params()
Get the parameters for this planner.
Definition Planner.h:336
void addPlannerProgressProperty(const std::string &progressPropertyName, const PlannerProgressProperty &prop)
Add a planner progress property called progressPropertyName with a property querying function prop to...
Definition Planner.h:394
PlannerSpecs specs_
The specifications of the planner (its capabilities).
Definition Planner.h:413
ProblemDefinitionPtr pdef_
The user set problem definition.
Definition Planner.h:404
virtual void clear()
Clear all internal datastructures. Planner settings are not affected. Subsequent calls to solve() wil...
Definition Planner.cpp:118
const std::string & getName() const
Get the name of the planner.
Definition Planner.cpp:56
SpaceInformationPtr si_
The space information for which planning is done.
Definition Planner.h:401
virtual void setProblemDefinition(const ProblemDefinitionPtr &pdef)
Set the problem definition for the planner. The problem needs to be set before calling solve()....
Definition Planner.cpp:81
virtual PlannerStatus solve(const PlannerTerminationCondition &ptc)=0
Function that can solve the motion planning problem. This function can be called multiple times on th...
bool hybridize_
Flag indicating whether to hybridize the set of solution paths.
base::PlannerPtr getPlanner(unsigned int idx) const
Retrieve a pointer to the ith planner instance.
~AnytimePathShortening() override
Destructor.
unsigned int defaultNumPlanners_
The number of planners to use if none are specified. This defaults to the number of cores....
void clear() override
Clear all internal planning datastructures. Planner settings are not affected. Subsequent calls to so...
std::string getBestCost() const
Return best cost found so far by algorithm.
void setPlanners(const std::string &plannerList)
Set the list of planners to use.
void getPlannerData(base::PlannerData &data) const override
Get information about the most recent run of the motion planner.
unsigned int maxHybridizationPaths() const
Return the maximum number of paths that will be hybridized.
virtual void threadSolve(base::Planner *planner, const base::PlannerTerminationCondition &ptc)
The function that the planning threads execute when solving a motion planning problem.
void setDefaultNumPlanners(unsigned int numPlanners)
Set default number of planners to use if none are specified.
bool shortcut_
Flag indicating whether to shortcut paths.
unsigned int maxHybridPaths_
The maximum number of paths that will be hybridized. This prohibits hybridization of a very large pat...
AnytimePathShortening(const base::SpaceInformationPtr &si)
Constructor requires the space information to plan in.
base::PlannerStatus solve(const base::PlannerTerminationCondition &ptc) override
Method that solves the motion planning problem. This method terminates under just two conditions,...
std::mutex invalidStartOrGoalLock_
Mutex for invalidStartStateCount_, invalidGoalCount_.
void printSettings(std::ostream &out) const override
Print settings of this planner as well as those of the planner instances it contains.
std::mutex lock_
mutex for updating bestCost_
void setup() override
Perform any necessary configuration steps. This method also invokes ompl::base::SpaceInformation::set...
bool isShortcutting() const
Return whether the anytime planner will perform shortcutting on paths.
bool isHybridizing() const
Return whether the anytime planner will extract a hybrid path from the set of solution paths.
unsigned int invalidStartStateCount_
The number of times (1 per planner) INVALID_START_STATE is returned by a planner.
void setMaxHybridizationPath(unsigned int maxPathCount)
Set the maximum number of paths that will be hybridized.
std::string getPlanners() const
Get a string representation of the planners and their parameters in the format of setPlanners.
void addPath(const geometric::PathGeometricPtr &path, base::Planner *planner)
add a path to set of solutions
unsigned int getNumPlanners() const
Retrieve the number of planners added.
unsigned int invalidGoalCount_
The number of times (1 per planner) INVALID_GOAL is returned by a planner.
void addPlanner(base::PlannerPtr &planner)
Adds the given planner to the set of planners used to compute candidate paths.
base::Cost bestCost_
Best cost found so far by algorithm.
unsigned int getDefaultNumPlanners() const
Get default number of planners used if none are specified.
void setHybridize(bool hybridize)
Enable/disable path hybridization on the set of solution paths.
std::vector< base::PlannerPtr > planners_
The list of planners used for solving the problem.
void setShortcut(bool shortcut)
Enable/disable shortcutting on paths.
void checkValidity() override
Check to see if the planners are in a working state (setup has been called, a goal was set,...
Definition of a geometric path.
Given multiple geometric paths, attempt to combine them in order to obtain a shorter solution.
const geometric::PathGeometricPtr & getHybridPath() const
Get the currently computed hybrid path. computeHybridPath() needs to have been called before.
void clear()
Clear all the stored paths.
void computeHybridPath()
Run Dijkstra's algorithm to find out the lowest-cost path among the mixed ones.
unsigned int recordPath(const geometric::PathGeometricPtr &pp, bool matchAcrossGaps)
Add a path to the hybridization. If matchAcrossGaps is true, more possible edge connections are evalu...
std::size_t pathCount() const
Get the number of paths that are currently considered as part of the hybridization.
This class contains routines that attempt to simplify geometric paths.
bool simplify(PathGeometric &path, double maxTime, bool atLeastOnce=true)
Run simplification algorithms on the path for at most maxTime seconds, and at least once if atLeastOn...
bool partialShortcutPath(PathGeometric &path, unsigned int maxSteps=0, unsigned int maxEmptySteps=0, double rangeRatio=0.33, double snapToVertex=0.005)
Given a path, attempt to shorten it while maintaining its validity. This is an iterative process that...
static base::PlannerPtr getDefaultPlanner(const base::GoalPtr &goal)
Given a goal specification, decide on a planner for that goal.
#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...
void setLogLevel(LogLevel level)
Set the minimum level of logging data to output. Messages with lower logging levels will not be recor...
Definition Console.cpp:136
LogLevel
The set of priorities for message logging.
Definition Console.h:85
LogLevel getLogLevel()
Retrieve the current level of logging data. Messages with lower logging levels will not be recorded.
Definition Console.cpp:142
Main namespace. Contains everything in this library.
std::string toString(float val)
convert float to string using classic "C" locale semantics
Definition String.cpp:82
STL namespace.
A class to store the exit status of Planner::solve().
StatusType
The possible values of the status returned by a planner.
@ 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.
@ UNKNOWN
Uninitialized status.