|
| 1 | +import numpy as np |
| 2 | +import matplotlib.pyplot as plt |
| 3 | +from mpl_toolkits.mplot3d import Axes3D |
| 4 | +from random import randint |
| 5 | + |
| 6 | +#make numpy raise errors instead of warn so can be caught by try except blocks |
| 7 | +np.seterr(all='raise') |
| 8 | + |
| 9 | +#plot axis origins onto point with rotation |
| 10 | +def plotOrigin(tx, ty, tz, rx, ry, rz, length): |
| 11 | + #transform origin |
| 12 | + rotall = np.matmul(np.matmul(Rz(rz), Ry(ry)), Rx(rx)) |
| 13 | + xplots = np.matmul(rotall, ((0,length), (0,0), (0,0))) |
| 14 | + yplots = np.matmul(rotall, ((0,0), (0,length), (0,0))) |
| 15 | + zplots = np.matmul(rotall, ((0,0), (0,0), (0,length))) |
| 16 | + xplots[0] += tx |
| 17 | + xplots[1] += ty |
| 18 | + xplots[2] += tz |
| 19 | + yplots[0] += tx |
| 20 | + yplots[1] += ty |
| 21 | + yplots[2] += tz |
| 22 | + zplots[0] += tx |
| 23 | + zplots[1] += ty |
| 24 | + zplots[2] += tz |
| 25 | + |
| 26 | + #plot origin |
| 27 | + ax.plot(*xplots, c='#ff0000') |
| 28 | + ax.plot(*yplots, c='#00ff00') |
| 29 | + ax.plot(*zplots, c='#0000ff') |
| 30 | + |
| 31 | +#find the position of point given a vector |
| 32 | +def findPoint(start, length, theta1, theta2): |
| 33 | + end = [0, 0, 0] |
| 34 | + end[0] = start[0] + length * np.cos(theta1) * np.cos(theta2) |
| 35 | + end[1] = start[1] + length * np.cos(theta1) * np.sin(theta2) |
| 36 | + end[2] = start[2] + length * np.sin(theta1) |
| 37 | + return end |
| 38 | + |
| 39 | +#define rotation matrices |
| 40 | +def Rx(a): |
| 41 | + return ((1, 0, 0), |
| 42 | + (0, np.cos(a), -np.sin(a)), |
| 43 | + (0, np.sin(a), np.cos(a))) |
| 44 | + |
| 45 | +def Ry(a): |
| 46 | + return ((np.cos(a), 0, np.sin(a)), |
| 47 | + (0, 1, 0), |
| 48 | + (-np.sin(a), 0, np.cos(a))) |
| 49 | + |
| 50 | +def Rz(a): |
| 51 | + return ((np.cos(a), -np.sin(a), 0), |
| 52 | + (np.sin(a), np.cos(a), 0), |
| 53 | + (0, 0, 1)) |
| 54 | + |
| 55 | +#function to find angle between two vectors |
| 56 | +def getAngle(vect1, vect2): |
| 57 | + try: |
| 58 | + temp1 = np.linalg.norm(vect1) |
| 59 | + temp2 = np.linalg.norm(vect2) |
| 60 | + unitvect1 = vect1 / temp1 |
| 61 | + unitvect2 = vect2 / temp2 |
| 62 | + theta = np.arccos(np.clip(np.dot(unitvect1, unitvect2), -1.0, 1.0)) |
| 63 | + return theta |
| 64 | + except Exception: |
| 65 | + return np.pi/2 if temp2 > 0 else -np.pi/2 |
| 66 | + |
| 67 | +#linear interpolation |
| 68 | +def lerp(a, b, t): |
| 69 | + return (a * (1 - t)) + (b * t) |
| 70 | + |
| 71 | +def fwdKin(lengths, angles): |
| 72 | + #trace the path to find plot points |
| 73 | + xplots = [0, 0] |
| 74 | + yplots = [0, 0] |
| 75 | + zplots = [0, lengths[0]] |
| 76 | + mainlength1 = lengths[1] |
| 77 | + mainlength2 = lengths[2] + lengths[3] |
| 78 | + mainlength3 = lengths[4] + lengths[5] |
| 79 | + theta1 = (np.pi/2)-angles[1] |
| 80 | + theta2 = angles[0] |
| 81 | + point = findPoint([xplots[1], yplots[1], zplots[1]], mainlength1, theta1, theta2) |
| 82 | + xplots.append(point[0]) |
| 83 | + yplots.append(point[1]) |
| 84 | + zplots.append(point[2]) |
| 85 | + theta1 = (np.pi/2)-angles[1]-angles[2] |
| 86 | + theat2 = angles[0] |
| 87 | + point = findPoint([xplots[2], yplots[2], zplots[2]], mainlength2, theta1, theta2) |
| 88 | + savedpoint = point #save the point for later derivation |
| 89 | + xplots.append(lerp(xplots[2], point[0], lengths[2]/mainlength2)) |
| 90 | + yplots.append(lerp(yplots[2], point[1], lengths[2]/mainlength2)) |
| 91 | + zplots.append(lerp(zplots[2], point[2], lengths[2]/mainlength2)) |
| 92 | + xplots.append(point[0]) |
| 93 | + yplots.append(point[1]) |
| 94 | + zplots.append(point[2]) |
| 95 | + transformed = np.array([mainlength3, 0, 0]) |
| 96 | + transformed = np.matmul(Ry(angles[4]), transformed) |
| 97 | + transformed = np.matmul(Rx(angles[3]), transformed) |
| 98 | + transformed = np.matmul(Ry(np.pi + theta1), transformed) |
| 99 | + transformed = np.matmul(Rz(np.pi + theta2), transformed) |
| 100 | + point = transformed + point |
| 101 | + xplots.append(lerp(xplots[4], point[0], lengths[4]/mainlength3)) |
| 102 | + yplots.append(lerp(yplots[4], point[1], lengths[4]/mainlength3)) |
| 103 | + zplots.append(lerp(zplots[4], point[2], lengths[4]/mainlength3)) |
| 104 | + xplots.append(point[0]) |
| 105 | + yplots.append(point[1]) |
| 106 | + zplots.append(point[2]) |
| 107 | + |
| 108 | + #convert the data type |
| 109 | + xplots = np.array(xplots) |
| 110 | + yplots = np.array(yplots) |
| 111 | + zplots = np.array(zplots) |
| 112 | + |
| 113 | + #we now know the end position |
| 114 | + tx = xplots[-1] |
| 115 | + ty = yplots[-1] |
| 116 | + tz = zplots[-1] |
| 117 | + |
| 118 | + #derive theta1 and theta2 of wrist vector |
| 119 | + #define root vectors |
| 120 | + wrstvect = point - savedpoint |
| 121 | + |
| 122 | + txdelta = wrstvect[0] |
| 123 | + tydelta = wrstvect[1] |
| 124 | + tzdelta = wrstvect[2] |
| 125 | + |
| 126 | + elevvect = np.array([txdelta, tydelta, 0]) |
| 127 | + azimvect = np.array([txdelta, 0, tzdelta]) |
| 128 | + |
| 129 | + elevangle = getAngle(elevvect, wrstvect) |
| 130 | + azimangle = getAngle(azimvect, wrstvect) |
| 131 | + |
| 132 | + if tzdelta < 0: |
| 133 | + elevangle *= -1 |
| 134 | + |
| 135 | + if tydelta < 0: |
| 136 | + azimangle *= -1 |
| 137 | + |
| 138 | + rx = angles[5] |
| 139 | + ry = -elevangle |
| 140 | + rz = azimangle |
| 141 | + |
| 142 | + return ((tx,ty,tz,rx,ry,rz), xplots, yplots, zplots) |
| 143 | + |
| 144 | +def main(): |
| 145 | + global ax |
| 146 | + |
| 147 | + #create and configure plot object |
| 148 | + fig = plt.figure(figsize=(8,8)) |
| 149 | + fig.subplots_adjust(left=0.01, bottom=0.01, right=0.96, top=0.96) |
| 150 | + ax = fig.add_subplot(111, projection='3d') |
| 151 | + #ax.set(xlim=(-260,260), ylim=(-260,260), zlim=(0,400)) |
| 152 | + ax.set(xlim=(0,260), ylim=(0,260), zlim=(0,200)) |
| 153 | + ax.w_xaxis.line.set_color('#ff0000') |
| 154 | + ax.w_yaxis.line.set_color('#00ff00') |
| 155 | + ax.w_zaxis.line.set_color('#0000ff') |
| 156 | + ax.set_xlabel('x-axis') |
| 157 | + ax.set_ylabel('y-axis') |
| 158 | + ax.set_zlabel('z-axis') |
| 159 | + #ax.view_init(elev=30, azim=-135) |
| 160 | + ax.view_init(elev=30, azim=135) |
| 161 | + |
| 162 | + lengths = np.array([50.0, 200.0, 100.0, 100.0, 25.0, 25.0]) |
| 163 | + |
| 164 | + angles = np.array([60, 30, 90, 0, 0, 0]) |
| 165 | + |
| 166 | + raw = fwdKin(lengths, np.radians(angles)) |
| 167 | + tx = raw[0][0] |
| 168 | + ty = raw[0][1] |
| 169 | + tz = raw[0][2] |
| 170 | + rx = raw[0][3] |
| 171 | + ry = raw[0][4] |
| 172 | + rz = raw[0][5] |
| 173 | + xplots = raw[1] |
| 174 | + yplots = raw[2] |
| 175 | + zplots = raw[3] |
| 176 | + |
| 177 | + #plot data |
| 178 | + ax.plot(xplots, yplots, zplots, c='#000000') |
| 179 | + ax.scatter(xplots, yplots, zplots, c='#00ffff') |
| 180 | + plotOrigin(xplots[0], yplots[0], zplots[0], 0, 0, 0, 50) |
| 181 | + plotOrigin(xplots[6], yplots[6], zplots[6], rx, ry, rz, 50) |
| 182 | + |
| 183 | + #output calculated position |
| 184 | + print(f'Tx = {round(tx,2)} mm') |
| 185 | + print(f'Ty = {round(ty,2)} mm') |
| 186 | + print(f'Tz = {round(tz,2)} mm') |
| 187 | + print(f'Rx = {round(np.degrees(rx),2)} deg') |
| 188 | + print(f'Ry = {round(np.degrees(ry),2)} deg') |
| 189 | + print(f'Rz = {round(np.degrees(rz),2)} deg') |
| 190 | + |
| 191 | + #show plotted data |
| 192 | + plt.show() |
| 193 | + |
| 194 | +if __name__ == '__main__': |
| 195 | + main() |
0 commit comments