blob: 046b9ddc4c9db8d0322b7ebb8bd4a4df560cf537 [file] [log] [blame]
Parker Schuh19b93b12018-03-02 23:26:58 -08001import numpy
2
3# joint_center in x-y space.
Parker Schuhdc682952018-03-03 18:24:01 -08004joint_center = (-0.299, 0.299)
Parker Schuh19b93b12018-03-02 23:26:58 -08005
6# Joint distances (l1 = "proximal", l2 = "distal")
Parker Schuhdc682952018-03-03 18:24:01 -08007l1 = 46.25 * 0.0254
8l2 = 43.75 * 0.0254
9
Parker Schuh19b93b12018-03-02 23:26:58 -080010
11# Convert from x-y coordinates to theta coordinates.
Parker Schuhdc682952018-03-03 18:24:01 -080012# orientation is a bool. This orientation is circular_index mod 2.
13# where circular_index is the circular index, or the position in the
Parker Schuh19b93b12018-03-02 23:26:58 -080014# "hyperextension" zones. "cross_point" allows shifting the place where
15# it rounds the result so that it draws nicer (no other functional differences).
Parker Schuhdc682952018-03-03 18:24:01 -080016def to_theta(pt, circular_index, cross_point=-numpy.pi):
17 orient = (circular_index % 2) == 0
18 x = pt[0]
19 y = pt[1]
20 x -= joint_center[0]
21 y -= joint_center[1]
22 l3 = numpy.hypot(x, y)
23 t3 = numpy.arctan2(y, x)
24 theta1 = numpy.arccos((l1**2 + l3**2 - l2**2) / (2 * l1 * l3))
Parker Schuh19b93b12018-03-02 23:26:58 -080025
Parker Schuhdc682952018-03-03 18:24:01 -080026 if orient:
27 theta1 = -theta1
28 theta1 += t3
29 theta1 = (theta1 - cross_point) % (2 * numpy.pi) + cross_point
30 theta2 = numpy.arctan2(y - l1 * numpy.sin(theta1),
31 x - l1 * numpy.cos(theta1))
32 return numpy.array((theta1, theta2))
33
Parker Schuh19b93b12018-03-02 23:26:58 -080034
35# Simple trig to go back from theta1, theta2 to x-y
Parker Schuhdc682952018-03-03 18:24:01 -080036def to_xy(theta1, theta2):
37 x = numpy.cos(theta1) * l1 + numpy.cos(theta2) * l2 + joint_center[0]
38 y = numpy.sin(theta1) * l1 + numpy.sin(theta2) * l2 + joint_center[1]
39 orient = ((theta2 - theta1) % (2.0 * numpy.pi)) < numpy.pi
40 return (x, y, orient)
41
42
43def get_circular_index(theta):
44 return int(numpy.floor((theta[1] - theta[0]) / numpy.pi))
45
46
47def get_xy(theta):
48 theta1 = theta[0]
49 theta2 = theta[1]
50 x = numpy.cos(theta1) * l1 + numpy.cos(theta2) * l2 + joint_center[0]
51 y = numpy.sin(theta1) * l1 + numpy.sin(theta2) * l2 + joint_center[1]
52 return numpy.array((x, y))
53
Parker Schuh19b93b12018-03-02 23:26:58 -080054
55# Draw a list of lines to a cairo context.
56def draw_lines(cr, lines):
Parker Schuhdc682952018-03-03 18:24:01 -080057 cr.move_to(lines[0][0], lines[0][1])
58 for pt in lines[1:]:
59 cr.line_to(pt[0], pt[1])
Parker Schuh19b93b12018-03-02 23:26:58 -080060
Parker Schuhdc682952018-03-03 18:24:01 -080061
62max_dist = 0.01
Parker Schuh19b93b12018-03-02 23:26:58 -080063max_dist_theta = numpy.pi / 64
Parker Schuhdc682952018-03-03 18:24:01 -080064xy_end_circle_size = 0.01
65theta_end_circle_size = 0.07
66
Parker Schuh19b93b12018-03-02 23:26:58 -080067
68# Subdivide in theta space.
69def subdivide_theta(lines):
Parker Schuhdc682952018-03-03 18:24:01 -080070 out = []
71 last_pt = lines[0]
72 out.append(last_pt)
73 for n_pt in lines[1:]:
74 for pt in subdivide(last_pt, n_pt, max_dist_theta):
75 out.append(pt)
76 last_pt = n_pt
Parker Schuh19b93b12018-03-02 23:26:58 -080077
Parker Schuhdc682952018-03-03 18:24:01 -080078 return out
79
Parker Schuh19b93b12018-03-02 23:26:58 -080080
81# subdivide in xy space.
Parker Schuhdc682952018-03-03 18:24:01 -080082def subdivide_xy(lines, max_dist=max_dist):
83 out = []
84 last_pt = lines[0]
85 out.append(last_pt)
86 for n_pt in lines[1:]:
87 for pt in subdivide(last_pt, n_pt, max_dist):
88 out.append(pt)
89 last_pt = n_pt
Parker Schuh19b93b12018-03-02 23:26:58 -080090
Parker Schuhdc682952018-03-03 18:24:01 -080091 return out
92
93
94def to_theta_with_ci(pt, circular_index):
95 return to_theta_with_circular_index(pt[0], pt[1], circular_index)
96
Parker Schuh19b93b12018-03-02 23:26:58 -080097
98# to_theta, but distinguishes between
Parker Schuhdc682952018-03-03 18:24:01 -080099def to_theta_with_circular_index(x, y, circular_index):
100 theta1, theta2 = to_theta((x, y), circular_index)
101 n_circular_index = int(numpy.floor((theta2 - theta1) / numpy.pi))
102 theta2 = theta2 + ((circular_index - n_circular_index)) * numpy.pi
103 return numpy.array((theta1, theta2))
104
Parker Schuh19b93b12018-03-02 23:26:58 -0800105
106# alpha is in [0, 1] and is the weight to merge a and b.
107def alpha_blend(a, b, alpha):
Parker Schuhdc682952018-03-03 18:24:01 -0800108 """Blends a and b.
Parker Schuh19b93b12018-03-02 23:26:58 -0800109
Parker Schuhdc682952018-03-03 18:24:01 -0800110 Args:
111 alpha: double, Ratio. Needs to be in [0, 1] and is the weight to blend a
112 and b.
113 """
114 return b * alpha + (1.0 - alpha) * a
115
116
Parker Schuh19b93b12018-03-02 23:26:58 -0800117def normalize(v):
Parker Schuhdc682952018-03-03 18:24:01 -0800118 """Normalize a vector while handling 0 length vectors."""
119 norm = numpy.linalg.norm(v)
120 if norm == 0:
121 return v
122 return v / norm
123
Parker Schuh19b93b12018-03-02 23:26:58 -0800124
125# CI is circular index and allows selecting between all the stats that map
126# to the same x-y state (by giving them an integer index).
127# This will compute approximate first and second derivatives with respect
128# to path length.
Parker Schuhdc682952018-03-03 18:24:01 -0800129def to_theta_with_circular_index_and_derivs(x, y, dx, dy,
130 circular_index_select):
131 a = to_theta_with_circular_index(x, y, circular_index_select)
132 b = to_theta_with_circular_index(x + dx * 0.0001, y + dy * 0.0001,
133 circular_index_select)
134 c = to_theta_with_circular_index(x - dx * 0.0001, y - dy * 0.0001,
135 circular_index_select)
136 d1 = normalize(b - a)
137 d2 = normalize(c - a)
138 accel = (d1 + d2) / numpy.linalg.norm(a - b)
139 return (a[0], a[1], d1[0], d1[1], accel[0], accel[1])
140
141
142def to_theta_with_ci_and_derivs(p_prev, p, p_next, c_i_select):
143 a = to_theta(p, c_i_select)
144 b = to_theta(p_next, c_i_select)
145 c = to_theta(p_prev, c_i_select)
146 d1 = normalize(b - a)
147 d2 = normalize(c - a)
148 accel = (d1 + d2) / numpy.linalg.norm(a - b)
149 return (a[0], a[1], d1[0], d1[1], accel[0], accel[1])
150
Parker Schuh19b93b12018-03-02 23:26:58 -0800151
152# Generic subdivision algorithm.
153def subdivide(p1, p2, max_dist):
Parker Schuhdc682952018-03-03 18:24:01 -0800154 dx = p2[0] - p1[0]
155 dy = p2[1] - p1[1]
156 dist = numpy.sqrt(dx**2 + dy**2)
157 n = int(numpy.ceil(dist / max_dist))
158 return [(alpha_blend(p1[0], p2[0],
159 float(i) / n), alpha_blend(p1[1], p2[1],
160 float(i) / n))
161 for i in range(1, n + 1)]
Parker Schuh19b93b12018-03-02 23:26:58 -0800162
Parker Schuh19b93b12018-03-02 23:26:58 -0800163
164# convert from an xy space loop into a theta loop.
165# All segements are expected go from one "hyper-extension" boundary
166# to another, thus we must go backwards over the "loop" to get a loop in
167# x-y space.
Parker Schuhdc682952018-03-03 18:24:01 -0800168def to_theta_loop(lines, cross_point=-numpy.pi):
169 out = []
170 last_pt = lines[0]
171 for n_pt in lines[1:]:
172 for pt in subdivide(last_pt, n_pt, max_dist):
173 out.append(to_theta(pt, 0, cross_point))
174 last_pt = n_pt
175 for n_pt in reversed(lines[:-1]):
176 for pt in subdivide(last_pt, n_pt, max_dist):
177 out.append(to_theta(pt, 1, cross_point))
178 last_pt = n_pt
179 return out
180
Parker Schuh19b93b12018-03-02 23:26:58 -0800181
182# Convert a loop (list of line segments) into
183# The name incorrectly suggests that it is cyclic.
184def back_to_xy_loop(lines):
Parker Schuhdc682952018-03-03 18:24:01 -0800185 out = []
186 last_pt = lines[0]
187 out.append(to_xy(last_pt[0], last_pt[1]))
188 for n_pt in lines[1:]:
189 for pt in subdivide(last_pt, n_pt, max_dist_theta):
190 out.append(to_xy(pt[0], pt[1]))
191 last_pt = n_pt
Parker Schuh19b93b12018-03-02 23:26:58 -0800192
Parker Schuhdc682952018-03-03 18:24:01 -0800193 return out
Parker Schuh19b93b12018-03-02 23:26:58 -0800194
Parker Schuh19b93b12018-03-02 23:26:58 -0800195
196# Segment in angle space.
197class AngleSegment:
Ravago Jones5127ccc2022-07-31 16:32:45 -0700198
Austin Schuh41c71e42018-04-04 20:11:20 -0700199 def __init__(self, start, end, name=None, alpha_unitizer=None, vmax=None):
Parker Schuhdc682952018-03-03 18:24:01 -0800200 """Creates an angle segment.
Parker Schuh19b93b12018-03-02 23:26:58 -0800201
Parker Schuhdc682952018-03-03 18:24:01 -0800202 Args:
203 start: (double, double), The start of the segment in theta1, theta2
204 coordinates in radians
205 end: (double, double), The end of the segment in theta1, theta2
206 coordinates in radians
207 """
208 self.start = start
209 self.end = end
210 self.name = name
Austin Schuh41c71e42018-04-04 20:11:20 -0700211 self.alpha_unitizer = alpha_unitizer
212 self.vmax = vmax
Parker Schuh19b93b12018-03-02 23:26:58 -0800213
Parker Schuhdc682952018-03-03 18:24:01 -0800214 def __repr__(self):
215 return "AngleSegment(%s, %s)" % (repr(self.start), repr(self.end))
Parker Schuh19b93b12018-03-02 23:26:58 -0800216
Parker Schuhdc682952018-03-03 18:24:01 -0800217 def DrawTo(self, cr, theta_version):
218 if theta_version:
219 cr.move_to(self.start[0], self.start[1] + theta_end_circle_size)
220 cr.arc(self.start[0], self.start[1], theta_end_circle_size, 0,
221 2.0 * numpy.pi)
222 cr.move_to(self.end[0], self.end[1] + theta_end_circle_size)
223 cr.arc(self.end[0], self.end[1], theta_end_circle_size, 0,
224 2.0 * numpy.pi)
225 cr.move_to(self.start[0], self.start[1])
226 cr.line_to(self.end[0], self.end[1])
227 else:
228 start_xy = to_xy(self.start[0], self.start[1])
229 end_xy = to_xy(self.end[0], self.end[1])
230 draw_lines(cr, back_to_xy_loop([self.start, self.end]))
231 cr.move_to(start_xy[0] + xy_end_circle_size, start_xy[1])
232 cr.arc(start_xy[0], start_xy[1], xy_end_circle_size, 0,
233 2.0 * numpy.pi)
234 cr.move_to(end_xy[0] + xy_end_circle_size, end_xy[1])
235 cr.arc(end_xy[0], end_xy[1], xy_end_circle_size, 0, 2.0 * numpy.pi)
236
237 def ToThetaPoints(self):
238 dx = self.end[0] - self.start[0]
239 dy = self.end[1] - self.start[1]
240 mag = numpy.hypot(dx, dy)
241 dx /= mag
242 dy /= mag
243
244 return [(self.start[0], self.start[1], dx, dy, 0.0, 0.0),
245 (self.end[0], self.end[1], dx, dy, 0.0, 0.0)]
246
247
Parker Schuh19b93b12018-03-02 23:26:58 -0800248class XYSegment:
Parker Schuhdc682952018-03-03 18:24:01 -0800249 """Straight line in XY space."""
Parker Schuh19b93b12018-03-02 23:26:58 -0800250
Austin Schuh41c71e42018-04-04 20:11:20 -0700251 def __init__(self, start, end, name=None, alpha_unitizer=None, vmax=None):
Parker Schuhdc682952018-03-03 18:24:01 -0800252 """Creates an XY segment.
Parker Schuh19b93b12018-03-02 23:26:58 -0800253
Parker Schuhdc682952018-03-03 18:24:01 -0800254 Args:
255 start: (double, double), The start of the segment in theta1, theta2
256 coordinates in radians
257 end: (double, double), The end of the segment in theta1, theta2
258 coordinates in radians
259 """
260 self.start = start
261 self.end = end
262 self.name = name
Austin Schuh41c71e42018-04-04 20:11:20 -0700263 self.alpha_unitizer = alpha_unitizer
264 self.vmax = vmax
Parker Schuh19b93b12018-03-02 23:26:58 -0800265
Parker Schuhdc682952018-03-03 18:24:01 -0800266 def __repr__(self):
267 return "XYSegment(%s, %s)" % (repr(self.start), repr(self.end))
Parker Schuh19b93b12018-03-02 23:26:58 -0800268
Parker Schuhdc682952018-03-03 18:24:01 -0800269 def DrawTo(self, cr, theta_version):
270 if theta_version:
271 theta1, theta2 = self.start
272 circular_index_select = int(
273 numpy.floor((self.start[1] - self.start[0]) / numpy.pi))
274 start = get_xy(self.start)
275 end = get_xy(self.end)
Parker Schuh19b93b12018-03-02 23:26:58 -0800276
Parker Schuhdc682952018-03-03 18:24:01 -0800277 ln = [(start[0], start[1]), (end[0], end[1])]
278 draw_lines(cr, [
279 to_theta_with_circular_index(x, y, circular_index_select)
280 for x, y in subdivide_xy(ln)
281 ])
282 cr.move_to(self.start[0] + theta_end_circle_size, self.start[1])
283 cr.arc(self.start[0], self.start[1], theta_end_circle_size, 0,
284 2.0 * numpy.pi)
285 cr.move_to(self.end[0] + theta_end_circle_size, self.end[1])
286 cr.arc(self.end[0], self.end[1], theta_end_circle_size, 0,
287 2.0 * numpy.pi)
288 else:
289 start = get_xy(self.start)
290 end = get_xy(self.end)
291 cr.move_to(start[0], start[1])
292 cr.line_to(end[0], end[1])
293 cr.move_to(start[0] + xy_end_circle_size, start[1])
294 cr.arc(start[0], start[1], xy_end_circle_size, 0, 2.0 * numpy.pi)
295 cr.move_to(end[0] + xy_end_circle_size, end[1])
296 cr.arc(end[0], end[1], xy_end_circle_size, 0, 2.0 * numpy.pi)
Parker Schuh19b93b12018-03-02 23:26:58 -0800297
Parker Schuhdc682952018-03-03 18:24:01 -0800298 def ToThetaPoints(self):
299 """ Converts to points in theta space via to_theta_with_circular_index_and_derivs"""
300 theta1, theta2 = self.start
301 circular_index_select = int(
302 numpy.floor((self.start[1] - self.start[0]) / numpy.pi))
303 start = get_xy(self.start)
304 end = get_xy(self.end)
305
306 ln = [(start[0], start[1]), (end[0], end[1])]
307
308 dx = end[0] - start[0]
309 dy = end[1] - start[1]
310 mag = numpy.hypot(dx, dy)
311 dx /= mag
312 dy /= mag
313
314 return [
315 to_theta_with_circular_index_and_derivs(x, y, dx, dy,
316 circular_index_select)
317 for x, y in subdivide_xy(ln, 0.01)
318 ]
319
320
321def spline_eval(start, control1, control2, end, alpha):
322 a = alpha_blend(start, control1, alpha)
323 b = alpha_blend(control1, control2, alpha)
324 c = alpha_blend(control2, end, alpha)
Ravago Jones5127ccc2022-07-31 16:32:45 -0700325 return alpha_blend(alpha_blend(a, b, alpha), alpha_blend(b, c, alpha),
326 alpha)
Parker Schuhdc682952018-03-03 18:24:01 -0800327
328
329def subdivide_spline(start, control1, control2, end):
330 # TODO: pick N based on spline parameters? or otherwise change it to be more evenly spaced?
331 n = 100
332 for i in range(0, n + 1):
333 yield i / float(n)
334
335
336class SplineSegment:
Ravago Jones5127ccc2022-07-31 16:32:45 -0700337
Austin Schuh41c71e42018-04-04 20:11:20 -0700338 def __init__(self,
339 start,
340 control1,
341 control2,
342 end,
343 name=None,
344 alpha_unitizer=None,
345 vmax=None):
Parker Schuhdc682952018-03-03 18:24:01 -0800346 self.start = start
347 self.control1 = control1
348 self.control2 = control2
349 self.end = end
350 self.name = name
Austin Schuh41c71e42018-04-04 20:11:20 -0700351 self.alpha_unitizer = alpha_unitizer
352 self.vmax = vmax
Parker Schuhdc682952018-03-03 18:24:01 -0800353
354 def __repr__(self):
Ravago Jones5127ccc2022-07-31 16:32:45 -0700355 return "SplineSegment(%s, %s, %s, %s)" % (repr(
356 self.start), repr(self.control1), repr(
357 self.control2), repr(self.end))
Parker Schuhdc682952018-03-03 18:24:01 -0800358
359 def DrawTo(self, cr, theta_version):
Austin Schuh41c71e42018-04-04 20:11:20 -0700360 if theta_version:
Parker Schuhdc682952018-03-03 18:24:01 -0800361 c_i_select = get_circular_index(self.start)
362 start = get_xy(self.start)
363 control1 = get_xy(self.control1)
364 control2 = get_xy(self.control2)
365 end = get_xy(self.end)
366
367 draw_lines(cr, [
Ravago Jones5127ccc2022-07-31 16:32:45 -0700368 to_theta(spline_eval(start, control1, control2, end, alpha),
369 c_i_select)
Parker Schuhdc682952018-03-03 18:24:01 -0800370 for alpha in subdivide_spline(start, control1, control2, end)
371 ])
Austin Schuh41c71e42018-04-04 20:11:20 -0700372 cr.move_to(self.start[0] + theta_end_circle_size, self.start[1])
373 cr.arc(self.start[0], self.start[1], theta_end_circle_size, 0,
374 2.0 * numpy.pi)
375 cr.move_to(self.end[0] + theta_end_circle_size, self.end[1])
376 cr.arc(self.end[0], self.end[1], theta_end_circle_size, 0,
377 2.0 * numpy.pi)
Parker Schuhdc682952018-03-03 18:24:01 -0800378 else:
379 start = get_xy(self.start)
380 control1 = get_xy(self.control1)
381 control2 = get_xy(self.control2)
382 end = get_xy(self.end)
Austin Schuh41c71e42018-04-04 20:11:20 -0700383
Parker Schuhdc682952018-03-03 18:24:01 -0800384 draw_lines(cr, [
385 spline_eval(start, control1, control2, end, alpha)
386 for alpha in subdivide_spline(start, control1, control2, end)
387 ])
Austin Schuh41c71e42018-04-04 20:11:20 -0700388
Austin Schuh822a1e52018-04-06 22:50:21 -0700389 cr.move_to(start[0] + xy_end_circle_size, start[1])
Austin Schuh41c71e42018-04-04 20:11:20 -0700390 cr.arc(start[0], start[1], xy_end_circle_size, 0, 2.0 * numpy.pi)
391 cr.move_to(end[0] + xy_end_circle_size, end[1])
392 cr.arc(end[0], end[1], xy_end_circle_size, 0, 2.0 * numpy.pi)
Parker Schuhdc682952018-03-03 18:24:01 -0800393
394 def ToThetaPoints(self):
395 t1, t2 = self.start
396 c_i_select = get_circular_index(self.start)
397 start = get_xy(self.start)
398 control1 = get_xy(self.control1)
399 control2 = get_xy(self.control2)
400 end = get_xy(self.end)
401
402 return [
403 to_theta_with_ci_and_derivs(
404 spline_eval(start, control1, control2, end, alpha - 0.00001),
405 spline_eval(start, control1, control2, end, alpha),
406 spline_eval(start, control1, control2, end, alpha + 0.00001),
407 c_i_select)
408 for alpha in subdivide_spline(start, control1, control2, end)
409 ]
410
411
Austin Schuh41c71e42018-04-04 20:11:20 -0700412def get_derivs(t_prev, t, t_next):
413 c, a, b = t_prev, t, t_next
414 d1 = normalize(b - a)
415 d2 = normalize(c - a)
416 accel = (d1 + d2) / numpy.linalg.norm(a - b)
417 return (a[0], a[1], d1[0], d1[1], accel[0], accel[1])
418
419
420class ThetaSplineSegment:
Ravago Jones5127ccc2022-07-31 16:32:45 -0700421
Austin Schuh41c71e42018-04-04 20:11:20 -0700422 def __init__(self,
423 start,
424 control1,
425 control2,
426 end,
427 name=None,
428 alpha_unitizer=None,
429 vmax=None):
430 self.start = start
431 self.control1 = control1
432 self.control2 = control2
433 self.end = end
434 self.name = name
435 self.alpha_unitizer = alpha_unitizer
436 self.vmax = vmax
437
438 def __repr__(self):
Ravago Jones5127ccc2022-07-31 16:32:45 -0700439 return "ThetaSplineSegment(%s, %s, &s, %s)" % (repr(
440 self.start), repr(self.control1), repr(
441 self.control2), repr(self.end))
Austin Schuh41c71e42018-04-04 20:11:20 -0700442
443 def DrawTo(self, cr, theta_version):
444 if (theta_version):
445 draw_lines(cr, [
446 spline_eval(self.start, self.control1, self.control2, self.end,
447 alpha)
448 for alpha in subdivide_spline(self.start, self.control1,
449 self.control2, self.end)
450 ])
451 else:
452 start = get_xy(self.start)
453 end = get_xy(self.end)
454
455 draw_lines(cr, [
456 get_xy(
457 spline_eval(self.start, self.control1, self.control2,
458 self.end, alpha))
459 for alpha in subdivide_spline(self.start, self.control1,
460 self.control2, self.end)
461 ])
462
463 cr.move_to(start[0] + xy_end_circle_size, start[1])
464 cr.arc(start[0], start[1], xy_end_circle_size, 0, 2.0 * numpy.pi)
465 cr.move_to(end[0] + xy_end_circle_size, end[1])
466 cr.arc(end[0], end[1], xy_end_circle_size, 0, 2.0 * numpy.pi)
467
468 def ToThetaPoints(self):
469 return [
470 get_derivs(
471 spline_eval(self.start, self.control1, self.control2, self.end,
472 alpha - 0.00001),
473 spline_eval(self.start, self.control1, self.control2, self.end,
474 alpha),
475 spline_eval(self.start, self.control1, self.control2, self.end,
476 alpha + 0.00001))
477 for alpha in subdivide_spline(self.start, self.control1,
478 self.control2, self.end)
479 ]
480
481
Austin Schuh83cdd8a2018-03-21 20:49:02 -0700482tall_box_x = 0.411
483tall_box_y = 0.125
Parker Schuhdc682952018-03-03 18:24:01 -0800484
485short_box_x = 0.431
486short_box_y = 0.082
487
Ravago Jones5127ccc2022-07-31 16:32:45 -0700488ready_above_box = to_theta_with_circular_index(tall_box_x,
489 tall_box_y + 0.08,
490 circular_index=-1)
491tall_box_grab = to_theta_with_circular_index(tall_box_x,
492 tall_box_y,
493 circular_index=-1)
494short_box_grab = to_theta_with_circular_index(short_box_x,
495 short_box_y,
496 circular_index=-1)
Parker Schuhdc682952018-03-03 18:24:01 -0800497
498# TODO(austin): Drive the front/back off the same numbers a bit better.
499front_high_box = to_theta_with_circular_index(0.378, 2.46, circular_index=-1)
Ravago Jones5127ccc2022-07-31 16:32:45 -0700500front_middle3_box = to_theta_with_circular_index(0.700,
501 2.125,
502 circular_index=-1.000000)
503front_middle2_box = to_theta_with_circular_index(0.700,
504 2.268,
505 circular_index=-1)
506front_middle1_box = to_theta_with_circular_index(0.800,
507 1.915,
508 circular_index=-1)
Austin Schuh83cdd8a2018-03-21 20:49:02 -0700509front_low_box = to_theta_with_circular_index(0.87, 1.572, circular_index=-1)
Parker Schuhdc682952018-03-03 18:24:01 -0800510back_high_box = to_theta_with_circular_index(-0.75, 2.48, circular_index=0)
Ravago Jones5127ccc2022-07-31 16:32:45 -0700511back_middle2_box = to_theta_with_circular_index(-0.700, 2.27, circular_index=0)
512back_middle1_box = to_theta_with_circular_index(-0.800, 1.93, circular_index=0)
Austin Schuhcf96d322018-04-07 15:52:31 -0700513back_low_box = to_theta_with_circular_index(-0.87, 1.64, circular_index=0)
514
Ravago Jones5127ccc2022-07-31 16:32:45 -0700515back_extra_low_box = to_theta_with_circular_index(-0.87,
516 1.52,
517 circular_index=0)
Parker Schuhdc682952018-03-03 18:24:01 -0800518
519front_switch = to_theta_with_circular_index(0.88, 0.967, circular_index=-1)
520back_switch = to_theta_with_circular_index(-0.88, 0.967, circular_index=-2)
521
522neutral = to_theta_with_circular_index(0.0, 0.33, circular_index=-1)
523
524up = to_theta_with_circular_index(0.0, 2.547, circular_index=-1)
525
Ravago Jones5127ccc2022-07-31 16:32:45 -0700526front_switch_auto = to_theta_with_circular_index(0.750,
527 2.20,
528 circular_index=-1.000000)
Austin Schuh83cdd8a2018-03-21 20:49:02 -0700529
Ravago Jones5127ccc2022-07-31 16:32:45 -0700530duck = numpy.array([numpy.pi / 2.0 - 0.92, numpy.pi / 2.0 - 4.26])
Austin Schuhccf56632018-04-04 20:14:44 -0700531
Ravago Jones5127ccc2022-07-31 16:32:45 -0700532starting = numpy.array([numpy.pi / 2.0 - 0.593329, numpy.pi / 2.0 - 3.749631])
533vertical_starting = numpy.array([numpy.pi / 2.0, -numpy.pi / 2.0])
Austin Schuh83cdd8a2018-03-21 20:49:02 -0700534
Ravago Jones5127ccc2022-07-31 16:32:45 -0700535self_hang = numpy.array([numpy.pi / 2.0 - 0.191611, numpy.pi / 2.0])
536partner_hang = numpy.array([numpy.pi / 2.0 - (-0.30), numpy.pi / 2.0])
Austin Schuh17e484e2018-03-11 01:11:36 -0800537
Ravago Jones5127ccc2022-07-31 16:32:45 -0700538above_hang = numpy.array([numpy.pi / 2.0 - 0.14, numpy.pi / 2.0 - (-0.165)])
539below_hang = numpy.array([numpy.pi / 2.0 - 0.39, numpy.pi / 2.0 - (-0.517)])
Austin Schuh17e484e2018-03-11 01:11:36 -0800540
Parker Schuhdc682952018-03-03 18:24:01 -0800541up_c1 = to_theta((0.63, 1.17), circular_index=-1)
542up_c2 = to_theta((0.65, 1.62), circular_index=-1)
543
544front_high_box_c1 = to_theta((0.63, 1.04), circular_index=-1)
545front_high_box_c2 = to_theta((0.50, 1.60), circular_index=-1)
546
547front_middle2_box_c1 = to_theta((0.41, 0.83), circular_index=-1)
548front_middle2_box_c2 = to_theta((0.52, 1.30), circular_index=-1)
549
550front_middle1_box_c1 = to_theta((0.34, 0.82), circular_index=-1)
551front_middle1_box_c2 = to_theta((0.48, 1.15), circular_index=-1)
552
Austin Schuhccf56632018-04-04 20:14:44 -0700553#c1: (1.421433, -1.070254)
554#c2: (1.434384, -1.057803
555ready_above_box_c1 = numpy.array([1.480802, -1.081218])
556ready_above_box_c2 = numpy.array([1.391449, -1.060331])
Parker Schuhdc682952018-03-03 18:24:01 -0800557
Austin Schuh214b91c2018-03-10 19:25:23 -0800558front_switch_c1 = numpy.array([1.903841, -0.622351])
559front_switch_c2 = numpy.array([1.903841, -0.622351])
560
Austin Schuh60cdb3e2018-04-06 21:52:32 -0700561
Austin Schuh822a1e52018-04-06 22:50:21 -0700562sparse_front_points = [
Austin Schuh60cdb3e2018-04-06 21:52:32 -0700563 (front_high_box, "FrontHighBox"),
564 (front_middle2_box, "FrontMiddle2Box"),
565 (front_middle3_box, "FrontMiddle3Box"),
566 (front_middle1_box, "FrontMiddle1Box"),
567 (front_low_box, "FrontLowBox"),
568 (front_switch, "FrontSwitch"),
569] # yapf: disable
570
Austin Schuh822a1e52018-04-06 22:50:21 -0700571sparse_back_points = [
Austin Schuh60cdb3e2018-04-06 21:52:32 -0700572 (back_high_box, "BackHighBox"),
573 (back_middle2_box, "BackMiddle2Box"),
574 (back_middle1_box, "BackMiddle1Box"),
575 (back_low_box, "BackLowBox"),
Austin Schuhcf96d322018-04-07 15:52:31 -0700576 (back_extra_low_box, "BackExtraLowBox"),
Austin Schuh60cdb3e2018-04-06 21:52:32 -0700577] # yapf: disable
578
Austin Schuh822a1e52018-04-06 22:50:21 -0700579def expand_points(points, max_distance):
580 """Expands a list of points to be at most max_distance apart
581
582 Generates the paths to connect the new points to the closest input points,
583 and the paths connecting the points.
584
585 Args:
586 points, list of tuple of point, name, The points to start with and fill
587 in.
588 max_distance, float, The max distance between two points when expanding
589 the graph.
590
591 Return:
592 points, edges
593 """
594 result_points = [points[0]]
595 result_paths = []
596 for point, name in points[1:]:
597 previous_point = result_points[-1][0]
598 previous_point_xy = get_xy(previous_point)
599 circular_index = get_circular_index(previous_point)
600
601 point_xy = get_xy(point)
602 norm = numpy.linalg.norm(point_xy - previous_point_xy)
603 num_points = int(numpy.ceil(norm / max_distance))
604 last_iteration_point = previous_point
605 for subindex in range(1, num_points):
Ravago Jones5127ccc2022-07-31 16:32:45 -0700606 subpoint = to_theta(alpha_blend(previous_point_xy, point_xy,
607 float(subindex) / num_points),
608 circular_index=circular_index)
609 result_points.append(
610 (subpoint, '%s%dof%d' % (name, subindex, num_points)))
Austin Schuhcf96d322018-04-07 15:52:31 -0700611 result_paths.append(
612 XYSegment(last_iteration_point, subpoint, vmax=6.0))
Austin Schuh822a1e52018-04-06 22:50:21 -0700613 if (last_iteration_point != previous_point).any():
Austin Schuhcf96d322018-04-07 15:52:31 -0700614 result_paths.append(XYSegment(previous_point, subpoint))
615 if subindex == num_points - 1:
Ravago Jones5127ccc2022-07-31 16:32:45 -0700616 result_paths.append(XYSegment(subpoint, point, vmax=6.0))
Austin Schuhcf96d322018-04-07 15:52:31 -0700617 else:
Ravago Jones5127ccc2022-07-31 16:32:45 -0700618 result_paths.append(XYSegment(subpoint, point))
Austin Schuh822a1e52018-04-06 22:50:21 -0700619 last_iteration_point = subpoint
620 result_points.append((point, name))
621
622 return result_points, result_paths
623
Ravago Jones5127ccc2022-07-31 16:32:45 -0700624
Austin Schuhcf96d322018-04-07 15:52:31 -0700625front_points, front_paths = expand_points(sparse_front_points, 0.06)
626back_points, back_paths = expand_points(sparse_back_points, 0.06)
Austin Schuh822a1e52018-04-06 22:50:21 -0700627
Parker Schuhdc682952018-03-03 18:24:01 -0800628points = [(ready_above_box, "ReadyAboveBox"),
629 (tall_box_grab, "TallBoxGrab"),
630 (short_box_grab, "ShortBoxGrab"),
Parker Schuhdc682952018-03-03 18:24:01 -0800631 (back_switch, "BackSwitch"),
632 (neutral, "Neutral"),
Austin Schuh17e484e2018-03-11 01:11:36 -0800633 (up, "Up"),
634 (above_hang, "AboveHang"),
635 (below_hang, "BelowHang"),
636 (self_hang, "SelfHang"),
637 (partner_hang, "PartnerHang"),
Austin Schuh83cdd8a2018-03-21 20:49:02 -0700638 (front_switch_auto, "FrontSwitchAuto"),
639 (starting, "Starting"),
Austin Schuhccf56632018-04-04 20:14:44 -0700640 (duck, "Duck"),
Austin Schuh83cdd8a2018-03-21 20:49:02 -0700641 (vertical_starting, "VerticalStarting"),
Austin Schuh60cdb3e2018-04-06 21:52:32 -0700642] + front_points + back_points # yapf: disable
Parker Schuhdc682952018-03-03 18:24:01 -0800643
Austin Schuhccf56632018-04-04 20:14:44 -0700644duck_c1 = numpy.array([1.337111, -1.721008])
645duck_c2 = numpy.array([1.283701, -1.795519])
646
647ready_to_up_c1 = numpy.array([1.792962, 0.198329])
648ready_to_up_c2 = numpy.array([1.792962, 0.198329])
649
Austin Schuh60cdb3e2018-04-06 21:52:32 -0700650front_switch_auto_c1 = numpy.array([1.792857, -0.372768])
651front_switch_auto_c2 = numpy.array([1.861885, -0.273664])
652
Parker Schuhdc682952018-03-03 18:24:01 -0800653# We need to define critical points so we can create paths connecting them.
654# TODO(austin): Attach velocities to the slow ones.
Austin Schuhcf96d322018-04-07 15:52:31 -0700655ready_to_back_low_c1 = numpy.array([2.524325, 0.046417])
656
657neutral_to_back_low_c1 = numpy.array([2.381942, -0.070220])
658
659tall_to_back_low_c1 = numpy.array([2.603918, 0.088298])
660tall_to_back_low_c2 = numpy.array([1.605624, 1.003434])
661
662tall_to_back_high_c2 = numpy.array([1.508610, 0.946147])
663
664# If true, only plot the first named segment
Austin Schuhed018082018-07-08 16:01:01 -0700665isolate = False
Austin Schuhcf96d322018-04-07 15:52:31 -0700666
667long_alpha_unitizer = numpy.matrix([[1.0 / 17.0, 0.0], [0.0, 1.0 / 17.0]])
668
Austin Schuhed018082018-07-08 16:01:01 -0700669neutral_to_back_c1 = numpy.array([0.702527, -2.618276])
670neutral_to_back_c2 = numpy.array([0.526914, -3.109691])
671
Parker Schuhdc682952018-03-03 18:24:01 -0800672named_segments = [
Ravago Jones5127ccc2022-07-31 16:32:45 -0700673 ThetaSplineSegment(neutral, neutral_to_back_c1, neutral_to_back_c2,
674 back_switch, "BackSwitch"),
675 ThetaSplineSegment(neutral,
676 neutral_to_back_low_c1,
677 tall_to_back_high_c2,
678 back_high_box,
679 "NeutralBoxToHigh",
680 alpha_unitizer=long_alpha_unitizer),
681 ThetaSplineSegment(neutral, neutral_to_back_low_c1, tall_to_back_high_c2,
682 back_middle2_box, "NeutralBoxToMiddle2",
683 long_alpha_unitizer),
684 ThetaSplineSegment(neutral, neutral_to_back_low_c1, tall_to_back_low_c2,
685 back_middle1_box, "NeutralBoxToMiddle1",
686 long_alpha_unitizer),
687 ThetaSplineSegment(neutral, neutral_to_back_low_c1, tall_to_back_low_c2,
688 back_low_box, "NeutralBoxToLow", long_alpha_unitizer),
689 ThetaSplineSegment(ready_above_box, ready_to_back_low_c1,
690 tall_to_back_high_c2, back_high_box, "ReadyBoxToHigh",
691 long_alpha_unitizer),
692 ThetaSplineSegment(ready_above_box, ready_to_back_low_c1,
693 tall_to_back_high_c2, back_middle2_box,
694 "ReadyBoxToMiddle2", long_alpha_unitizer),
695 ThetaSplineSegment(ready_above_box, ready_to_back_low_c1,
696 tall_to_back_low_c2, back_middle1_box,
697 "ReadyBoxToMiddle1", long_alpha_unitizer),
698 ThetaSplineSegment(ready_above_box, ready_to_back_low_c1,
699 tall_to_back_low_c2, back_low_box, "ReadyBoxToLow",
700 long_alpha_unitizer),
701 ThetaSplineSegment(short_box_grab, tall_to_back_low_c1,
702 tall_to_back_high_c2, back_high_box, "ShortBoxToHigh",
703 long_alpha_unitizer),
704 ThetaSplineSegment(short_box_grab, tall_to_back_low_c1,
705 tall_to_back_high_c2, back_middle2_box,
706 "ShortBoxToMiddle2", long_alpha_unitizer),
707 ThetaSplineSegment(short_box_grab, tall_to_back_low_c1,
708 tall_to_back_low_c2, back_middle1_box,
709 "ShortBoxToMiddle1", long_alpha_unitizer),
710 ThetaSplineSegment(short_box_grab, tall_to_back_low_c1,
711 tall_to_back_low_c2, back_low_box, "ShortBoxToLow",
712 long_alpha_unitizer),
713 ThetaSplineSegment(tall_box_grab, tall_to_back_low_c1,
714 tall_to_back_high_c2, back_high_box, "TallBoxToHigh",
715 long_alpha_unitizer),
716 ThetaSplineSegment(tall_box_grab, tall_to_back_low_c1,
717 tall_to_back_high_c2, back_middle2_box,
718 "TallBoxToMiddle2", long_alpha_unitizer),
719 ThetaSplineSegment(tall_box_grab, tall_to_back_low_c1, tall_to_back_low_c2,
720 back_middle1_box, "TallBoxToMiddle1",
721 long_alpha_unitizer),
722 ThetaSplineSegment(tall_box_grab, tall_to_back_low_c1, tall_to_back_low_c2,
723 back_low_box, "TallBoxToLow", long_alpha_unitizer),
Parker Schuhdc682952018-03-03 18:24:01 -0800724 SplineSegment(neutral, ready_above_box_c1, ready_above_box_c2,
725 ready_above_box, "ReadyToNeutral"),
Austin Schuh41c71e42018-04-04 20:11:20 -0700726 XYSegment(ready_above_box, tall_box_grab, "ReadyToTallBox", vmax=6.0),
727 XYSegment(ready_above_box, short_box_grab, "ReadyToShortBox", vmax=6.0),
728 XYSegment(tall_box_grab, short_box_grab, "TallToShortBox", vmax=6.0),
Austin Schuhb874fd32018-03-05 00:27:10 -0800729 SplineSegment(neutral, ready_above_box_c1, ready_above_box_c2,
730 tall_box_grab, "TallToNeutral"),
731 SplineSegment(neutral, ready_above_box_c1, ready_above_box_c2,
732 short_box_grab, "ShortToNeutral"),
Parker Schuhdc682952018-03-03 18:24:01 -0800733 SplineSegment(neutral, up_c1, up_c2, up, "NeutralToUp"),
734 SplineSegment(neutral, front_high_box_c1, front_high_box_c2,
735 front_high_box, "NeutralToFrontHigh"),
736 SplineSegment(neutral, front_middle2_box_c1, front_middle2_box_c2,
Austin Schuh7dfccf62018-03-03 21:28:14 -0800737 front_middle2_box, "NeutralToFrontMiddle2"),
Parker Schuhdc682952018-03-03 18:24:01 -0800738 SplineSegment(neutral, front_middle1_box_c1, front_middle1_box_c2,
Austin Schuh7dfccf62018-03-03 21:28:14 -0800739 front_middle1_box, "NeutralToFrontMiddle1"),
Parker Schuhdc682952018-03-03 18:24:01 -0800740]
741
742unnamed_segments = [
Ravago Jones5127ccc2022-07-31 16:32:45 -0700743 SplineSegment(neutral, front_switch_auto_c1, front_switch_auto_c2,
744 front_switch_auto),
Austin Schuhccf56632018-04-04 20:14:44 -0700745 SplineSegment(tall_box_grab, ready_to_up_c1, ready_to_up_c2, up),
746 SplineSegment(short_box_grab, ready_to_up_c1, ready_to_up_c2, up),
747 SplineSegment(ready_above_box, ready_to_up_c1, ready_to_up_c2, up),
748 ThetaSplineSegment(duck, duck_c1, duck_c2, neutral),
Austin Schuh214b91c2018-03-10 19:25:23 -0800749 SplineSegment(neutral, front_switch_c1, front_switch_c2, front_switch),
Austin Schuhccf56632018-04-04 20:14:44 -0700750 XYSegment(ready_above_box, front_low_box),
751 XYSegment(ready_above_box, front_switch),
752 XYSegment(ready_above_box, front_middle1_box),
753 XYSegment(ready_above_box, front_middle2_box),
754 XYSegment(ready_above_box, front_middle3_box),
Ravago Jones5127ccc2022-07-31 16:32:45 -0700755 SplineSegment(ready_above_box, ready_to_up_c1, ready_to_up_c2,
756 front_high_box),
Austin Schuh83cdd8a2018-03-21 20:49:02 -0700757 AngleSegment(starting, vertical_starting),
758 AngleSegment(vertical_starting, neutral),
Austin Schuh214b91c2018-03-10 19:25:23 -0800759 XYSegment(neutral, front_low_box),
Parker Schuhdc682952018-03-03 18:24:01 -0800760 XYSegment(up, front_high_box),
761 XYSegment(up, front_middle2_box),
Austin Schuh83cdd8a2018-03-21 20:49:02 -0700762 XYSegment(front_middle3_box, up),
763 XYSegment(front_middle3_box, front_high_box),
764 XYSegment(front_middle3_box, front_middle2_box),
765 XYSegment(front_middle3_box, front_middle1_box),
Parker Schuhdc682952018-03-03 18:24:01 -0800766 XYSegment(up, front_middle1_box),
767 XYSegment(up, front_low_box),
768 XYSegment(front_high_box, front_middle2_box),
769 XYSegment(front_high_box, front_middle1_box),
770 XYSegment(front_high_box, front_low_box),
771 XYSegment(front_middle2_box, front_middle1_box),
772 XYSegment(front_middle2_box, front_low_box),
773 XYSegment(front_middle1_box, front_low_box),
774 XYSegment(front_switch, front_low_box),
775 XYSegment(front_switch, up),
776 XYSegment(front_switch, front_high_box),
777 AngleSegment(up, back_high_box),
778 AngleSegment(up, back_middle2_box),
779 AngleSegment(up, back_middle1_box),
Austin Schuhcf96d322018-04-07 15:52:31 -0700780 AngleSegment(up, back_low_box),
Parker Schuhdc682952018-03-03 18:24:01 -0800781 XYSegment(back_high_box, back_middle2_box),
782 XYSegment(back_high_box, back_middle1_box),
783 XYSegment(back_high_box, back_low_box),
784 XYSegment(back_middle2_box, back_middle1_box),
785 XYSegment(back_middle2_box, back_low_box),
786 XYSegment(back_middle1_box, back_low_box),
Austin Schuh17e484e2018-03-11 01:11:36 -0800787 AngleSegment(up, above_hang),
788 AngleSegment(above_hang, below_hang),
789 AngleSegment(up, below_hang),
790 AngleSegment(up, self_hang),
791 AngleSegment(up, partner_hang),
Austin Schuh822a1e52018-04-06 22:50:21 -0700792] + front_paths + back_paths
Parker Schuhdc682952018-03-03 18:24:01 -0800793
Austin Schuhcf96d322018-04-07 15:52:31 -0700794segments = []
795if isolate:
796 segments += named_segments[:isolate]
797else:
798 segments += named_segments + unnamed_segments