39 from __future__
import print_function
41 from os.path
import dirname, join
44 from functools
import partial
46 from ConstrainedPlanningCommon
import *
55 def __init__(self, outer, inner, maze):
56 super(TorusConstraint, self).__init__(3, 1)
60 self.
ppm.loadFile(maze)
62 def getStartAndGoalStates(self):
63 h = self.
ppm.getHeight()
64 w = self.
ppm.getWidth()
68 p = np.array([x / (w - 1.), y / (h - 1.)], dtype=np.float64)
69 c = self.
ppm.getPixel(x, y)
70 if c.red == 255
and c.blue == 0
and c.green == 0:
72 elif c.green == 255
and c.blue == 0
and c.red == 0:
76 def function(self, x, out):
77 c = np.array([x[0], x[1], 0])
78 nrm = math.sqrt(x[0] * x[0] + x[1] * x[1])
79 if not np.isfinite(nrm)
or nrm == 0:
81 out[0] = np.linalg.norm(x - self.
outer * c / nrm) - self.
inner
83 def jacobian(self, x, out):
84 xySquaredNorm = x[0] * x[0] + x[1] * x[1]
85 xyNorm = math.sqrt(xySquaredNorm)
86 denom = math.sqrt(x[2] * x[2] + (xyNorm - self.
outer)
87 * (xyNorm - self.
outer))
88 c = (xyNorm - self.
outer) * (xyNorm * xySquaredNorm) / \
89 (xySquaredNorm * xySquaredNorm * denom)
90 out[0, :] = [x[0] * c, x[1] * c, x[2] / denom]
92 def ambientToMaze(self, x):
93 nrm = math.sqrt(x[0] * x[0] + x[1] * x[1])
94 h = self.
ppm.getHeight()
95 w = self.
ppm.getWidth()
97 mx = math.atan2(x[2], nrm - self.
outer) / PI2
100 my = math.atan2(x[1], x[0]) / PI2
105 def mazeToAmbient(self, x):
107 b = [math.cos(a[0]), 0, math.sin(a[0])] * self.
inner
110 norm = math.sqrt(b[0] * b[0] + b[1] * b[1])
111 out = np.array([math.cos(a[1]), math.sin(a[1]), 0], dtype=np.float64)
116 def mazePixel(self, x):
117 h = self.
ppm.getHeight()
118 w = self.
ppm.getWidth()
120 if x[0] < 0
or x[0] >= w
or x[1] < 0
or x[1] >= h:
123 c = self.
ppm.getPixel(int(x[0]), int(x[1]))
124 return not (c.red == 0
and c.blue == 0
and c.green == 0)
126 def isValid(self, state):
130 def torusPlanningBench(cp, planners):
132 cp.setupBenchmark(planners,
"torus")
136 def torusPlanningOnce(cp, planner, output):
137 cp.setPlanner(planner)
140 stat = cp.solveOnce(output,
"torus")
143 ou.OMPL_INFORM(
"Dumping problem information to `torus_info.txt`.")
144 with open(
"torus_info.txt",
"w")
as infofile:
145 print(cp.spaceType, file=infofile)
150 cp.dumpGraph(
"torus")
155 def torusPlanning(options):
160 bounds.setLow(-(options.outer + options.inner))
161 bounds.setHigh(options.outer + options.inner)
163 rvss.setBounds(bounds)
166 constraint =
TorusConstraint(options.outer, options.inner, options.maze)
170 start, goal = constraint.getStartAndGoalStates()
171 print(
"Start = ", start)
172 print(
"Goal = ", goal)
179 cp.setStartAndGoalStates(sstart, sgoal)
181 TorusConstraint.isValid, constraint)))
183 planners = options.planner.split(
",")
184 if not options.bench:
185 torusPlanningOnce(cp, planners[0], options.output)
187 torusPlanningBench(cp, planners)
189 if __name__ ==
"__main__":
190 defaultMaze = join(join(dirname(__file__),
"mazes"),
"normal.ppm")
191 parser = argparse.ArgumentParser()
192 parser.add_argument(
"-o",
"--output", action=
"store_true",
193 help=
"Dump found solution path (if one exists) in plain text and planning "
194 "graph in GraphML to `torus_path.txt` and `torus_graph.graphml` "
196 parser.add_argument(
"--bench", action=
"store_true",
197 help=
"Do benchmarking on provided planner list.")
198 parser.add_argument(
"--outer", type=float, default=2,
199 help=
"Outer radius of torus.")
200 parser.add_argument(
"--inner", type=float, default=1,
201 help=
"Inner radius of torus.")
202 parser.add_argument(
"--maze", default=defaultMaze,
203 help=
"Filename of maze image (in .ppm format) to use as obstacles on the "
204 "surface of the torus.")
205 addSpaceOption(parser)
206 addPlannerOption(parser)
207 addConstrainedOptions(parser)
208 addAtlasOptions(parser)
210 torusPlanning(parser.parse_args())