blob: 73039db0c449fcd4bcc36a1407edfb7668f608b5 [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
16 # Free Speed in RPM, pulled from drivetrain
Austin Schuh01c652b2014-02-21 23:13:42 -080017 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 Kuszmaule1755b32014-02-13 06:27:48 -080021 # approzimately 0.76 (without ball) in CAD
Austin Schuh01c652b2014-02-21 23:13:42 -080022 self.J = 0.70
Austin Schuhc8ca2442013-02-23 12:29:33 -080023 # Resistance of the motor
24 self.R = 12.0 / self.stall_current + 0.024 + .003
25 # Motor velocity constant
Austin Schuh3c542312013-02-24 01:53:50 -080026 self.Kv = ((self.free_speed / 60.0 * 2.0 * numpy.pi) /
27 (13.5 - self.R * self.free_current))
Austin Schuhc8ca2442013-02-23 12:29:33 -080028 # Torque constant
29 self.Kt = self.stall_torque / self.stall_current
30 # Gear ratio
James Kuszmaule1755b32014-02-13 06:27:48 -080031 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 -080032 # Control loop time step
33 self.dt = 0.01
34
Austin Schuh0d9467a2014-02-15 22:36:45 -080035 # State is [bottom position, top - bottom position,
36 # bottom velocity, top - bottom velocity]
37 # Input is [bottom power, top power]
38 # Motor time constant.
39 self.motor_timeconstant = self.Kt / self.Kv / (self.J * self.G * self.G * self.R)
Austin Schuhc8ca2442013-02-23 12:29:33 -080040 # State feedback matrices
41 self.A_continuous = numpy.matrix(
Austin Schuh0d9467a2014-02-15 22:36:45 -080042 [[0, 0, 1, 0],
43 [0, 0, 0, 1],
44 [0, 0, -self.motor_timeconstant, 0],
45 [0, 0, 0, -self.motor_timeconstant]])
46
47 self.motor_feedforward = self.Kt / (self.J * self.G * self.R)
48
Austin Schuhc8ca2442013-02-23 12:29:33 -080049 self.B_continuous = numpy.matrix(
Austin Schuh0d9467a2014-02-15 22:36:45 -080050 [[0, 0],
51 [0, 0],
52 [self.motor_feedforward, 0],
Austin Schuhcda86af2014-02-16 16:16:39 -080053 [0.0, self.motor_feedforward]])
Austin Schuh0d9467a2014-02-15 22:36:45 -080054 self.C = numpy.matrix([[1, 0, 0, 0],
55 [1, 1, 0, 0]])
56 self.D = numpy.matrix([[0, 0],
57 [0, 0]])
Austin Schuhc8ca2442013-02-23 12:29:33 -080058
Austin Schuhc1f68892013-03-16 17:06:27 -070059 self.A, self.B = self.ContinuousToDiscrete(
60 self.A_continuous, self.B_continuous, self.dt)
Austin Schuhc8ca2442013-02-23 12:29:33 -080061
Austin Schuh0d9467a2014-02-15 22:36:45 -080062 #controlability = controls.ctrb(self.A, self.B);
63 #print "Rank of controlability matrix.", numpy.linalg.matrix_rank(controlability)
64
Austin Schuhcda86af2014-02-16 16:16:39 -080065 self.Q = numpy.matrix([[(1.0 / (0.10 ** 2.0)), 0.0, 0.0, 0.0],
Austin Schuh01c652b2014-02-21 23:13:42 -080066 [0.0, (1.0 / (0.06 ** 2.0)), 0.0, 0.0],
67 [0.0, 0.0, 0.10, 0.0],
68 [0.0, 0.0, 0.0, 0.1]])
Austin Schuhcda86af2014-02-16 16:16:39 -080069
70 self.R = numpy.matrix([[(1.0 / (20.0 ** 2.0)), 0.0],
71 [0.0, (1.0 / (20.0 ** 2.0))]])
72 self.K = controls.dlqr(self.A, self.B, self.Q, self.R)
73
74 print "K unaugmented"
75 print self.K
Austin Schuh01c652b2014-02-21 23:13:42 -080076 print "Placed controller poles"
77 print numpy.linalg.eig(self.A - self.B * self.K)[0]
Austin Schuhc8ca2442013-02-23 12:29:33 -080078
79 self.rpl = .05
80 self.ipl = 0.008
Austin Schuh3c542312013-02-24 01:53:50 -080081 self.PlaceObserverPoles([self.rpl + 1j * self.ipl,
Austin Schuh0d9467a2014-02-15 22:36:45 -080082 self.rpl - 1j * self.ipl,
83 self.rpl + 1j * self.ipl,
Austin Schuh3c542312013-02-24 01:53:50 -080084 self.rpl - 1j * self.ipl])
Austin Schuhc8ca2442013-02-23 12:29:33 -080085
Austin Schuhcda86af2014-02-16 16:16:39 -080086 self.U_max = numpy.matrix([[12.0], [24.0]])
87 self.U_min = numpy.matrix([[-12.0], [-24.0]])
Austin Schuhc8ca2442013-02-23 12:29:33 -080088
Austin Schuhc1f68892013-03-16 17:06:27 -070089 self.InitializeState()
90
91
Austin Schuh3bb9a442014-02-02 16:01:45 -080092class ClawDeltaU(Claw):
93 def __init__(self, name="Claw"):
94 super(ClawDeltaU, self).__init__(name)
Austin Schuhc1f68892013-03-16 17:06:27 -070095 A_unaugmented = self.A
96 B_unaugmented = self.B
Austin Schuh0d9467a2014-02-15 22:36:45 -080097 C_unaugmented = self.C
Austin Schuhc1f68892013-03-16 17:06:27 -070098
Austin Schuh0d9467a2014-02-15 22:36:45 -080099 self.A = numpy.matrix([[0.0, 0.0, 0.0, 0.0, 0.0],
100 [0.0, 0.0, 0.0, 0.0, 0.0],
101 [0.0, 0.0, 0.0, 0.0, 0.0],
102 [0.0, 0.0, 0.0, 0.0, 0.0],
103 [0.0, 0.0, 0.0, 0.0, 1.0]])
104 self.A[0:4, 0:4] = A_unaugmented
105 self.A[0:4, 4] = B_unaugmented[0:4, 0]
Austin Schuhc1f68892013-03-16 17:06:27 -0700106
Austin Schuh0d9467a2014-02-15 22:36:45 -0800107 self.B = numpy.matrix([[0.0, 0.0],
108 [0.0, 0.0],
109 [0.0, 0.0],
110 [0.0, 0.0],
111 [1.0, 0.0]])
112 self.B[0:4, 1] = B_unaugmented[0:4, 1]
Austin Schuhc1f68892013-03-16 17:06:27 -0700113
Austin Schuh0d9467a2014-02-15 22:36:45 -0800114 self.C = numpy.concatenate((C_unaugmented, numpy.matrix([[0.0], [0.0]])),
115 axis=1)
116 self.D = numpy.matrix([[0.0, 0.0],
117 [0.0, 0.0]])
Austin Schuhc1f68892013-03-16 17:06:27 -0700118
Austin Schuh0d9467a2014-02-15 22:36:45 -0800119 #self.PlaceControllerPoles([0.55, 0.35, 0.55, 0.35, 0.80])
120 self.Q = numpy.matrix([[(1.0 / (0.04 ** 2.0)), 0.0, 0.0, 0.0, 0.0],
121 [0.0, (1.0 / (0.01 ** 2)), 0.0, 0.0, 0.0],
122 [0.0, 0.0, 0.01, 0.0, 0.0],
123 [0.0, 0.0, 0.0, 0.08, 0.0],
124 [0.0, 0.0, 0.0, 0.0, (1.0 / (10.0 ** 2))]])
125
126 self.R = numpy.matrix([[0.000001, 0.0],
127 [0.0, 1.0 / (10.0 ** 2.0)]])
128 self.K = controls.dlqr(self.A, self.B, self.Q, self.R)
129
130 self.K = numpy.matrix([[50.0, 0.0, 10.0, 0.0, 1.0],
131 [50.0, 0.0, 10.0, 0.0, 1.0]])
132 #self.K = numpy.matrix([[50.0, -100.0, 0, -10, 0],
133 # [50.0, 100.0, 0, 10, 0]])
134
135 controlability = controls.ctrb(self.A, self.B);
136 print "Rank of augmented controlability matrix.", numpy.linalg.matrix_rank(controlability)
Austin Schuhc1f68892013-03-16 17:06:27 -0700137
138 print "K"
139 print self.K
140 print "Placed controller poles are"
141 print numpy.linalg.eig(self.A - self.B * self.K)[0]
Austin Schuh0d9467a2014-02-15 22:36:45 -0800142 print [numpy.abs(x) for x in numpy.linalg.eig(self.A - self.B * self.K)[0]]
Austin Schuhc1f68892013-03-16 17:06:27 -0700143
144 self.rpl = .05
145 self.ipl = 0.008
Austin Schuh0d9467a2014-02-15 22:36:45 -0800146 self.PlaceObserverPoles([self.rpl + 1j * self.ipl, 0.10, 0.09,
Brian Silverman23a67ca2013-03-16 23:48:50 -0700147 self.rpl - 1j * self.ipl, 0.90])
Austin Schuh0d9467a2014-02-15 22:36:45 -0800148 #print "A is"
149 #print self.A
150 #print "L is"
151 #print self.L
152 #print "C is"
153 #print self.C
154 #print "A - LC is"
155 #print self.A - self.L * self.C
Austin Schuhc1f68892013-03-16 17:06:27 -0700156
Austin Schuh0d9467a2014-02-15 22:36:45 -0800157 #print "Placed observer poles are"
158 #print numpy.linalg.eig(self.A - self.L * self.C)[0]
159
160 self.U_max = numpy.matrix([[12.0], [12.0]])
161 self.U_min = numpy.matrix([[-12.0], [-12.0]])
Austin Schuhc1f68892013-03-16 17:06:27 -0700162
163 self.InitializeState()
164
165
Austin Schuhcda86af2014-02-16 16:16:39 -0800166def FullSeparationPriority(claw, U):
167 bottom_u = U[0, 0]
168 top_u = U[1, 0] + bottom_u
169
170 #print "Bottom is", new_unclipped_bottom_u, "Top is", top_u
171 if bottom_u > claw.U_max[0, 0]:
172 #print "Bottom is too big. Was", new_unclipped_bottom_u, "changing top by", new_unclipped_bottom_u - claw.U_max[0, 0]
173 top_u -= bottom_u - claw.U_max[0, 0]
174 if top_u < claw.U_min[1, 0]:
175 top_u = claw.U_min[1, 0]
176
177 bottom_u = claw.U_max[0, 0]
178 if top_u > claw.U_max[1, 0]:
179 bottom_u -= top_u - claw.U_max[1, 0]
180 if bottom_u < claw.U_min[0, 0]:
181 bottom_u = claw.U_min[0, 0]
182
183 top_u = claw.U_max[1, 0]
184 if top_u < claw.U_min[1, 0]:
185 bottom_u -= top_u - claw.U_min[1, 0]
186 if bottom_u > claw.U_max[0, 0]:
187 bottom_u = claw.U_max[0, 0]
188
189 top_u = claw.U_min[1, 0]
190 if bottom_u < claw.U_min[0, 0]:
191 top_u -= bottom_u - claw.U_min[0, 0]
192 if top_u > claw.U_max[1, 0]:
193 top_u = claw.U_max[1, 0]
194
195 bottom_u = claw.U_min[0, 0]
196
197 return numpy.matrix([[bottom_u], [top_u - bottom_u]])
198
199def AverageUFix(claw, U):
200 bottom_u = U[0, 0]
201 top_u = U[1, 0] + bottom_u
202
203 #print "Bottom is", new_unclipped_bottom_u, "Top is", top_u
204 if (bottom_u > claw.U_max[0, 0] or top_u > claw.U_max[1, 0] or
205 top_u < claw.U_min[1, 0] or bottom_u < claw.U_min[0, 0]):
206 scalar = 12.0 / max(numpy.abs(top_u), numpy.abs(bottom_u))
207 top_u *= scalar
208 bottom_u *= scalar
209
210 return numpy.matrix([[bottom_u], [top_u - bottom_u]])
211
Austin Schuh0d9467a2014-02-15 22:36:45 -0800212def ClipDeltaU(claw, U):
213 delta_u = U[0, 0]
214 top_u = U[1, 0]
215 old_bottom_u = claw.X[4, 0]
216
217 # TODO(austin): Preserve the difference between the top and bottom power.
218 new_unclipped_bottom_u = old_bottom_u + delta_u
219
220 #print "Bottom is", new_unclipped_bottom_u, "Top is", top_u
221 if new_unclipped_bottom_u > claw.U_max[0, 0]:
222 #print "Bottom is too big. Was", new_unclipped_bottom_u, "changing top by", new_unclipped_bottom_u - claw.U_max[0, 0]
223 top_u -= new_unclipped_bottom_u - claw.U_max[0, 0]
224 new_unclipped_bottom_u = claw.U_max[0, 0]
225 if top_u > claw.U_max[1, 0]:
226 new_unclipped_bottom_u -= top_u - claw.U_max[1, 0]
227 top_u = claw.U_max[1, 0]
228 if top_u < claw.U_min[1, 0]:
229 new_unclipped_bottom_u -= top_u - claw.U_min[1, 0]
230 top_u = claw.U_min[1, 0]
231 if new_unclipped_bottom_u < claw.U_min[0, 0]:
232 top_u -= new_unclipped_bottom_u - claw.U_min[0, 0]
233 new_unclipped_bottom_u = claw.U_min[0, 0]
234
235 new_bottom_u = numpy.clip(new_unclipped_bottom_u, claw.U_min[0, 0], claw.U_max[0, 0])
236 new_top_u = numpy.clip(top_u, claw.U_min[1, 0], claw.U_max[1, 0])
237
238 return numpy.matrix([[new_bottom_u - old_bottom_u], [new_top_u]])
Austin Schuhc1f68892013-03-16 17:06:27 -0700239
Austin Schuhc8ca2442013-02-23 12:29:33 -0800240def main(argv):
Austin Schuh3c542312013-02-24 01:53:50 -0800241 # Simulate the response of the system to a step input.
Austin Schuh0d9467a2014-02-15 22:36:45 -0800242 #claw = ClawDeltaU()
243 #simulated_x = []
244 #for _ in xrange(100):
245 # claw.Update(numpy.matrix([[12.0]]))
246 # simulated_x.append(claw.X[0, 0])
Austin Schuhc8ca2442013-02-23 12:29:33 -0800247
Austin Schuh0d9467a2014-02-15 22:36:45 -0800248 #pylab.plot(range(100), simulated_x)
249 #pylab.show()
Austin Schuhc8ca2442013-02-23 12:29:33 -0800250
Austin Schuh3c542312013-02-24 01:53:50 -0800251 # Simulate the closed loop response of the system to a step input.
Austin Schuhcda86af2014-02-16 16:16:39 -0800252 claw = Claw("TopClaw")
253 t = []
Austin Schuh0d9467a2014-02-15 22:36:45 -0800254 close_loop_x_bottom = []
255 close_loop_x_sep = []
256 close_loop_u_bottom = []
257 close_loop_u_top = []
Austin Schuhcda86af2014-02-16 16:16:39 -0800258 R = numpy.matrix([[1.0], [1.0], [0.0], [0.0]])
259 claw.X[0, 0] = 0
260 for i in xrange(100):
261 #print "Error is", (R - claw.X_hat)
262 U = claw.K * (R - claw.X_hat)
263 #U = numpy.clip(claw.K * (R - claw.X_hat), claw.U_min, claw.U_max)
264 #U = FullSeparationPriority(claw, U)
265 U = AverageUFix(claw, U)
266 #U = claw.K * (R - claw.X_hat)
267 #U = ClipDeltaU(claw, U)
268 claw.UpdateObserver(U)
269 claw.Update(U)
270 close_loop_x_bottom.append(claw.X[0, 0] * 10)
271 close_loop_u_bottom.append(U[0, 0])
272 close_loop_x_sep.append(claw.X[1, 0] * 10)
273 close_loop_u_top.append(U[1, 0] + U[0, 0])
274 t.append(0.01 * i)
Austin Schuhc8ca2442013-02-23 12:29:33 -0800275
Austin Schuhcda86af2014-02-16 16:16:39 -0800276 pylab.plot(t, close_loop_x_bottom, label='x bottom')
Brian Silverman7c021c42014-02-17 15:15:56 -0800277 pylab.plot(t, close_loop_x_sep, label='separation')
Austin Schuhcda86af2014-02-16 16:16:39 -0800278 pylab.plot(t, close_loop_u_bottom, label='u bottom')
279 pylab.plot(t, close_loop_u_top, label='u top')
Austin Schuh0d9467a2014-02-15 22:36:45 -0800280 pylab.legend()
Austin Schuhfa033692013-02-24 01:00:55 -0800281 pylab.show()
Austin Schuhc8ca2442013-02-23 12:29:33 -0800282
Austin Schuh3c542312013-02-24 01:53:50 -0800283 # Write the generated constants out to a file.
Austin Schuhcda86af2014-02-16 16:16:39 -0800284 if len(argv) != 3:
285 print "Expected .h file name and .cc file name for the claw."
Austin Schuhc8ca2442013-02-23 12:29:33 -0800286 else:
Austin Schuhcda86af2014-02-16 16:16:39 -0800287 claw = Claw("Claw")
288 loop_writer = control_loop.ControlLoopWriter("Claw", [claw])
Austin Schuh683a0d02013-03-02 01:51:31 -0800289 if argv[1][-3:] == '.cc':
Austin Schuhcda86af2014-02-16 16:16:39 -0800290 loop_writer.Write(argv[2], argv[1])
Austin Schuh683a0d02013-03-02 01:51:31 -0800291 else:
Austin Schuhcda86af2014-02-16 16:16:39 -0800292 loop_writer.Write(argv[1], argv[2])
Austin Schuhc8ca2442013-02-23 12:29:33 -0800293
294if __name__ == '__main__':
295 sys.exit(main(sys.argv))