blob: aacf31e9186b71ab466eea2d0a2fe685faec16c4 [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 Schuhfe6d37e2014-03-24 09:45:01 -07005import polytope
6import polydrivetrain
Austin Schuhc8ca2442013-02-23 12:29:33 -08007import numpy
Austin Schuhc8ca2442013-02-23 12:29:33 -08008import sys
Austin Schuhc8ca2442013-02-23 12:29:33 -08009from matplotlib import pylab
Austin Schuhc8ca2442013-02-23 12:29:33 -080010
Austin Schuh3bb9a442014-02-02 16:01:45 -080011class Claw(control_loop.ControlLoop):
12 def __init__(self, name="RawClaw"):
13 super(Claw, self).__init__(name)
Austin Schuhc8ca2442013-02-23 12:29:33 -080014 # Stall Torque in N m
James Kuszmaule1755b32014-02-13 06:27:48 -080015 self.stall_torque = 2.42
Austin Schuhc8ca2442013-02-23 12:29:33 -080016 # Stall Current in Amps
James Kuszmaule1755b32014-02-13 06:27:48 -080017 self.stall_current = 133
James Kuszmaul92797402014-02-17 14:08:49 -080018 # Free Speed in RPM
19 self.free_speed = 5500.0
Austin Schuh3c542312013-02-24 01:53:50 -080020 # Free Current in Amps
James Kuszmaule1755b32014-02-13 06:27:48 -080021 self.free_current = 2.7
Austin Schuh3bb9a442014-02-02 16:01:45 -080022 # Moment of inertia of the claw in kg m^2
James Kuszmaul92797402014-02-17 14:08:49 -080023 # measured from CAD
24 self.J_top = 0.3
25 self.J_bottom = 0.9
Austin Schuhc8ca2442013-02-23 12:29:33 -080026 # Resistance of the motor
James Kuszmaul92797402014-02-17 14:08:49 -080027 self.R = 12.0 / self.stall_current
Austin Schuhc8ca2442013-02-23 12:29:33 -080028 # Motor velocity constant
Austin Schuh3c542312013-02-24 01:53:50 -080029 self.Kv = ((self.free_speed / 60.0 * 2.0 * numpy.pi) /
30 (13.5 - self.R * self.free_current))
Austin Schuhc8ca2442013-02-23 12:29:33 -080031 # Torque constant
32 self.Kt = self.stall_torque / self.stall_current
33 # Gear ratio
James Kuszmaule1755b32014-02-13 06:27:48 -080034 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 -080035 # Control loop time step
36 self.dt = 0.01
37
James Kuszmaule2afbe42014-02-17 22:29:59 -080038 # State is [bottom position, bottom velocity, top - bottom position,
39 # top - bottom velocity]
40 # Input is [bottom power, top power - bottom power * J_top / J_bottom]
James Kuszmaul92797402014-02-17 14:08:49 -080041 # Motor time constants. difference_bottom refers to the constant for how the
42 # bottom velocity affects the difference of the top and bottom velocities.
43 self.common_motor_constant = -self.Kt / self.Kv / (self.G * self.G * self.R)
44 self.bottom_bottom = self.common_motor_constant / self.J_bottom
James Kuszmaulc02a39a2014-02-18 15:45:16 -080045 self.difference_bottom = -self.common_motor_constant * (1 / self.J_bottom
James Kuszmaul92797402014-02-17 14:08:49 -080046 - 1 / self.J_top)
47 self.difference_difference = self.common_motor_constant / self.J_top
Austin Schuhc8ca2442013-02-23 12:29:33 -080048 # State feedback matrices
James Kuszmaule2afbe42014-02-17 22:29:59 -080049
Austin Schuhc8ca2442013-02-23 12:29:33 -080050 self.A_continuous = numpy.matrix(
James Kuszmaulc02a39a2014-02-18 15:45:16 -080051 [[0, 0, 1, 0],
52 [0, 0, 0, 1],
James Kuszmaul92797402014-02-17 14:08:49 -080053 [0, 0, self.bottom_bottom, 0],
54 [0, 0, self.difference_bottom, self.difference_difference]])
Austin Schuh0d9467a2014-02-15 22:36:45 -080055
James Kuszmaule2afbe42014-02-17 22:29:59 -080056 self.A_bottom_cont = numpy.matrix(
57 [[0, 1],
58 [0, self.bottom_bottom]])
59
60 self.A_diff_cont = numpy.matrix(
61 [[0, 1],
62 [0, self.difference_difference]])
63
James Kuszmaul92797402014-02-17 14:08:49 -080064 self.motor_feedforward = self.Kt / (self.G * self.R)
James Kuszmaulf2ed6e62014-02-17 17:52:07 -080065 self.motor_feedforward_bottom = self.motor_feedforward / self.J_bottom
66 self.motor_feedforward_difference = self.motor_feedforward / self.J_top
James Kuszmaul92797402014-02-17 14:08:49 -080067 self.motor_feedforward_difference_bottom = (
68 self.motor_feedforward * (1 / self.J_bottom - 1 / self.J_top))
Austin Schuhc8ca2442013-02-23 12:29:33 -080069 self.B_continuous = numpy.matrix(
Austin Schuh0d9467a2014-02-15 22:36:45 -080070 [[0, 0],
James Kuszmaule2afbe42014-02-17 22:29:59 -080071 [0, 0],
James Kuszmaulc02a39a2014-02-18 15:45:16 -080072 [self.motor_feedforward_bottom, 0],
Austin Schuh170fe252014-02-22 15:52:01 -080073 [-self.motor_feedforward_bottom, self.motor_feedforward_difference]])
James Kuszmaule2afbe42014-02-17 22:29:59 -080074
James Kuszmaulc02a39a2014-02-18 15:45:16 -080075 print "Cont X_ss", self.motor_feedforward / -self.common_motor_constant
76
James Kuszmaule2afbe42014-02-17 22:29:59 -080077 self.B_bottom_cont = numpy.matrix(
78 [[0],
79 [self.motor_feedforward_bottom]])
80
81 self.B_diff_cont = numpy.matrix(
82 [[0],
83 [self.motor_feedforward_difference]])
84
Austin Schuh0d9467a2014-02-15 22:36:45 -080085 self.C = numpy.matrix([[1, 0, 0, 0],
James Kuszmaulc02a39a2014-02-18 15:45:16 -080086 [1, 1, 0, 0]])
Austin Schuh0d9467a2014-02-15 22:36:45 -080087 self.D = numpy.matrix([[0, 0],
88 [0, 0]])
Austin Schuhc8ca2442013-02-23 12:29:33 -080089
Austin Schuhc1f68892013-03-16 17:06:27 -070090 self.A, self.B = self.ContinuousToDiscrete(
91 self.A_continuous, self.B_continuous, self.dt)
Austin Schuhc8ca2442013-02-23 12:29:33 -080092
James Kuszmaule2afbe42014-02-17 22:29:59 -080093 self.A_bottom, self.B_bottom = controls.c2d(
94 self.A_bottom_cont, self.B_bottom_cont, self.dt)
95 self.A_diff, self.B_diff = controls.c2d(
96 self.A_diff_cont, self.B_diff_cont, self.dt)
97
Austin Schuh2758ade2014-02-22 16:58:21 -080098 self.K_bottom = controls.dplace(self.A_bottom, self.B_bottom, [.65, .45])
99 self.K_diff = controls.dplace(self.A_diff, self.B_diff, [.40, .28])
100
101 print "K_diff", self.K_diff
102 print "K_bottom", self.K_bottom
103
James Kuszmaulc02a39a2014-02-18 15:45:16 -0800104 print "A"
105 print self.A
106 print "B"
107 print self.B
James Kuszmaule2afbe42014-02-17 22:29:59 -0800108
James Kuszmaulc02a39a2014-02-18 15:45:16 -0800109
Austin Schuhcda86af2014-02-16 16:16:39 -0800110 self.Q = numpy.matrix([[(1.0 / (0.10 ** 2.0)), 0.0, 0.0, 0.0],
Austin Schuh01c652b2014-02-21 23:13:42 -0800111 [0.0, (1.0 / (0.06 ** 2.0)), 0.0, 0.0],
112 [0.0, 0.0, 0.10, 0.0],
113 [0.0, 0.0, 0.0, 0.1]])
Austin Schuhcda86af2014-02-16 16:16:39 -0800114
James Kuszmaulf2ed6e62014-02-17 17:52:07 -0800115 self.R = numpy.matrix([[(1.0 / (40.0 ** 2.0)), 0.0],
116 [0.0, (1.0 / (5.0 ** 2.0))]])
Austin Schuh170fe252014-02-22 15:52:01 -0800117 #self.K = controls.dlqr(self.A, self.B, self.Q, self.R)
James Kuszmaule2afbe42014-02-17 22:29:59 -0800118
Austin Schuh2758ade2014-02-22 16:58:21 -0800119 self.K = numpy.matrix([[self.K_bottom[0, 0], 0.0, self.K_bottom[0, 1], 0.0],
120 [0.0, self.K_diff[0, 0], 0.0, self.K_diff[0, 1]]])
Austin Schuh170fe252014-02-22 15:52:01 -0800121
122 # Compute the feed forwards aceleration term.
123 self.K[1, 0] = -self.B[1, 0] * self.K[0, 0] / self.B[1, 1]
124
James Kuszmaulc02a39a2014-02-18 15:45:16 -0800125 lstsq_A = numpy.identity(2)
Austin Schuh170fe252014-02-22 15:52:01 -0800126 lstsq_A[0, :] = self.B[1, :]
127 lstsq_A[1, :] = self.B[3, :]
James Kuszmaulc02a39a2014-02-18 15:45:16 -0800128 print "System of Equations coefficients:"
Austin Schuh170fe252014-02-22 15:52:01 -0800129 print lstsq_A
James Kuszmaulc02a39a2014-02-18 15:45:16 -0800130 print "det", numpy.linalg.det(lstsq_A)
Austin Schuh170fe252014-02-22 15:52:01 -0800131
James Kuszmaulc02a39a2014-02-18 15:45:16 -0800132 out_x = numpy.linalg.lstsq(
133 lstsq_A,
134 numpy.matrix([[self.A[1, 2]], [self.A[3, 2]]]))[0]
135 self.K[1, 2] = -lstsq_A[0, 0] * (self.K[0, 2] - out_x[0]) / lstsq_A[0, 1] + out_x[1]
Austin Schuhcda86af2014-02-16 16:16:39 -0800136
137 print "K unaugmented"
138 print self.K
James Kuszmaule2afbe42014-02-17 22:29:59 -0800139 print "B * K unaugmented"
140 print self.B * self.K
141 F = self.A - self.B * self.K
James Kuszmaule2afbe42014-02-17 22:29:59 -0800142 print "A - B * K unaugmented"
143 print F
144 print "eigenvalues"
145 print numpy.linalg.eig(F)[0]
Austin Schuhc8ca2442013-02-23 12:29:33 -0800146
147 self.rpl = .05
148 self.ipl = 0.008
Austin Schuh3c542312013-02-24 01:53:50 -0800149 self.PlaceObserverPoles([self.rpl + 1j * self.ipl,
Austin Schuh0d9467a2014-02-15 22:36:45 -0800150 self.rpl + 1j * self.ipl,
James Kuszmaule2afbe42014-02-17 22:29:59 -0800151 self.rpl - 1j * self.ipl,
Austin Schuh3c542312013-02-24 01:53:50 -0800152 self.rpl - 1j * self.ipl])
Austin Schuhc8ca2442013-02-23 12:29:33 -0800153
James Kuszmaul92797402014-02-17 14:08:49 -0800154 # The box formed by U_min and U_max must encompass all possible values,
155 # or else Austin's code gets angry.
James Kuszmauld536a402014-02-18 22:32:12 -0800156 self.U_max = numpy.matrix([[12.0], [12.0]])
157 self.U_min = numpy.matrix([[-12.0], [-12.0]])
Austin Schuhc8ca2442013-02-23 12:29:33 -0800158
Austin Schuh2758ade2014-02-22 16:58:21 -0800159 # Compute the steady state velocities for a given applied voltage.
160 # The top and bottom of the claw should spin at the same rate if the
161 # physics is right.
162 X_ss = numpy.matrix([[0], [0], [0.0], [0]])
163
164 U = numpy.matrix([[1.0],[1.0]])
165 A = self.A
166 B = self.B
167 #X_ss[2, 0] = X_ss[2, 0] * A[2, 2] + B[2, 0] * U[0, 0]
168 X_ss[2, 0] = 1 / (1 - A[2, 2]) * B[2, 0] * U[0, 0]
169 #X_ss[3, 0] = X_ss[3, 0] * A[3, 3] + X_ss[2, 0] * A[3, 2] + B[3, 0] * U[0, 0] + B[3, 1] * U[1, 0]
170 #X_ss[3, 0] * (1 - A[3, 3]) = X_ss[2, 0] * A[3, 2] + B[3, 0] * U[0, 0] + B[3, 1] * U[1, 0]
171 X_ss[3, 0] = 1 / (1 - A[3, 3]) * (X_ss[2, 0] * A[3, 2] + B[3, 1] * U[1, 0] + B[3, 0] * U[0, 0])
172 #X_ss[3, 0] = 1 / (1 - A[3, 3]) / (1 - A[2, 2]) * B[2, 0] * U[0, 0] * A[3, 2] + B[3, 0] * U[0, 0] + B[3, 1] * U[1, 0]
173 X_ss[0, 0] = A[0, 2] * X_ss[2, 0] + B[0, 0] * U[0, 0]
174 X_ss[1, 0] = A[1, 2] * X_ss[2, 0] + A[1, 3] * X_ss[3, 0] + B[1, 0] * U[0, 0] + B[1, 1] * U[1, 0]
175
176 print "X_ss", X_ss
177
Austin Schuhfe6d37e2014-03-24 09:45:01 -0700178 self.U_poly = polytope.HPolytope(
179 numpy.matrix([[1, 0],
180 [-1, 0],
181 [0, 1],
182 [0, -1]]),
183 numpy.matrix([[12],
184 [12],
185 [12],
186 [12]]))
187
Austin Schuhc1f68892013-03-16 17:06:27 -0700188 self.InitializeState()
189
190
Austin Schuh3bb9a442014-02-02 16:01:45 -0800191class ClawDeltaU(Claw):
192 def __init__(self, name="Claw"):
193 super(ClawDeltaU, self).__init__(name)
Austin Schuhc1f68892013-03-16 17:06:27 -0700194 A_unaugmented = self.A
195 B_unaugmented = self.B
Austin Schuh0d9467a2014-02-15 22:36:45 -0800196 C_unaugmented = self.C
Austin Schuhc1f68892013-03-16 17:06:27 -0700197
Austin Schuh0d9467a2014-02-15 22:36:45 -0800198 self.A = numpy.matrix([[0.0, 0.0, 0.0, 0.0, 0.0],
199 [0.0, 0.0, 0.0, 0.0, 0.0],
200 [0.0, 0.0, 0.0, 0.0, 0.0],
201 [0.0, 0.0, 0.0, 0.0, 0.0],
202 [0.0, 0.0, 0.0, 0.0, 1.0]])
203 self.A[0:4, 0:4] = A_unaugmented
204 self.A[0:4, 4] = B_unaugmented[0:4, 0]
Austin Schuhc1f68892013-03-16 17:06:27 -0700205
Austin Schuh0d9467a2014-02-15 22:36:45 -0800206 self.B = numpy.matrix([[0.0, 0.0],
207 [0.0, 0.0],
208 [0.0, 0.0],
209 [0.0, 0.0],
210 [1.0, 0.0]])
211 self.B[0:4, 1] = B_unaugmented[0:4, 1]
Austin Schuhc1f68892013-03-16 17:06:27 -0700212
Austin Schuh0d9467a2014-02-15 22:36:45 -0800213 self.C = numpy.concatenate((C_unaugmented, numpy.matrix([[0.0], [0.0]])),
214 axis=1)
215 self.D = numpy.matrix([[0.0, 0.0],
216 [0.0, 0.0]])
Austin Schuhc1f68892013-03-16 17:06:27 -0700217
Austin Schuh0d9467a2014-02-15 22:36:45 -0800218 #self.PlaceControllerPoles([0.55, 0.35, 0.55, 0.35, 0.80])
219 self.Q = numpy.matrix([[(1.0 / (0.04 ** 2.0)), 0.0, 0.0, 0.0, 0.0],
220 [0.0, (1.0 / (0.01 ** 2)), 0.0, 0.0, 0.0],
221 [0.0, 0.0, 0.01, 0.0, 0.0],
222 [0.0, 0.0, 0.0, 0.08, 0.0],
223 [0.0, 0.0, 0.0, 0.0, (1.0 / (10.0 ** 2))]])
224
225 self.R = numpy.matrix([[0.000001, 0.0],
226 [0.0, 1.0 / (10.0 ** 2.0)]])
227 self.K = controls.dlqr(self.A, self.B, self.Q, self.R)
228
229 self.K = numpy.matrix([[50.0, 0.0, 10.0, 0.0, 1.0],
230 [50.0, 0.0, 10.0, 0.0, 1.0]])
231 #self.K = numpy.matrix([[50.0, -100.0, 0, -10, 0],
232 # [50.0, 100.0, 0, 10, 0]])
233
234 controlability = controls.ctrb(self.A, self.B);
235 print "Rank of augmented controlability matrix.", numpy.linalg.matrix_rank(controlability)
Austin Schuhc1f68892013-03-16 17:06:27 -0700236
237 print "K"
238 print self.K
239 print "Placed controller poles are"
240 print numpy.linalg.eig(self.A - self.B * self.K)[0]
Austin Schuh0d9467a2014-02-15 22:36:45 -0800241 print [numpy.abs(x) for x in numpy.linalg.eig(self.A - self.B * self.K)[0]]
Austin Schuhc1f68892013-03-16 17:06:27 -0700242
243 self.rpl = .05
244 self.ipl = 0.008
Austin Schuh0d9467a2014-02-15 22:36:45 -0800245 self.PlaceObserverPoles([self.rpl + 1j * self.ipl, 0.10, 0.09,
Brian Silverman23a67ca2013-03-16 23:48:50 -0700246 self.rpl - 1j * self.ipl, 0.90])
Austin Schuh0d9467a2014-02-15 22:36:45 -0800247 #print "A is"
248 #print self.A
249 #print "L is"
250 #print self.L
251 #print "C is"
252 #print self.C
253 #print "A - LC is"
254 #print self.A - self.L * self.C
Austin Schuhc1f68892013-03-16 17:06:27 -0700255
Austin Schuh0d9467a2014-02-15 22:36:45 -0800256 #print "Placed observer poles are"
257 #print numpy.linalg.eig(self.A - self.L * self.C)[0]
258
259 self.U_max = numpy.matrix([[12.0], [12.0]])
260 self.U_min = numpy.matrix([[-12.0], [-12.0]])
Austin Schuhc1f68892013-03-16 17:06:27 -0700261
262 self.InitializeState()
263
264
Austin Schuhcda86af2014-02-16 16:16:39 -0800265def FullSeparationPriority(claw, U):
266 bottom_u = U[0, 0]
Austin Schuh2758ade2014-02-22 16:58:21 -0800267 top_u = U[1, 0]
Austin Schuhcda86af2014-02-16 16:16:39 -0800268
Austin Schuhcda86af2014-02-16 16:16:39 -0800269 if bottom_u > claw.U_max[0, 0]:
Austin Schuhcda86af2014-02-16 16:16:39 -0800270 top_u -= bottom_u - claw.U_max[0, 0]
271 if top_u < claw.U_min[1, 0]:
272 top_u = claw.U_min[1, 0]
273
274 bottom_u = claw.U_max[0, 0]
275 if top_u > claw.U_max[1, 0]:
276 bottom_u -= top_u - claw.U_max[1, 0]
277 if bottom_u < claw.U_min[0, 0]:
278 bottom_u = claw.U_min[0, 0]
279
280 top_u = claw.U_max[1, 0]
281 if top_u < claw.U_min[1, 0]:
282 bottom_u -= top_u - claw.U_min[1, 0]
283 if bottom_u > claw.U_max[0, 0]:
284 bottom_u = claw.U_max[0, 0]
285
286 top_u = claw.U_min[1, 0]
287 if bottom_u < claw.U_min[0, 0]:
288 top_u -= bottom_u - claw.U_min[0, 0]
289 if top_u > claw.U_max[1, 0]:
290 top_u = claw.U_max[1, 0]
291
292 bottom_u = claw.U_min[0, 0]
293
Austin Schuh2758ade2014-02-22 16:58:21 -0800294 return numpy.matrix([[bottom_u], [top_u]])
Austin Schuhcda86af2014-02-16 16:16:39 -0800295
Austin Schuhfe6d37e2014-03-24 09:45:01 -0700296def ScaleU(claw, U, K, error):
297 """Clips U as necessary.
298
299 Args:
300 claw: claw object containing moments of inertia and U limits.
301 U: Input matrix to clip as necessary.
302 """
303 bottom_u = U[0, 0]
304 top_u = U[1, 0]
305
306 if (bottom_u > claw.U_max[0, 0] or bottom_u < claw.U_min[0, 0] or
307 top_u > claw.U_max[0, 0] or top_u < claw.U_min[0, 0]):
308
309 position_K = K[:, 0:2]
310 velocity_K = K[:, 2:]
311 position_error = error[0:2, 0]
312 velocity_error = error[2:, 0]
313
314 # H * U <= k
315 # U = UPos + UVel
316 # H * (UPos + UVel) <= k
317 # H * UPos <= k - H * UVel
318 #
319 # Now, we can do a coordinate transformation and say the following.
320 #
321 # UPos = position_K * position_error
322 # (H * position_K) * position_error <= k - H * UVel
323 #
324 # Add in the constraint that 0 <= t <= 1
325 # Now, there are 2 ways this can go. Either we have a region, or we don't
326 # have a region. If we have a region, then pick the largest t and go for it.
327 # If we don't have a region, we need to pick a good comprimise.
328
329 # First prototype! -> Only cap the position power, leave the velocity power active.
330
331 #u_velocity = velocity_K * velocity_error
332 #u_position = position_K * position_error
333 #scalar = min(1.0, claw.U_max[0, 0] / max(numpy.abs(u_position[0, 0]), numpy.abs(u_position[1, 0])))
334 #return u_velocity + scalar * u_position
335
336 pos_poly = polytope.HPolytope(
337 claw.U_poly.H * position_K,
338 claw.U_poly.k - claw.U_poly.H * velocity_K * velocity_error)
339
340 adjusted_pos_error = polydrivetrain.CoerceGoal(pos_poly, numpy.matrix([[position_error[1, 0], -position_error[0, 0]]]), 0.0, position_error)
341
342 return velocity_K * velocity_error + position_K * adjusted_pos_error
343
344 #U = Kpos * poserror + Kvel * velerror
345
346 #scalar = claw.U_max[0, 0] / max(numpy.abs(top_u), numpy.abs(bottom_u))
347
348 #top_u *= scalar
349 #bottom_u *= scalar
350
351 return numpy.matrix([[bottom_u], [top_u]])
352
James Kuszmaulc02a39a2014-02-18 15:45:16 -0800353def AverageUFix(claw, U, preserve_v3=False):
354 """Clips U as necessary.
Austin Schuhcda86af2014-02-16 16:16:39 -0800355
James Kuszmaulc02a39a2014-02-18 15:45:16 -0800356 Args:
357 claw: claw object containing moments of inertia and U limits.
358 U: Input matrix to clip as necessary.
359 preserve_v3: There are two ways to attempt to clip the voltages:
Austin Schuh170fe252014-02-22 15:52:01 -0800360 -If you preserve the imaginary v3, then it will attempt to
James Kuszmaulc02a39a2014-02-18 15:45:16 -0800361 preserve the effect on the separation of the two claws.
362 If it is not able to do this, then it calls itself with preserve_v3
363 set to False.
364 -If you preserve the ratio of the voltage of the bottom and the top,
365 then it will attempt to preserve the ratio of those two. This is
366 equivalent to preserving the ratio of v3 and the bottom voltage.
367 """
368 bottom_u = U[0, 0]
369 top_u = U[1, 0]
370 seperation_u = top_u - bottom_u * claw.J_top / claw.J_bottom
371
Austin Schuh2758ade2014-02-22 16:58:21 -0800372 bottom_bad = bottom_u > claw.U_max[0, 0] or bottom_u < claw.U_min[0, 0]
Austin Schuh170fe252014-02-22 15:52:01 -0800373 top_bad = top_u > claw.U_max[0, 0] or top_u < claw.U_min[0, 0]
374
James Kuszmaulc02a39a2014-02-18 15:45:16 -0800375 scalar = claw.U_max[0, 0] / max(numpy.abs(top_u), numpy.abs(bottom_u))
376 if bottom_bad and preserve_v3:
377 bottom_u *= scalar
378 top_u = seperation_u + bottom_u * claw.J_top / claw.J_bottom
379 if abs(top_u) > claw.U_max[0, 0]:
380 return AverageUFix(claw, U, preserve_v3=False)
381 elif top_bad and preserve_v3:
382 top_u *= scalar
383 bottom_u = (top_u - seperation_u) * claw.J_bottom / claw.J_top
384 if abs(bottom_u) > claw.U_max[0, 0]:
385 return AverageUFix(claw, U, preserve_v3=False)
386 elif (bottom_bad or top_bad) and not preserve_v3:
Austin Schuhcda86af2014-02-16 16:16:39 -0800387 top_u *= scalar
388 bottom_u *= scalar
Austin Schuh2758ade2014-02-22 16:58:21 -0800389 print "Scaling"
Austin Schuhcda86af2014-02-16 16:16:39 -0800390
James Kuszmaulc02a39a2014-02-18 15:45:16 -0800391 return numpy.matrix([[bottom_u], [top_u]])
Austin Schuhcda86af2014-02-16 16:16:39 -0800392
Austin Schuh0d9467a2014-02-15 22:36:45 -0800393def ClipDeltaU(claw, U):
394 delta_u = U[0, 0]
395 top_u = U[1, 0]
396 old_bottom_u = claw.X[4, 0]
397
398 # TODO(austin): Preserve the difference between the top and bottom power.
399 new_unclipped_bottom_u = old_bottom_u + delta_u
400
401 #print "Bottom is", new_unclipped_bottom_u, "Top is", top_u
402 if new_unclipped_bottom_u > claw.U_max[0, 0]:
Austin Schuh0d9467a2014-02-15 22:36:45 -0800403 top_u -= new_unclipped_bottom_u - claw.U_max[0, 0]
404 new_unclipped_bottom_u = claw.U_max[0, 0]
405 if top_u > claw.U_max[1, 0]:
406 new_unclipped_bottom_u -= top_u - claw.U_max[1, 0]
407 top_u = claw.U_max[1, 0]
408 if top_u < claw.U_min[1, 0]:
409 new_unclipped_bottom_u -= top_u - claw.U_min[1, 0]
410 top_u = claw.U_min[1, 0]
411 if new_unclipped_bottom_u < claw.U_min[0, 0]:
412 top_u -= new_unclipped_bottom_u - claw.U_min[0, 0]
413 new_unclipped_bottom_u = claw.U_min[0, 0]
414
415 new_bottom_u = numpy.clip(new_unclipped_bottom_u, claw.U_min[0, 0], claw.U_max[0, 0])
416 new_top_u = numpy.clip(top_u, claw.U_min[1, 0], claw.U_max[1, 0])
417
418 return numpy.matrix([[new_bottom_u - old_bottom_u], [new_top_u]])
Austin Schuhc1f68892013-03-16 17:06:27 -0700419
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700420def run_test(claw, initial_X, goal, max_separation_error=0.01, show_graph=True, iterations=200):
421 """Runs the claw plant on a given claw (claw) with an initial condition (initial_X) and goal (goal).
Austin Schuhc8ca2442013-02-23 12:29:33 -0800422
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700423 The tests themselves are not terribly sophisticated; I just test for
424 whether the goal has been reached and whether the separation goes
425 outside of the initial and goal values by more then max_separation_error.
426 Prints out something for a failure of either condition and returns
427 False if tests fail.
428 Args:
429 claw: claw object to use.
430 initial_X: starting state.
431 goal: goal state.
432 show_graph: Whether or not to display a graph showing the changing
433 states and voltages.
434 iterations: Number of timesteps to run the model for."""
Austin Schuhc8ca2442013-02-23 12:29:33 -0800435
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700436 claw.X = initial_X
437
438 # Various lists for graphing things.
Austin Schuhcda86af2014-02-16 16:16:39 -0800439 t = []
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700440 x_bottom = []
441 x_top = []
442 u_bottom = []
443 u_top = []
444 x_separation = []
Austin Schuhc8ca2442013-02-23 12:29:33 -0800445
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700446 tests_passed = True
447
448 # Bounds which separation should not exceed.
449 lower_bound = (initial_X[1, 0] if initial_X[1, 0] < goal[1, 0]
450 else goal[1, 0]) - max_separation_error
451 upper_bound = (initial_X[1, 0] if initial_X[1, 0] > goal[1, 0]
452 else goal[1, 0]) + max_separation_error
453
454 for i in xrange(iterations):
455 U = claw.K * (goal - claw.X)
456 U = ScaleU(claw, U, claw.K, goal - claw.X)
457 claw.Update(U)
458
459 if claw.X[1, 0] > upper_bound or claw.X[1, 0] < lower_bound:
460 tests_passed = False
461 print "Claw separation was", claw.X[1, 0]
462 print "Should have been between", lower_bound, "and", upper_bound
463
464 t.append(i * claw.dt)
465 x_bottom.append(claw.X[0, 0] * 10.0)
466 x_top.append((claw.X[1, 0] + claw.X[0, 0]) * 10.0)
467 u_bottom.append(U[0, 0])
468 u_top.append(U[1, 0])
469 x_separation.append(claw.X[1, 0] * 10.0)
470
471 if show_graph:
472 pylab.plot(t, x_bottom, label='x bottom * 10')
473 pylab.plot(t, x_top, label='x top * 10')
474 pylab.plot(t, u_bottom, label='u bottom')
475 pylab.plot(t, u_top, label='u top')
476 pylab.plot(t, x_separation, label='separation * 10')
477 pylab.legend()
478 pylab.show()
479
480 # Test to make sure that we are near the goal.
481 if numpy.max(abs(claw.X - goal)) > 1e-4:
482 tests_passed = False
483 print "X was", claw.X, "Expected", goal
484
485 return tests_passed
486
487def main(argv):
488 claw = Claw()
489
490 # Test moving the claw with constant separation.
491 initial_X = numpy.matrix([[-1.0], [0.0], [0.0], [0.0]])
492 R = numpy.matrix([[1.0], [0.0], [0.0], [0.0]])
493 run_test(claw, initial_X, R)
494
495 # Test just changing separation.
496 initial_X = numpy.matrix([[0.0], [0.0], [0.0], [0.0]])
497 R = numpy.matrix([[0.0], [1.0], [0.0], [0.0]])
498 run_test(claw, initial_X, R)
499
500 # Test changing both separation and position at once..
501 initial_X = numpy.matrix([[0.0], [0.0], [0.0], [0.0]])
502 R = numpy.matrix([[1.0], [1.0], [0.0], [0.0]])
503 run_test(claw, initial_X, R)
Austin Schuhc8ca2442013-02-23 12:29:33 -0800504
Austin Schuh3c542312013-02-24 01:53:50 -0800505 # Write the generated constants out to a file.
Austin Schuhcda86af2014-02-16 16:16:39 -0800506 if len(argv) != 3:
507 print "Expected .h file name and .cc file name for the claw."
Austin Schuhc8ca2442013-02-23 12:29:33 -0800508 else:
Austin Schuhcda86af2014-02-16 16:16:39 -0800509 claw = Claw("Claw")
510 loop_writer = control_loop.ControlLoopWriter("Claw", [claw])
Austin Schuh683a0d02013-03-02 01:51:31 -0800511 if argv[1][-3:] == '.cc':
Austin Schuhcda86af2014-02-16 16:16:39 -0800512 loop_writer.Write(argv[2], argv[1])
Austin Schuh683a0d02013-03-02 01:51:31 -0800513 else:
Austin Schuhcda86af2014-02-16 16:16:39 -0800514 loop_writer.Write(argv[1], argv[2])
Austin Schuhc8ca2442013-02-23 12:29:33 -0800515
516if __name__ == '__main__':
517 sys.exit(main(sys.argv))