blob: 408390626cd6eb2e696938dca3048c1fdea6cb45 [file] [log] [blame]
milind-u18a901d2023-02-17 21:51:55 -08001import abc
2import numpy as np
3import sys
4import traceback
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -08005
6# joint_center in x-y space.
milind-u18a901d2023-02-17 21:51:55 -08007IN_TO_M = 0.0254
8joint_center = (-0.203, 0.787)
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -08009
10# Joint distances (l1 = "proximal", l2 = "distal")
milind-u18a901d2023-02-17 21:51:55 -080011l1 = 20.0 * IN_TO_M
12l2 = 31.5 * IN_TO_M
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -080013
14max_dist = 0.01
milind-u18a901d2023-02-17 21:51:55 -080015max_dist_theta = np.pi / 64
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -080016xy_end_circle_size = 0.01
17theta_end_circle_size = 0.07
18
19
milind-u060e4cf2023-02-22 00:08:52 -080020# Shift the angle between the convention used for input/output and the convention we use for some computations here
21def shift_angle(theta):
22 return np.pi / 2 - theta
23
24
25def shift_angles(thetas):
26 return [shift_angle(theta) for theta in thetas]
27
28
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -080029# Convert from x-y coordinates to theta coordinates.
30# orientation is a bool. This orientation is circular_index mod 2.
31# where circular_index is the circular index, or the position in the
32# "hyperextension" zones. "cross_point" allows shifting the place where
33# it rounds the result so that it draws nicer (no other functional differences).
milind-u18a901d2023-02-17 21:51:55 -080034def to_theta(pt, circular_index, cross_point=-np.pi):
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -080035 orient = (circular_index % 2) == 0
36 x = pt[0]
37 y = pt[1]
38 x -= joint_center[0]
39 y -= joint_center[1]
milind-u18a901d2023-02-17 21:51:55 -080040 l3 = np.hypot(x, y)
41 t3 = np.arctan2(y, x)
42 theta1 = np.arccos((l1**2 + l3**2 - l2**2) / (2 * l1 * l3))
43 if np.isnan(theta1):
44 traceback.print_stack()
45 sys.exit("Couldn't fit triangle to %f, %f, %f" % (l1, l2, l3))
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -080046
47 if orient:
48 theta1 = -theta1
49 theta1 += t3
milind-u18a901d2023-02-17 21:51:55 -080050 theta1 = (theta1 - cross_point) % (2 * np.pi) + cross_point
51 theta2 = np.arctan2(y - l1 * np.sin(theta1), x - l1 * np.cos(theta1))
52 return np.array((theta1, theta2))
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -080053
54
55# Simple trig to go back from theta1, theta2 to x-y
56def to_xy(theta1, theta2):
milind-u18a901d2023-02-17 21:51:55 -080057 x = np.cos(theta1) * l1 + np.cos(theta2) * l2 + joint_center[0]
58 y = np.sin(theta1) * l1 + np.sin(theta2) * l2 + joint_center[1]
59 orient = ((theta2 - theta1) % (2.0 * np.pi)) < np.pi
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -080060 return (x, y, orient)
61
62
milind-u18a901d2023-02-17 21:51:55 -080063END_EFFECTOR_X_LEN = (-1.0 * IN_TO_M, 10.425 * IN_TO_M)
64END_EFFECTOR_Y_LEN = (-4.875 * IN_TO_M, 7.325 * IN_TO_M)
65END_EFFECTOR_Z_LEN = (-11.0 * IN_TO_M, 11.0 * IN_TO_M)
66
67
68def abs_sum(l):
69 result = 0
70 for e in l:
71 result += abs(e)
72 return result
73
74
75def affine_3d(R, T):
76 H = np.eye(4)
77 H[:3, 3] = T
78 H[:3, :3] = R
79 return H
80
81
82# Simple trig to go back from theta1, theta2, and theta3 to
83# the 8 corners on the roll joint x-y-z
84def to_end_effector_points(theta1, theta2, theta3):
85 x, y, _ = to_xy(theta1, theta2)
86 # Homogeneous end effector points relative to the end_effector
87 # ee = end effector
88 endpoints_ee = []
89 for i in range(2):
90 for j in range(2):
91 for k in range(2):
92 endpoints_ee.append(
93 np.array((END_EFFECTOR_X_LEN[i], END_EFFECTOR_Y_LEN[j],
94 END_EFFECTOR_Z_LEN[k], 1.0)))
95
96 # Only roll.
97 # rj = roll joint
98 roll = theta3
99 T_rj_ee = np.zeros(3)
100 R_rj_ee = np.array([[1.0, 0.0, 0.0], [0.0,
101 np.cos(roll), -np.sin(roll)],
102 [0.0, np.sin(roll), np.cos(roll)]])
103 H_rj_ee = affine_3d(R_rj_ee, T_rj_ee)
104
105 # Roll joint pose relative to the origin
106 # o = origin
107 T_o_rj = np.array((x, y, 0))
108 # Only yaw
109 yaw = theta1 + theta2
110 R_o_rj = [[np.cos(yaw), -np.sin(yaw), 0.0],
111 [np.sin(yaw), np.cos(yaw), 0.0], [0.0, 0.0, 1.0]]
112 H_o_rj = affine_3d(R_o_rj, T_o_rj)
113
114 # Now compute the pose of the end effector relative to the origin
115 H_o_ee = H_o_rj @ H_rj_ee
116
117 # Get the translation from these transforms
118 endpoints_o = [(H_o_ee @ endpoint_ee)[:3] for endpoint_ee in endpoints_ee]
119
120 diagonal_distance = np.linalg.norm(
121 np.array(endpoints_o[0]) - np.array(endpoints_o[-1]))
122 actual_diagonal_distance = np.linalg.norm(
123 np.array((abs_sum(END_EFFECTOR_X_LEN), abs_sum(END_EFFECTOR_Y_LEN),
124 abs_sum(END_EFFECTOR_Z_LEN))))
125 assert abs(diagonal_distance - actual_diagonal_distance) < 1e-5
126
127 return np.array(endpoints_o)
128
129
130# Returns all permutations of rectangle points given two opposite corners.
131# x is the two x values, y is the two y values, z is the two z values
132def rect_points(x, y, z):
133 points = []
134 for i in range(2):
135 for j in range(2):
136 for k in range(2):
137 points.append((x[i], y[j], z[k]))
138 return np.array(points)
139
140
141DRIVER_CAM_Z_OFFSET = 3.225 * IN_TO_M
142DRIVER_CAM_POINTS = rect_points(
143 (-5.126 * IN_TO_M + joint_center[0], 0.393 * IN_TO_M + joint_center[0]),
144 (5.125 * IN_TO_M + joint_center[1], 17.375 * IN_TO_M + joint_center[1]),
145 (-8.475 * IN_TO_M - DRIVER_CAM_Z_OFFSET,
146 -4.350 * IN_TO_M - DRIVER_CAM_Z_OFFSET))
147
148
149def compute_face_normals(points):
150 # Return the normal vectors of all the faces
151 normals = []
152 for i in range(points.shape[0]):
153 v1 = points[i]
154 v2 = points[(i + 1) % points.shape[0]]
155 normal = np.cross(v1, v2)
156 normals.append(normal)
157 return np.array(normals)
158
159
160def project_points_onto_axis(points, axis):
161 projections = np.dot(points, axis)
162 return np.min(projections), np.max(projections)
163
164
165def roll_joint_collision(theta1, theta2, theta3):
milind-u060e4cf2023-02-22 00:08:52 -0800166 theta1 = shift_angle(theta1)
167 theta2 = shift_angle(theta2)
168 theta3 = shift_angle(theta3)
169
milind-u18a901d2023-02-17 21:51:55 -0800170 end_effector_points = to_end_effector_points(theta1, theta2, theta3)
171
172 assert len(end_effector_points) == 8 and len(end_effector_points[0]) == 3
173 assert len(DRIVER_CAM_POINTS) == 8 and len(DRIVER_CAM_POINTS[0]) == 3
174
175 # Use the Separating Axis Theorem to check for collision
176 end_effector_normals = compute_face_normals(end_effector_points)
177 driver_cam_normals = compute_face_normals(DRIVER_CAM_POINTS)
178
179 collision = True
180 # Check for separating axes
181 for normal in np.concatenate((end_effector_normals, driver_cam_normals)):
182 min_ee, max_ee = project_points_onto_axis(end_effector_points, normal)
183 min_dc, max_dc = project_points_onto_axis(DRIVER_CAM_POINTS, normal)
184 if max_ee < min_dc or min_ee > max_dc:
185 # Separating axis found, rectangles don't intersect
186 collision = False
187 break
188
189 return collision
190
191
milind-ueeb08c52023-02-21 22:30:16 -0800192# Delta limit means theta2 - theta1.
193# The limit for the proximal and distal is relative,
194# so define constraints for this delta.
195UPPER_DELTA_LIMIT = 0.0
196LOWER_DELTA_LIMIT = -1.9 * np.pi
197
198# TODO(milind): put actual proximal limits
199UPPER_PROXIMAL_LIMIT = np.pi * 1.5
200LOWER_PROXIMAL_LIMIT = -np.pi
201
202UPPER_ROLL_JOINT_LIMIT = 0.75 * np.pi
203LOWER_ROLL_JOINT_LIMIT = -0.75 * np.pi
204
205
206def arm_past_limit(theta1, theta2, theta3):
207 delta = theta2 - theta1
208 return (delta > UPPER_DELTA_LIMIT or delta < LOWER_DELTA_LIMIT) or (
209 theta3 > UPPER_ROLL_JOINT_LIMIT or
210 theta3 < LOWER_ROLL_JOINT_LIMIT) or (theta1 > UPPER_PROXIMAL_LIMIT
211 or theta1 < LOWER_PROXIMAL_LIMIT)
212
213
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -0800214def get_circular_index(theta):
milind-u18a901d2023-02-17 21:51:55 -0800215 return int(np.floor((theta[1] - theta[0]) / np.pi))
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -0800216
217
218def get_xy(theta):
milind-u060e4cf2023-02-22 00:08:52 -0800219 theta1 = shift_angle(theta[0])
220 theta2 = shift_angle(theta[1])
milind-u18a901d2023-02-17 21:51:55 -0800221 x = np.cos(theta1) * l1 + np.cos(theta2) * l2 + joint_center[0]
222 y = np.sin(theta1) * l1 + np.sin(theta2) * l2 + joint_center[1]
223 return np.array((x, y))
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -0800224
225
226# Subdivide in theta space.
227def subdivide_theta(lines):
228 out = []
229 last_pt = lines[0]
230 out.append(last_pt)
231 for n_pt in lines[1:]:
232 for pt in subdivide(last_pt, n_pt, max_dist_theta):
233 out.append(pt)
234 last_pt = n_pt
235
236 return out
237
238
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -0800239def to_theta_with_ci(pt, circular_index):
milind-u18a901d2023-02-17 21:51:55 -0800240 return (to_theta_with_circular_index(pt[0], pt[1], circular_index))
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -0800241
242
243# to_theta, but distinguishes between
244def to_theta_with_circular_index(x, y, circular_index):
245 theta1, theta2 = to_theta((x, y), circular_index)
milind-u18a901d2023-02-17 21:51:55 -0800246 n_circular_index = int(np.floor((theta2 - theta1) / np.pi))
247 theta2 = theta2 + ((circular_index - n_circular_index)) * np.pi
milind-u060e4cf2023-02-22 00:08:52 -0800248 return np.array((shift_angle(theta1), shift_angle(theta2)))
milind-u18a901d2023-02-17 21:51:55 -0800249
250
251# to_theta, but distinguishes between
252def to_theta_with_circular_index_and_roll(x, y, roll, circular_index):
253 theta12 = to_theta_with_circular_index(x, y, circular_index)
254 theta3 = roll
255 return np.array((theta12[0], theta12[1], theta3))
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -0800256
257
258# alpha is in [0, 1] and is the weight to merge a and b.
259def alpha_blend(a, b, alpha):
260 """Blends a and b.
261
262 Args:
263 alpha: double, Ratio. Needs to be in [0, 1] and is the weight to blend a
264 and b.
265 """
266 return b * alpha + (1.0 - alpha) * a
267
268
269def normalize(v):
270 """Normalize a vector while handling 0 length vectors."""
milind-u18a901d2023-02-17 21:51:55 -0800271 norm = np.linalg.norm(v)
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -0800272 if norm == 0:
273 return v
274 return v / norm
275
276
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -0800277# Generic subdivision algorithm.
278def subdivide(p1, p2, max_dist):
279 dx = p2[0] - p1[0]
280 dy = p2[1] - p1[1]
milind-u18a901d2023-02-17 21:51:55 -0800281 dist = np.sqrt(dx**2 + dy**2)
282 n = int(np.ceil(dist / max_dist))
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -0800283 return [(alpha_blend(p1[0], p2[0],
284 float(i) / n), alpha_blend(p1[1], p2[1],
285 float(i) / n))
286 for i in range(1, n + 1)]
287
288
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -0800289def spline_eval(start, control1, control2, end, alpha):
290 a = alpha_blend(start, control1, alpha)
291 b = alpha_blend(control1, control2, alpha)
292 c = alpha_blend(control2, end, alpha)
293 return alpha_blend(alpha_blend(a, b, alpha), alpha_blend(b, c, alpha),
294 alpha)
295
296
milind-u18a901d2023-02-17 21:51:55 -0800297SPLINE_SUBDIVISIONS = 100
298
299
300def subdivide_multistep():
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -0800301 # TODO: pick N based on spline parameters? or otherwise change it to be more evenly spaced?
milind-u18a901d2023-02-17 21:51:55 -0800302 for i in range(0, SPLINE_SUBDIVISIONS + 1):
303 yield i / float(SPLINE_SUBDIVISIONS)
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -0800304
305
milind-u18a901d2023-02-17 21:51:55 -0800306def get_proximal_distal_derivs(t_prev, t, t_next):
307 d_prev = normalize(t - t_prev)
308 d_next = normalize(t_next - t)
309 accel = (d_next - d_prev) / np.linalg.norm(t - t_next)
310 return (ThetaPoint(t[0], d_next[0],
311 accel[0]), ThetaPoint(t[1], d_next[1], accel[1]))
312
313
314def get_roll_joint_theta(theta_i, theta_f, t):
315 # Fit a theta(t) = (1 - cos(pi*t)) / 2,
316 # so that theta(0) = theta_i, and theta(1) = theta_f
317 offset = theta_i
318 scalar = (theta_f - theta_i) / 2.0
319 freq = np.pi
320 theta_curve = lambda t: scalar * (1 - np.cos(freq * t)) + offset
321
322 return theta_curve(t)
323
324
325def get_roll_joint_theta_multistep(alpha_rolls, alpha):
326 # Figure out which segment in the motion we're in
327 theta_i = None
328 theta_f = None
329 t = None
330
331 for i in range(len(alpha_rolls) - 1):
332 # Find the alpha segment we're in
333 if alpha_rolls[i][0] <= alpha <= alpha_rolls[i + 1][0]:
334 theta_i = alpha_rolls[i][1]
335 theta_f = alpha_rolls[i + 1][1]
336
337 total_dalpha = alpha_rolls[-1][0] - alpha_rolls[0][0]
338 assert total_dalpha == 1.0
339 dalpha = alpha_rolls[i + 1][0] - alpha_rolls[i][0]
340 t = (alpha - alpha_rolls[i][0]) * (total_dalpha / dalpha)
341 break
342 assert theta_i is not None
343 assert theta_f is not None
344 assert t is not None
345
346 return get_roll_joint_theta(theta_i, theta_f, t)
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -0800347
348
Maxwell Henderson83cf6d62023-02-10 20:29:26 -0800349# Draw a list of lines to a cairo context.
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -0800350def draw_lines(cr, lines):
351 cr.move_to(lines[0][0], lines[0][1])
352 for pt in lines[1:]:
353 cr.line_to(pt[0], pt[1])
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -0800354
355
milind-u18a901d2023-02-17 21:51:55 -0800356class Path(abc.ABC):
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -0800357
milind-u18a901d2023-02-17 21:51:55 -0800358 def __init__(self, name):
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -0800359 self.name = name
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -0800360
milind-u18a901d2023-02-17 21:51:55 -0800361 @abc.abstractmethod
362 def DoToThetaPoints(self):
363 pass
364
365 @abc.abstractmethod
366 def DoDrawTo(self):
367 pass
368
369 @abc.abstractmethod
370 def roll_joint_thetas(self):
371 pass
372
373 @abc.abstractmethod
374 def intersection(self, event):
375 pass
376
377 def roll_joint_collision(self, points, verbose=False):
378 for point in points:
379 if roll_joint_collision(*point):
380 if verbose:
381 print("Roll joint collision for path %s in point %s" %
382 (self.name, point))
383 return True
384 return False
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -0800385
milind-ueeb08c52023-02-21 22:30:16 -0800386 def arm_past_limit(self, points, verbose=True):
387 for point in points:
388 if arm_past_limit(*point):
389 if verbose:
390 print("Arm past limit for path %s in point %s" %
391 (self.name, point))
392 return True
393 return False
394
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -0800395 def DrawTo(self, cr, theta_version):
milind-ueeb08c52023-02-21 22:30:16 -0800396 points = self.DoToThetaPoints()
397 if self.roll_joint_collision(points):
398 # Draw the spline red
milind-u18a901d2023-02-17 21:51:55 -0800399 cr.set_source_rgb(1.0, 0.0, 0.0)
milind-ueeb08c52023-02-21 22:30:16 -0800400 elif self.arm_past_limit(points):
401 # Draw the spline orange
402 cr.set_source_rgb(1.0, 0.5, 0.0)
403
milind-u18a901d2023-02-17 21:51:55 -0800404 self.DoDrawTo(cr, theta_version)
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -0800405
406 def ToThetaPoints(self):
milind-u18a901d2023-02-17 21:51:55 -0800407 points = self.DoToThetaPoints()
milind-ueeb08c52023-02-21 22:30:16 -0800408 if self.roll_joint_collision(points, verbose=True) or \
409 self.arm_past_limit(points, verbose=True):
milind-u18a901d2023-02-17 21:51:55 -0800410 sys.exit(1)
411 return points
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -0800412
413
milind-u18a901d2023-02-17 21:51:55 -0800414class SplineSegmentBase(Path):
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -0800415
milind-u18a901d2023-02-17 21:51:55 -0800416 def __init__(self, name):
417 super().__init__(name)
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -0800418
milind-u18a901d2023-02-17 21:51:55 -0800419 @abc.abstractmethod
420 # Returns (start, control1, control2, end), each in the form
421 # (theta1, theta2, theta3)
422 def get_controls_theta(self):
423 pass
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -0800424
milind-u18a901d2023-02-17 21:51:55 -0800425 def intersection(self, event):
426 start, control1, control2, end = self.get_controls_theta()
427 for alpha in subdivide_multistep():
428 x, y = get_xy(spline_eval(start, control1, control2, end, alpha))
429 spline_point = np.array([x, y])
430 hovered_point = np.array([event.x, event.y])
431 if np.linalg.norm(hovered_point - spline_point) < 0.03:
432 return alpha
433 return None
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -0800434
435
milind-u18a901d2023-02-17 21:51:55 -0800436class ThetaSplineSegment(SplineSegmentBase):
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -0800437
milind-u18a901d2023-02-17 21:51:55 -0800438 # start and end are [theta1, theta2, theta3].
439 # controls are just [theta1, theta2].
440 # control_alpha_rolls are a list of [alpha, roll]
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -0800441 def __init__(self,
milind-u18a901d2023-02-17 21:51:55 -0800442 name,
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -0800443 start,
444 control1,
445 control2,
446 end,
milind-u18a901d2023-02-17 21:51:55 -0800447 control_alpha_rolls=[],
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -0800448 alpha_unitizer=None,
449 vmax=None):
milind-u18a901d2023-02-17 21:51:55 -0800450 super().__init__(name)
451 self.start = start[:2]
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -0800452 self.control1 = control1
453 self.control2 = control2
milind-u18a901d2023-02-17 21:51:55 -0800454 self.end = end[:2]
455 # There will always be roll at alpha = 0 and 1
456 self.alpha_rolls = [[0.0, start[2]]
457 ] + control_alpha_rolls + [[1.0, end[2]]]
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -0800458 self.alpha_unitizer = alpha_unitizer
459 self.vmax = vmax
460
461 def __repr__(self):
milind-u18a901d2023-02-17 21:51:55 -0800462 return "ThetaSplineSegment(%s, %s, %s, %s)" % (repr(
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -0800463 self.start), repr(self.control1), repr(
464 self.control2), repr(self.end))
465
milind-u18a901d2023-02-17 21:51:55 -0800466 def DoDrawTo(self, cr, theta_version):
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -0800467 if (theta_version):
468 draw_lines(cr, [
milind-u060e4cf2023-02-22 00:08:52 -0800469 shift_angles(
470 spline_eval(self.start, self.control1, self.control2,
471 self.end, alpha))
472 for alpha in subdivide_multistep()
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -0800473 ])
474 else:
475 start = get_xy(self.start)
476 end = get_xy(self.end)
477
478 draw_lines(cr, [
479 get_xy(
480 spline_eval(self.start, self.control1, self.control2,
481 self.end, alpha))
milind-u18a901d2023-02-17 21:51:55 -0800482 for alpha in subdivide_multistep()
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -0800483 ])
484
485 cr.move_to(start[0] + xy_end_circle_size, start[1])
milind-u18a901d2023-02-17 21:51:55 -0800486 cr.arc(start[0], start[1], xy_end_circle_size, 0, 2.0 * np.pi)
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -0800487 cr.move_to(end[0] + xy_end_circle_size, end[1])
milind-u18a901d2023-02-17 21:51:55 -0800488 cr.arc(end[0], end[1], xy_end_circle_size, 0, 2.0 * np.pi)
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -0800489
milind-u18a901d2023-02-17 21:51:55 -0800490 def DoToThetaPoints(self):
491 points = []
492 for alpha in subdivide_multistep():
493 proximal, distal = spline_eval(self.start, self.control1,
494 self.control2, self.end, alpha)
495 roll_joint = get_roll_joint_theta_multistep(
496 self.alpha_rolls, alpha)
497 points.append((proximal, distal, roll_joint))
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -0800498
milind-u18a901d2023-02-17 21:51:55 -0800499 return points
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -0800500
milind-u18a901d2023-02-17 21:51:55 -0800501 def get_controls_theta(self):
502 return (self.start, self.control1, self.control2, self.end)
Maxwell Hendersonf5123fe2023-02-04 13:44:41 -0800503
milind-u18a901d2023-02-17 21:51:55 -0800504 def roll_joint_thetas(self):
505 ts = []
506 thetas = []
507 for alpha in subdivide_multistep():
508 roll_joint = get_roll_joint_theta_multistep(
509 self.alpha_rolls, alpha)
510 thetas.append(roll_joint)
511 ts.append(alpha)
512 return ts, thetas