Loading...
Searching...
No Matches
ConstrainedPlanningCommon.py
1#!/usr/bin/env python3
2
3
36
37# Author: Mark Moll, Weihang Guo
38
39from __future__ import print_function
40from ompl import util as ou
41from ompl import base as ob
42from ompl import geometric as og
43from ompl import tools as ot
44
45import datetime
46
47
48def addSpaceOption(parser):
49 parser.add_argument(
50 "-s",
51 "--space",
52 default="PJ",
53 choices=["PJ", "AT", "TB"],
54 help="""Choose which constraint handling methodology to use. One of:
55 PJ - Projection (Default)
56 AT - Atlas
57 TB - Tangent Bundle.""",
58 )
59
60
61def addPlannerOption(parser):
62 parser.add_argument(
63 "-p",
64 "--planner",
65 default="RRT",
66 help="Comma-separated list of which motion planner to use (multiple if "
67 "benchmarking, one if planning).\n Choose from, e.g.:\n"
68 "RRT (Default), RRTConnect, RRTstar, "
69 "EST, BiEST, ProjEST, "
70 "BITstar, "
71 "PRM, SPARS, "
72 "KPIECE1, BKPIECE1.",
73 )
74
75
76def addConstrainedOptions(parser):
77 group = parser.add_argument_group("Constrained planning options")
78 group.add_argument(
79 "-d",
80 "--delta",
81 type=float,
82 default=ob.CONSTRAINED_STATE_SPACE_DELTA,
83 help="Step-size for discrete geodesic on manifold.",
84 )
85 group.add_argument(
86 "--lambda",
87 type=float,
88 dest="lambda_",
89 metavar="LAMBDA",
90 default=ob.CONSTRAINED_STATE_SPACE_LAMBDA,
91 help="Maximum `wandering` allowed during atlas traversal. Must be greater "
92 "than 1.",
93 )
94 group.add_argument(
95 "--tolerance",
96 type=float,
97 default=ob.CONSTRAINT_PROJECTION_TOLERANCE,
98 help="Constraint satisfaction tolerance.",
99 )
100 group.add_argument("--time", type=float, default=5.0, help="Planning time allowed.")
101 group.add_argument(
102 "--tries",
103 type=int,
104 default=ob.CONSTRAINT_PROJECTION_MAX_ITERATIONS,
105 help="Maximum number sample tries per sample.",
106 )
107 group.add_argument(
108 "-r",
109 "--range",
110 type=float,
111 default=0.0,
112 help="Planner `range` value for planners that support this parameter. "
113 "Automatically determined otherwise (when 0).",
114 )
115
116
117def list2vec(l):
118 ret = ou.vectorDouble()
119 for e in l:
120 ret.append(e)
121 return ret
122
123
124def clearSpaceAndPlanner(planner):
125 planner.getSpaceInformation().getStateSpace().clear()
126 planner.clear()
127
128
129def addAtlasOptions(parser):
130 group = parser.add_argument_group("Atlas options")
131 group.add_argument(
132 "--epsilon",
133 type=float,
134 default=ob.ATLAS_STATE_SPACE_EPSILON,
135 help="Maximum distance from an atlas chart to the manifold. Must be positive.",
136 )
137 group.add_argument(
138 "--rho",
139 type=float,
140 default=ob.CONSTRAINED_STATE_SPACE_DELTA * ob.ATLAS_STATE_SPACE_RHO_MULTIPLIER,
141 help="Maximum radius for an atlas chart. Must be positive.",
142 )
143 group.add_argument(
144 "--exploration",
145 type=float,
146 default=ob.ATLAS_STATE_SPACE_EXPLORATION,
147 help="Value in [0, 1] which tunes balance of refinement and exploration in "
148 "atlas sampling.",
149 )
150 group.add_argument(
151 "--alpha",
152 type=float,
153 default=ob.ATLAS_STATE_SPACE_ALPHA,
154 help="Maximum angle between an atlas chart and the manifold. Must be in "
155 "[0, PI/2].",
156 )
157 group.add_argument(
158 "--bias",
159 action="store_true",
160 help="Sets whether the atlas should use frontier-biased chart sampling "
161 "rather than uniform.",
162 )
163 group.add_argument(
164 "--no-separate",
165 action="store_true",
166 help="Sets that the atlas should not compute chart separating halfspaces.",
167 )
168 group.add_argument(
169 "--charts",
170 type=int,
171 default=ob.ATLAS_STATE_SPACE_MAX_CHARTS_PER_EXTENSION,
172 help="Maximum number of atlas charts that can be generated during one "
173 "manifold traversal.",
174 )
175
176
177class ConstrainedProblem(object):
178 def __init__(self, spaceType, space, constraint, options):
179 self.spaceType = spaceType
180 self.space = space
181 self.constraint = constraint
182 self.constraint.setTolerance(options.tolerance)
183 self.constraint.setMaxIterations(options.tries)
184 self.options = options
185 self.bench = None
186 self.request = None
187 self.pp = None
188
189 if spaceType == "PJ":
190 ou.OMPL_INFORM("Using Projection-Based State Space!")
191 self.css = ob.ProjectedStateSpace(space, constraint)
193 elif spaceType == "AT":
194 ou.OMPL_INFORM("Using Atlas-Based State Space!")
195 self.css = ob.AtlasStateSpace(space, constraint)
197 elif spaceType == "TB":
198 ou.OMPL_INFORM("Using Tangent Bundle-Based State Space!")
199 self.css = ob.TangentBundleStateSpace(space, constraint)
201
202 self.css.setup()
203 self.css.setDelta(options.delta)
204 self.css.setLambda(options.lambda_)
205 if not spaceType == "PJ":
206 self.css.setExploration(options.exploration)
207 self.css.setEpsilon(options.epsilon)
208 self.css.setRho(options.rho)
209 self.css.setAlpha(options.alpha)
210 self.css.setMaxChartsPerExtension(options.charts)
211 if options.bias:
212 self.css.setBiasFunction(
213 ob.AtlasChartBiasFunction(
214 lambda c, atlas=self.css: atlas.getChartCount()
215 - c.getNeighborCount()
216 + 1.0
217 )
218 )
219 if spaceType == "AT":
220 self.css.setSeparated(not options.no_separate)
221 self.css.setup()
222 self.ss = og.SimpleSetup(self.csi)
223
224 def setStartAndGoalStates(self, start, goal):
225 sstart = self.css.allocState()
226 sgoal = self.css.allocState()
227
228 sstart.copy(start)
229 sgoal.copy(goal)
230
231 # Create start and goal states
232 if self.spaceType == "AT" or self.spaceType == "TB":
233 self.css.anchorChart(sstart)
234 self.css.anchorChart(sgoal)
235
236 # Setup problem
237 self.ss.setStartAndGoalStates(sstart, sgoal)
238
239 def getPlanner(self, plannerName, projectionName=None):
240 planner = eval("og.%s(self.csi)" % plannerName)
241 try:
242 if self.options.range == 0:
243 if not self.spaceType == "PJ":
244 planner.setRange(self.css.getRho_s())
245 else:
246 planner.setRange(self.options.range)
247 except:
248 pass
249 try:
250 if projectionName:
251 planner.setProjectionEvaluator(projectionName)
252 except:
253 pass
254 return planner
255
256 def setPlanner(self, plannerName, projectionName=None):
257 self.pp = self.getPlanner(plannerName, projectionName)
258 self.ss.setPlanner(self.pp)
259
260 def solveOnce(self, output=False, name="ompl"):
261 self.ss.setup()
262 stat = self.ss.solve(self.options.time)
263
264 if stat:
265 # Get solution and validate
266 path = self.ss.getSolutionPath()
267 if not path.check():
268 ou.OMPL_WARN("Path fails check!")
269
270 if stat == ob.PlannerStatus.APPROXIMATE_SOLUTION:
271 ou.OMPL_WARN("Solution is approximate.")
272
273 # Simplify solution and validate simplified solution path.
274 ou.OMPL_INFORM("Simplifying solution...")
275 self.ss.simplifySolution(5.0)
276
277 simplePath = self.ss.getSolutionPath()
278 ou.OMPL_INFORM(
279 "Simplified Path Length: %.3f -> %.3f"
280 % (path.length(), simplePath.length())
281 )
282
283 if not simplePath.check():
284 ou.OMPL_WARN("Simplified path fails check!")
285
286 # Interpolate and validate interpolated solution path.
287 ou.OMPL_INFORM("Interpolating path...")
288 path.interpolate()
289
290 if not path.check():
291 ou.OMPL_WARN("Interpolated simplified path fails check!")
292
293 ou.OMPL_INFORM("Interpolating simplified path...")
294 simplePath.interpolate()
295
296 if not simplePath.check():
297 ou.OMPL_WARN("Interpolated simplified path fails check!")
298
299 if output:
300 ou.OMPL_INFORM("Dumping path to `%s_path.txt`." % name)
301 with open("%s_path.txt" % name, "w") as pathfile:
302 print(path.printAsMatrix(), file=pathfile)
303
304 ou.OMPL_INFORM("Dumping simplified path to `%s_simplepath.txt`." % name)
305 with open("%s_simplepath.txt" % name, "w") as simplepathfile:
306 print(simplePath.printAsMatrix(), file=simplepathfile)
307 else:
308 ou.OMPL_WARN("No solution found.")
309
310 return stat
311
312 def setupBenchmark(self, planners, problem):
313 if not ot:
314 print("Benchmarking not available, no ompl.tools")
315 sys.exit(0)
316
317 self.bench = ot.Benchmark(self.ss, problem)
318
319 self.bench.addExperimentParameter(
320 "n", "INTEGER", str(self.constraint.getAmbientDimension())
321 )
322 self.bench.addExperimentParameter(
323 "k", "INTEGER", str(self.constraint.getManifoldDimension())
324 )
325 self.bench.addExperimentParameter(
326 "n - k", "INTEGER", str(self.constraint.getCoDimension())
327 )
328 self.bench.addExperimentParameter("space", "INTEGER", self.spaceType)
329
331 self.request.maxTime = self.options.time
332 self.request.maxMem = 1e9
333 self.request.runCount = 100
334 self.request.timeBetweenUpdates = 0.1
335 self.request.saveConsoleOutput = False
336 for planner in planners:
337 self.bench.addPlanner(self.getPlanner(planner, problem))
338
339 self.bench.setPreRunEvent(ot.PreSetupEvent(clearSpaceAndPlanner))
340
341 def runBenchmark(self):
342 self.bench.benchmark(self.request)
343 filename = (
344 str(datetime.datetime.now())
345 + "_"
346 + self.bench.getExperimentName()
347 + "_"
348 + self.spaceType
349 )
350 self.bench.saveResultsToFile(filename)
351
352 def atlasStats(self):
353 # For atlas types, output information about size of atlas and amount of
354 # space explored
355 if self.spaceType == "AT" or self.spaceType == "TB":
356 ou.OMPL_INFORM("Atlas has %d charts" % self.css.getChartCount())
357 if self.spaceType == "AT":
358 ou.OMPL_INFORM(
359 "Atlas is approximately %.3f%% open"
360 % self.css.estimateFrontierPercent()
361 )
362
363 def dumpGraph(self, name):
364 ou.OMPL_INFORM("Dumping planner graph to `%s_graph.graphml`." % name)
365 data = ob.PlannerData(self.csi)
366 self.pp.getPlannerData(data)
367
368 with open("%s_graph.graphml" % name, "w") as graphfile:
369 print(data.printGraphML(), file=graphfile)
370
371 if self.spaceType == "AT" or self.spaceType == "TB":
372 ou.OMPL_INFORM("Dumping atlas to `%s_atlas.ply`." % name)
373 with open("%s_atlas.ply" % name, "w") as atlasfile:
374 print(self.css.printPLY(), file=atlasfile)
getPlanner(self, plannerName, projectionName=None)
ConstrainedStateSpace encapsulating a planner-agnostic atlas algorithm for planning on a constraint m...
Space information for a constrained state space. Implements more direct for getting motion states.
Object containing planner generated vertex and edge data. It is assumed that all vertices are unique,...
ConstrainedStateSpace encapsulating a projection-based methodology for planning with constraints.
Space information for a tangent bundle-based state space. Implements more direct for getting motion s...
ConstrainedStateSpace encapsulating a planner-agnostic lazy atlas algorithm for planning on a constra...
Create the set of classes typically needed to solve a geometric problem.
Definition SimpleSetup.h:63
Benchmark a set of planners on a problem instance.
Definition Benchmark.h:49
Representation of a benchmark request.
Definition Benchmark.h:152