blob: 315b402bc01152df9897de44d9b12b5263e5b6fa [file] [log] [blame]
Austin Schuhc8ca2442013-02-23 12:29:33 -08001#!/usr/bin/python
2
Austin Schuh3c542312013-02-24 01:53:50 -08003import control_loop
Austin Schuh0d9467a2014-02-15 22:36:45 -08004import controls
Austin Schuhc8ca2442013-02-23 12:29:33 -08005import numpy
Austin Schuhc8ca2442013-02-23 12:29:33 -08006import sys
Austin Schuhc8ca2442013-02-23 12:29:33 -08007from matplotlib import pylab
Austin Schuhc8ca2442013-02-23 12:29:33 -08008
Austin Schuh3bb9a442014-02-02 16:01:45 -08009class Claw(control_loop.ControlLoop):
10 def __init__(self, name="RawClaw"):
11 super(Claw, self).__init__(name)
Austin Schuhc8ca2442013-02-23 12:29:33 -080012 # Stall Torque in N m
James Kuszmaule1755b32014-02-13 06:27:48 -080013 self.stall_torque = 2.42
Austin Schuhc8ca2442013-02-23 12:29:33 -080014 # Stall Current in Amps
James Kuszmaule1755b32014-02-13 06:27:48 -080015 self.stall_current = 133
James Kuszmaul92797402014-02-17 14:08:49 -080016 # Free Speed in RPM
17 self.free_speed = 5500.0
Austin Schuh3c542312013-02-24 01:53:50 -080018 # Free Current in Amps
James Kuszmaule1755b32014-02-13 06:27:48 -080019 self.free_current = 2.7
Austin Schuh3bb9a442014-02-02 16:01:45 -080020 # Moment of inertia of the claw in kg m^2
James Kuszmaul92797402014-02-17 14:08:49 -080021 # measured from CAD
22 self.J_top = 0.3
23 self.J_bottom = 0.9
Austin Schuhc8ca2442013-02-23 12:29:33 -080024 # Resistance of the motor
James Kuszmaul92797402014-02-17 14:08:49 -080025 self.R = 12.0 / self.stall_current
Austin Schuhc8ca2442013-02-23 12:29:33 -080026 # Motor velocity constant
Austin Schuh3c542312013-02-24 01:53:50 -080027 self.Kv = ((self.free_speed / 60.0 * 2.0 * numpy.pi) /
28 (13.5 - self.R * self.free_current))
Austin Schuhc8ca2442013-02-23 12:29:33 -080029 # Torque constant
30 self.Kt = self.stall_torque / self.stall_current
31 # Gear ratio
James Kuszmaule1755b32014-02-13 06:27:48 -080032 self.G = 14.0 / 48.0 * 18.0 / 32.0 * 18.0 / 66.0 * 12.0 / 60.0
Austin Schuhc8ca2442013-02-23 12:29:33 -080033 # Control loop time step
34 self.dt = 0.01
35
Austin Schuh0d9467a2014-02-15 22:36:45 -080036 # State is [bottom position, top - bottom position,
37 # bottom velocity, top - bottom velocity]
James Kuszmaul92797402014-02-17 14:08:49 -080038 # Input is [bottom power, top - bottom power]
39 # Motor time constants. difference_bottom refers to the constant for how the
40 # bottom velocity affects the difference of the top and bottom velocities.
41 self.common_motor_constant = -self.Kt / self.Kv / (self.G * self.G * self.R)
42 self.bottom_bottom = self.common_motor_constant / self.J_bottom
43 self.difference_bottom = self.common_motor_constant * (1 / self.J_bottom
44 - 1 / self.J_top)
45 self.difference_difference = self.common_motor_constant / self.J_top
Austin Schuhc8ca2442013-02-23 12:29:33 -080046 # State feedback matrices
47 self.A_continuous = numpy.matrix(
Austin Schuh0d9467a2014-02-15 22:36:45 -080048 [[0, 0, 1, 0],
49 [0, 0, 0, 1],
James Kuszmaul92797402014-02-17 14:08:49 -080050 [0, 0, self.bottom_bottom, 0],
51 [0, 0, self.difference_bottom, self.difference_difference]])
Austin Schuh0d9467a2014-02-15 22:36:45 -080052
James Kuszmaul92797402014-02-17 14:08:49 -080053 self.motor_feedforward = self.Kt / (self.G * self.R)
James Kuszmaulf2ed6e62014-02-17 17:52:07 -080054 self.motor_feedforward_bottom = self.motor_feedforward / self.J_bottom
55 self.motor_feedforward_difference = self.motor_feedforward / self.J_top
James Kuszmaul92797402014-02-17 14:08:49 -080056 self.motor_feedforward_difference_bottom = (
57 self.motor_feedforward * (1 / self.J_bottom - 1 / self.J_top))
Austin Schuhc8ca2442013-02-23 12:29:33 -080058 self.B_continuous = numpy.matrix(
Austin Schuh0d9467a2014-02-15 22:36:45 -080059 [[0, 0],
60 [0, 0],
James Kuszmaul92797402014-02-17 14:08:49 -080061 [self.motor_feedforward_bottom, 0],
62 [self.motor_feedforward_difference_bottom,
63 self.motor_feedforward_difference]])
Austin Schuh0d9467a2014-02-15 22:36:45 -080064 self.C = numpy.matrix([[1, 0, 0, 0],
65 [1, 1, 0, 0]])
66 self.D = numpy.matrix([[0, 0],
67 [0, 0]])
Austin Schuhc8ca2442013-02-23 12:29:33 -080068
Austin Schuhc1f68892013-03-16 17:06:27 -070069 self.A, self.B = self.ContinuousToDiscrete(
70 self.A_continuous, self.B_continuous, self.dt)
Austin Schuhc8ca2442013-02-23 12:29:33 -080071
Austin Schuh0d9467a2014-02-15 22:36:45 -080072 #controlability = controls.ctrb(self.A, self.B);
73 #print "Rank of controlability matrix.", numpy.linalg.matrix_rank(controlability)
74
James Kuszmaulf2ed6e62014-02-17 17:52:07 -080075 self.Q = numpy.matrix([[(1.0 / (0.40 ** 2.0)), 0.0, 0.0, 0.0],
76 [0.0, (1.0 / (0.007 ** 2.0)), 0.0, 0.0],
Austin Schuhcda86af2014-02-16 16:16:39 -080077 [0.0, 0.0, 0.2, 0.0],
78 [0.0, 0.0, 0.0, 2.00]])
79
James Kuszmaulf2ed6e62014-02-17 17:52:07 -080080 self.R = numpy.matrix([[(1.0 / (40.0 ** 2.0)), 0.0],
81 [0.0, (1.0 / (5.0 ** 2.0))]])
Austin Schuhcda86af2014-02-16 16:16:39 -080082 self.K = controls.dlqr(self.A, self.B, self.Q, self.R)
83
84 print "K unaugmented"
85 print self.K
Austin Schuhc8ca2442013-02-23 12:29:33 -080086
87 self.rpl = .05
88 self.ipl = 0.008
Austin Schuh3c542312013-02-24 01:53:50 -080089 self.PlaceObserverPoles([self.rpl + 1j * self.ipl,
Austin Schuh0d9467a2014-02-15 22:36:45 -080090 self.rpl - 1j * self.ipl,
91 self.rpl + 1j * self.ipl,
Austin Schuh3c542312013-02-24 01:53:50 -080092 self.rpl - 1j * self.ipl])
Austin Schuhc8ca2442013-02-23 12:29:33 -080093
James Kuszmaul92797402014-02-17 14:08:49 -080094 # The box formed by U_min and U_max must encompass all possible values,
95 # or else Austin's code gets angry.
Austin Schuhcda86af2014-02-16 16:16:39 -080096 self.U_max = numpy.matrix([[12.0], [24.0]])
97 self.U_min = numpy.matrix([[-12.0], [-24.0]])
Austin Schuhc8ca2442013-02-23 12:29:33 -080098
Austin Schuhc1f68892013-03-16 17:06:27 -070099 self.InitializeState()
100
101
Austin Schuh3bb9a442014-02-02 16:01:45 -0800102class ClawDeltaU(Claw):
103 def __init__(self, name="Claw"):
104 super(ClawDeltaU, self).__init__(name)
Austin Schuhc1f68892013-03-16 17:06:27 -0700105 A_unaugmented = self.A
106 B_unaugmented = self.B
Austin Schuh0d9467a2014-02-15 22:36:45 -0800107 C_unaugmented = self.C
Austin Schuhc1f68892013-03-16 17:06:27 -0700108
Austin Schuh0d9467a2014-02-15 22:36:45 -0800109 self.A = numpy.matrix([[0.0, 0.0, 0.0, 0.0, 0.0],
110 [0.0, 0.0, 0.0, 0.0, 0.0],
111 [0.0, 0.0, 0.0, 0.0, 0.0],
112 [0.0, 0.0, 0.0, 0.0, 0.0],
113 [0.0, 0.0, 0.0, 0.0, 1.0]])
114 self.A[0:4, 0:4] = A_unaugmented
115 self.A[0:4, 4] = B_unaugmented[0:4, 0]
Austin Schuhc1f68892013-03-16 17:06:27 -0700116
Austin Schuh0d9467a2014-02-15 22:36:45 -0800117 self.B = numpy.matrix([[0.0, 0.0],
118 [0.0, 0.0],
119 [0.0, 0.0],
120 [0.0, 0.0],
121 [1.0, 0.0]])
122 self.B[0:4, 1] = B_unaugmented[0:4, 1]
Austin Schuhc1f68892013-03-16 17:06:27 -0700123
Austin Schuh0d9467a2014-02-15 22:36:45 -0800124 self.C = numpy.concatenate((C_unaugmented, numpy.matrix([[0.0], [0.0]])),
125 axis=1)
126 self.D = numpy.matrix([[0.0, 0.0],
127 [0.0, 0.0]])
Austin Schuhc1f68892013-03-16 17:06:27 -0700128
Austin Schuh0d9467a2014-02-15 22:36:45 -0800129 #self.PlaceControllerPoles([0.55, 0.35, 0.55, 0.35, 0.80])
130 self.Q = numpy.matrix([[(1.0 / (0.04 ** 2.0)), 0.0, 0.0, 0.0, 0.0],
131 [0.0, (1.0 / (0.01 ** 2)), 0.0, 0.0, 0.0],
132 [0.0, 0.0, 0.01, 0.0, 0.0],
133 [0.0, 0.0, 0.0, 0.08, 0.0],
134 [0.0, 0.0, 0.0, 0.0, (1.0 / (10.0 ** 2))]])
135
136 self.R = numpy.matrix([[0.000001, 0.0],
137 [0.0, 1.0 / (10.0 ** 2.0)]])
138 self.K = controls.dlqr(self.A, self.B, self.Q, self.R)
139
140 self.K = numpy.matrix([[50.0, 0.0, 10.0, 0.0, 1.0],
141 [50.0, 0.0, 10.0, 0.0, 1.0]])
142 #self.K = numpy.matrix([[50.0, -100.0, 0, -10, 0],
143 # [50.0, 100.0, 0, 10, 0]])
144
145 controlability = controls.ctrb(self.A, self.B);
146 print "Rank of augmented controlability matrix.", numpy.linalg.matrix_rank(controlability)
Austin Schuhc1f68892013-03-16 17:06:27 -0700147
148 print "K"
149 print self.K
150 print "Placed controller poles are"
151 print numpy.linalg.eig(self.A - self.B * self.K)[0]
Austin Schuh0d9467a2014-02-15 22:36:45 -0800152 print [numpy.abs(x) for x in numpy.linalg.eig(self.A - self.B * self.K)[0]]
Austin Schuhc1f68892013-03-16 17:06:27 -0700153
154 self.rpl = .05
155 self.ipl = 0.008
Austin Schuh0d9467a2014-02-15 22:36:45 -0800156 self.PlaceObserverPoles([self.rpl + 1j * self.ipl, 0.10, 0.09,
Brian Silverman23a67ca2013-03-16 23:48:50 -0700157 self.rpl - 1j * self.ipl, 0.90])
Austin Schuh0d9467a2014-02-15 22:36:45 -0800158 #print "A is"
159 #print self.A
160 #print "L is"
161 #print self.L
162 #print "C is"
163 #print self.C
164 #print "A - LC is"
165 #print self.A - self.L * self.C
Austin Schuhc1f68892013-03-16 17:06:27 -0700166
Austin Schuh0d9467a2014-02-15 22:36:45 -0800167 #print "Placed observer poles are"
168 #print numpy.linalg.eig(self.A - self.L * self.C)[0]
169
170 self.U_max = numpy.matrix([[12.0], [12.0]])
171 self.U_min = numpy.matrix([[-12.0], [-12.0]])
Austin Schuhc1f68892013-03-16 17:06:27 -0700172
173 self.InitializeState()
174
175
Austin Schuhcda86af2014-02-16 16:16:39 -0800176def FullSeparationPriority(claw, U):
177 bottom_u = U[0, 0]
178 top_u = U[1, 0] + bottom_u
179
180 #print "Bottom is", new_unclipped_bottom_u, "Top is", top_u
181 if bottom_u > claw.U_max[0, 0]:
182 #print "Bottom is too big. Was", new_unclipped_bottom_u, "changing top by", new_unclipped_bottom_u - claw.U_max[0, 0]
183 top_u -= bottom_u - claw.U_max[0, 0]
184 if top_u < claw.U_min[1, 0]:
185 top_u = claw.U_min[1, 0]
186
187 bottom_u = claw.U_max[0, 0]
188 if top_u > claw.U_max[1, 0]:
189 bottom_u -= top_u - claw.U_max[1, 0]
190 if bottom_u < claw.U_min[0, 0]:
191 bottom_u = claw.U_min[0, 0]
192
193 top_u = claw.U_max[1, 0]
194 if top_u < claw.U_min[1, 0]:
195 bottom_u -= top_u - claw.U_min[1, 0]
196 if bottom_u > claw.U_max[0, 0]:
197 bottom_u = claw.U_max[0, 0]
198
199 top_u = claw.U_min[1, 0]
200 if bottom_u < claw.U_min[0, 0]:
201 top_u -= bottom_u - claw.U_min[0, 0]
202 if top_u > claw.U_max[1, 0]:
203 top_u = claw.U_max[1, 0]
204
205 bottom_u = claw.U_min[0, 0]
206
207 return numpy.matrix([[bottom_u], [top_u - bottom_u]])
208
209def AverageUFix(claw, U):
210 bottom_u = U[0, 0]
James Kuszmaulf2ed6e62014-02-17 17:52:07 -0800211 top_u = bottom_u + U[1, 0]
212#top_u = claw.J_top * (bottom_u / claw.J_bottom - U[1, 0])
Austin Schuhcda86af2014-02-16 16:16:39 -0800213
214 #print "Bottom is", new_unclipped_bottom_u, "Top is", top_u
James Kuszmaul92797402014-02-17 14:08:49 -0800215 if (bottom_u > claw.U_max[0, 0] or top_u > claw.U_max[0, 0] or
216 top_u < claw.U_min[0, 0] or bottom_u < claw.U_min[0, 0]):
Austin Schuhcda86af2014-02-16 16:16:39 -0800217 scalar = 12.0 / max(numpy.abs(top_u), numpy.abs(bottom_u))
218 top_u *= scalar
219 bottom_u *= scalar
220
221 return numpy.matrix([[bottom_u], [top_u - bottom_u]])
James Kuszmaulf2ed6e62014-02-17 17:52:07 -0800222 #return numpy.matrix([[bottom_u], [bottom_u / claw.J_bottom - top_u / claw.J_top]])
Austin Schuhcda86af2014-02-16 16:16:39 -0800223
Austin Schuh0d9467a2014-02-15 22:36:45 -0800224def ClipDeltaU(claw, U):
225 delta_u = U[0, 0]
226 top_u = U[1, 0]
227 old_bottom_u = claw.X[4, 0]
228
229 # TODO(austin): Preserve the difference between the top and bottom power.
230 new_unclipped_bottom_u = old_bottom_u + delta_u
231
232 #print "Bottom is", new_unclipped_bottom_u, "Top is", top_u
233 if new_unclipped_bottom_u > claw.U_max[0, 0]:
234 #print "Bottom is too big. Was", new_unclipped_bottom_u, "changing top by", new_unclipped_bottom_u - claw.U_max[0, 0]
235 top_u -= new_unclipped_bottom_u - claw.U_max[0, 0]
236 new_unclipped_bottom_u = claw.U_max[0, 0]
237 if top_u > claw.U_max[1, 0]:
238 new_unclipped_bottom_u -= top_u - claw.U_max[1, 0]
239 top_u = claw.U_max[1, 0]
240 if top_u < claw.U_min[1, 0]:
241 new_unclipped_bottom_u -= top_u - claw.U_min[1, 0]
242 top_u = claw.U_min[1, 0]
243 if new_unclipped_bottom_u < claw.U_min[0, 0]:
244 top_u -= new_unclipped_bottom_u - claw.U_min[0, 0]
245 new_unclipped_bottom_u = claw.U_min[0, 0]
246
247 new_bottom_u = numpy.clip(new_unclipped_bottom_u, claw.U_min[0, 0], claw.U_max[0, 0])
248 new_top_u = numpy.clip(top_u, claw.U_min[1, 0], claw.U_max[1, 0])
249
250 return numpy.matrix([[new_bottom_u - old_bottom_u], [new_top_u]])
Austin Schuhc1f68892013-03-16 17:06:27 -0700251
Austin Schuhc8ca2442013-02-23 12:29:33 -0800252def main(argv):
Austin Schuh3c542312013-02-24 01:53:50 -0800253 # Simulate the response of the system to a step input.
Austin Schuh0d9467a2014-02-15 22:36:45 -0800254 #claw = ClawDeltaU()
255 #simulated_x = []
256 #for _ in xrange(100):
257 # claw.Update(numpy.matrix([[12.0]]))
258 # simulated_x.append(claw.X[0, 0])
Austin Schuhc8ca2442013-02-23 12:29:33 -0800259
Austin Schuh0d9467a2014-02-15 22:36:45 -0800260 #pylab.plot(range(100), simulated_x)
261 #pylab.show()
Austin Schuhc8ca2442013-02-23 12:29:33 -0800262
James Kuszmaulf2ed6e62014-02-17 17:52:07 -0800263 # Simulate the closed loop response of the system.
Austin Schuhcda86af2014-02-16 16:16:39 -0800264 claw = Claw("TopClaw")
265 t = []
Austin Schuh0d9467a2014-02-15 22:36:45 -0800266 close_loop_x_bottom = []
267 close_loop_x_sep = []
James Kuszmaulf2ed6e62014-02-17 17:52:07 -0800268 close_loop_x_top = []
Austin Schuh0d9467a2014-02-15 22:36:45 -0800269 close_loop_u_bottom = []
270 close_loop_u_top = []
James Kuszmaulf2ed6e62014-02-17 17:52:07 -0800271 R = numpy.matrix([[1.1], [0.05], [0.0], [0.0]])
Austin Schuhcda86af2014-02-16 16:16:39 -0800272 claw.X[0, 0] = 0
James Kuszmaulf2ed6e62014-02-17 17:52:07 -0800273 claw.X[1, 0] = .05
Austin Schuhcda86af2014-02-16 16:16:39 -0800274 for i in xrange(100):
275 #print "Error is", (R - claw.X_hat)
276 U = claw.K * (R - claw.X_hat)
277 #U = numpy.clip(claw.K * (R - claw.X_hat), claw.U_min, claw.U_max)
278 #U = FullSeparationPriority(claw, U)
279 U = AverageUFix(claw, U)
280 #U = claw.K * (R - claw.X_hat)
281 #U = ClipDeltaU(claw, U)
282 claw.UpdateObserver(U)
283 claw.Update(U)
284 close_loop_x_bottom.append(claw.X[0, 0] * 10)
285 close_loop_u_bottom.append(U[0, 0])
James Kuszmaulf2ed6e62014-02-17 17:52:07 -0800286 close_loop_x_sep.append(claw.X[1, 0] * 100)
287 close_loop_x_top.append((claw.X[1, 0] + claw.X[0, 0]) * 10)
Austin Schuhcda86af2014-02-16 16:16:39 -0800288 close_loop_u_top.append(U[1, 0] + U[0, 0])
289 t.append(0.01 * i)
Austin Schuhc8ca2442013-02-23 12:29:33 -0800290
Austin Schuhcda86af2014-02-16 16:16:39 -0800291 pylab.plot(t, close_loop_x_bottom, label='x bottom')
James Kuszmaulf2ed6e62014-02-17 17:52:07 -0800292 pylab.plot(t, close_loop_x_sep, label='separation')
293 pylab.plot(t, close_loop_x_top, label='x top')
Austin Schuhcda86af2014-02-16 16:16:39 -0800294 pylab.plot(t, close_loop_u_bottom, label='u bottom')
295 pylab.plot(t, close_loop_u_top, label='u top')
Austin Schuh0d9467a2014-02-15 22:36:45 -0800296 pylab.legend()
Austin Schuhfa033692013-02-24 01:00:55 -0800297 pylab.show()
Austin Schuhc8ca2442013-02-23 12:29:33 -0800298
Austin Schuh3c542312013-02-24 01:53:50 -0800299 # Write the generated constants out to a file.
Austin Schuhcda86af2014-02-16 16:16:39 -0800300 if len(argv) != 3:
301 print "Expected .h file name and .cc file name for the claw."
Austin Schuhc8ca2442013-02-23 12:29:33 -0800302 else:
Austin Schuhcda86af2014-02-16 16:16:39 -0800303 claw = Claw("Claw")
304 loop_writer = control_loop.ControlLoopWriter("Claw", [claw])
Austin Schuh683a0d02013-03-02 01:51:31 -0800305 if argv[1][-3:] == '.cc':
Austin Schuhcda86af2014-02-16 16:16:39 -0800306 loop_writer.Write(argv[2], argv[1])
Austin Schuh683a0d02013-03-02 01:51:31 -0800307 else:
Austin Schuhcda86af2014-02-16 16:16:39 -0800308 loop_writer.Write(argv[1], argv[2])
Austin Schuhc8ca2442013-02-23 12:29:33 -0800309
310if __name__ == '__main__':
311 sys.exit(main(sys.argv))