-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrotary.py
386 lines (296 loc) · 14.4 KB
/
rotary.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
# -*- coding: utf-8 -*-
"""
Created on Sun Jan 10 14:24:01 2016
@author: Jak
"""
import math
import numpy
import numpy as np
import time
def rue(bPos, pPos, s, c, w, sigma):
#Find distance/radius from centre of base to centre of platform
pass
return None
def rotMat(roll, pitch, yaw):
cphi = np.cos(pitch)
sphi = np.sin(pitch)
cth = np.cos(roll)
sth = np.sin(roll)
cpsi = np.cos(yaw)
spsi = np.sin(yaw)
#Hence calculate rotation matrix
#Note that it is a 3-2-1 rotation matrix
Rzyx = numpy.array([[cpsi*cth, cpsi*sth*sphi - spsi*cphi, cpsi*sth*cphi + spsi*sphi] \
,[spsi*cth, spsi*sth*sphi + cpsi*cphi, spsi*sth*cphi - cpsi*sphi] \
, [-sth, cth*sphi, cth*cphi]])
return Rzyx
def ik(baseCentre, pPos, s, c, w, a):
"""
:param baseCentre:
:param pPos:
:param a:
"""
Rzyx = rotMat(a[3], a[4], a[5])
# Hence platform centre with respect to the base coordinate system
xbar = a[0:3] - baseCentre
# Hence orientation of platform points wrt platform centre
uvw = numpy.zeros(pPos.shape)
for i in range(pPos.shape[0]):
uvw[i, :] = Rzyx @ pPos[i, :]
# Hence location of platform points wrt baseCentre
upper = xbar+uvw
return upper
def circleCircleIntersection(c1, r1, c2, r2, n):
"""
:param c1: Centre of circle 1
:param r1: Radius of circle 1
:param c2: Centre of circle 2
:param r2: Radius of circle 2
:param n: Normal of the plane of the two circles
Returns the intersection points of the two circles
"""
# From stackoverflow: https://gamedev.stackexchange.com/questions/75756/sphere-sphere-intersection-and-circle-sphere-intersection
planeTangentVec = np.cross(c1-c2, n)
t = planeTangentVec/np.sqrt(np.sum(np.square(planeTangentVec)))
# Find the location of the midpoint of the chord
# connecting the two intersection points
d2 = np.sum(np.square(c1-c2))
h = 1/2.0 + (r1**2 -r2**2)/(2*d2)
c_i = c1 + h*(c2-c1)
r_i = np.sqrt(r1**2 - d2*h**2)
p1 = c_i -t*r_i
p2 = c_i +t*r_i
return(p1, p2)
def circleSphereIntersection(c_c, r_c, n_c, c_s, r_s):
"""
:param c_c: Centre of the circle
:param r_c: Radius of the circle
:param n_c: A normal vector of the circle
:param c_s: Centre of the sphere
:param r_s: Radius of the sphere
Find the points where the circle intersects the sphere
"""
# From stackoverflow: https://gamedev.stackexchange.com/questions/75756/sphere-sphere-intersection-and-circle-sphere-intersection
# d = distance from sphere centre the plane cuts the sphere
d = np.dot(n_c, c_c - c_s)
if np.abs(d) > r_s:
return ValueError("Circle does not intersect sphere")
# Forms a new circle
c_p = c_s + d*n_c
r_p = np.sqrt(r_s**2 - d**2)
# Problem collapses to circle-circle intersection
if d == r_s:
# Single point
return (c_p, c_p)
else:
return circleCircleIntersection(c_c, r_c, c_p, r_p, n_c)
def legPos(bPos, pPos, s, c, legYawAngle=None):
"""
Calculate the knee/midJoint position of each leg, and the lower leg angle
with respect to the horizontal.
"""
virtualLegs = pPos - bPos
# Virtual leg lengths
l_i = numpy.sqrt(numpy.sum(numpy.square(virtualLegs),1))
# Angle between virtual leg and lever
cosAlpha = (np.square(s) + np.square(l_i) - np.square(c))/(2*s*l_i)
alpha = np.arccos(cosAlpha)
if legYawAngle is None:
legYawAngle = np.arctan2(bPos[:, 0], bPos[:, 1])
# Calculate the angle of the lower leg/lever arm
# The lever arm end is constrained to a circle
# The upper arm is constrained to a sphere about the upper pos
# => intersection
# This will return two points - want the one that leads to legs like:
# \
# \ /
# \ /
# \./
# rather than:
#
# /.\
# / \
# / \
# /
midJoint = np.full(pPos.shape, np.nan)
leverAngles = np.full(legYawAngle.shape, np.nan)
for legNum in range(pPos.shape[0]):
#print("\n", legNum)
planeYawAngle = legYawAngle[legNum]
#print("plane yaw angle", planeYawAngle)
upperCentre = pPos[legNum, :]
lowerCentre = bPos[legNum, :]
upperLength = c[legNum]
lowerLength = s[legNum]
lowerNorm = np.array([np.sin(planeYawAngle), -np.cos(planeYawAngle), 0])
points = circleSphereIntersection(lowerCentre, lowerLength, lowerNorm, upperCentre, upperLength)
#print(points)
#Then calculate angle in the plane of the lever arm
lowerTangent = rotMat(0, 0, planeYawAngle) @ np.array([1, 0, 0])
#print("tangentVec", lowerTangent)
# Use the dot product to calculate the angle
angles = np.full((2,), np.nan)
midJointAngles = np.full((2,), np.nan)
for i, p in enumerate(points):
p = p - bPos[legNum, :]
pNorm = p/np.sqrt(np.sum(np.square(p)))
#print(p)
#print(p/np.sqrt(np.sum(np.square(p))))
# Calculate the first angle - wrt the horizontal
cosLeverAngle = np.dot(pNorm, lowerTangent)
#print(cosLeverAngle)
leverAngle = np.arccos(cosLeverAngle)
angles[i] = leverAngle
#print("leverAngle", np.degrees(leverAngle))
# Choose which of the two solutions to pick
# Calculate the angle at the midjoint
# lower -> midJoint -> upper
# Use the dot-product
u = pPos[legNum, :] - p
uNorm = u/np.sqrt(np.sum(np.square(u)))
midCosLeverAngle = np.dot(pNorm, uNorm)
midJointAngle = np.arccos(midCosLeverAngle)
midJointAngles[i] = midJointAngle
midJointAngles = np.mod(midJointAngles, np.pi*2) # Wrap to 0->360 deg
#print(legNum, np.degrees(angles), np.degrees(midJointAngles))
# Select the solution with the smaller angle - due to the
# construction of the angle, this will give us the outward
# facing joint we want
pointSelectedIndex = np.argmin(midJointAngles)
leverAngles[legNum] = angles[pointSelectedIndex]
midJoint[legNum, :] = points[pointSelectedIndex]
return midJoint, leverAngles
def fk(bPos, pPos, s, c, w, sigma):
"""
base attachmetn loc in base cs -bPos
platform attachment loc in platform cs -pPos
length of pivot arm -s
length of connecting rods -c
rotation about z axis of pivot arm -w
measured angles -sigma
"""
#newton-raphson
tol_f = 1e-3;
tol_a = 1e-3;
#iteration limits
maxIters = 5
iterNum = 0
#initial guess position
# a = [x, y, z, phi, theta, psi] - angles in degrees initially
a = [0, 0, 130, 0.1, 0.1, 0.1]
a[3:] = [math.radians(x) for x in a[3:]] #convert to radians
a = numpy.array(a).transpose()
while iterNum < maxIters:
#time.sleep(3)
iterNum += 1
phi = a[3]
th = a[4]
psi = a[5]
#Must translate platform coordinates into base coordinate system
#Calculate rotation matrix elements
cphi = math.cos(phi)
sphi = math.sin(phi)
cth = math.cos(th)
sth = math.sin(th)
cpsi = math.cos(psi)
spsi = math.sin(psi)
#Hence calculate rotation matrix
#Note that it is a 3-2-1 rotation matrix
Rzyx = numpy.array([[cpsi*cth, cpsi*sth*sphi - spsi*cphi, cpsi*sth*cphi + spsi*sphi] \
,[spsi*cth, spsi*sth*sphi + cpsi*cphi, spsi*sth*cphi - cpsi*sphi] \
, [-sth, cth*sphi, cth*cphi]])
#Hence platform sensor points with respect to the base coordinate system
xbar = a[0:3] - bPos
#Hence orientation of platform wrt base
uvw = numpy.zeros(pPos.shape)
for i in xrange(6):
uvw[i, :] = numpy.dot(Rzyx, pPos[i, :])
l_i = numpy.sum(numpy.square(xbar + uvw),1)
#lever arm location in base CS
A_i = bPos + numpy.array([s*numpy.cos(w), s*numpy.sin(w), s*numpy.sin(sigma)]).transpose()
d2 = pPos + (xbar+uvw) - A_i
"""
#xz plane
deltaSubSigma = numpy.arccos(d2[:,2]/d2[:, 1])
delta = sigma + deltaSubSigma
#yz plane
eta = numpy.arctan2(d2[:, 1], d2[:, 2])
"""
#Find static arm length
c_i = numpy.sum(numpy.square(d2), 1)
print(numpy.sqrt(c_i))
#print c
#import sys
#sys.exit()
#Hence find value of objective function
#The calculated lengths minus the actual length
#f = -1 * (l_i - numpy.square(L))
f = -1 * (c_i - numpy.square(c))
sumF = numpy.sum(numpy.abs(f))
if sumF < tol_f:
#success!
#print "Converged! Output is in 'a' variable"
break
#As using the newton-raphson matrix, need the jacobian (/hessian?) matrix
#Using paper linked above:
dfda = numpy.zeros((6, 6))
sin = math.sin
cos = math.cos
alpha = phi
beta = th
gamma = psi
x = a[0]
y = a[1]
z = a[2]
for i in xrange(6):
#from mathematica
b = bPos[i]
p = pPos[i]
sig = sigma[i]
s_i = s[i]
lam = w[i]
dfda[i, 0] = 2*(x-s_i*cos(lam) - 2*b[0] + p[0] + cos(alpha)*cos(beta)*p[0] + (-cos(gamma)*sin(alpha) + cos(alpha)*sin(beta)*sin(gamma))*p[1] + (cos(alpha)*cos(gamma)*sin(beta) + sin(alpha)*sin(gamma))*p[2])
dfda[i, 1] = 2*(y-s_i*sin(lam) - 2*b[1] + cos(beta)*sin(alpha)*p[0] + p[1] + (cos(alpha)*cos(gamma) + sin(alpha)*sin(beta)*sin(gamma))*p[1] + (cos(gamma)*sin(alpha)*sin(beta) - cos(alpha)*sin(gamma))*p[2])
dfda[i, 2] = 2*(z-s_i*sin(sig) - 2*b[2] -sin(beta)*p[0] + cos(beta)*sin(gamma)*p[1] + p[2] + cos(beta)*cos(gamma)*p[2])
dfda[i, 3] = 2*(y-s_i*sin(lam) - 2*b[1] + cos(beta)*sin(alpha)*p[0] + p[1] + (cos(alpha)*cos(gamma) + sin(alpha)*sin(beta)*sin(gamma))*p[1] + (cos(gamma)*sin(alpha)*sin(beta) - cos(alpha)*sin(gamma))*p[2]) \
*(cos(alpha)*cos(beta)*p[0] + (-cos(gamma)*sin(alpha)+cos(alpha)*sin(beta)*sin(gamma))*p[1] + (cos(alpha)*cos(gamma)*sin(beta) + sin(alpha)*sin(gamma))*p[2]) \
+2*(-cos(beta)*sin(alpha)*p[0] + (-cos(alpha)*cos(gamma) - sin(alpha)*sin(beta)*sin(gamma))*p[1] + (-cos(gamma)*sin(alpha)*sin(beta) + cos(alpha)*sin(gamma))*p[0]) \
*(x-s_i*cos(lam) - 2*b[0] + p[0] + cos(alpha)*cos(beta)*p[0] + (-cos(gamma)*sin(alpha) + cos(alpha)*sin(beta)*sin(gamma))*p[1] + (cos(alpha)*cos(gamma)*sin(beta) + sin(alpha)*sin(gamma))*p[0])
dfda[i, 4] = 2*(z-s_i*sin(sig) - 2*b[0] -sin(beta)*p[0] + cos(beta)*sin(gamma)*p[1] + p[2] + cos(beta)*cos(gamma)*p[2])*(-cos(beta)*p[0] -sin(beta)*sin(gamma)*p[1] - cos(gamma)*sin(beta)*p[2]) \
+2*(-sin(alpha)*sin(beta)*p[0] + cos(beta)*sin(alpha)*sin(gamma)*p[1] + cos(beta)*cos(gamma)*sin(alpha)*p[2])*(y-s_i*sin(lam) - 2*b[1] + cos(beta)*sin(alpha)*p[0] + p[1] + (cos(alpha)*cos(gamma) + sin(alpha)*sin(beta)*sin(gamma))*p[1] + (cos(gamma)*sin(alpha)*sin(beta) - cos(alpha)*sin(gamma))*p[2]) \
+2*(-cos(alpha)*sin(beta)*p[0] + cos(alpha)*cos(beta)*sin(gamma)*p[1] + cos(alpha)*cos(beta)*cos(gamma)*p[2])*(x-s_i*cos(lam) - 2*b[0] + p[0] + cos(alpha)*cos(beta)*p[0] + (-cos(gamma)*sin(alpha) + cos(alpha)*sin(beta)*sin(gamma))*p[1] + (cos(alpha)*cos(gamma)*sin(beta) + sin(alpha)*sin(gamma))*p[2])
dfda[1, 5] = 2*(z-s_i*sin(sig) - 2*b[2] - sin(beta)*p[0] + cos(beta)*sin(gamma)*p[1] + p[2] + cos(beta)*cos(gamma)*p[2])*(cos(beta)*cos(gamma)*p[2] - cos(beta)*sin(gamma)*p[2]) \
+2*(x-s_i*cos(lam) - 2*b[0] + p[0] + cos(alpha)*cos(beta)*p[0] + (-cos(gamma)*sin(alpha) + cos(alpha)*sin(beta)*sin(gamma))*p[1] + (cos(alpha)*cos(gamma)*sin(beta) + sin(alpha)*sin(gamma))*p[2]) \
*((cos(alpha)*cos(gamma)*sin(beta) + sin(alpha)*sin(gamma))*p[1] + (cos(gamma)*sin(alpha) - cos(alpha)*sin(beta)*sin(gamma))*p[2]) \
+2*(y-s_i*sin(lam) - 2*b[1] + cos(beta)*sin(alpha)*p[0] + p[1] + (cos(alpha)*cos(gamma) + sin(alpha)*sin(beta)*sin(gamma))*p[1] + (cos(gamma)*sin(alpha)*sin(beta) - cos(alpha)*sin(gamma))*p[2]) \
*((cos(gamma)*sin(alpha)*sin(beta) - cos(alpha)*sin(gamma))*p[1] + (-cos(alpha)*cos(gamma) - sin(alpha)*sin(beta)*sin(gamma))*p[2])
#Hence solve system for delta_{a} - The change in lengths
delta_a = numpy.linalg.solve(dfda, f)
if abs(numpy.sum(delta_a)) < tol_a:
#print "Small change in lengths -- converged?"
break
a = a + delta_a
if iterNum >= maxIters:
print("max iterations exceeded")
#for i in xrange(3,6):
# a[i] = math.degrees(a[i])
print("In %d iterations" % (iterNum))
return a
"""
class ConfigBased():
def __init__(self):
from configuration import *
self.bPos = numpy.array(bPos)
self.pPos = numpy.array(pPos)
self.s_i = numpy.array([35]*6) #lever arm length
self.c_i = numpy.array([125]*6) #rotary arm length
self.eta_i = numpy.radians(numpy.array([-90, 90, 180, -270, 270, 0])) #rotation from x axis
def fk(self, angles):
return fk(self.bPos, self.pPos, self.s_i, self.c_i, self.eta_i, numpy.radians(numpy.array(angles)))
def ik(self, a):
return ik(self.bPos, self.pPos, self.s_i, self.c_i, self.eta_i, a)
"""
if __name__ == "__main__":
c = ConfigBased()
c.ik([0, 0, 100, 0, 0, 0])
#c.fk([0, 0, 0, 0, 0, 0])