Loading...
Searching...
No Matches
ConstrainedPlanningCommon.h
1/*********************************************************************
2 * Software License Agreement (BSD License)
3 *
4 * Copyright (c) 2018, 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: Zachary Kingston */
36
37#ifndef OMPL_DEMO_CONSTRAINED_COMMON_
38#define OMPL_DEMO_CONSTRAINED_COMMON_
39
40#include <iostream>
41#include <fstream>
42
43#include <boost/format.hpp>
44#include <boost/program_options.hpp>
45
46#include <ompl/geometric/SimpleSetup.h>
47#include <ompl/geometric/PathGeometric.h>
48
49#include <ompl/base/Constraint.h>
50#include <ompl/base/ConstrainedSpaceInformation.h>
51#include <ompl/base/spaces/constraint/ConstrainedStateSpace.h>
52#include <ompl/base/spaces/constraint/AtlasStateSpace.h>
53#include <ompl/base/spaces/constraint/TangentBundleStateSpace.h>
54#include <ompl/base/spaces/constraint/ProjectedStateSpace.h>
55
56#include <ompl/geometric/planners/rrt/RRT.h>
57#include <ompl/geometric/planners/rrt/RRTConnect.h>
58#include <ompl/geometric/planners/rrt/RRTstar.h>
59#include <ompl/geometric/planners/est/EST.h>
60#include <ompl/geometric/planners/est/BiEST.h>
61#include <ompl/geometric/planners/est/ProjEST.h>
62#include <ompl/geometric/planners/informedtrees/BITstar.h>
63#include <ompl/geometric/planners/prm/PRM.h>
64#include <ompl/geometric/planners/prm/SPARS.h>
65#include <ompl/geometric/planners/prm/SPARStwo.h>
66#include <ompl/geometric/planners/kpiece/KPIECE1.h>
67#include <ompl/geometric/planners/kpiece/BKPIECE1.h>
68
69#include <ompl/tools/benchmark/Benchmark.h>
70
71namespace po = boost::program_options;
72namespace ob = ompl::base;
73namespace og = ompl::geometric;
74namespace om = ompl::magic;
75namespace ot = ompl::tools;
76
77enum SPACE_TYPE
78{
79 PJ = 0,
80 AT = 1,
81 TB = 2
82};
83
84const std::string spaceStr[3] = {"PJ", "AT", "TB"};
85
86std::istream &operator>>(std::istream &in, enum SPACE_TYPE &type)
87{
88 std::string token;
89 in >> token;
90 if (token == "PJ")
91 type = PJ;
92 else if (token == "AT")
93 type = AT;
94 else if (token == "TB")
95 type = TB;
96 else
97 in.setstate(std::ios_base::failbit);
98
99 return in;
100}
101
102void addSpaceOption(po::options_description &desc, enum SPACE_TYPE *space)
103{
104 auto space_msg = "Choose which constraint handling methodology to use. One of:\n"
105 "PJ - Projection (Default), "
106 "AT - Atlas, "
107 "TB - Tangent Bundle.";
108
109 desc.add_options()("space,s", po::value<enum SPACE_TYPE>(space), space_msg);
110}
111
112enum PLANNER_TYPE
113{
114 RRT,
115 RRT_I,
116 RRTConnect,
117 RRTConnect_I,
118 RRTstar,
119 EST,
120 BiEST,
121 ProjEST,
122 BITstar,
123 PRM,
124 SPARS,
125 KPIECE,
126 BKPIECE
127};
128
129std::istream &operator>>(std::istream &in, enum PLANNER_TYPE &type)
130{
131 std::string token;
132 in >> token;
133 if (token == "RRT")
134 type = RRT;
135 else if (token == "RRT_I")
136 type = RRT_I;
137 else if (token == "RRTConnect")
138 type = RRTConnect;
139 else if (token == "RRTConnect_I")
140 type = RRTConnect_I;
141 else if (token == "RRTstar")
142 type = RRTstar;
143 else if (token == "EST")
144 type = EST;
145 else if (token == "BiEST")
146 type = BiEST;
147 else if (token == "ProjEST")
148 type = ProjEST;
149 else if (token == "BITstar")
150 type = BITstar;
151 else if (token == "PRM")
152 type = PRM;
153 else if (token == "SPARS")
154 type = SPARS;
155 else if (token == "KPIECE")
156 type = KPIECE;
157 else if (token == "BKPIECE")
158 type = BKPIECE;
159 else
160 in.setstate(std::ios_base::failbit);
161
162 return in;
163}
164
165void addPlannerOption(po::options_description &desc, std::vector<enum PLANNER_TYPE> *planners)
166{
167 auto planner_msg = "List of which motion planner to use (multiple if benchmarking, one if planning). Choose from:\n"
168 "RRT (Default), RRT_I, RRTConnect, RRTConnect_I, RRTstar, "
169 "EST, BiEST, ProjEST, "
170 "BITstar, "
171 "PRM, SPARS, "
172 "KPIECE, BKPIECE.";
173
174 desc.add_options()("planner,p", po::value<std::vector<enum PLANNER_TYPE>>(planners)->multitoken(), planner_msg);
175}
176
178{
179 double delta;
180 double lambda;
181 double tolerance;
182 double time;
183 unsigned int tries;
184 double range;
185};
186
187void addConstrainedOptions(po::options_description &desc, struct ConstrainedOptions *options)
188{
189 auto delta_msg = "Step-size for discrete geodesic on manifold.";
190 auto lambda_msg = "Maximum `wandering` allowed during traversal. Must be greater than 1.";
191 auto tolerance_msg = "Constraint satisfaction tolerance.";
192 auto time_msg = "Planning time allowed.";
193 auto tries_msg = "Maximum number sample tries per sample.";
194 auto range_msg = "Planner `range` value for planners that support this parameter. Automatically determined "
195 "otherwise (when 0).";
196
197 desc.add_options()("delta,d", po::value<double>(&options->delta)->default_value(om::CONSTRAINED_STATE_SPACE_DELTA),
198 delta_msg);
199 desc.add_options()("lambda", po::value<double>(&options->lambda)->default_value(om::CONSTRAINED_STATE_SPACE_LAMBDA),
200 lambda_msg);
201 desc.add_options()("tolerance",
202 po::value<double>(&options->tolerance)->default_value(om::CONSTRAINT_PROJECTION_TOLERANCE),
203 tolerance_msg);
204 desc.add_options()("time", po::value<double>(&options->time)->default_value(5.), time_msg);
205 desc.add_options()(
206 "tries", po::value<unsigned int>(&options->tries)->default_value(om::CONSTRAINT_PROJECTION_MAX_ITERATIONS),
207 tries_msg);
208 desc.add_options()("range,r", po::value<double>(&options->range)->default_value(0), range_msg);
209}
210
212{
213 double epsilon;
214 double rho;
215 double exploration;
216 double alpha;
217 bool bias;
218 bool separate;
219 unsigned int charts;
220};
221
222void addAtlasOptions(po::options_description &desc, struct AtlasOptions *options)
223{
224 auto epsilon_msg = "Maximum distance from an atlas chart to the manifold. Must be positive.";
225 auto rho_msg = "Maximum radius for an atlas chart. Must be positive.";
226 auto exploration_msg = "Value in [0, 1] which tunes balance of refinement and exploration in "
227 "atlas sampling.";
228 auto alpha_msg = "Maximum angle between an atlas chart and the manifold. Must be in [0, PI/2].";
229 auto bias_msg = "Sets whether the atlas should use frontier-biased chart sampling rather than "
230 "uniform.";
231 auto separate_msg = "Sets that the atlas should not compute chart separating halfspaces.";
232 auto charts_msg = "Maximum number of atlas charts that can be generated during one manifold "
233 "traversal.";
234
235 desc.add_options()("epsilon", po::value<double>(&options->epsilon)->default_value(om::ATLAS_STATE_SPACE_EPSILON),
236 epsilon_msg);
237 desc.add_options()("rho",
238 po::value<double>(&options->rho)
239 ->default_value(om::CONSTRAINED_STATE_SPACE_DELTA * om::ATLAS_STATE_SPACE_RHO_MULTIPLIER),
240 rho_msg);
241 desc.add_options()("exploration",
242 po::value<double>(&options->exploration)->default_value(om::ATLAS_STATE_SPACE_EXPLORATION),
243 exploration_msg);
244 desc.add_options()("alpha", po::value<double>(&options->alpha)->default_value(om::ATLAS_STATE_SPACE_ALPHA),
245 alpha_msg);
246 desc.add_options()("bias", po::bool_switch(&options->bias)->default_value(false), bias_msg);
247 desc.add_options()("no-separate", po::bool_switch(&options->separate)->default_value(false), separate_msg);
248 desc.add_options()(
249 "charts",
250 po::value<unsigned int>(&options->charts)->default_value(om::ATLAS_STATE_SPACE_MAX_CHARTS_PER_EXTENSION),
251 charts_msg);
252}
253
254class ConstrainedProblem
255{
256public:
257 ConstrainedProblem(enum SPACE_TYPE type, ob::StateSpacePtr space_, ob::ConstraintPtr constraint_)
258 : type(type), space(std::move(space_)), constraint(std::move(constraint_))
259 {
260 // Combine the ambient state space and the constraint to create the
261 // constrained state space.
262 switch (type)
263 {
264 case PJ:
265 OMPL_INFORM("Using Projection-Based State Space!");
266 css = std::make_shared<ob::ProjectedStateSpace>(space, constraint);
267 csi = std::make_shared<ob::ConstrainedSpaceInformation>(css);
268 break;
269 case AT:
270 OMPL_INFORM("Using Atlas-Based State Space!");
271 css = std::make_shared<ob::AtlasStateSpace>(space, constraint);
272 csi = std::make_shared<ob::ConstrainedSpaceInformation>(css);
273 break;
274 case TB:
275 OMPL_INFORM("Using Tangent Bundle-Based State Space!");
276 css = std::make_shared<ob::TangentBundleStateSpace>(space, constraint);
277 csi = std::make_shared<ob::TangentBundleSpaceInformation>(css);
278 break;
279 }
280
281 css->setup();
282 ss = std::make_shared<og::SimpleSetup>(csi);
283 }
284
285 void setConstrainedOptions(struct ConstrainedOptions &opt)
286 {
287 c_opt = opt;
288
289 constraint->setTolerance(opt.tolerance);
290 constraint->setMaxIterations(opt.tries);
291
292 css->setDelta(opt.delta);
293 css->setLambda(opt.lambda);
294 }
295
296 void setAtlasOptions(struct AtlasOptions &opt)
297 {
298 a_opt = opt;
299
300 if (!(type == AT || type == TB))
301 return;
302
303 auto &&atlas = css->as<ob::AtlasStateSpace>();
304 atlas->setExploration(opt.exploration);
305 atlas->setEpsilon(opt.epsilon);
306 atlas->setRho(opt.rho);
307 atlas->setAlpha(opt.alpha);
308 atlas->setMaxChartsPerExtension(opt.charts);
309
310 if (opt.bias)
311 atlas->setBiasFunction([atlas](ompl::base::AtlasChart *c) -> double
312 { return (atlas->getChartCount() - c->getNeighborCount()) + 1; });
313
314 if (type == AT)
315 atlas->setSeparated(!opt.separate);
316
317 atlas->setup();
318 }
319
320 void setStartAndGoalStates(const Eigen::Ref<const Eigen::VectorXd> &start,
321 const Eigen::Ref<const Eigen::VectorXd> &goal)
322 {
323 // Create start and goal states (poles of the sphere)
324 ob::ScopedState<> sstart(css);
325 ob::ScopedState<> sgoal(css);
326
327 sstart->as<ob::ConstrainedStateSpace::StateType>()->copy(start);
328 sgoal->as<ob::ConstrainedStateSpace::StateType>()->copy(goal);
329
330 switch (type)
331 {
332 case AT:
333 case TB:
334 css->as<ob::AtlasStateSpace>()->anchorChart(sstart.get());
335 css->as<ob::AtlasStateSpace>()->anchorChart(sgoal.get());
336 break;
337 default:
338 break;
339 }
340
341 // Setup problem
342 ss->setStartAndGoalStates(sstart, sgoal);
343 }
344
345 template <typename _T>
346 std::shared_ptr<_T> createPlanner()
347 {
348 auto &&planner = std::make_shared<_T>(csi);
349 return std::move(planner);
350 }
351
352 template <typename _T>
353 std::shared_ptr<_T> createPlannerIntermediate()
354 {
355 auto &&planner = std::make_shared<_T>(csi, true);
356 return std::move(planner);
357 }
358
359 template <typename _T>
360 std::shared_ptr<_T> createPlannerRange()
361 {
362 auto &&planner = createPlanner<_T>();
363
364 if (c_opt.range == 0)
365 {
366 if (type == AT || type == TB)
367 planner->setRange(css->as<ob::AtlasStateSpace>()->getRho_s());
368 }
369 else
370 planner->setRange(c_opt.range);
371
372 return std::move(planner);
373 }
374
375 template <typename _T>
376 std::shared_ptr<_T> createPlannerRange(bool /*intermediate*/)
377 {
378 auto &&planner = createPlannerIntermediate<_T>();
379
380 if (c_opt.range == 0)
381 {
382 if (type == AT || type == TB)
383 planner->setRange(css->as<ob::AtlasStateSpace>()->getRho_s());
384 }
385 else
386 planner->setRange(c_opt.range);
387
388 return std::move(planner);
389 }
390
391 template <typename _T>
392 std::shared_ptr<_T> createPlannerRangeProj(const std::string &projection)
393 {
394 const bool isProj = projection != "";
395 auto &&planner = createPlannerRange<_T>();
396
397 if (isProj)
398 planner->setProjectionEvaluator(projection);
399
400 return std::move(planner);
401 }
402
403 ob::PlannerPtr getPlanner(enum PLANNER_TYPE planner, const std::string &projection = "")
404 {
405 ob::PlannerPtr p;
406 switch (planner)
407 {
408 case RRT:
409 p = createPlannerRange<og::RRT>();
410 break;
411 case RRT_I:
412 p = createPlannerRange<og::RRT>(true);
413 break;
414 case RRTConnect:
415 p = createPlannerRange<og::RRTConnect>();
416 break;
417 case RRTConnect_I:
418 p = createPlannerRange<og::RRTConnect>(true);
419 break;
420 case RRTstar:
421 p = createPlannerRange<og::RRTstar>();
422 break;
423 case EST:
424 p = createPlannerRange<og::EST>();
425 break;
426 case BiEST:
427 p = createPlannerRange<og::BiEST>();
428 break;
429 case ProjEST:
430 p = createPlannerRangeProj<og::ProjEST>(projection);
431 break;
432 case BITstar:
433 p = createPlanner<og::BITstar>();
434 break;
435 case PRM:
436 p = createPlanner<og::PRM>();
437 break;
438 case SPARS:
439 p = createPlanner<og::SPARS>();
440 break;
441 case KPIECE:
442 p = createPlannerRangeProj<og::KPIECE1>(projection);
443 break;
444 case BKPIECE:
445 p = createPlannerRangeProj<og::BKPIECE1>(projection);
446 break;
447 }
448 return p;
449 }
450
451 void setPlanner(enum PLANNER_TYPE planner, const std::string &projection = "")
452 {
453 pp = getPlanner(planner, projection);
454 ss->setPlanner(pp);
455 }
456
457 ob::PlannerStatus solveOnce(bool output = false, const std::string &name = "ompl")
458 {
459 ss->setup();
460
461 ob::PlannerStatus stat = ss->solve(c_opt.time);
462 std::cout << std::endl;
463
464 if (stat)
465 {
466 // Get solution and validate
467 auto path = ss->getSolutionPath();
468 if (!path.check())
469 OMPL_WARN("Path fails check!");
470
472 OMPL_WARN("Solution is approximate.");
473
474 // Simplify solution and validate simplified solution path.
475 OMPL_INFORM("Simplifying solution...");
476 ss->simplifySolution(5.);
477
478 auto simplePath = ss->getSolutionPath();
479 OMPL_INFORM("Simplified Path Length: %.3f -> %.3f", path.length(), simplePath.length());
480
481 if (!simplePath.check())
482 OMPL_WARN("Simplified path fails check!");
483
484 // Interpolate and validate interpolated solution path.
485 OMPL_INFORM("Interpolating path...");
486 path.interpolate();
487
488 if (!path.check())
489 OMPL_WARN("Interpolated simplified path fails check!");
490
491 OMPL_INFORM("Interpolating simplified path...");
492 simplePath.interpolate();
493
494 if (!simplePath.check())
495 OMPL_WARN("Interpolated simplified path fails check!");
496
497 if (output)
498 {
499 OMPL_INFORM("Dumping path to `%s_path.txt`.", name.c_str());
500 std::ofstream pathfile((boost::format("%1%_path.txt") % name).str());
501 path.printAsMatrix(pathfile);
502 pathfile.close();
503
504 OMPL_INFORM("Dumping simplified path to `%s_simplepath.txt`.", name.c_str());
505 std::ofstream simplepathfile((boost::format("%1%_simplepath.txt") % name).str());
506 simplePath.printAsMatrix(simplepathfile);
507 simplepathfile.close();
508 }
509 }
510 else
511 OMPL_WARN("No solution found.");
512
513 return stat;
514 }
515
516 void setupBenchmark(std::vector<enum PLANNER_TYPE> &planners, const std::string &problem)
517 {
518 bench = new ot::Benchmark(*ss, problem);
519
520 bench->addExperimentParameter("n", "INTEGER", std::to_string(constraint->getAmbientDimension()));
521 bench->addExperimentParameter("k", "INTEGER", std::to_string(constraint->getManifoldDimension()));
522 bench->addExperimentParameter("n - k", "INTEGER", std::to_string(constraint->getCoDimension()));
523 bench->addExperimentParameter("space", "INTEGER", std::to_string(type));
524
525 request = ot::Benchmark::Request(c_opt.time, 2048, 100, 0.1, true, false, true);
526
527 for (auto planner : planners)
528 bench->addPlanner(getPlanner(planner, problem));
529
530 bench->setPreRunEvent(
531 [&](const ob::PlannerPtr &planner)
532 {
533 if (type == AT || type == TB)
534 planner->getSpaceInformation()->getStateSpace()->as<ompl::base::AtlasStateSpace>()->clear();
535 else
536 planner->getSpaceInformation()->getStateSpace()->as<ompl::base::ConstrainedStateSpace>()->clear();
537
538 planner->clear();
539 });
540 }
541
542 void runBenchmark()
543 {
544 bench->benchmark(request);
545
547 const std::string filename =
548 (boost::format("%1%_%2%_%3%_benchmark.log") % now % bench->getExperimentName() % spaceStr[type]).str();
549
550 bench->saveResultsToFile(filename.c_str());
551 }
552
553 void atlasStats()
554 {
555 // For atlas types, output information about size of atlas and amount of space explored
556 if (type == AT || type == TB)
557 {
558 auto at = css->as<ob::AtlasStateSpace>();
559 OMPL_INFORM("Atlas has %zu charts", at->getChartCount());
560 if (type == AT)
561 OMPL_INFORM("Atlas is approximately %.3f%% open", at->estimateFrontierPercent());
562 }
563 }
564
565 void dumpGraph(const std::string &name)
566 {
567 OMPL_INFORM("Dumping planner graph to `%s_graph.graphml`.", name.c_str());
568 ob::PlannerData data(csi);
569 pp->getPlannerData(data);
570
571 std::ofstream graphfile((boost::format("%1%_graph.graphml") % name).str());
572 data.printGraphML(graphfile);
573 graphfile.close();
574
575 if (type == AT || type == TB)
576 {
577 OMPL_INFORM("Dumping atlas to `%s_atlas.ply`.", name.c_str());
578 std::ofstream atlasfile((boost::format("%1%_atlas.ply") % name).str());
579 css->as<ob::AtlasStateSpace>()->printPLY(atlasfile);
580 atlasfile.close();
581 }
582 }
583
584 enum SPACE_TYPE type;
585
586 ob::StateSpacePtr space;
587 ob::ConstraintPtr constraint;
588
589 ob::ConstrainedStateSpacePtr css;
590 ob::ConstrainedSpaceInformationPtr csi;
591
592 ob::PlannerPtr pp;
593
594 og::SimpleSetupPtr ss;
595
596 struct ConstrainedOptions c_opt;
597 struct AtlasOptions a_opt;
598
599 ot::Benchmark *bench;
601};
602
603#endif
Tangent space and bounding polytope approximating some patch of the manifold.
Definition AtlasChart.h:53
std::size_t getNeighborCount() const
Returns the number of charts this chart shares a halfspace boundary with.
Definition AtlasChart.h:221
ConstrainedStateSpace encapsulating a planner-agnostic atlas algorithm for planning on a constraint m...
double getRho_s() const
Get the sampling radius.
void setExploration(double exploration)
Set the exploration parameter, which tunes the balance of refinement (sampling within known regions) ...
A State in a ConstrainedStateSpace, represented as a dense real vector of values. For convenience and...
A StateSpace that has a Constraint imposed upon it. Underlying space functions are passed to the ambi...
Object containing planner generated vertex and edge data. It is assumed that all vertices are unique,...
void printGraphML(std::ostream &out=std::cout) const
Writes a GraphML file of this structure to the given stream.
Definition of a scoped state.
Definition ScopedState.h:57
StateType * get()
Returns a pointer to the contained state.
Benchmark a set of planners on a problem instance.
Definition Benchmark.h:49
#define OMPL_INFORM(fmt,...)
Log a formatted information string.
Definition Console.h:68
#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...
This namespace contains code that is specific to planning under geometric constraints.
This namespace includes magic constants used in various places in OMPL.
Definition Constraint.h:52
static const double CONSTRAINT_PROJECTION_TOLERANCE
Default projection tolerance of a constraint unless otherwise specified.
Definition Constraint.h:55
static const unsigned int CONSTRAINT_PROJECTION_MAX_ITERATIONS
Maximum number of iterations in projection routine until giving up.
Definition Constraint.h:59
point now()
Get the current time point.
Definition Time.h:58
std::string as_string(const point &p)
Return string representation of point in time.
Definition Time.h:78
Includes various tools such as self config, benchmarking, etc.
A class to store the exit status of Planner::solve().
@ APPROXIMATE_SOLUTION
The planner found an approximate solution.
Representation of a benchmark request.
Definition Benchmark.h:152