Loading...
Searching...
No Matches
Thunder.cpp
1/*********************************************************************
2 * Software License Agreement (BSD License)
3 *
4 * Copyright (c) 2014, JSK, The University of Tokyo.
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 JSK, The University of Tokyo 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: Dave Coleman */
36
37#include <ompl/tools/thunder/Thunder.h>
38#include <ompl/geometric/planners/rrt/RRTConnect.h>
39#include <ompl/base/PlannerStatus.h>
40#include <ompl/util/Console.h>
41
42namespace og = ompl::geometric;
43namespace ob = ompl::base;
44namespace ot = ompl::tools;
45
46ompl::tools::Thunder::Thunder(const base::SpaceInformationPtr &si) : ompl::tools::ExperienceSetup(si)
47{
48 initialize();
49}
50
51ompl::tools::Thunder::Thunder(const base::StateSpacePtr &space) : ompl::tools::ExperienceSetup(space)
52{
53 initialize();
54}
55
56void ompl::tools::Thunder::initialize()
57{
58 OMPL_INFORM("Initializing Thunder Framework");
59
60 filePath_ = "unloaded";
61
62 // Load the experience database
63 experienceDB_ = std::make_shared<ompl::tools::ThunderDB>(si_->getStateSpace());
64
65 // Load the Retrieve repair database. We do it here so that setRepairPlanner() works
66 rrPlanner_ = std::make_shared<og::ThunderRetrieveRepair>(si_, experienceDB_);
67
68 OMPL_INFORM("Thunder Framework initialized.");
69}
70
72{
73 if (filePath_ == "unloaded" || filePath_.empty())
74 {
75 OMPL_WARN("Database filepath has not been set. Unable to setup!");
76 return;
77 }
78
79 if (!configured_ || !si_->isSetup() || !planner_->isSetup() || !rrPlanner_->isSetup())
80 {
81 SimpleSetup::setup();
82
83 // Decide if we should setup the second planning from scratch planner for benchmarking w/o recall
85 {
86 // Setup planning from scratch planner 2
87 if (!planner2_)
88 {
89 if (pa_)
90 planner2_ = pa_(si_);
91 if (!planner2_)
92 {
93 OMPL_INFORM("Getting default planner: ");
94 planner2_ = std::make_shared<ompl::geometric::RRTConnect>(si_);
95 // This was disabled because i like to use Thunder / SPARSdb without setting a goal definition
96 // planner2_ = ompl::geometric::getDefaultPlanner(pdef_->getGoal()); // we could use the
97 // repairProblemDef_ here but that isn't setup yet
98
99 OMPL_INFORM("No planner 2 specified. Using default: %s", planner2_->getName().c_str());
100 }
101 }
102 planner2_->setProblemDefinition(pdef_);
103 if (!planner2_->isSetup())
104 planner2_->setup();
105 }
106
107 // Setup planning from experience planner
108 rrPlanner_->setProblemDefinition(pdef_);
109
110 if (!rrPlanner_->isSetup())
111 rrPlanner_->setup();
112
113 // Create the parallel component for splitting into two threads
114 pp_ = std::make_shared<ot::ParallelPlan>(pdef_);
116 {
117 throw Exception("Both planning from scratch and experience have been disabled, unable to plan");
118 }
119 if (recallEnabled_)
120 pp_->addPlanner(rrPlanner_); // Add the planning from experience planner if desired
121 if (scratchEnabled_)
122 pp_->addPlanner(planner_); // Add the planning from scratch planner if desired
124 {
125 OMPL_INFORM("Adding second planning from scratch planner");
126 pp_->addPlanner(planner2_); // Add a SECOND planning from scratch planner if desired
127 }
128
129 // Setup SPARS
130 if (!experienceDB_->getSPARSdb())
131 {
132 OMPL_INFORM("Calling setup() for SPARSdb");
133
134 // Load SPARSdb
135 experienceDB_->getSPARSdb() = std::make_shared<ompl::geometric::SPARSdb>(si_);
136 experienceDB_->getSPARSdb()->setProblemDefinition(pdef_);
137 experienceDB_->getSPARSdb()->setup();
138
139 experienceDB_->getSPARSdb()->setStretchFactor(1.2);
140 experienceDB_->getSPARSdb()->setSparseDeltaFraction(0.05); // vertex visibility range = maximum_extent *
141 // this_fraction
142 // experienceDB_->getSPARSdb()->setDenseDeltaFraction(0.001);
143
144 experienceDB_->getSPARSdb()->printDebug();
145
146 experienceDB_->load(filePath_); // load from file
147 }
148 }
149}
150
152{
153 if (planner_)
154 planner_->clear();
155 if (rrPlanner_)
156 rrPlanner_->clear();
157 if (planner2_)
158 planner2_->clear();
159 if (pdef_)
160 pdef_->clearSolutionPaths();
161 if (pp_)
162 {
163 pp_->clearHybridizationPaths();
164 }
165}
166
168{
169 pa_ = pa;
170 planner_.reset();
171 // note: the rrPlanner_ never uses the allocator so does not need to be reset
172 configured_ = false;
173}
174
176{
177 // we provide a duplicate implementation here to allow the planner to choose how the time is turned into a planner
178 // termination condition
179
180 OMPL_INFORM("Thunder Framework: Starting solve()");
181
182 // Setup again in case it has not been done yet
183 setup();
184
186 time::point start = time::now();
187
188 // Warn if there are queued paths that have not been added to the experience database
189 if (!queuedSolutionPaths_.empty())
190 {
191 OMPL_WARN("Previous solved paths are currently uninserted into the experience database and are in the "
192 "post-proccessing queue");
193 }
194
195 // There are two modes for running parallel plan - one in which both threads are run until they both return a result
196 // and/or fail
197 // The second mode stops with the first solution found - we want this one
198 bool stopWhenFirstSolutionFound = true;
199
200 if (stopWhenFirstSolutionFound)
201 {
202 // If \e hybridize is false, when the first solution is found, the rest of the planners are stopped as well.
203 // OMPL_DEBUG("Thunder: stopping when first solution is found from either thread");
204 // Start both threads
205 bool hybridize = false;
206 lastStatus_ = pp_->solve(ptc, hybridize);
207 }
208 else
209 {
210 OMPL_WARN("Thunder: not stopping until a solution or a failure is found from both threads. THIS MODE IS JUST "
211 "FOR TESTING");
212 // This mode is more for benchmarking, since I don't care about optimality
213 // If \e hybridize is false, when \e minSolCount new solutions are found (added to the set of solutions
214 // maintained by ompl::base::Goal), the rest of the planners are stopped as well.
215
216 // Start both threads
217 std::size_t minSolCount = 2;
218 std::size_t maxSolCount = 2;
219 bool hybridize = false;
220 lastStatus_ = pp_->solve(ptc, minSolCount, maxSolCount, hybridize);
221 }
222
223 // Planning time
224 planTime_ = time::seconds(time::now() - start);
225
226 // Create log
227 ExperienceLog log;
228 log.planning_time = planTime_;
229
230 // Record stats
231 stats_.totalPlanningTime_ += planTime_; // used for averaging
232 stats_.numProblems_++; // used for averaging
233
235 {
236 // Skip further processing if absolutely no path is available
237 OMPL_ERROR("Thunder Solve: No solution found after %f seconds", planTime_);
238
239 stats_.numSolutionsTimedout_++;
240
241 // Logging
242 log.planner = "neither_planner";
243 log.result = "timedout";
244 log.is_saved = "not_saved";
245 }
249 {
250 // Skip further processing if absolutely no path is available
251 OMPL_ERROR("Lightning Solve: invalid start or goal, planner status: %s", lastStatus_.asString().c_str());
252 stats_.numSolutionsFailed_++;
253
254 // Logging
255 log.planner = "neither_planner";
256 log.result = "failed";
257 log.is_saved = "not_saved";
258 }
259 else if (!lastStatus_)
260 {
261 // Skip further processing if absolutely no path is available
262 OMPL_ERROR("Thunder Solve: Unknown failure, planner status: %s", lastStatus_.asString().c_str());
263 stats_.numSolutionsFailed_++;
264
265 // Logging
266 log.planner = "neither_planner";
267 log.result = "failed";
268 log.is_saved = "not_saved";
269 }
270 else
271 {
272 OMPL_INFORM("Thunder Solve: Possible solution found in %f seconds", planTime_);
273
274 // Smooth the result
275 simplifySolution(ptc);
276
277 og::PathGeometric solutionPath = getSolutionPath(); // copied so that it is non-const
278 OMPL_INFORM("Solution path has %d states and was generated from planner %s", solutionPath.getStateCount(),
279 getSolutionPlannerName().c_str());
280
281 // Logging
282 log.planner = getSolutionPlannerName();
283
284 // Do not save if approximate
286 {
287 OMPL_INFORM("THUNDER RESULTS: Approximate");
288
289 // Logging
290 log.result = "not_exact_solution";
291 log.is_saved = "not_saved";
292 log.approximate = true;
293
294 // Stats
295 stats_.numSolutionsApproximate_++;
296
297 // TODO not sure what to do here, use case not tested
298 OMPL_WARN("NOT saving to database because the solution is APPROXIMATE");
299 }
300 else if (getSolutionPlannerName() == rrPlanner_->getName())
301 {
302 OMPL_INFORM("THUNDER RESULTS: From Recall");
303
304 // Stats
305 stats_.numSolutionsFromRecall_++;
306
307 // Logging
308 log.result = "from_recall";
309
310 // Make sure solution has at least 2 states
311 if (solutionPath.getStateCount() < 2)
312 {
313 OMPL_INFORM("NOT saving to database because solution is less than 2 states long");
314 stats_.numSolutionsTooShort_++;
315
316 // Logging
317 log.is_saved = "less_2_states";
318 log.too_short = true;
319 }
320 else if (false) // always add when from recall
321 {
322 OMPL_INFORM("Adding path to database because SPARS will decide for us if we should keep the nodes");
323
324 // Stats
325 stats_.numSolutionsFromRecallSaved_++;
326
327 // Queue the solution path for future insertion into experience database (post-processing)
328 queuedSolutionPaths_.push_back(solutionPath);
329
330 // Logging
331 log.insertion_failed = false; // TODO this is wrong logging data
332 log.is_saved = "always_attempt";
333 }
334 else // never add when from recall
335 {
336 OMPL_INFORM("NOT adding path to database because SPARS already has it");
337
338 // Logging
339 log.is_saved = "skipped";
340 }
341 }
342 else
343 {
344 OMPL_INFORM("THUNDER RESULTS: From Scratch");
345
346 // Logging
347 log.result = "from_scratch";
348
349 // Stats
350 stats_.numSolutionsFromScratch_++;
351
352 // Make sure solution has at least 2 states
353 if (solutionPath.getStateCount() < 2)
354 {
355 OMPL_INFORM("NOT saving to database because solution is less than 2 states long");
356
357 // Logging
358 log.is_saved = "less_2_states";
359 log.too_short = true;
360
361 // Stats
362 stats_.numSolutionsTooShort_++;
363 }
364 else
365 {
366 OMPL_INFORM("Adding path to database because best solution was not from database");
367
368 // Logging
369 log.result = "from_scratch";
370 log.is_saved = "saving";
371
372 // Queue the solution path for future insertion into experience database (post-processing)
373 queuedSolutionPaths_.push_back(solutionPath);
374
375 log.insertion_failed = false; // TODO fix this wrong logging info
376 }
377 }
378 }
379
380 // Final log data
381 // log.insertion_time = insertionTime; TODO fix this
382 log.num_vertices = experienceDB_->getSPARSdb()->getNumVertices();
383 log.num_edges = experienceDB_->getSPARSdb()->getNumEdges();
384 log.num_connected_components = experienceDB_->getSPARSdb()->getNumConnectedComponents();
385
386 // Flush the log to buffer
388
389 return lastStatus_;
390}
391
397
399{
400 setup(); // ensure the PRM db has been loaded to the Experience DB
401 return experienceDB_->save(filePath_);
402}
403
405{
406 setup(); // ensure the PRM db has been loaded to the Experience DB
407 return experienceDB_->saveIfChanged(filePath_);
408}
409
410void ompl::tools::Thunder::printResultsInfo(std::ostream &out) const
411{
412 for (std::size_t i = 0; i < pdef_->getSolutionCount(); ++i)
413 {
414 out << "Solution " << i << "\t | Length: " << pdef_->getSolutions()[i].length_
415 << "\t | Approximate: " << (pdef_->getSolutions()[i].approximate_ ? "true" : "false")
416 << "\t | Planner: " << pdef_->getSolutions()[i].plannerName_ << std::endl;
417 }
418}
419
420void ompl::tools::Thunder::print(std::ostream &out) const
421{
422 if (si_)
423 {
424 si_->printProperties(out);
425 si_->printSettings(out);
426 }
427 if (planner_)
428 {
429 planner_->printProperties(out);
430 planner_->printSettings(out);
431 }
432 if (rrPlanner_)
433 {
434 rrPlanner_->printProperties(out);
435 rrPlanner_->printSettings(out);
436 }
437 if (planner2_)
438 {
439 planner2_->printProperties(out);
440 planner2_->printSettings(out);
441 }
442 if (pdef_)
443 pdef_->print(out);
444}
445
446void ompl::tools::Thunder::printLogs(std::ostream &out) const
447{
448 if (!recallEnabled_)
449 out << "Scratch Planning Logging Results (inside Thunder Framework)" << std::endl;
450 else
451 out << "Thunder Framework Logging Results" << std::endl;
452 out << " Solutions Attempted: " << stats_.numProblems_ << std::endl;
453 out << " Solved from scratch: " << stats_.numSolutionsFromScratch_ << " ("
454 << stats_.numSolutionsFromScratch_ / stats_.numProblems_ * 100 << "%)" << std::endl;
455 out << " Solved from recall: " << stats_.numSolutionsFromRecall_ << " ("
456 << stats_.numSolutionsFromRecall_ / stats_.numProblems_ * 100 << "%)" << std::endl;
457 out << " That were saved: " << stats_.numSolutionsFromRecallSaved_ << std::endl;
458 out << " That were discarded: " << stats_.numSolutionsFromRecall_ - stats_.numSolutionsFromRecallSaved_
459 << std::endl;
460 out << " Less than 2 states: " << stats_.numSolutionsTooShort_ << std::endl;
461 out << " Failed: " << stats_.numSolutionsFailed_ << std::endl;
462 out << " Timedout: " << stats_.numSolutionsTimedout_ << std::endl;
463 out << " Approximate: " << stats_.numSolutionsApproximate_ << std::endl;
464 out << " SPARSdb " << std::endl;
465 out << " Vertices: " << experienceDB_->getSPARSdb()->getNumVertices() << std::endl;
466 out << " Edges: " << experienceDB_->getSPARSdb()->getNumEdges() << std::endl;
467 out << " Connected Components: " << experienceDB_->getSPARSdb()->getNumConnectedComponents() << std::endl;
468 out << " Unsaved paths inserted: " << experienceDB_->getNumPathsInserted() << std::endl;
469 out << " Consecutive state failures: " << experienceDB_->getSPARSdb()->getNumConsecutiveFailures() << std::endl;
470 out << " Connected path failures: " << experienceDB_->getSPARSdb()->getNumPathInsertionFailed() << std::endl;
471 out << " Sparse Delta Fraction: " << experienceDB_->getSPARSdb()->getSparseDeltaFraction() << std::endl;
472 out << " Average planning time: " << stats_.getAveragePlanningTime() << std::endl;
473 out << " Average insertion time: " << stats_.getAverageInsertionTime() << std::endl;
474}
475
477{
478 return experienceDB_->getSPARSdb()->getNumVertices();
479}
480
481void ompl::tools::Thunder::getAllPlannerDatas(std::vector<ob::PlannerDataPtr> &plannerDatas) const
482{
483 experienceDB_->getAllPlannerDatas(plannerDatas);
484}
485
486void ompl::tools::Thunder::convertPlannerData(const ob::PlannerDataPtr &plannerData, og::PathGeometric &path)
487{
488 // Convert the planner data vertices into a vector of states
489 for (std::size_t i = 0; i < plannerData->numVertices(); ++i)
490 path.append(plannerData->getVertex(i).getState());
491}
492
494{
495 // Reverse path2 if it matches better
496 const ob::State *s1 = path1.getState(0);
497 const ob::State *s2 = path2.getState(0);
498 const ob::State *g1 = path1.getState(path1.getStateCount() - 1);
499 const ob::State *g2 = path2.getState(path2.getStateCount() - 1);
500
501 double regularDistance = si_->distance(s1, s2) + si_->distance(g1, g2);
502 double reversedDistance = si_->distance(s1, g2) + si_->distance(s2, g1);
503
504 // Check if path is reversed from normal [start->goal] direction
505 if (regularDistance > reversedDistance)
506 {
507 // needs to be reversed
508 path2.reverse();
509 return true;
510 }
511
512 return false;
513}
514
515ompl::tools::ThunderDBPtr ompl::tools::Thunder::getExperienceDB()
516{
517 return experienceDB_;
518}
519
521{
522 OMPL_INFORM("Performing post-processing");
523
524 for (auto &queuedSolutionPath : queuedSolutionPaths_)
525 {
526 // Time to add a path to experience database
527 double insertionTime;
528
529 experienceDB_->addPath(queuedSolutionPath, insertionTime);
530 OMPL_INFORM("Finished inserting experience path in %f seconds", insertionTime);
531 stats_.totalInsertionTime_ += insertionTime; // used for averaging
532 }
533
534 // Remove all inserted paths from the queue
535 queuedSolutionPaths_.clear();
536
537 return true;
538}
The exception type for ompl.
Definition Exception.h:47
Encapsulate a termination condition for a motion planner. Planners will call operator() to decide whe...
Definition of an abstract state.
Definition State.h:50
Definition of a geometric path.
std::size_t getStateCount() const
Get the number of states (way-points) that make up this path.
void reverse()
Reverse the path.
base::State * getState(unsigned int index)
Get the state located at index along the path.
PathGeometric & getSolutionPath() const
Get the solution path. Throw an exception if no solution is available.
double planTime_
The amount of time the last planning step took.
base::PlannerStatus lastStatus_
The status of the last planning request.
bool haveExactSolutionPath() const
Return true if a solution path is available (previous call to solve() was successful) and the solutio...
const std::string getSolutionPlannerName() const
Get the best solution's planer name. Throw an exception if no solution is available.
bool configured_
Flag indicating whether the classes needed for planning are set up.
base::PlannerPtr planner_
The maintained planner instance.
void simplifySolution(double duration=0.0)
Attempt to simplify the current solution path. Spent at most duration seconds in the simplification p...
base::SpaceInformationPtr si_
The created space information.
base::ProblemDefinitionPtr pdef_
The created problem definition.
base::PlannerAllocator pa_
The optional planner allocator.
std::string filePath_
File location of database.
void convertLogToString(const ExperienceLog &log)
Move data to string format and put in buffer.
ExperienceStats stats_
States data for display to console.
bool recallEnabled_
Flag indicating whether recalled plans should be used to find solutions. Enabled by default.
bool scratchEnabled_
Flag indicating whether planning from scratch should be used to find solutions. Enabled by default.
ExperienceSetup(const base::SpaceInformationPtr &si)
Constructor needs the state space used for planning.
bool reversePathIfNecessary(ompl::geometric::PathGeometric &path1, ompl::geometric::PathGeometric &path2)
If path1 and path2 have a better start/goal match when reverse, then reverse path2.
Definition Thunder.cpp:493
void printResultsInfo(std::ostream &out=std::cout) const override
Display debug data about potential available solutions.
Definition Thunder.cpp:410
bool doPostProcessing() override
Allow accumlated experiences to be processed.
Definition Thunder.cpp:520
void printLogs(std::ostream &out=std::cout) const override
Display debug data about overall results from Thunder since being loaded.
Definition Thunder.cpp:446
base::PlannerStatus solve(double time=1.0) override
Run the planner for up to a specified amount of time (default is 1 second).
Definition Thunder.cpp:392
void print(std::ostream &out=std::cout) const override
Print information about the current setup.
Definition Thunder.cpp:420
base::PlannerPtr planner2_
Definition Thunder.h:186
Thunder(const base::SpaceInformationPtr &si)
Constructor needs the state space used for planning.
Definition Thunder.cpp:46
bool save() override
Save the experience database to file.
Definition Thunder.cpp:398
void clear() override
Clear all planning data. This only includes data generated by motion plan computation....
Definition Thunder.cpp:151
bool saveIfChanged() override
Save the experience database to file if there has been a change.
Definition Thunder.cpp:404
std::vector< ompl::geometric::PathGeometric > queuedSolutionPaths_
Accumulated experiences to be later added to experience database.
Definition Thunder.h:198
std::size_t getExperiencesCount() const override
Get the total number of paths stored in the database.
Definition Thunder.cpp:476
bool dualThreadScratchEnabled_
Definition Thunder.h:189
ompl::tools::ThunderDBPtr getExperienceDB()
Hook for getting access to debug data.
Definition Thunder.cpp:515
ompl::tools::ThunderDBPtr experienceDB_
A shared object between all the planners for saving and loading previous experience.
Definition Thunder.h:195
void setup() override
This method will create the necessary classes for planning. The solve() method will call this functio...
Definition Thunder.cpp:71
void setPlannerAllocator(const base::PlannerAllocator &pa)
Set the planner allocator to use. This is only used if no planner has been set. This is optional – a ...
Definition Thunder.cpp:167
ompl::tools::ParallelPlanPtr pp_
Instance of parallel planning to use for computing solutions in parallel.
Definition Thunder.h:192
void convertPlannerData(const ompl::base::PlannerDataPtr &plannerData, ompl::geometric::PathGeometric &path)
Convert PlannerData to PathGeometric. Assume ordering of vertices is order of path.
Definition Thunder.cpp:486
base::PlannerPtr rrPlanner_
Definition Thunder.h:183
void getAllPlannerDatas(std::vector< ompl::base::PlannerDataPtr > &plannerDatas) const override
Get a vector of all the planning data in the database.
Definition Thunder.cpp:481
#define OMPL_INFORM(fmt,...)
Log a formatted information string.
Definition Console.h:68
#define OMPL_ERROR(fmt,...)
Log a formatted error string.
Definition Console.h:64
#define OMPL_WARN(fmt,...)
Log a formatted warning string.
Definition Console.h:66
This namespace contains sampling based planning routines shared by both planning under geometric cons...
std::function< PlannerPtr(const SpaceInformationPtr &)> PlannerAllocator
Definition of a function that can allocate a planner.
Definition Planner.h:428
PlannerTerminationCondition timedPlannerTerminationCondition(double duration)
Return a termination condition that will become true duration seconds in the future (wall-time).
This namespace contains code that is specific to planning under geometric constraints.
Namespace containing time datatypes and time operations.
Definition Time.h:50
std::chrono::system_clock::time_point point
Representation of a point in time.
Definition Time.h:52
point now()
Get the current time point.
Definition Time.h:58
duration seconds(double sec)
Return the time duration representing a given number of seconds.
Definition Time.h:64
Includes various tools such as self config, benchmarking, etc.
Main namespace. Contains everything in this library.
A class to store the exit status of Planner::solve().
@ INVALID_START
Invalid start state or no start state specified.
@ INVALID_GOAL
Invalid goal state.
@ UNRECOGNIZED_GOAL_TYPE
The goal is of a type that a planner does not recognize.
@ TIMEOUT
The planner failed to find a solution.
@ UNKNOWN
Uninitialized status.
Single entry for the csv data logging file.