blob: 5c275be77f5b6f26743ee0ad8678c54f51440a2a [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
Brian Silverman6dd2c532014-03-29 23:34:39 -070023 self.J_top = 2.8
24 self.J_bottom = 3.0
James Kuszmaul3ac190a2014-03-26 20:11:04 -070025
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
Brian Silverman6dd2c532014-03-29 23:34:39 -070098 self.K_bottom = controls.dplace(self.A_bottom, self.B_bottom, [.75 + 0.1j, .75 - 0.1j])
99 self.K_diff = controls.dplace(self.A_diff, self.B_diff, [.75 + 0.1j, .75 - 0.1j])
Austin Schuh2758ade2014-02-22 16:58:21 -0800100
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
Brian Silverman6dd2c532014-03-29 23:34:39 -0700147 self.rpl = .05
148 self.ipl = 0.010
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 """
Brian Silverman6dd2c532014-03-29 23:34:39 -0700303
Austin Schuhfe6d37e2014-03-24 09:45:01 -0700304 bottom_u = U[0, 0]
305 top_u = U[1, 0]
306
307 if (bottom_u > claw.U_max[0, 0] or bottom_u < claw.U_min[0, 0] or
308 top_u > claw.U_max[0, 0] or top_u < claw.U_min[0, 0]):
309
310 position_K = K[:, 0:2]
311 velocity_K = K[:, 2:]
312 position_error = error[0:2, 0]
313 velocity_error = error[2:, 0]
314
315 # H * U <= k
316 # U = UPos + UVel
317 # H * (UPos + UVel) <= k
318 # H * UPos <= k - H * UVel
319 #
320 # Now, we can do a coordinate transformation and say the following.
321 #
322 # UPos = position_K * position_error
323 # (H * position_K) * position_error <= k - H * UVel
324 #
325 # Add in the constraint that 0 <= t <= 1
326 # Now, there are 2 ways this can go. Either we have a region, or we don't
327 # have a region. If we have a region, then pick the largest t and go for it.
328 # If we don't have a region, we need to pick a good comprimise.
329
330 # First prototype! -> Only cap the position power, leave the velocity power active.
331
332 #u_velocity = velocity_K * velocity_error
333 #u_position = position_K * position_error
334 #scalar = min(1.0, claw.U_max[0, 0] / max(numpy.abs(u_position[0, 0]), numpy.abs(u_position[1, 0])))
335 #return u_velocity + scalar * u_position
336
337 pos_poly = polytope.HPolytope(
338 claw.U_poly.H * position_K,
339 claw.U_poly.k - claw.U_poly.H * velocity_K * velocity_error)
340
Brian Silverman6dd2c532014-03-29 23:34:39 -0700341 P = position_error
342 #K = numpy.matrix([[position_error[1, 0], -position_error[0, 0]]])
Brian Silvermanf05948b2014-03-30 00:24:36 -0700343 L45 = numpy.matrix([[numpy.sign(P[1, 0]) * numpy.sqrt(3), -numpy.sign(P[0, 0])]])
Brian Silverman6dd2c532014-03-29 23:34:39 -0700344 if L45[0, 1] == 0:
345 L45[0, 1] = 1
346 if L45[0, 0] == 0:
347 L45[0, 0] = 1
348 w45 = numpy.matrix([[0]])
Austin Schuhfe6d37e2014-03-24 09:45:01 -0700349
Brian Silverman6dd2c532014-03-29 23:34:39 -0700350 if numpy.abs(P[0, 0]) > numpy.abs(P[1, 0]):
351 LH = numpy.matrix([[0, 1]])
352 else:
353 LH = numpy.matrix([[1, 0]])
354 wh = LH * P
355 standard = numpy.concatenate((L45, LH))
356 #print "Standard", standard
357 W = numpy.concatenate((w45, wh))
358 #print "W is", W
359 intersection = numpy.linalg.inv(standard) * W
360 print "intersection point %s" % intersection
361 print "Intersection power is ", velocity_K * velocity_error + position_K * intersection
362 adjusted_pos_error_h, is_inside_h = polydrivetrain.DoCoerceGoal(pos_poly,
363 LH, wh, position_error)
364 adjusted_pos_error_45, is_inside_45 = polydrivetrain.DoCoerceGoal(pos_poly,
365 L45, w45, intersection)
366 if pos_poly.IsInside(intersection):
367 adjusted_pos_error = adjusted_pos_error_h
368 print "horizontal"
369 else:
370 if is_inside_h:
371 if numpy.linalg.norm(adjusted_pos_error_h) > numpy.linalg.norm(adjusted_pos_error_45):
372 adjusted_pos_error = adjusted_pos_error_h
373 else:
374 adjusted_pos_error = adjusted_pos_error_45
375 else:
376 adjusted_pos_error = adjusted_pos_error_45
377 print "45"
378 print velocity_K * velocity_error + position_K * adjusted_pos_error
379 print "45"
380 print adjusted_pos_error
381
382 print "Actual power is ", velocity_K * velocity_error + position_K * adjusted_pos_error
Austin Schuhfe6d37e2014-03-24 09:45:01 -0700383 return velocity_K * velocity_error + position_K * adjusted_pos_error
384
385 #U = Kpos * poserror + Kvel * velerror
386
387 #scalar = claw.U_max[0, 0] / max(numpy.abs(top_u), numpy.abs(bottom_u))
388
389 #top_u *= scalar
390 #bottom_u *= scalar
391
392 return numpy.matrix([[bottom_u], [top_u]])
393
James Kuszmaulc02a39a2014-02-18 15:45:16 -0800394def AverageUFix(claw, U, preserve_v3=False):
395 """Clips U as necessary.
Austin Schuhcda86af2014-02-16 16:16:39 -0800396
James Kuszmaulc02a39a2014-02-18 15:45:16 -0800397 Args:
398 claw: claw object containing moments of inertia and U limits.
399 U: Input matrix to clip as necessary.
400 preserve_v3: There are two ways to attempt to clip the voltages:
Austin Schuh170fe252014-02-22 15:52:01 -0800401 -If you preserve the imaginary v3, then it will attempt to
James Kuszmaulc02a39a2014-02-18 15:45:16 -0800402 preserve the effect on the separation of the two claws.
403 If it is not able to do this, then it calls itself with preserve_v3
404 set to False.
405 -If you preserve the ratio of the voltage of the bottom and the top,
406 then it will attempt to preserve the ratio of those two. This is
407 equivalent to preserving the ratio of v3 and the bottom voltage.
408 """
409 bottom_u = U[0, 0]
410 top_u = U[1, 0]
411 seperation_u = top_u - bottom_u * claw.J_top / claw.J_bottom
412
Austin Schuh2758ade2014-02-22 16:58:21 -0800413 bottom_bad = bottom_u > claw.U_max[0, 0] or bottom_u < claw.U_min[0, 0]
Austin Schuh170fe252014-02-22 15:52:01 -0800414 top_bad = top_u > claw.U_max[0, 0] or top_u < claw.U_min[0, 0]
415
James Kuszmaulc02a39a2014-02-18 15:45:16 -0800416 scalar = claw.U_max[0, 0] / max(numpy.abs(top_u), numpy.abs(bottom_u))
417 if bottom_bad and preserve_v3:
418 bottom_u *= scalar
419 top_u = seperation_u + bottom_u * claw.J_top / claw.J_bottom
420 if abs(top_u) > claw.U_max[0, 0]:
421 return AverageUFix(claw, U, preserve_v3=False)
422 elif top_bad and preserve_v3:
423 top_u *= scalar
424 bottom_u = (top_u - seperation_u) * claw.J_bottom / claw.J_top
425 if abs(bottom_u) > claw.U_max[0, 0]:
426 return AverageUFix(claw, U, preserve_v3=False)
427 elif (bottom_bad or top_bad) and not preserve_v3:
Austin Schuhcda86af2014-02-16 16:16:39 -0800428 top_u *= scalar
429 bottom_u *= scalar
Austin Schuh2758ade2014-02-22 16:58:21 -0800430 print "Scaling"
Austin Schuhcda86af2014-02-16 16:16:39 -0800431
James Kuszmaulc02a39a2014-02-18 15:45:16 -0800432 return numpy.matrix([[bottom_u], [top_u]])
Austin Schuhcda86af2014-02-16 16:16:39 -0800433
Austin Schuh0d9467a2014-02-15 22:36:45 -0800434def ClipDeltaU(claw, U):
435 delta_u = U[0, 0]
436 top_u = U[1, 0]
437 old_bottom_u = claw.X[4, 0]
438
439 # TODO(austin): Preserve the difference between the top and bottom power.
440 new_unclipped_bottom_u = old_bottom_u + delta_u
441
442 #print "Bottom is", new_unclipped_bottom_u, "Top is", top_u
443 if new_unclipped_bottom_u > claw.U_max[0, 0]:
Austin Schuh0d9467a2014-02-15 22:36:45 -0800444 top_u -= new_unclipped_bottom_u - claw.U_max[0, 0]
445 new_unclipped_bottom_u = claw.U_max[0, 0]
446 if top_u > claw.U_max[1, 0]:
447 new_unclipped_bottom_u -= top_u - claw.U_max[1, 0]
448 top_u = claw.U_max[1, 0]
449 if top_u < claw.U_min[1, 0]:
450 new_unclipped_bottom_u -= top_u - claw.U_min[1, 0]
451 top_u = claw.U_min[1, 0]
452 if new_unclipped_bottom_u < claw.U_min[0, 0]:
453 top_u -= new_unclipped_bottom_u - claw.U_min[0, 0]
454 new_unclipped_bottom_u = claw.U_min[0, 0]
455
456 new_bottom_u = numpy.clip(new_unclipped_bottom_u, claw.U_min[0, 0], claw.U_max[0, 0])
457 new_top_u = numpy.clip(top_u, claw.U_min[1, 0], claw.U_max[1, 0])
458
459 return numpy.matrix([[new_bottom_u - old_bottom_u], [new_top_u]])
Austin Schuhc1f68892013-03-16 17:06:27 -0700460
Brian Silverman1704f332014-03-26 21:50:09 -0700461def run_test(claw, initial_X, goal, max_separation_error=0.01, show_graph=False, iterations=200):
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700462 """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 -0800463
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700464 The tests themselves are not terribly sophisticated; I just test for
465 whether the goal has been reached and whether the separation goes
Brian Silverman6dd2c532014-03-29 23:34:39 -0700466 outside of the initial and goal values by more than max_separation_error.
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700467 Prints out something for a failure of either condition and returns
468 False if tests fail.
469 Args:
470 claw: claw object to use.
471 initial_X: starting state.
472 goal: goal state.
473 show_graph: Whether or not to display a graph showing the changing
474 states and voltages.
475 iterations: Number of timesteps to run the model for."""
Austin Schuhc8ca2442013-02-23 12:29:33 -0800476
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700477 claw.X = initial_X
478
479 # Various lists for graphing things.
Austin Schuhcda86af2014-02-16 16:16:39 -0800480 t = []
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700481 x_bottom = []
482 x_top = []
483 u_bottom = []
484 u_top = []
485 x_separation = []
Austin Schuhc8ca2442013-02-23 12:29:33 -0800486
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700487 tests_passed = True
488
489 # Bounds which separation should not exceed.
490 lower_bound = (initial_X[1, 0] if initial_X[1, 0] < goal[1, 0]
491 else goal[1, 0]) - max_separation_error
492 upper_bound = (initial_X[1, 0] if initial_X[1, 0] > goal[1, 0]
493 else goal[1, 0]) + max_separation_error
494
495 for i in xrange(iterations):
496 U = claw.K * (goal - claw.X)
497 U = ScaleU(claw, U, claw.K, goal - claw.X)
498 claw.Update(U)
499
500 if claw.X[1, 0] > upper_bound or claw.X[1, 0] < lower_bound:
501 tests_passed = False
502 print "Claw separation was", claw.X[1, 0]
503 print "Should have been between", lower_bound, "and", upper_bound
504
505 t.append(i * claw.dt)
506 x_bottom.append(claw.X[0, 0] * 10.0)
507 x_top.append((claw.X[1, 0] + claw.X[0, 0]) * 10.0)
508 u_bottom.append(U[0, 0])
509 u_top.append(U[1, 0])
510 x_separation.append(claw.X[1, 0] * 10.0)
511
512 if show_graph:
513 pylab.plot(t, x_bottom, label='x bottom * 10')
514 pylab.plot(t, x_top, label='x top * 10')
515 pylab.plot(t, u_bottom, label='u bottom')
516 pylab.plot(t, u_top, label='u top')
517 pylab.plot(t, x_separation, label='separation * 10')
518 pylab.legend()
519 pylab.show()
520
521 # Test to make sure that we are near the goal.
522 if numpy.max(abs(claw.X - goal)) > 1e-4:
523 tests_passed = False
524 print "X was", claw.X, "Expected", goal
525
526 return tests_passed
527
528def main(argv):
529 claw = Claw()
530
531 # Test moving the claw with constant separation.
532 initial_X = numpy.matrix([[-1.0], [0.0], [0.0], [0.0]])
533 R = numpy.matrix([[1.0], [0.0], [0.0], [0.0]])
534 run_test(claw, initial_X, R)
535
536 # Test just changing separation.
537 initial_X = numpy.matrix([[0.0], [0.0], [0.0], [0.0]])
538 R = numpy.matrix([[0.0], [1.0], [0.0], [0.0]])
539 run_test(claw, initial_X, R)
540
Brian Silverman6dd2c532014-03-29 23:34:39 -0700541 # Test changing both separation and position at once.
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700542 initial_X = numpy.matrix([[0.0], [0.0], [0.0], [0.0]])
543 R = numpy.matrix([[1.0], [1.0], [0.0], [0.0]])
544 run_test(claw, initial_X, R)
Austin Schuhc8ca2442013-02-23 12:29:33 -0800545
Brian Silverman6dd2c532014-03-29 23:34:39 -0700546 # Test a small separation error and a large position one.
547 initial_X = numpy.matrix([[0.0], [0.0], [0.0], [0.0]])
548 R = numpy.matrix([[2.0], [0.05], [0.0], [0.0]])
549 run_test(claw, initial_X, R)
550
Brian Silvermanf05948b2014-03-30 00:24:36 -0700551 # Test a small separation error and a large position one.
552 initial_X = numpy.matrix([[0.0], [0.0], [0.0], [0.0]])
553 R = numpy.matrix([[-0.5], [1.0], [0.0], [0.0]])
554 run_test(claw, initial_X, R)
555
556 initial_X = numpy.matrix([[0.0], [0.0], [0.0], [0.0]])
557 R = numpy.matrix([[-0.05], [2.0], [0.0], [0.0]])
558 run_test(claw, initial_X, R, show_graph=True)
559
Austin Schuh3c542312013-02-24 01:53:50 -0800560 # Write the generated constants out to a file.
Austin Schuhcda86af2014-02-16 16:16:39 -0800561 if len(argv) != 3:
562 print "Expected .h file name and .cc file name for the claw."
Austin Schuhc8ca2442013-02-23 12:29:33 -0800563 else:
Austin Schuhcda86af2014-02-16 16:16:39 -0800564 claw = Claw("Claw")
565 loop_writer = control_loop.ControlLoopWriter("Claw", [claw])
Austin Schuh683a0d02013-03-02 01:51:31 -0800566 if argv[1][-3:] == '.cc':
Austin Schuhcda86af2014-02-16 16:16:39 -0800567 loop_writer.Write(argv[2], argv[1])
Austin Schuh683a0d02013-03-02 01:51:31 -0800568 else:
Austin Schuhcda86af2014-02-16 16:16:39 -0800569 loop_writer.Write(argv[1], argv[2])
Austin Schuhc8ca2442013-02-23 12:29:33 -0800570
571if __name__ == '__main__':
572 sys.exit(main(sys.argv))