|
a |
|
b/URBasic/manipulation.py |
|
|
1 |
import math |
|
|
2 |
from numpy import * |
|
|
3 |
|
|
|
4 |
### HELPER FUNCTIONS ### |
|
|
5 |
def randomVec(x): |
|
|
6 |
''' |
|
|
7 |
Generates a vector of length x, each component being a random float b/w -10 and 10 |
|
|
8 |
''' |
|
|
9 |
return random.uniform(-10,10,(x,1)) |
|
|
10 |
|
|
|
11 |
|
|
|
12 |
def randomUnitAxisAngle(): |
|
|
13 |
''' |
|
|
14 |
Generates a random unit axis and an angle |
|
|
15 |
''' |
|
|
16 |
# Random longitude |
|
|
17 |
u = random.uniform(0, 1) |
|
|
18 |
longitude = 2*math.pi*u |
|
|
19 |
|
|
|
20 |
# Random latitude |
|
|
21 |
v = random.uniform(0, 1) |
|
|
22 |
latitude = 2*math.pi*v |
|
|
23 |
|
|
|
24 |
# Random unit rotation axis |
|
|
25 |
axis = zeros((3,1)) |
|
|
26 |
axis[0] = math.cos(latitude)*math.cos(longitude) |
|
|
27 |
axis[1] = math.cos(latitude)*math.sin(longitude) |
|
|
28 |
axis[2] = math.sin(latitude) |
|
|
29 |
|
|
|
30 |
# Random angle b/w 0 and 2pi |
|
|
31 |
theta = 2*math.pi*random.uniform(0, 1) |
|
|
32 |
|
|
|
33 |
return axis, theta |
|
|
34 |
|
|
|
35 |
|
|
|
36 |
def normalize(v): |
|
|
37 |
''' |
|
|
38 |
Returns the normalized version of the vector v |
|
|
39 |
''' |
|
|
40 |
norm = linalg.norm(v) |
|
|
41 |
if norm == 0: |
|
|
42 |
return v |
|
|
43 |
return v/norm |
|
|
44 |
|
|
|
45 |
|
|
|
46 |
def is_identity_matrix(M): |
|
|
47 |
''' |
|
|
48 |
Returns True if input M is close to an identity matrix |
|
|
49 |
''' |
|
|
50 |
if len(M) != len(M[0]): |
|
|
51 |
return False |
|
|
52 |
|
|
|
53 |
c = list() |
|
|
54 |
for i, row in enumerate(M): |
|
|
55 |
for j, val in enumerate(row): |
|
|
56 |
if i==j: |
|
|
57 |
if val < 1.001 and val > 0.999: |
|
|
58 |
c.append(True) |
|
|
59 |
else: |
|
|
60 |
c.append(False) |
|
|
61 |
else: |
|
|
62 |
if abs(val) < 0.001: |
|
|
63 |
c.append(True) |
|
|
64 |
else: |
|
|
65 |
c.append(False) |
|
|
66 |
|
|
|
67 |
return all(c) |
|
|
68 |
|
|
|
69 |
|
|
|
70 |
def is_rot_matrix(R): |
|
|
71 |
''' |
|
|
72 |
Returns True if input R is a valid rotation matrix. |
|
|
73 |
''' |
|
|
74 |
R = asarray(R) |
|
|
75 |
return is_identity_matrix(dot(R.T,R)) and (abs(linalg.det(R)-1) < 0.001) |
|
|
76 |
|
|
|
77 |
|
|
|
78 |
### MAIN FUNCTIONS ### |
|
|
79 |
def RotInv(R): |
|
|
80 |
''' |
|
|
81 |
Takes a rotation matrix belonging to SO(3) and returns its inverse. |
|
|
82 |
Example: |
|
|
83 |
|
|
|
84 |
R = [[.707,-.707,0],[.707,.707,0],[0,0,1]] |
|
|
85 |
RotInv(R) |
|
|
86 |
>> array([[ 0.707, 0.707, 0. ], |
|
|
87 |
[-0.707, 0.707, 0. ], |
|
|
88 |
[ 0. , 0. , 1. ]]) |
|
|
89 |
''' |
|
|
90 |
R = asarray(R) |
|
|
91 |
assert is_rot_matrix(R), 'Not a valid rotation matrix' |
|
|
92 |
|
|
|
93 |
return R.T |
|
|
94 |
|
|
|
95 |
|
|
|
96 |
def VecToso3(w): |
|
|
97 |
''' |
|
|
98 |
Takes a 3-vector representing angular velocity and returns the 3x3 skew-symmetric matrix version, an element |
|
|
99 |
of so(3). |
|
|
100 |
Example: |
|
|
101 |
|
|
|
102 |
w = [2, 1, -4] |
|
|
103 |
VecToso3(w) |
|
|
104 |
>> array([[ 0, 4, 1], |
|
|
105 |
[-4, 0, -2], |
|
|
106 |
[-1, 2, 0]]) |
|
|
107 |
''' |
|
|
108 |
w = asarray(w) |
|
|
109 |
assert len(w) == 3, 'Not a 3-vector' |
|
|
110 |
|
|
|
111 |
w = w.flatten() |
|
|
112 |
w_so3mat = array([[0, -w[2], w[1]], [w[2], 0, -w[0]], [-w[1], w[0], 0]]) |
|
|
113 |
return w_so3mat |
|
|
114 |
|
|
|
115 |
|
|
|
116 |
def so3ToVec(w_so3mat): |
|
|
117 |
''' |
|
|
118 |
Takes a 3x3 skew-symmetric matrix (an element of so(3)) and returns the corresponding 3-vector. |
|
|
119 |
Example: |
|
|
120 |
|
|
|
121 |
w_so3mat = [[ 0, 4, 1],[-4, 0, -2],[-1, 2, 0]] |
|
|
122 |
so3ToVec(w_so3mat) |
|
|
123 |
>> array([[ 2], |
|
|
124 |
[ 1], |
|
|
125 |
[-4]]) |
|
|
126 |
''' |
|
|
127 |
w_so3mat = asarray(w_so3mat) |
|
|
128 |
assert w_so3mat.shape == (3,3), 'Not a 3x3 matrix' |
|
|
129 |
|
|
|
130 |
w = array([[w_so3mat[2,1]], [w_so3mat[0,2]], [w_so3mat[1,0]]]) |
|
|
131 |
return w |
|
|
132 |
|
|
|
133 |
|
|
|
134 |
def AxisAng3(r): |
|
|
135 |
''' |
|
|
136 |
Takes a 3-vector of exp coords r = w_unit*theta and returns w_unit and theta. |
|
|
137 |
Example: |
|
|
138 |
|
|
|
139 |
r = [2, 1, -4] |
|
|
140 |
w_unit, theta = AxisAng3(r) |
|
|
141 |
w_unit |
|
|
142 |
>> array([ 0.43643578, 0.21821789, -0.87287156]) |
|
|
143 |
theta |
|
|
144 |
>> 4.5825756949558398 |
|
|
145 |
''' |
|
|
146 |
r = asarray(r) |
|
|
147 |
assert len(r) == 3, 'Not a 3-vector' |
|
|
148 |
|
|
|
149 |
theta = linalg.norm(r) |
|
|
150 |
w_unit = normalize(r) |
|
|
151 |
|
|
|
152 |
return w_unit, theta |
|
|
153 |
|
|
|
154 |
|
|
|
155 |
def MatrixExp3(r): |
|
|
156 |
''' |
|
|
157 |
Takes a 3-vector of exp coords r = w_unit*theta and returns the corresponding |
|
|
158 |
rotation matrix R (an element of SO(3)). |
|
|
159 |
Example: |
|
|
160 |
|
|
|
161 |
r = [2, 1, -4] |
|
|
162 |
MatrixExp3(r) |
|
|
163 |
>> array([[ 0.08568414, -0.75796072, -0.64664811], |
|
|
164 |
[ 0.97309386, -0.07566572, 0.2176305 ], |
|
|
165 |
[-0.21388446, -0.64789679, 0.73108357]]) |
|
|
166 |
''' |
|
|
167 |
r = asarray(r) |
|
|
168 |
assert len(r) == 3, 'Not a 3-vector' |
|
|
169 |
|
|
|
170 |
w_unit, theta = AxisAng3(r) |
|
|
171 |
w_so3mat = VecToso3(w_unit) |
|
|
172 |
|
|
|
173 |
R = identity(3) + math.sin(theta)*w_so3mat + (1-math.cos(theta))*dot(w_so3mat, w_so3mat) |
|
|
174 |
assert is_rot_matrix(R), 'Did not produce a valid rotation matrix' |
|
|
175 |
return R |
|
|
176 |
|
|
|
177 |
|
|
|
178 |
def MatrixLog3(R): |
|
|
179 |
''' |
|
|
180 |
Takes a rotation matrix R and returns the corresponding 3-vector of exp coords r = w_unit*theta. |
|
|
181 |
Example: |
|
|
182 |
|
|
|
183 |
R = [[.707,-.707,0],[.707,.707,0],[0,0,1]] |
|
|
184 |
MatrixLog3(R) |
|
|
185 |
>> array([[ 0. ], |
|
|
186 |
[ 0. ], |
|
|
187 |
[ 0.78554916]]) |
|
|
188 |
''' |
|
|
189 |
R = asarray(R) |
|
|
190 |
assert is_rot_matrix(R), 'Not a valid rotation matrix' |
|
|
191 |
|
|
|
192 |
if is_identity_matrix(R): |
|
|
193 |
return zeros((3,1)) |
|
|
194 |
|
|
|
195 |
if trace(R) > -1.001 and trace(R) < -0.999: |
|
|
196 |
theta = math.pi |
|
|
197 |
c = max(diag(R)) |
|
|
198 |
if c == R[2,2]: |
|
|
199 |
w_unit = array([[R[0,2]],[R[1,2]],[1+c]])*1/((2*(1+c))**0.5) |
|
|
200 |
return w_unit*theta |
|
|
201 |
elif c == R[1,1]: |
|
|
202 |
w_unit = array([[R[0,1]],[1+c],[R[2,1]]])*1/((2*(1+c))**0.5) |
|
|
203 |
return w_unit*theta |
|
|
204 |
elif c == R[0,0]: |
|
|
205 |
w_unit = array([[1+c],[R[1,0]],[R[2,0]]])*1/((2*(1+c))**0.5) |
|
|
206 |
return w_unit*theta |
|
|
207 |
|
|
|
208 |
theta = math.acos((trace(R)-1)/2) |
|
|
209 |
w_so3mat = (R - R.T)/(2*math.sin(theta)) |
|
|
210 |
w_unit = normalize(so3ToVec(w_so3mat)) |
|
|
211 |
return w_unit*theta |
|
|
212 |
|
|
|
213 |
|
|
|
214 |
def RpToTrans(R,p): |
|
|
215 |
''' |
|
|
216 |
Takes a rotation matrix R and a point (3-vector) p, and returns the corresponding |
|
|
217 |
4x4 transformation matrix T, an element of SE(3). |
|
|
218 |
Example: |
|
|
219 |
|
|
|
220 |
R = [[.707,-.707,0],[.707,.707,0],[0,0,1]] |
|
|
221 |
p = [5,-4,9] |
|
|
222 |
RpToTrans(R,p) |
|
|
223 |
>> array([[ 0.707, -0.707, 0. , 5. ], |
|
|
224 |
[ 0.707, 0.707, 0. , -4. ], |
|
|
225 |
[ 0. , 0. , 1. , 9. ], |
|
|
226 |
[ 0. , 0. , 0. , 1. ]]) |
|
|
227 |
''' |
|
|
228 |
p = asarray(p) |
|
|
229 |
R = asarray(R) |
|
|
230 |
assert len(p) == 3, "Point not a 3-vector" |
|
|
231 |
assert is_rot_matrix(R), "R not a valid rotation matrix" |
|
|
232 |
|
|
|
233 |
p.shape = (3,1) |
|
|
234 |
T = vstack((hstack((R,p)), array([0,0,0,1]))) |
|
|
235 |
return T |
|
|
236 |
|
|
|
237 |
|
|
|
238 |
def TransToRp(T): |
|
|
239 |
''' |
|
|
240 |
Takes a transformation matrix T and returns the corresponding R and p. |
|
|
241 |
Example: |
|
|
242 |
|
|
|
243 |
T = [[0.707,-0.707,0,5],[0.707,0.707,0,-4],[0,0,1,9],[0,0,0,1]] |
|
|
244 |
R, p = TransToRp(T) |
|
|
245 |
R |
|
|
246 |
>> array([[ 0.707, -0.707, 0. ], |
|
|
247 |
[ 0.707, 0.707, 0. ], |
|
|
248 |
[ 0. , 0. , 1. ]]) |
|
|
249 |
p |
|
|
250 |
>> array([[ 5.], |
|
|
251 |
[-4.], |
|
|
252 |
[ 9.]]) |
|
|
253 |
''' |
|
|
254 |
T = asarray(T) |
|
|
255 |
assert T.shape == (4,4), "Input not a 4x4 matrix" |
|
|
256 |
|
|
|
257 |
R = T[:3,:3] |
|
|
258 |
assert is_rot_matrix(R), "Input not a valid transformation matrix" |
|
|
259 |
|
|
|
260 |
assert allclose([0,0,0,1],T[-1],atol=1e-03), "Last row of homogenous T matrix should be [0,0,0,1]" |
|
|
261 |
|
|
|
262 |
p = T[:3,-1] |
|
|
263 |
p.shape = (3,1) |
|
|
264 |
|
|
|
265 |
return R, p |
|
|
266 |
|
|
|
267 |
|
|
|
268 |
def TransInv(T): |
|
|
269 |
''' |
|
|
270 |
Returns inverse of transformation matrix T. |
|
|
271 |
Example: |
|
|
272 |
|
|
|
273 |
T = [[0.707,-0.707,0,5],[0.707,0.707,0,-4],[0,0,1,9],[0,0,0,1]] |
|
|
274 |
TransInv(T) |
|
|
275 |
>> array([[ 0.707, 0.707, 0. , -0.707], |
|
|
276 |
[-0.707, 0.707, 0. , 6.363], |
|
|
277 |
[ 0. , 0. , 1. , -9. ], |
|
|
278 |
[ 0. , 0. , 0. , 1. ]]) |
|
|
279 |
''' |
|
|
280 |
T = asarray(T) |
|
|
281 |
R, p = TransToRp(T) |
|
|
282 |
|
|
|
283 |
T_inv = RpToTrans(RotInv(R), dot(-RotInv(R),p)) |
|
|
284 |
return T_inv |
|
|
285 |
|
|
|
286 |
|
|
|
287 |
def VecTose3(V): |
|
|
288 |
''' |
|
|
289 |
Takes a 6-vector (representing spatial velocity) and returns the corresponding 4x4 matrix, |
|
|
290 |
an element of se(3). |
|
|
291 |
Example: |
|
|
292 |
|
|
|
293 |
V = [3,2,5,-3,7,0] |
|
|
294 |
VecTose3(V) |
|
|
295 |
>> array([[ 0., -5., 2., -3.], |
|
|
296 |
[ 5., 0., -3., 7.], |
|
|
297 |
[-2., 3., 0., 0.], |
|
|
298 |
[ 0., 0., 0., 0.]]) |
|
|
299 |
''' |
|
|
300 |
V = asarray(V) |
|
|
301 |
assert len(V) == 6, "Input not a 6-vector" |
|
|
302 |
|
|
|
303 |
V.shape = (6,1) |
|
|
304 |
w = V[:3] |
|
|
305 |
w_so3mat = VecToso3(w) |
|
|
306 |
v = V[3:] |
|
|
307 |
V_se3mat = vstack((hstack((w_so3mat,v)), zeros(4))) |
|
|
308 |
return V_se3mat |
|
|
309 |
|
|
|
310 |
|
|
|
311 |
def se3ToVec(V_se3mat): |
|
|
312 |
''' |
|
|
313 |
Takes an element of se(3) and returns the corresponding (6-vector) spatial velocity. |
|
|
314 |
Example: |
|
|
315 |
|
|
|
316 |
V_se3mat = [[ 0., -5., 2., -3.], |
|
|
317 |
[ 5., 0., -3., 7.], |
|
|
318 |
[-2., 3., 0., 0.], |
|
|
319 |
[ 0., 0., 0., 0.]] |
|
|
320 |
se3ToVec(V_se3mat) |
|
|
321 |
>> array([[ 3.], |
|
|
322 |
[ 2.], |
|
|
323 |
[ 5.], |
|
|
324 |
[-3.], |
|
|
325 |
[ 7.], |
|
|
326 |
[ 0.]]) |
|
|
327 |
''' |
|
|
328 |
V_se3mat = asarray(V_se3mat) |
|
|
329 |
assert V_se3mat.shape == (4,4), "Matrix is not 4x4" |
|
|
330 |
|
|
|
331 |
w_so3mat = V_se3mat[:3,:3] |
|
|
332 |
w = so3ToVec(w_so3mat) |
|
|
333 |
|
|
|
334 |
v = V_se3mat[:3,-1] |
|
|
335 |
v.shape = (3,1) |
|
|
336 |
|
|
|
337 |
V = vstack((w,v)) |
|
|
338 |
return V |
|
|
339 |
|
|
|
340 |
|
|
|
341 |
def Adjoint(T): |
|
|
342 |
''' |
|
|
343 |
Takes a transformation matrix T and returns the 6x6 adjoint representation [Ad_T] |
|
|
344 |
Example: |
|
|
345 |
|
|
|
346 |
T = [[0.707,-0.707,0,5],[0.707,0.707,0,-4],[0,0,1,9],[0,0,0,1]] |
|
|
347 |
Adjoint(T) |
|
|
348 |
>> array([[ 0.707, -0.707, 0. , 0. , 0. , 0. ], |
|
|
349 |
[ 0.707, 0.707, 0. , 0. , 0. , 0. ], |
|
|
350 |
[ 0. , 0. , 1. , 0. , 0. , 0. ], |
|
|
351 |
[-6.363, -6.363, -4. , 0.707, -0.707, 0. ], |
|
|
352 |
[ 6.363, -6.363, -5. , 0.707, 0.707, 0. ], |
|
|
353 |
[ 6.363, 0.707, 0. , 0. , 0. , 1. ]]) |
|
|
354 |
''' |
|
|
355 |
T = asarray(T) |
|
|
356 |
assert T.shape == (4,4), "Input not a 4x4 matrix" |
|
|
357 |
|
|
|
358 |
R, p = TransToRp(T) |
|
|
359 |
p = p.flatten() |
|
|
360 |
p_skew = array([[0, -p[2], p[1]], [p[2], 0, -p[0]], [-p[1], p[0], 0]]) |
|
|
361 |
|
|
|
362 |
ad1 = vstack((R, dot(p_skew,R))) |
|
|
363 |
ad2 = vstack((zeros((3,3)),R)) |
|
|
364 |
adT = hstack((ad1,ad2)) |
|
|
365 |
return adT |
|
|
366 |
|
|
|
367 |
|
|
|
368 |
def ScrewToAxis(q,s_hat,h): |
|
|
369 |
''' |
|
|
370 |
Takes a point q (3-vector) on the screw, a unit axis s_hat (3-vector) in the direction of the screw, |
|
|
371 |
and a screw pitch h (scalar), and returns the corresponding 6-vector screw axis S (a normalized |
|
|
372 |
spatial velocity). |
|
|
373 |
Example: |
|
|
374 |
|
|
|
375 |
q = [3,0,0] |
|
|
376 |
s_hat = [0,0,1] |
|
|
377 |
h = 2 |
|
|
378 |
ScrewToAxis(q,s_hat,h) |
|
|
379 |
>> array([[ 0], |
|
|
380 |
[ 0], |
|
|
381 |
[ 1], |
|
|
382 |
[ 0], |
|
|
383 |
[-3], |
|
|
384 |
[ 2]]) |
|
|
385 |
''' |
|
|
386 |
q = asarray(q) |
|
|
387 |
s_hat = asarray(s_hat) |
|
|
388 |
assert len(q) == len(s_hat) == 3, "q or s_hat not a 3-vector" |
|
|
389 |
assert abs(linalg.norm(s_hat) - 1) < 0.001, "s_hat not a valid unit vector" |
|
|
390 |
assert isscalar(h), "h not a scalar" |
|
|
391 |
|
|
|
392 |
q = q.flatten() |
|
|
393 |
s_hat = s_hat.flatten() |
|
|
394 |
|
|
|
395 |
v_wnorm = -cross(s_hat, q) + h*s_hat |
|
|
396 |
v_wnorm.shape = (3,1) |
|
|
397 |
w_unit = s_hat |
|
|
398 |
w_unit.shape = (3,1) |
|
|
399 |
S = vstack((w_unit,v_wnorm)) |
|
|
400 |
return S |
|
|
401 |
|
|
|
402 |
|
|
|
403 |
def AxisAng6(STheta): |
|
|
404 |
''' |
|
|
405 |
Takes a 6-vector of exp coords STheta and returns the screw axis S and the distance traveled along/ |
|
|
406 |
about the screw axis theta. |
|
|
407 |
Example: |
|
|
408 |
|
|
|
409 |
STheta = [0,0,1,0,-3,2] |
|
|
410 |
S, theta = AxisAng6(STheta) |
|
|
411 |
S |
|
|
412 |
>> array([[ 0.], |
|
|
413 |
[ 0.], |
|
|
414 |
[ 1.], |
|
|
415 |
[ 0.], |
|
|
416 |
[-3.], |
|
|
417 |
[ 2.]]) |
|
|
418 |
theta |
|
|
419 |
>> 1.0 |
|
|
420 |
''' |
|
|
421 |
STheta = asarray(STheta) |
|
|
422 |
assert len(STheta) == 6, 'Input not a 6-vector' |
|
|
423 |
|
|
|
424 |
w = STheta[:3] |
|
|
425 |
v = STheta[3:] |
|
|
426 |
|
|
|
427 |
if linalg.norm(w) == 0: |
|
|
428 |
theta = linalg.norm(v) |
|
|
429 |
v_unit = normalize(v) |
|
|
430 |
v_unit.shape = (3,1) |
|
|
431 |
S = vstack((zeros((3,1)), v_unit)) |
|
|
432 |
return S, theta |
|
|
433 |
|
|
|
434 |
theta = linalg.norm(w) |
|
|
435 |
w_unit = normalize(w) |
|
|
436 |
w_unit.shape = (3,1) |
|
|
437 |
v_unit = v/theta |
|
|
438 |
v_unit.shape = (3,1) |
|
|
439 |
S = vstack((w_unit,v_unit)) |
|
|
440 |
return S, theta |
|
|
441 |
|
|
|
442 |
|
|
|
443 |
def MatrixExp6(STheta): |
|
|
444 |
''' |
|
|
445 |
Takes a 6-vector of exp coords STheta and returns the corresponding 4x4 transformation matrix T. |
|
|
446 |
Example: |
|
|
447 |
|
|
|
448 |
STheta = [0,0,1,0,-3,2] |
|
|
449 |
MatrixExp6(STheta) |
|
|
450 |
>> array([[ 0.54030231, -0.84147098, 0. , 1.37909308], |
|
|
451 |
[ 0.84147098, 0.54030231, 0. , -2.52441295], |
|
|
452 |
[ 0. , 0. , 1. , 2. ], |
|
|
453 |
[ 0. , 0. , 0. , 1. ]]) |
|
|
454 |
''' |
|
|
455 |
STheta = asarray(STheta) |
|
|
456 |
assert len(STheta) == 6, 'Input not a 6-vector' |
|
|
457 |
|
|
|
458 |
S, theta = AxisAng6(STheta) |
|
|
459 |
|
|
|
460 |
w_unit = S[:3] |
|
|
461 |
v_unit = S[3:] |
|
|
462 |
|
|
|
463 |
vTheta = STheta[3:] |
|
|
464 |
vTheta.shape = (3,1) |
|
|
465 |
|
|
|
466 |
if linalg.norm(w_unit) == 0: |
|
|
467 |
R = identity(3) |
|
|
468 |
p = vTheta |
|
|
469 |
T = RpToTrans(R,p) |
|
|
470 |
return T |
|
|
471 |
|
|
|
472 |
r = w_unit*theta |
|
|
473 |
R = MatrixExp3(r) |
|
|
474 |
w_so3mat = VecToso3(w_unit) |
|
|
475 |
p = dot((identity(3)*theta+(1-math.cos(theta))*w_so3mat+(theta-math.sin(theta))*dot(w_so3mat,w_so3mat)), v_unit) |
|
|
476 |
T = RpToTrans(R,p) |
|
|
477 |
return T |
|
|
478 |
|
|
|
479 |
|
|
|
480 |
def MatrixLog6(T): |
|
|
481 |
''' |
|
|
482 |
Takes a transformation matrix T and returns the corresponding 6-vector of exp coords STheta. |
|
|
483 |
Example: |
|
|
484 |
|
|
|
485 |
T = [[ 0.54030231, -0.84147098, 0. , 1.37909308], |
|
|
486 |
[ 0.84147098, 0.54030231, 0. , -2.52441295], |
|
|
487 |
[ 0. , 0. , 1. , 2. ], |
|
|
488 |
[ 0. , 0. , 0. , 1. ]] |
|
|
489 |
MatrixLog6(T): |
|
|
490 |
>> array([[ 0.00000000e+00], |
|
|
491 |
[ 0.00000000e+00], |
|
|
492 |
[ 9.99999995e-01], |
|
|
493 |
[ 1.12156694e-08], |
|
|
494 |
[ -2.99999999e+00], |
|
|
495 |
[ 2.00000000e+00]]) |
|
|
496 |
''' |
|
|
497 |
T = asarray(T) |
|
|
498 |
assert T.shape == (4,4), "Input not a 4x4 matrix" |
|
|
499 |
|
|
|
500 |
R, p = TransToRp(T) |
|
|
501 |
|
|
|
502 |
if is_identity_matrix(R): |
|
|
503 |
w_unit = zeros((3,1)) |
|
|
504 |
vTheta = p |
|
|
505 |
STheta = vstack((w_unit, p)) |
|
|
506 |
return STheta |
|
|
507 |
|
|
|
508 |
if trace(R) > -1.001 and trace(R) < -0.999: |
|
|
509 |
theta = math.pi |
|
|
510 |
wTheta = MatrixLog3(R) |
|
|
511 |
w_unit = wTheta/theta |
|
|
512 |
w_so3mat = VecToso3(w_unit) |
|
|
513 |
Ginv = identity(3)/theta - w_so3mat/2 + (1/theta - 1/(math.tan(theta/2)*2))*dot(w_so3mat,w_so3mat) |
|
|
514 |
v_unit = dot(Ginv, p) |
|
|
515 |
vTheta = v_unit*theta |
|
|
516 |
STheta = vstack((wTheta, vTheta)) |
|
|
517 |
return STheta |
|
|
518 |
|
|
|
519 |
theta = math.acos((trace(R)-1)/2) |
|
|
520 |
w_so3mat = (R - R.T)/(2*math.sin(theta)) |
|
|
521 |
Ginv = identity(3)/theta - w_so3mat/2 + (1/theta - 1/(math.tan(theta/2)*2))*dot(w_so3mat,w_so3mat) |
|
|
522 |
wTheta = MatrixLog3(R) |
|
|
523 |
v_unit = dot(Ginv, p) |
|
|
524 |
vTheta = v_unit*theta |
|
|
525 |
STheta = vstack((wTheta, vTheta)) |
|
|
526 |
return STheta |
|
|
527 |
|
|
|
528 |
|
|
|
529 |
def FKinFixed(M,Slist,thetalist): |
|
|
530 |
''' |
|
|
531 |
Takes |
|
|
532 |
- an element of SE(3): M representing the configuration of the end-effector frame when |
|
|
533 |
the manipulator is at its home position (all joint thetas = 0), |
|
|
534 |
- a list of screw axes Slist for the joints w.r.t fixed world frame, |
|
|
535 |
- a list of joint coords thetalist, |
|
|
536 |
and returns the T of end-effector frame w.r.t. fixed world frame when the joints are at |
|
|
537 |
the thetas specified. |
|
|
538 |
Example: |
|
|
539 |
|
|
|
540 |
S1 = [0,0,1,4,0,0] |
|
|
541 |
S2 = [0,0,0,0,1,0] |
|
|
542 |
S3 = [0,0,-1,-6,0,-0.1] |
|
|
543 |
Slist = [S1, S2, S3] |
|
|
544 |
thetalist = [math.pi/2, 3, math.pi] |
|
|
545 |
M = [[-1, 0, 0, 0], |
|
|
546 |
[ 0, 1, 0, 6], |
|
|
547 |
[ 0, 0, -1, 2], |
|
|
548 |
[ 0, 0, 0, 1]] |
|
|
549 |
FKinFixed(M,Slist,thetalist) |
|
|
550 |
>> array([[ -1.14423775e-17, 1.00000000e+00, 0.00000000e+00, -5.00000000e+00], |
|
|
551 |
[ 1.00000000e+00, 1.14423775e-17, 0.00000000e+00, 4.00000000e+00], |
|
|
552 |
[ 0.00000000e+00, 0.00000000e+00, -1.00000000e+00, 1.68584073e+00], |
|
|
553 |
[ 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 1.00000000e+00]]) |
|
|
554 |
''' |
|
|
555 |
M = asarray(M) |
|
|
556 |
R_M = TransToRp(M)[0] |
|
|
557 |
assert M.shape == (4,4), "M not a 4x4 matrix" |
|
|
558 |
assert len(Slist[0]) == 6, "Incorrect Screw Axis length" |
|
|
559 |
Slist = asarray(Slist).T |
|
|
560 |
|
|
|
561 |
c = MatrixExp6(Slist[:,0]*thetalist[0]) |
|
|
562 |
for i in range(len(thetalist)-1): |
|
|
563 |
nex = MatrixExp6(Slist[:,i+1]*thetalist[i+1]) |
|
|
564 |
c = dot(c, nex) |
|
|
565 |
|
|
|
566 |
T_se = dot(c, M) |
|
|
567 |
return T_se |
|
|
568 |
|
|
|
569 |
|
|
|
570 |
def FKinBody(M,Blist,thetalist): |
|
|
571 |
''' |
|
|
572 |
Same as FKinFixed, except here the screw axes are expressed in the end-effector frame. |
|
|
573 |
Example: |
|
|
574 |
|
|
|
575 |
B1 = [0,0,-1,2,0,0] |
|
|
576 |
B2 = [0,0,0,0,1,0] |
|
|
577 |
B3 = [0,0,1,0,0,0.1] |
|
|
578 |
Blist = [S1b, S2b, S3b] |
|
|
579 |
thetalist = [math.pi/2, 3, math.pi] |
|
|
580 |
M = [[-1, 0, 0, 0], |
|
|
581 |
[ 0, 1, 0, 6], |
|
|
582 |
[ 0, 0, -1, 2], |
|
|
583 |
[ 0, 0, 0, 1]] |
|
|
584 |
FKinBody(M,Blist,thetalist) |
|
|
585 |
>> array([[ -1.14423775e-17, 1.00000000e+00, 0.00000000e+00, -5.00000000e+00], |
|
|
586 |
[ 1.00000000e+00, 1.14423775e-17, 0.00000000e+00, 4.00000000e+00], |
|
|
587 |
[ 0.00000000e+00, 0.00000000e+00, -1.00000000e+00, 1.68584073e+00], |
|
|
588 |
[ 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 1.00000000e+00]]) |
|
|
589 |
''' |
|
|
590 |
M = asarray(M) |
|
|
591 |
R_M = TransToRp(M)[0] |
|
|
592 |
assert M.shape == (4,4), "M not a 4x4 matrix" |
|
|
593 |
assert len(Blist[0]) == 6, "Incorrect Screw Axis length" |
|
|
594 |
Blist = asarray(Blist).T |
|
|
595 |
|
|
|
596 |
c = dot(M, MatrixExp6(Blist[:,0]*thetalist[0])) |
|
|
597 |
for i in range(len(thetalist)-1): |
|
|
598 |
nex = MatrixExp6(Blist[:,i+1]*thetalist[i+1]) |
|
|
599 |
c = dot(c, nex) |
|
|
600 |
|
|
|
601 |
T_se = c |
|
|
602 |
return T_se |
|
|
603 |
|
|
|
604 |
|
|
|
605 |
### end of HW2 functions ############################# |
|
|
606 |
|
|
|
607 |
### start of HW3 functions ########################### |
|
|
608 |
|
|
|
609 |
|
|
|
610 |
def FixedJacobian(Slist,thetalist): |
|
|
611 |
''' |
|
|
612 |
Takes a list of joint angles (thetalist) and a list of screw axes (Slist) expressed in |
|
|
613 |
fixed space frame, and returns the space Jacobian (a 6xN matrix, where N is the # joints). |
|
|
614 |
Example: |
|
|
615 |
|
|
|
616 |
S1 = [0,0,1,4,0,0] |
|
|
617 |
S2 = [0,0,0,0,1,0] |
|
|
618 |
S3 = [0,0,-1,-6,0,-0.1] |
|
|
619 |
Slist = [S1, S2, S3] |
|
|
620 |
thetalist = [math.pi/2, 3, math.pi] |
|
|
621 |
FixedJacobian(Slist,thetalist) |
|
|
622 |
>> array([[ 0.00000000e+00, 0.00000000e+00, 0.00000000e+00], |
|
|
623 |
[ 0.00000000e+00, 0.00000000e+00, 0.00000000e+00], |
|
|
624 |
[ 1.00000000e+00, 0.00000000e+00, -1.00000000e+00], |
|
|
625 |
[ 4.00000000e+00, -1.00000000e+00, -4.00000000e+00], |
|
|
626 |
[ 0.00000000e+00, 1.11022302e-16, -5.00000000e+00], |
|
|
627 |
[ 0.00000000e+00, 0.00000000e+00, -1.00000000e-01]]) |
|
|
628 |
''' |
|
|
629 |
N = len(thetalist) |
|
|
630 |
J = zeros((6,N)) |
|
|
631 |
Slist = asarray(Slist).T |
|
|
632 |
|
|
|
633 |
J[:,0] = Slist[:,0] |
|
|
634 |
for k in range(1,N): |
|
|
635 |
c = MatrixExp6(Slist[:,0]*thetalist[0]) |
|
|
636 |
for i in range(k-1): |
|
|
637 |
nex = MatrixExp6(Slist[:,i+1]*thetalist[i+1]) |
|
|
638 |
c = dot(c, nex) |
|
|
639 |
J[:,k] = dot(Adjoint(c), Slist[:,k]) |
|
|
640 |
|
|
|
641 |
return J |
|
|
642 |
|
|
|
643 |
|
|
|
644 |
def BodyJacobian(Blist,thetalist): |
|
|
645 |
''' |
|
|
646 |
Takes a list of joint angles (thetalist) and a list of screw axes (Blist) expressed in |
|
|
647 |
end-effector body frame, and returns the body Jacobian (a 6xN matrix, where N is the # joints). |
|
|
648 |
Example: |
|
|
649 |
|
|
|
650 |
B1 = [0,0,-1,2,0,0] |
|
|
651 |
B2 = [0,0,0,0,1,0] |
|
|
652 |
B3 = [0,0,1,0,0,0.1] |
|
|
653 |
Blist = [B1, B2, B3] |
|
|
654 |
thetalist = [math.pi/2, 3, math.pi] |
|
|
655 |
BodyJacobian(Blist,thetalist) |
|
|
656 |
>> array([[ 0.00000000e+00, 0.00000000e+00, 0.00000000e+00], |
|
|
657 |
[ 0.00000000e+00, 0.00000000e+00, 0.00000000e+00], |
|
|
658 |
[ -1.00000000e+00, 0.00000000e+00, 1.00000000e+00], |
|
|
659 |
[ -5.00000000e+00, 1.22464680e-16, 0.00000000e+00], |
|
|
660 |
[ -6.12323400e-16, -1.00000000e+00, 0.00000000e+00], |
|
|
661 |
[ 0.00000000e+00, 0.00000000e+00, 1.00000000e-01]]) |
|
|
662 |
''' |
|
|
663 |
N = len(thetalist) |
|
|
664 |
J = zeros((6,N)) |
|
|
665 |
Blist = asarray(Blist).T |
|
|
666 |
|
|
|
667 |
J[:,N-1] = Blist[:,N-1] |
|
|
668 |
for k in range(N-1): |
|
|
669 |
c = MatrixExp6(-Blist[:,k+1]*thetalist[k+1]) |
|
|
670 |
for i in range(k+2, len(thetalist)): |
|
|
671 |
nex = MatrixExp6(-Blist[:,i]*thetalist[i]) |
|
|
672 |
c = dot(nex, c) |
|
|
673 |
J[:,k] = dot(Adjoint(c), Blist[:,k]) |
|
|
674 |
|
|
|
675 |
return J |
|
|
676 |
|
|
|
677 |
|
|
|
678 |
def IKinBody(Blist, M, T_sd, thetalist_init, wthresh, vthresh): |
|
|
679 |
''' |
|
|
680 |
A numerical inverse kinematics routine based on Newton-Raphson method. |
|
|
681 |
Takes a list of screw axes (Blist) expressed in end-effector body frame, the end-effector zero |
|
|
682 |
configuration (M), the desired end-effector configuration (T_sd), an initial guess of joint angles |
|
|
683 |
(thetalist_init), and small positive scalar thresholds (wthresh, vthresh) controlling how close the |
|
|
684 |
final solution thetas must be to the desired thetas. |
|
|
685 |
Example: |
|
|
686 |
|
|
|
687 |
wthresh = 0.01 |
|
|
688 |
vthresh = 0.001 |
|
|
689 |
M = [[1,0,0,-.817],[0,0,-1,-.191],[0,1,0,-.006],[0,0,0,1]] |
|
|
690 |
T_sd = [[0,1,0,-.6],[0,0,-1,.1],[-1,0,0,.1],[0,0,0,1]] |
|
|
691 |
thetalist_init = [0]*6 |
|
|
692 |
B1 = [0,1,0,.191,0,.817] |
|
|
693 |
B2 = [0,0,1,.095,-.817,0] |
|
|
694 |
B3 = [0,0,1,.095,-.392,0] |
|
|
695 |
B4 = [0,0,1,.095,0,0] |
|
|
696 |
B5 = [0,-1,0,-.082,0,0] |
|
|
697 |
B6 = [0,0,1,0,0,0] |
|
|
698 |
Blist = [B1,B2,B3,B4,B5,B6] |
|
|
699 |
round(IKinBody(Blist, M, T_sd, thetalist_init, wthresh, vthresh), 3) |
|
|
700 |
>> |
|
|
701 |
array([[ 0. , 0. , 0. , 0. , 0. , 0. ], |
|
|
702 |
[-0.356, -0.535, 0.468, 1.393, -0.356, -2.897], |
|
|
703 |
[-0.399, -1.005, 1.676, -0.434, -0.1 , -1.801], |
|
|
704 |
[-0.516, -1.062, 1.731, -1.63 , -0.502, -0.607], |
|
|
705 |
[-0.493, -0.923, 1.508, -0.73 , -0.289, -1.42 ], |
|
|
706 |
[-0.472, -0.818, 1.365, -0.455, -0.467, -1.662], |
|
|
707 |
[-0.469, -0.834, 1.395, -0.561, -0.467, -1.571]]) |
|
|
708 |
''' |
|
|
709 |
T_sd = asarray(T_sd) |
|
|
710 |
assert T_sd.shape == (4,4), "T_sd not a 4x4 matrix" |
|
|
711 |
|
|
|
712 |
maxiterates = 100 |
|
|
713 |
N = len(thetalist_init) |
|
|
714 |
|
|
|
715 |
jointAngles = asarray(thetalist_init).reshape(1,N) |
|
|
716 |
|
|
|
717 |
T_sb = FKinBody(M, Blist, thetalist_init) |
|
|
718 |
Vb = MatrixLog6(dot(TransInv(T_sb), T_sd)) |
|
|
719 |
wb = Vb[:3, 0] |
|
|
720 |
vb = Vb[3:, 0] |
|
|
721 |
|
|
|
722 |
thetalist_i = asarray(thetalist_init) |
|
|
723 |
|
|
|
724 |
i = 0 |
|
|
725 |
|
|
|
726 |
while i<maxiterates and (linalg.norm(wb)>wthresh or linalg.norm(vb)>vthresh): |
|
|
727 |
thetalist_next = thetalist_i.reshape(N,1) + dot(linalg.pinv(BodyJacobian(Blist,thetalist_i)), Vb) |
|
|
728 |
# thetalist_next = (thetalist_next + pi)%(2*pi) - pi |
|
|
729 |
jointAngles = vstack((jointAngles, thetalist_next.reshape(1,N))) |
|
|
730 |
T_sb = FKinBody(M, Blist, thetalist_next.flatten()) |
|
|
731 |
Vb = MatrixLog6(dot(TransInv(T_sb), T_sd)) |
|
|
732 |
thetalist_i = thetalist_next.reshape(N,) |
|
|
733 |
wb = Vb[:3, 0] |
|
|
734 |
vb = Vb[3:, 0] |
|
|
735 |
i += 1 |
|
|
736 |
|
|
|
737 |
return jointAngles |
|
|
738 |
|
|
|
739 |
|
|
|
740 |
def IKinFixed(Slist, M, T_sd, thetalist_init, wthresh, vthresh): |
|
|
741 |
''' |
|
|
742 |
Similar to IKinBody, except the screw axes are in the fixed space frame. |
|
|
743 |
Example: |
|
|
744 |
|
|
|
745 |
M = [[1,0,0,0],[0,1,0,0],[0,0,1,0.910],[0,0,0,1]] |
|
|
746 |
T_sd = [[1,0,0,.4],[0,1,0,0],[0,0,1,.4],[0,0,0,1]] |
|
|
747 |
thetalist_init = [0]*7 |
|
|
748 |
S1 = [0,0,1,0,0,0] |
|
|
749 |
S2 = [0,1,0,0,0,0] |
|
|
750 |
S3 = [0,0,1,0,0,0] |
|
|
751 |
S4 = [0,1,0,-0.55,0,0.045] |
|
|
752 |
S5 = [0,0,1,0,0,0] |
|
|
753 |
S6 = [0,1,0,-0.85,0,0] |
|
|
754 |
S7 = [0,0,1,0,0,0] |
|
|
755 |
Slist = [S1,S2,S3,S4,S5,S6,S7] |
|
|
756 |
round(IKinFixed(Slist, M, T_sd, thetas, wthresh, vthresh), 3) |
|
|
757 |
>> |
|
|
758 |
array([[ 0. , 0. , 0. , 0. , 0. , 0. , 0. ], |
|
|
759 |
[ 0. , 4.471, -0. , -11.333, -0. , 6.863, -0. ], |
|
|
760 |
[ -0. , 3.153, -0. , -5.462, -0. , 2.309, 0. ], |
|
|
761 |
[ -0. , -1.006, 0. , 5.679, -0. , -4.673, 0. ], |
|
|
762 |
[ -0. , 1.757, 0. , -0.953, 0. , -0.804, -0. ], |
|
|
763 |
[ -0. , 1.754, 0. , -2.27 , -0. , 0.516, -0. ], |
|
|
764 |
[ 0. , 1.27 , 0. , -1.85 , -0. , 0.58 , -0. ], |
|
|
765 |
[ 0. , 1.367, 0. , -1.719, -0. , 0.351, -0. ], |
|
|
766 |
[ 0. , 1.354, 0. , -1.71 , -0. , 0.356, -0. ]]) |
|
|
767 |
''' |
|
|
768 |
T_sd = asarray(T_sd) |
|
|
769 |
assert T_sd.shape == (4,4), "T_sd not a 4x4 matrix" |
|
|
770 |
|
|
|
771 |
maxiterates = 100 |
|
|
772 |
|
|
|
773 |
N = len(thetalist_init) |
|
|
774 |
|
|
|
775 |
jointAngles = asarray(thetalist_init).reshape(1,N) |
|
|
776 |
|
|
|
777 |
T_sb = FKinFixed(M, Slist, thetalist_init) |
|
|
778 |
Vb = MatrixLog6(dot(TransInv(T_sb), T_sd)) |
|
|
779 |
wb = Vb[:3, 0] |
|
|
780 |
vb = Vb[3:, 0] |
|
|
781 |
|
|
|
782 |
thetalist_i = asarray(thetalist_init) |
|
|
783 |
|
|
|
784 |
i = 0 |
|
|
785 |
|
|
|
786 |
while i<maxiterates and (linalg.norm(wb)>wthresh or linalg.norm(vb)>vthresh): |
|
|
787 |
Jb = dot(Adjoint(TransInv(T_sb)), FixedJacobian(Slist,thetalist_i)) |
|
|
788 |
thetalist_next = thetalist_i.reshape(N,1) + dot(linalg.pinv(Jb), Vb) |
|
|
789 |
jointAngles = vstack((jointAngles, thetalist_next.reshape(1,N))) |
|
|
790 |
|
|
|
791 |
T_sb = FKinFixed(M, Slist, thetalist_next.flatten()) |
|
|
792 |
Vb = MatrixLog6(dot(TransInv(T_sb), T_sd)) |
|
|
793 |
thetalist_i = thetalist_next.reshape(N,) |
|
|
794 |
wb = Vb[:3, 0] |
|
|
795 |
vb = Vb[3:, 0] |
|
|
796 |
i += 1 |
|
|
797 |
|
|
|
798 |
return jointAngles |
|
|
799 |
|
|
|
800 |
|
|
|
801 |
### end of HW3 functions ############################# |
|
|
802 |
|
|
|
803 |
### start of HW4 functions ########################### |
|
|
804 |
|
|
|
805 |
|
|
|
806 |
def CubicTimeScaling(T,t): |
|
|
807 |
''' |
|
|
808 |
Takes a total travel time T and the current time t satisfying 0 <= t <= T and returns |
|
|
809 |
the path parameter s corresponding to a motion that begins and ends at zero velocity. |
|
|
810 |
Example: |
|
|
811 |
|
|
|
812 |
CubicTimeScaling(10, 7) |
|
|
813 |
>> 0.78399 |
|
|
814 |
''' |
|
|
815 |
assert t >= 0 and t <= T, 'Invalid t' |
|
|
816 |
|
|
|
817 |
s = (3./(T**2))*(t**2) - (2./(T**3))*(t**3) |
|
|
818 |
return s |
|
|
819 |
|
|
|
820 |
|
|
|
821 |
def QuinticTimeScaling(T,t): |
|
|
822 |
''' |
|
|
823 |
Takes a total travel time T and the current time t satisfying 0 <= t <= T and returns |
|
|
824 |
the path parameter s corresponding to a motion that begins and ends at zero velocity |
|
|
825 |
and zero acceleration. |
|
|
826 |
Example: |
|
|
827 |
|
|
|
828 |
QuinticTimeScaling(10,7) |
|
|
829 |
>> 0.83692 |
|
|
830 |
''' |
|
|
831 |
assert t >= 0 and t <= T, 'Invalid t' |
|
|
832 |
|
|
|
833 |
s = (10./(T**3))*(t**3) + (-15./(T**4))*(t**4) + (6./(T**5))*(t**5) |
|
|
834 |
return s |
|
|
835 |
|
|
|
836 |
|
|
|
837 |
def JointTrajectory(thetas_start, thetas_end, T, N, method='cubic'): |
|
|
838 |
''' |
|
|
839 |
Takes initial joint positions (n-dim) thetas_start, final joint positions thetas_end, the time of |
|
|
840 |
the motion T in seconds, the number of points N >= 2 in the discrete representation of the trajectory, |
|
|
841 |
and the time-scaling method (cubic or quintic) and returns a trajectory as a matrix with N rows, |
|
|
842 |
where each row is an n-vector of joint positions at an instant in time. The trajectory is a straight-line |
|
|
843 |
motion in joint space. |
|
|
844 |
Example: |
|
|
845 |
|
|
|
846 |
thetas_start = [0.1]*6 |
|
|
847 |
thetas_end = [pi/2]*6 |
|
|
848 |
T = 2 |
|
|
849 |
N = 5 |
|
|
850 |
JointTrajectory(thetas_start, thetas_end, T, N, 'cubic') |
|
|
851 |
>> |
|
|
852 |
array([[ 0.1 , 0.1 , 0.1 , 0.1 , 0.1 , 0.1 ], |
|
|
853 |
[ 0.33 , 0.33 , 0.33 , 0.33 , 0.33 , 0.33 ], |
|
|
854 |
[ 0.835, 0.835, 0.835, 0.835, 0.835, 0.835], |
|
|
855 |
[ 1.341, 1.341, 1.341, 1.341, 1.341, 1.341], |
|
|
856 |
[ 1.571, 1.571, 1.571, 1.571, 1.571, 1.571]]) |
|
|
857 |
''' |
|
|
858 |
assert len(thetas_start) == len(thetas_end), 'Incompatible thetas' |
|
|
859 |
assert N >= 2, 'N must be >= 2' |
|
|
860 |
assert isinstance(N, int), 'N must be an integer' |
|
|
861 |
assert method == 'cubic' or method == 'quintic', 'Incorrect time-scaling method argument' |
|
|
862 |
|
|
|
863 |
trajectory = asarray(thetas_start) |
|
|
864 |
|
|
|
865 |
if method == 'cubic': |
|
|
866 |
for i in range(1,N): |
|
|
867 |
t = i*float(T)/(N-1) |
|
|
868 |
s = CubicTimeScaling(T,t) |
|
|
869 |
thetas_s = asarray(thetas_start)*(1-s) + asarray(thetas_end)*s |
|
|
870 |
trajectory = vstack((trajectory, thetas_s)) |
|
|
871 |
return trajectory |
|
|
872 |
|
|
|
873 |
elif method == 'quintic': |
|
|
874 |
for i in range(1,N): |
|
|
875 |
t = i*float(T)/(N-1) |
|
|
876 |
s = QuinticTimeScaling(T,t) |
|
|
877 |
thetas_s = asarray(thetas_start)*(1-s) + asarray(thetas_end)*s |
|
|
878 |
trajectory = vstack((trajectory, thetas_s)) |
|
|
879 |
return trajectory |
|
|
880 |
|
|
|
881 |
|
|
|
882 |
def ScrewTrajectory(X_start, X_end, T, N, method='cubic'): |
|
|
883 |
''' |
|
|
884 |
Similar to JointTrajectory, except that it takes the initial end-effector configuration X_start in SE(3), |
|
|
885 |
the final configuration X_end, and returns the trajectory as a 4N x 4 matrix in which every 4 rows is an |
|
|
886 |
element of SE(3) separated in time by T/(N-1). This represents a discretized trajectory of the screw motion |
|
|
887 |
from X_start to X_end. |
|
|
888 |
Example: |
|
|
889 |
|
|
|
890 |
thetas_start = [0.1]*6 |
|
|
891 |
thetas_end = [pi/2]*6 |
|
|
892 |
M_ur5 = [[1,0,0,-.817],[0,0,-1,-.191],[0,1,0,-.006],[0,0,0,1]] |
|
|
893 |
S1_ur5 = [0,0,1,0,0,0] |
|
|
894 |
S2_ur5 = [0,-1,0,.089,0,0] |
|
|
895 |
S3_ur5 = [0,-1,0,.089,0,.425] |
|
|
896 |
S4_ur5 = [0,-1,0,.089,0,.817] |
|
|
897 |
S5_ur5 = [0,0,-1,.109,-.817,0] |
|
|
898 |
S6_ur5 = [0,-1,0,-.006,0,.817] |
|
|
899 |
Slist_ur5 = [S1_ur5,S2_ur5,S3_ur5,S4_ur5,S5_ur5,S6_ur5] |
|
|
900 |
X_start = FKinFixed(M_ur5, Slist_ur5, thetas_start) |
|
|
901 |
X_end = FKinFixed(M_ur5, Slist_ur5, thetas_end) |
|
|
902 |
T = 2 |
|
|
903 |
N = 3 |
|
|
904 |
ScrewTrajectory(X_start, X_end, T, N, 'quintic') |
|
|
905 |
>> |
|
|
906 |
array([[ 0.922, -0.388, 0.004, -0.764], |
|
|
907 |
[-0.007, -0.029, -1. , -0.268], |
|
|
908 |
[ 0.388, 0.921, -0.03 , -0.124], |
|
|
909 |
[ 0. , 0. , 0. , 1. ], |
|
|
910 |
[ 0.534, -0.806, 0.253, -0.275], |
|
|
911 |
[ 0.681, 0.234, -0.694, -0.067], |
|
|
912 |
[ 0.5 , 0.543, 0.674, -0.192], |
|
|
913 |
[ 0. , 0. , 0. , 1. ], |
|
|
914 |
[ 0. , -1. , -0. , 0.109], |
|
|
915 |
[ 1. , 0. , 0. , 0.297], |
|
|
916 |
[-0. , -0. , 1. , -0.254], |
|
|
917 |
[ 0. , 0. , 0. , 1. ]]) |
|
|
918 |
''' |
|
|
919 |
R_start, p_start = TransToRp(X_start) #Just to ensure X_start is valid |
|
|
920 |
R_end ,p_end = TransToRp(X_end) #Just to ensure X_end is valid |
|
|
921 |
assert N >= 2, 'N must be >= 2' |
|
|
922 |
assert isinstance(N, int), 'N must be an integer' |
|
|
923 |
assert method == 'cubic' or method == 'quintic', 'Incorrect time-scaling method argument' |
|
|
924 |
|
|
|
925 |
trajectory = X_start |
|
|
926 |
|
|
|
927 |
if method == 'cubic': |
|
|
928 |
for i in range(1,N): |
|
|
929 |
t = i*float(T)/(N-1) |
|
|
930 |
s = CubicTimeScaling(T,t) |
|
|
931 |
X_s = X_start.dot(MatrixExp6(MatrixLog6(TransInv(X_start).dot(X_end))*s)) |
|
|
932 |
trajectory = vstack((trajectory, X_s)) |
|
|
933 |
return trajectory |
|
|
934 |
|
|
|
935 |
elif method == 'quintic': |
|
|
936 |
for i in range(1,N): |
|
|
937 |
t = i*float(T)/(N-1) |
|
|
938 |
s = QuinticTimeScaling(T,t) |
|
|
939 |
X_s = X_start.dot(MatrixExp6(MatrixLog6(TransInv(X_start).dot(X_end))*s)) |
|
|
940 |
trajectory = vstack((trajectory, X_s)) |
|
|
941 |
return trajectory |
|
|
942 |
|
|
|
943 |
|
|
|
944 |
def CartesianTrajectory(X_start, X_end, T, N, method='cubic'): |
|
|
945 |
''' |
|
|
946 |
Similar to ScrewTrajectory, except the origin of the end-effector frame follows a straight line, |
|
|
947 |
decoupled from the rotational motion. |
|
|
948 |
Example: |
|
|
949 |
|
|
|
950 |
thetas_start = [0.1]*6 |
|
|
951 |
thetas_end = [pi/2]*6 |
|
|
952 |
M_ur5 = [[1,0,0,-.817],[0,0,-1,-.191],[0,1,0,-.006],[0,0,0,1]] |
|
|
953 |
S1_ur5 = [0,0,1,0,0,0] |
|
|
954 |
S2_ur5 = [0,-1,0,.089,0,0] |
|
|
955 |
S3_ur5 = [0,-1,0,.089,0,.425] |
|
|
956 |
S4_ur5 = [0,-1,0,.089,0,.817] |
|
|
957 |
S5_ur5 = [0,0,-1,.109,-.817,0] |
|
|
958 |
S6_ur5 = [0,-1,0,-.006,0,.817] |
|
|
959 |
Slist_ur5 = [S1_ur5,S2_ur5,S3_ur5,S4_ur5,S5_ur5,S6_ur5] |
|
|
960 |
X_start = FKinFixed(M_ur5, Slist_ur5, thetas_start) |
|
|
961 |
X_end = FKinFixed(M_ur5, Slist_ur5, thetas_end) |
|
|
962 |
T = 2 |
|
|
963 |
N = 3 |
|
|
964 |
CartesianTrajectory(X_start, X_end, T, N, 'quintic') |
|
|
965 |
>> |
|
|
966 |
array([[ 0.922, -0.388, 0.004, -0.764], |
|
|
967 |
[-0.007, -0.029, -1. , -0.268], |
|
|
968 |
[ 0.388, 0.921, -0.03 , -0.124], |
|
|
969 |
[ 0. , 0. , 0. , 1. ], |
|
|
970 |
[ 0.534, -0.806, 0.253, -0.327], |
|
|
971 |
[ 0.681, 0.234, -0.694, 0.014], |
|
|
972 |
[ 0.5 , 0.543, 0.674, -0.189], |
|
|
973 |
[ 0. , 0. , 0. , 1. ], |
|
|
974 |
[ 0. , -1. , -0. , 0.109], |
|
|
975 |
[ 1. , 0. , 0. , 0.297], |
|
|
976 |
[-0. , -0. , 1. , -0.254], |
|
|
977 |
[ 0. , 0. , 0. , 1. ]]) |
|
|
978 |
|
|
|
979 |
Notice the R of every T is same for ScrewTrajectory and CartesianTrajectory, but the translations |
|
|
980 |
are different. |
|
|
981 |
''' |
|
|
982 |
R_start, p_start = TransToRp(X_start) |
|
|
983 |
R_end ,p_end = TransToRp(X_end) |
|
|
984 |
assert N >= 2, 'N must be >= 2' |
|
|
985 |
assert isinstance(N, int), 'N must be an integer' |
|
|
986 |
assert method == 'cubic' or method == 'quintic', 'Incorrect time-scaling method argument' |
|
|
987 |
|
|
|
988 |
trajectory = X_start |
|
|
989 |
|
|
|
990 |
if method == 'cubic': |
|
|
991 |
for i in range(1,N): |
|
|
992 |
t = i*float(T)/(N-1) |
|
|
993 |
s = CubicTimeScaling(T,t) |
|
|
994 |
p_s = (1-s)*p_start + s*p_end |
|
|
995 |
R_s = R_start.dot(MatrixExp3(MatrixLog3(RotInv(R_start).dot(R_end))*s)) |
|
|
996 |
X_s = RpToTrans(R_s, p_s) |
|
|
997 |
trajectory = vstack((trajectory, X_s)) |
|
|
998 |
return trajectory |
|
|
999 |
|
|
|
1000 |
elif method == 'quintic': |
|
|
1001 |
for i in range(1,N): |
|
|
1002 |
t = i*float(T)/(N-1) |
|
|
1003 |
s = QuinticTimeScaling(T,t) |
|
|
1004 |
p_s = (1-s)*p_start + s*p_end |
|
|
1005 |
R_s = R_start.dot(MatrixExp3(MatrixLog3(RotInv(R_start).dot(R_end))*s)) |
|
|
1006 |
X_s = RpToTrans(R_s, p_s) |
|
|
1007 |
trajectory = vstack((trajectory, X_s)) |
|
|
1008 |
return trajectory |
|
|
1009 |
|
|
|
1010 |
|
|
|
1011 |
### end of HW4 functions ############################# |
|
|
1012 |
|
|
|
1013 |
### start of HW5 functions ########################### |
|
|
1014 |
|
|
|
1015 |
|
|
|
1016 |
def LieBracket(V1, V2): |
|
|
1017 |
assert len(V1)==len(V2)==6, 'Needs two 6 vectors' |
|
|
1018 |
V1.shape = (6,1) |
|
|
1019 |
w1 = V1[:3,:] |
|
|
1020 |
v1 = V1[3:,:] |
|
|
1021 |
|
|
|
1022 |
w1_mat = VecToso3(w1) |
|
|
1023 |
v1_mat = VecToso3(v1) |
|
|
1024 |
col1 = vstack((w1_mat, v1_mat)) |
|
|
1025 |
col2 = vstack((zeros((3,3)), w1_mat)) |
|
|
1026 |
mat1 = hstack((col1, col2)) |
|
|
1027 |
|
|
|
1028 |
return mat1.dot(V2) |
|
|
1029 |
|
|
|
1030 |
|
|
|
1031 |
def TruthBracket(V1, V2): # Transposed version of Lie Bracket |
|
|
1032 |
assert len(V1)==len(V2)==6, 'Needs two 6 vectors' |
|
|
1033 |
V1.shape = (6,1) |
|
|
1034 |
w1 = V1[:3,:] |
|
|
1035 |
v1 = V1[3:,:] |
|
|
1036 |
|
|
|
1037 |
w1_mat = VecToso3(w1) |
|
|
1038 |
v1_mat = VecToso3(v1) |
|
|
1039 |
col1 = vstack((w1_mat, v1_mat)) |
|
|
1040 |
col2 = vstack((zeros((3,3)), w1_mat)) |
|
|
1041 |
mat1 = hstack((col1, col2)) |
|
|
1042 |
|
|
|
1043 |
return (mat1.T).dot(V2) |
|
|
1044 |
|
|
|
1045 |
|
|
|
1046 |
def InverseDynamics(thetas, thetadots, thetadotdots, g, Ftip, M_rels, Glist, Slist): |
|
|
1047 |
''' |
|
|
1048 |
M1 = array(([1.,0.,0.,0.],[0.,1.,0.,0.],[0.,0.,1.,0.],[0.,0.,.089159,1.])).T |
|
|
1049 |
M2 = array(([0.,0.,-1.,0.],[0.,1.,0.,0.],[1.,0.,0.,0.],[.28,.13585,.089159,1.])).T |
|
|
1050 |
M3 = array(([0.,0.,-1.,0.],[0.,1.,0.,0.],[1.,0.,0.,0.],[.675,.01615,.089159,1.])).T |
|
|
1051 |
M4 = array(([-1.,0.,0.,0.],[0.,1.,0.,0.],[0.,0.,-1.,0.],[.81725,.01615,.089159,1.])).T |
|
|
1052 |
M5 = array(([-1.,0.,0.,0.],[0.,1.,0.,0.],[0.,0.,-1.,0.],[.81725,.10915,.089159,1.])).T |
|
|
1053 |
M6 = array(([-1.,0.,0.,0.],[0.,1.,0.,0.],[0.,0.,-1.,0.],[.81725,.10915,-.005491,1.])).T |
|
|
1054 |
M01 = array(([1.,0.,0.,0.],[0.,1.,0.,0.],[0.,0.,1.,0.],[0.,0.,.089159,1.])).T |
|
|
1055 |
M12 = array(([0.,0.,-1.,0.],[0.,1.,0.,0.],[1.,0.,0.,0.],[.28,.13585,0.,1.])).T |
|
|
1056 |
M23 = array(([1.,0.,0.,0.],[0.,1.,0.,0.],[0.,0.,1.,0.],[0.,-.1197,.395,1])).T |
|
|
1057 |
M34 = array(([0.,0.,-1.,0.],[0.,1.,0.,0.],[1.,0.,0.,0.],[0.,0.,.14225,1.])).T |
|
|
1058 |
M45 = array(([1.,0.,0.,0.],[0.,1.,0.,0.],[0.,0.,1.,0.],[0.,.093,0.,1.])).T |
|
|
1059 |
M56 = array(([1.,0.,0.,0.],[0.,1.,0.,0.],[0.,0.,1.,0.],[0.,0.,.09465,1.])).T |
|
|
1060 |
|
|
|
1061 |
G1 = array(([.010267,0.,0.,0.,0.,0.],[0.,.010267,0.,0.,0.,0.],[0.,0.,.00666,0.,0.,0.],[0.,0.,0.,3.7,0.,0.],[0.,0.,0.,0.,3.7,0.],[0.,0.,0.,0.,0.,3.7])) |
|
|
1062 |
G2 = array(([.22689,0.,0.,0.,0.,0.],[0.,.22689,0.,0.,0.,0.],[0.,0.,.0151074,0.,0.,0.],[0.,0.,0.,8.393,0.,0.],[0.,0.,0.,0.,8.393,0.],[0.,0.,0.,0.,0.,8.393])) |
|
|
1063 |
G3 = array(([.0494433,0.,0.,0.,0.,0.],[0.,.0494433,0.,0.,0.,0.],[0.,0.,.004095,0.,0.,0.],[0.,0.,0.,2.275,0.,0.],[0.,0.,0.,0.,2.275,0.],[0.,0.,0.,0.,0.,2.275])) |
|
|
1064 |
G4 = array(([.111172,0.,0.,0.,0.,0.],[0.,.111172,0.,0.,0.,0.],[0.,0.,.21942,0.,0.,0.],[0.,0.,0.,1.219,0.,0.],[0.,0.,0.,0.,1.219,0.],[0.,0.,0.,0.,0.,1.219])) |
|
|
1065 |
G5 = array(([.111172,0.,0.,0.,0.,0.],[0.,.111172,0.,0.,0.,0.],[0.,0.,.21942,0.,0.,0.],[0.,0.,0.,1.219,0.,0.],[0.,0.,0.,0.,1.219,0.],[0.,0.,0.,0.,0.,1.219])) |
|
|
1066 |
G6 = array(([.0171364,0.,0.,0.,0.,0.],[0.,.0171364,0.,0.,0.,0.],[0.,0.,.033822,.0,0.,0.],[0.,0.,0.,.1879,0.,0.],[0.,0.,0.,0.,.1879,0.],[0.,0.,0.,0.,0.,.1879])) |
|
|
1067 |
|
|
|
1068 |
Slist = [[0.,0.,1.,0.,0.,0.],[0.,1.,0.,-.089,0.,0.],[0.,1.,0.,-.089,0.,.425],[0.,1.,0.,-.089,0.,.817],[0.,0.,-1.,-.109,.817,.0],[0.,1.,0.,.006,0.,.817]] |
|
|
1069 |
|
|
|
1070 |
Glist = [G1,G2,G3,G4,G5,G6] |
|
|
1071 |
M_rels = [M01,M12,M23,M34,M45,M56] |
|
|
1072 |
Ftip = [0.,0.,0.,0.,0.,0.] |
|
|
1073 |
''' |
|
|
1074 |
assert len(thetas) == len(thetadots) == len(thetadotdots), 'Joint inputs mismatch' |
|
|
1075 |
Slist = asarray(Slist) |
|
|
1076 |
N = len(thetas) # no. of links |
|
|
1077 |
Ftip = asarray(Ftip) |
|
|
1078 |
|
|
|
1079 |
# initialize iterables |
|
|
1080 |
Mlist = [0]*N |
|
|
1081 |
T_rels = [0]*N |
|
|
1082 |
Vlist = [0]*N |
|
|
1083 |
Vdotlist = [0]*N |
|
|
1084 |
Alist = [0]*N |
|
|
1085 |
Flist = [0]*N |
|
|
1086 |
Taulist = [0]*N |
|
|
1087 |
|
|
|
1088 |
|
|
|
1089 |
Mlist[0] = M_rels[0] |
|
|
1090 |
for i in range(1, N): |
|
|
1091 |
Mlist[i] = Mlist[i-1].dot(M_rels[i]) |
|
|
1092 |
|
|
|
1093 |
|
|
|
1094 |
for i in range(N): |
|
|
1095 |
Alist[i] = Adjoint(TransInv(Mlist[i])).dot(Slist[i]) |
|
|
1096 |
|
|
|
1097 |
|
|
|
1098 |
for i in range(N): |
|
|
1099 |
T_rels[i] = M_rels[i].dot(MatrixExp6(Alist[i]*thetas[i])) |
|
|
1100 |
|
|
|
1101 |
|
|
|
1102 |
V0 = zeros((6,1)) |
|
|
1103 |
Vlist[0] = V0 |
|
|
1104 |
|
|
|
1105 |
Vdot0 = -asarray([0,0,0]+g) |
|
|
1106 |
Vdotlist[0] = Vdot0 |
|
|
1107 |
|
|
|
1108 |
F_ip1 = asarray(Ftip).reshape(6,1) |
|
|
1109 |
|
|
|
1110 |
# forward iterations |
|
|
1111 |
for i in range(1, N): |
|
|
1112 |
# print i |
|
|
1113 |
T_i_im1 = TransInv(T_rels[i]) #### should it be i-1??? |
|
|
1114 |
|
|
|
1115 |
Vlist[i] = Adjoint(T_i_im1).dot(Vlist[i-1]) + (Alist[i]*thetadots[i]).reshape(6,1) |
|
|
1116 |
|
|
|
1117 |
Vdotlist[i] = Adjoint(T_i_im1).dot(Vdotlist[i-1]) + LieBracket(Vlist[i], Alist[i])*thetadots[i] + Alist[i]*thetadotdots[i] |
|
|
1118 |
|
|
|
1119 |
|
|
|
1120 |
# print T_rels |
|
|
1121 |
# print Vlist |
|
|
1122 |
# print Vdotlist |
|
|
1123 |
# print Alist |
|
|
1124 |
|
|
|
1125 |
# backward iterations |
|
|
1126 |
for i in range(N-1,-1,-1): |
|
|
1127 |
T_ip1_i = TransInv(T_rels[i]) |
|
|
1128 |
Flist[i] = (Adjoint(T_ip1_i).T).dot(F_ip1) + (Glist[i].dot(Vdotlist[i])).reshape(6,1) - TruthBracket(Vlist[i], Glist[i].dot(Vlist[i])) |
|
|
1129 |
Taulist[i] = (Flist[i].T).dot(Alist[i]) |
|
|
1130 |
|
|
|
1131 |
return asarray(Taulist).flatten() |
|
|
1132 |
|
|
|
1133 |
|
|
|
1134 |
def InertiaMatrix(thetas, M_rels, Glist, Slist): |
|
|
1135 |
Slist = asarray(Slist) |
|
|
1136 |
N = len(thetas) # no. of links |
|
|
1137 |
|
|
|
1138 |
M = zeros((N,N)) |
|
|
1139 |
|
|
|
1140 |
for i in range(N): |
|
|
1141 |
thetadotdots = [0]*N |
|
|
1142 |
thetadotdots[i] = 1 |
|
|
1143 |
M[:, i] = InverseDynamics(thetas, [0]*6, thetadotdots, [0]*3, [0]*6, M_rels, Glist, Slist) |
|
|
1144 |
|
|
|
1145 |
return M |
|
|
1146 |
|
|
|
1147 |
|
|
|
1148 |
def CoriolisForces(thetas, thetadots, M_rels, Glist, Slist): |
|
|
1149 |
C = InverseDynamics(thetas, thetadots, [0]*6, [0]*3, [0]*6, M_rels, Glist, Slist) |
|
|
1150 |
return C |
|
|
1151 |
|
|
|
1152 |
|
|
|
1153 |
def GravityForces(thetas, g, M_rels, Glist, Slist): |
|
|
1154 |
Gvec = InverseDynamics(thetas, [0]*6, [0]*6, g, [0]*6, M_rels, Glist, Slist) |
|
|
1155 |
return Gvec |
|
|
1156 |
|
|
|
1157 |
|
|
|
1158 |
def EndEffectorForces(Ftip, thetas, M_rels, Glist, Slist): |
|
|
1159 |
return InverseDynamics(thetas, [0]*6, [0]*6, [0]*3, Ftip, M_rels, Glist, Slist) |
|
|
1160 |
|
|
|
1161 |
|
|
|
1162 |
def ForwardDynamics(thetas, thetadots, taus, g, Ftip, M_rels, Glist, Slist): |
|
|
1163 |
M = InertiaMatrix(thetas, M_rels, Glist, Slist) |
|
|
1164 |
C = CoriolisForces(thetas, thetadots, M_rels, Glist, Slist) |
|
|
1165 |
Gvec = GravityForces(thetas, g, M_rels, Glist, Slist) |
|
|
1166 |
eeF = EndEffectorForces(Ftip, thetas, M_rels, Glist, Slist) |
|
|
1167 |
|
|
|
1168 |
thetadotdots = (linalg.pinv(M)).dot(taus - C - Gvec - eeF) |
|
|
1169 |
return thetadotdots |
|
|
1170 |
|
|
|
1171 |
|
|
|
1172 |
def EulerStep(thetas_t, thetadots_t, thetadotdots_t, delt): |
|
|
1173 |
thetas_t = asarray(thetas_t) |
|
|
1174 |
thetadots_t = asarray(thetadots_t) |
|
|
1175 |
thetadotdots_t = asarray(thetadotdots_t) |
|
|
1176 |
|
|
|
1177 |
thetas_next = thetas_t + delt*thetadots_t |
|
|
1178 |
thetadots_next = thetadots_t + delt*thetadotdots_t |
|
|
1179 |
|
|
|
1180 |
return thetas_next, thetadots_next |
|
|
1181 |
|
|
|
1182 |
|
|
|
1183 |
def InverseDynamicsTrajectory(thetas_traj, thetadots_traj, thetadotdots_traj, Ftip_traj, g, M_rels, Glist, Slist): |
|
|
1184 |
Np1 = len(thetas_traj) |
|
|
1185 |
# N = Np1-1 |
|
|
1186 |
idtraj = [] |
|
|
1187 |
for i in range(Np1): |
|
|
1188 |
taus = InverseDynamics(thetas_traj[i], thetadots_traj[i], thetadotdots_traj[i], g, Ftip_traj[i], M_rels, Glist, Slist) |
|
|
1189 |
idtraj.append(taus) |
|
|
1190 |
|
|
|
1191 |
return asarray(idtraj) |
|
|
1192 |
|
|
|
1193 |
|
|
|
1194 |
def ForwardDynamicsTrajectory(thetas_init, thetadots_init, tau_hist, delt, g, Ftip_traj, M_rels, Glist, Slist): |
|
|
1195 |
Np1 = len(tau_hist) |
|
|
1196 |
thetas_traj = asarray(thetas_init) |
|
|
1197 |
thetadots_traj = asarray(thetadots_init) |
|
|
1198 |
|
|
|
1199 |
for i in range(Np1): |
|
|
1200 |
thetadotdots_init = ForwardDynamics(thetas_init, thetadots_init, tau_hist[i], g, Ftip_traj[i], M_rels, Glist, Slist) |
|
|
1201 |
thetas_next, thetadots_next = EulerStep(thetas_init, thetadots_init, thetadotdots_init, delt) |
|
|
1202 |
# append new state |
|
|
1203 |
thetas_traj = hstack((thetas_traj, thetas_next)) |
|
|
1204 |
thetadots_traj = hstack((thetadots_traj, thetadots_next)) |
|
|
1205 |
# update initial conditions |
|
|
1206 |
thetas_init = thetas_next |
|
|
1207 |
thetadots_init = thetadots_next |
|
|
1208 |
|
|
|
1209 |
return thetas_traj.reshape(Np1+1, len(thetas_init)), thetadots_traj.reshape(Np1+1, len(thetas_init)) |
|
|
1210 |
|
|
|
1211 |
|
|
|
1212 |
### end of HW5 functions ############################# |