-
Notifications
You must be signed in to change notification settings - Fork 3
/
trajectoryPlanner.py
291 lines (242 loc) · 10.6 KB
/
trajectoryPlanner.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
#Patrick Johnson 2020
#Joint Trajectory Planner
#given a list of waypoints, this will output position/velocity/acceleration
#values for each joint at a desired sampling interval.
import numpy as np
import csv
class wayPoints:
def __init__(self,filename = None):
self.waypoints = []
if filename != None:
self.readFromFile(filename)
def addWP(self,wp):
#wp is (j1,j2,jn,t) tuple
self.waypoints.append(wp)
def readFromFile(self,filename):
#assuming CSV file
with open(filename,newline='') as wpFile:
wpReader = csv.reader(wpFile, delimiter=',', quotechar='|')
for wp in wpReader:
newRow = [float(val) for val in wp]
self.addWP(tuple(newRow))
class trajectoryPlanner:
def __init__(self, jointNum):
self.jointNum = jointNum
self.method = None
def waypointsParse(self,wayP,method):
#interpret a list of waypoints
#list of tuples, [(joint1_i,joint2_i,...,t_i)...]
#interpolate between points with method
self.method = method
if len(wayP) < 2:
print("not enough waypoints!")
return
#check for positive time increments
tLast = 0
for waypoint in wayP:
tDiff = waypoint[-1]-tLast
tLast = waypoint[-1]
if tDiff < 0:
print("not a positive time sequence!")
return
if len(wayP[0])-1 != self.jointNum:
print("waypoints/joint mismatch!")
return
self.joints = []
for i in range(self.jointNum):
self.joints.append([])
#split waypoints into individual [(position,time)] per joint
for waypoint in wayP:
for i in range(self.jointNum):
self.joints[i].append([waypoint[i],waypoint[-1]])
#joints[] is now a list of points,time per joint
if method == "cubic":
#cubic interpolation
self.cubicInterpolate()
elif method == "quintic":
#quintic interpolation
self.quinticInterpolate()
elif method == "trapVel":
self.trapVelInterpolate()
else:
pass
def trapVelInterpolate(self):
#interpolate between points with trapezoidal velocity
#later - respect max velocity and acceleration constraints
#no blending - stop at waypoints.
pass
def cubicInterpolate(self):
#for each joint, calculate the coefficients for the cubic
#interpolating polynomials
#modify joints from [position,time] to [position,velocity,time]
self.cubicCoeffs = []
for j in self.joints:
self.cubicCoeffs.append([])
#null starting and ending velocity
for j in self.joints:
for i in range(len(j)):
j[i].insert(1,0)
#compute intermediate velocities
#if the last and next velocities are of different sign, make
#velocity = 0 at that point.
for j in self.joints:
for i in range(1,len(j)-1):
#(pos-lastPos)/(tdiffLast)
velBefore = (j[i][0]-j[i-1][0])/(j[i][-1]-j[i-1][-1])
#(nextPos-pos)/(tdiffNext)
velAfter = (j[i+1][0]-j[i][0])/(j[i+1][-1]-j[i][-1])
if sign(velBefore) == sign(velAfter):
intermediateVelocity = (1/2)*(velBefore+velAfter)
else:
intermediateVelocity = 0
j[i][1] = intermediateVelocity
#now, do the interpolation
#treat each points starting time as 0, simpler math
for num,j in enumerate(self.joints):
for i in range(len(j)-1):
#calculate a3...a0 for the cubic polynomial between
#each pair of points
qi = j[i][0]
qi_dot = j[i][1]
qf = j[i+1][0]
qf_dot = j[i+1][1]
tf = j[i+1][-1]-j[i][-1]
A = np.array([[1,0,0,0],[0,1,0,0],[1,tf,tf**2,tf**3],[0,1,2*tf,3*tf**2]])
B = np.array([[qi],[qi_dot],[qf],[qf_dot]])
coeffs = np.linalg.solve(A,B)
coeffs = np.append(coeffs,tf)
self.cubicCoeffs[num].append(coeffs)
def quinticInterpolate(self):
#same as cubic but with continuous accelerations
#modify joints from [position,time] to [position,velocity,accel,time]
self.quinticCoeffs = []
for j in self.joints:
self.quinticCoeffs.append([])
#null starting and ending velocity and acceleration
for j in self.joints:
for i in range(len(j)):
j[i].insert(1,0)
j[i].insert(1,0)
#compute intermediate velocities
#if the last and next velocities are of different sign, make
#velocity = 0 at that point.
for j in self.joints:
for i in range(1,len(j)-1):
#(pos-lastPos)/(tdiffLast)
velBefore = (j[i][0]-j[i-1][0])/(j[i][-1]-j[i-1][-1])
#(nextPos-pos)/(tdiffNext)
velAfter = (j[i+1][0]-j[i][0])/(j[i+1][-1]-j[i][-1])
if sign(velBefore) == sign(velAfter):
intermediateVelocity = (1/2)*(velBefore+velAfter)
else:
intermediateVelocity = 0
j[i][1] = intermediateVelocity
#compute intermediate accelerations
#if the last an next accel are diff sign, make accel = 0
#at midpoint
for j in self.joints:
for i in range(1,len(j)-1):
#(vel-lastVel)/(tdiffLast)
accelBefore = (j[i][1]-j[i-1][1])/(j[i][-1]-j[i-1][-1])
#(nextVel-vel)/(tdiffNext)
accelAfter = (j[i+1][1]-j[i][1])/(j[i+1][-1]-j[i][-1])
if sign(accelBefore) == sign(accelAfter):
intermediateAccel = (1/2)*(accelBefore+accelAfter)
else:
intermediateAccel = 0
j[i][2] = intermediateAccel
#now, do the interpolation
#treat each points starting time as 0, simpler math
for num,j in enumerate(self.joints):
for i in range(len(j)-1):
#calculate a3...a0 for the cubic polynomial between
#each pair of points
qi = j[i][0]
qi_dot = j[i][1]
qi_ddot = j[i][2]
qf = j[i+1][0]
qf_dot = j[i+1][1]
qf_ddot = j[i+1][2]
tf = j[i+1][-1]-j[i][-1]
A = np.array([[1,0,0,0,0,0],[0,1,0,0,0,0],[0,0,2,0,0,0],[1,tf,tf**2,tf**3,tf**4,tf**5],[0,1,2*tf,3*tf**2,4*tf**3,5*tf**4],[0,0,2,6*tf,12*tf**2,20*tf**3]])
B = np.array([[qi],[qi_dot],[qi_ddot],[qf],[qf_dot],[qf_ddot]])
coeffs = np.linalg.solve(A,B)
coeffs = np.append(coeffs,tf)
self.quinticCoeffs[num].append(coeffs)
def calcOutputs(self,Ts):
#calculate p,v,a outputs for each joint
#sample polynomials with time interval Ts
#self.outputs is a list, [[[j1_pi,j1_vi,j1_ai],...]...]
self.outputs = []
for j in self.joints:
self.outputs.append([])
if self.method == None:
print("Paths have not been interpolated")
return
elif self.method == "cubic":
i = 0
for joint in self.cubicCoeffs:
for j, c in enumerate(joint):
if j == 0:
#create 1st value
_t = 0
p = c[0] + c[1]*_t + c[2]*_t**2 + c[3]*_t**3
v = c[1] + 2*c[2]*_t + 3*c[3]*_t**2
a = 2*c[2] + 6*c[3]*_t
self.outputs[i].append([p,v,a])
for _t in np.arange(Ts,c[-1]+Ts,Ts):
p = c[0] + c[1]*_t + c[2]*_t**2 + c[3]*_t**3
v = c[1] + 2*c[2]*_t + 3*c[3]*_t**2
a = 2*c[2] + 6*c[3]*_t
self.outputs[i].append([p,v,a])
i = i + 1
elif self.method == "quintic":
i = 0
for joint in self.quinticCoeffs:
for j, c in enumerate(joint):
if j == 0:
#create 1st value
_t = 0
p = c[0] + c[1]*_t + c[2]*_t**2 + c[3]*_t**3 + c[4]*_t**4 + c[5]*_t**5
v = c[1] + 2*c[2]*_t + 3*c[3]*_t**2 + 4*c[4]*_t**3 + 5*c[5]*_t**4
a = 2*c[2] + 6*c[3]*_t + 12*c[4]*_t**2 + 20*c[5]*_t**3
self.outputs[i].append([p,v,a])
for _t in np.arange(Ts,c[-1]+Ts,Ts):
p = c[0] + c[1]*_t + c[2]*_t**2 + c[3]*_t**3 + c[4]*_t**4 + c[5]*_t**5
v = c[1] + 2*c[2]*_t + 3*c[3]*_t**2 + 4*c[4]*_t**3 + 5*c[5]*_t**4
a = 2*c[2] + 6*c[3]*_t + 12*c[4]*_t**2 + 20*c[5]*_t**3
self.outputs[i].append([p,v,a])
i = i + 1
elif self.method == "trapVel":
pass
else:
return
def sign(num):
if num > 0:
return 1
elif num < 0:
return -1
else:
return 0
def speedChange(waypoints, multiplier):
newPoints = []
for i in range(len(waypoints)):
newPoints.append((waypoints[i][0],waypoints[i][1],waypoints[i][2]*multiplier))
return newPoints
if __name__ == "__main__":
#example usage
#create a wayPoints class and parse a file of waypoints
newWP = wayPoints()
newWP.readFromFile('circle.csv')
print("Waypoints: " + str(newWP.waypoints))
#instantiate a trajectoryPlanner with the correct number of joints
tp = trajectoryPlanner(2)
#parse the waypoints and generate the polynomials to interpolate
#between them
tp.waypointsParse(newWP.waypoints, "cubic")
#sample the polynomial sequence at the desired period
tp.calcOutputs(0.01)
#tp.outputs[0] is the sequence of P,V,A values for joint 0
#tp.outputs[1] is the same for joint 1
print("Outputs")
print(tp.outputs[1])