blob: ca69a2b0f23cd198283fb280509aca7c7f5ff358 [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
James Kuszmaul3ac190a2014-03-26 20:11:04 -070026
Austin Schuhc8ca2442013-02-23 12:29:33 -080027 # Resistance of the motor
James Kuszmaul92797402014-02-17 14:08:49 -080028 self.R = 12.0 / self.stall_current
Austin Schuhc8ca2442013-02-23 12:29:33 -080029 # Motor velocity constant
Austin Schuh3c542312013-02-24 01:53:50 -080030 self.Kv = ((self.free_speed / 60.0 * 2.0 * numpy.pi) /
31 (13.5 - self.R * self.free_current))
Austin Schuhc8ca2442013-02-23 12:29:33 -080032 # Torque constant
33 self.Kt = self.stall_torque / self.stall_current
34 # Gear ratio
James Kuszmaule1755b32014-02-13 06:27:48 -080035 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 -080036 # Control loop time step
37 self.dt = 0.01
38
James Kuszmaule2afbe42014-02-17 22:29:59 -080039 # State is [bottom position, bottom velocity, top - bottom position,
40 # top - bottom velocity]
41 # Input is [bottom power, top power - bottom power * J_top / J_bottom]
James Kuszmaul92797402014-02-17 14:08:49 -080042 # Motor time constants. difference_bottom refers to the constant for how the
43 # bottom velocity affects the difference of the top and bottom velocities.
44 self.common_motor_constant = -self.Kt / self.Kv / (self.G * self.G * self.R)
45 self.bottom_bottom = self.common_motor_constant / self.J_bottom
James Kuszmaulc02a39a2014-02-18 15:45:16 -080046 self.difference_bottom = -self.common_motor_constant * (1 / self.J_bottom
James Kuszmaul92797402014-02-17 14:08:49 -080047 - 1 / self.J_top)
48 self.difference_difference = self.common_motor_constant / self.J_top
Austin Schuhc8ca2442013-02-23 12:29:33 -080049 # State feedback matrices
James Kuszmaule2afbe42014-02-17 22:29:59 -080050
Austin Schuhc8ca2442013-02-23 12:29:33 -080051 self.A_continuous = numpy.matrix(
James Kuszmaulc02a39a2014-02-18 15:45:16 -080052 [[0, 0, 1, 0],
53 [0, 0, 0, 1],
James Kuszmaul92797402014-02-17 14:08:49 -080054 [0, 0, self.bottom_bottom, 0],
55 [0, 0, self.difference_bottom, self.difference_difference]])
Austin Schuh0d9467a2014-02-15 22:36:45 -080056
James Kuszmaule2afbe42014-02-17 22:29:59 -080057 self.A_bottom_cont = numpy.matrix(
58 [[0, 1],
59 [0, self.bottom_bottom]])
60
61 self.A_diff_cont = numpy.matrix(
62 [[0, 1],
63 [0, self.difference_difference]])
64
James Kuszmaul92797402014-02-17 14:08:49 -080065 self.motor_feedforward = self.Kt / (self.G * self.R)
James Kuszmaulf2ed6e62014-02-17 17:52:07 -080066 self.motor_feedforward_bottom = self.motor_feedforward / self.J_bottom
67 self.motor_feedforward_difference = self.motor_feedforward / self.J_top
James Kuszmaul92797402014-02-17 14:08:49 -080068 self.motor_feedforward_difference_bottom = (
69 self.motor_feedforward * (1 / self.J_bottom - 1 / self.J_top))
Austin Schuhc8ca2442013-02-23 12:29:33 -080070 self.B_continuous = numpy.matrix(
Austin Schuh0d9467a2014-02-15 22:36:45 -080071 [[0, 0],
James Kuszmaule2afbe42014-02-17 22:29:59 -080072 [0, 0],
James Kuszmaulc02a39a2014-02-18 15:45:16 -080073 [self.motor_feedforward_bottom, 0],
Austin Schuh170fe252014-02-22 15:52:01 -080074 [-self.motor_feedforward_bottom, self.motor_feedforward_difference]])
James Kuszmaule2afbe42014-02-17 22:29:59 -080075
James Kuszmaulc02a39a2014-02-18 15:45:16 -080076 print "Cont X_ss", self.motor_feedforward / -self.common_motor_constant
77
James Kuszmaule2afbe42014-02-17 22:29:59 -080078 self.B_bottom_cont = numpy.matrix(
79 [[0],
80 [self.motor_feedforward_bottom]])
81
82 self.B_diff_cont = numpy.matrix(
83 [[0],
84 [self.motor_feedforward_difference]])
85
Austin Schuh0d9467a2014-02-15 22:36:45 -080086 self.C = numpy.matrix([[1, 0, 0, 0],
James Kuszmaulc02a39a2014-02-18 15:45:16 -080087 [1, 1, 0, 0]])
Austin Schuh0d9467a2014-02-15 22:36:45 -080088 self.D = numpy.matrix([[0, 0],
89 [0, 0]])
Austin Schuhc8ca2442013-02-23 12:29:33 -080090
Austin Schuhc1f68892013-03-16 17:06:27 -070091 self.A, self.B = self.ContinuousToDiscrete(
92 self.A_continuous, self.B_continuous, self.dt)
Austin Schuhc8ca2442013-02-23 12:29:33 -080093
James Kuszmaule2afbe42014-02-17 22:29:59 -080094 self.A_bottom, self.B_bottom = controls.c2d(
95 self.A_bottom_cont, self.B_bottom_cont, self.dt)
96 self.A_diff, self.B_diff = controls.c2d(
97 self.A_diff_cont, self.B_diff_cont, self.dt)
98
Austin Schuh2758ade2014-02-22 16:58:21 -080099 self.K_bottom = controls.dplace(self.A_bottom, self.B_bottom, [.65, .45])
100 self.K_diff = controls.dplace(self.A_diff, self.B_diff, [.40, .28])
101
102 print "K_diff", self.K_diff
103 print "K_bottom", self.K_bottom
104
James Kuszmaulc02a39a2014-02-18 15:45:16 -0800105 print "A"
106 print self.A
107 print "B"
108 print self.B
James Kuszmaule2afbe42014-02-17 22:29:59 -0800109
James Kuszmaulc02a39a2014-02-18 15:45:16 -0800110
Austin Schuhcda86af2014-02-16 16:16:39 -0800111 self.Q = numpy.matrix([[(1.0 / (0.10 ** 2.0)), 0.0, 0.0, 0.0],
Austin Schuh01c652b2014-02-21 23:13:42 -0800112 [0.0, (1.0 / (0.06 ** 2.0)), 0.0, 0.0],
113 [0.0, 0.0, 0.10, 0.0],
114 [0.0, 0.0, 0.0, 0.1]])
Austin Schuhcda86af2014-02-16 16:16:39 -0800115
James Kuszmaulf2ed6e62014-02-17 17:52:07 -0800116 self.R = numpy.matrix([[(1.0 / (40.0 ** 2.0)), 0.0],
117 [0.0, (1.0 / (5.0 ** 2.0))]])
Austin Schuh170fe252014-02-22 15:52:01 -0800118 #self.K = controls.dlqr(self.A, self.B, self.Q, self.R)
James Kuszmaule2afbe42014-02-17 22:29:59 -0800119
Austin Schuh2758ade2014-02-22 16:58:21 -0800120 self.K = numpy.matrix([[self.K_bottom[0, 0], 0.0, self.K_bottom[0, 1], 0.0],
121 [0.0, self.K_diff[0, 0], 0.0, self.K_diff[0, 1]]])
Austin Schuh170fe252014-02-22 15:52:01 -0800122
123 # Compute the feed forwards aceleration term.
124 self.K[1, 0] = -self.B[1, 0] * self.K[0, 0] / self.B[1, 1]
125
James Kuszmaulc02a39a2014-02-18 15:45:16 -0800126 lstsq_A = numpy.identity(2)
Austin Schuh170fe252014-02-22 15:52:01 -0800127 lstsq_A[0, :] = self.B[1, :]
128 lstsq_A[1, :] = self.B[3, :]
James Kuszmaulc02a39a2014-02-18 15:45:16 -0800129 print "System of Equations coefficients:"
Austin Schuh170fe252014-02-22 15:52:01 -0800130 print lstsq_A
James Kuszmaulc02a39a2014-02-18 15:45:16 -0800131 print "det", numpy.linalg.det(lstsq_A)
Austin Schuh170fe252014-02-22 15:52:01 -0800132
James Kuszmaulc02a39a2014-02-18 15:45:16 -0800133 out_x = numpy.linalg.lstsq(
134 lstsq_A,
135 numpy.matrix([[self.A[1, 2]], [self.A[3, 2]]]))[0]
136 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 -0800137
138 print "K unaugmented"
139 print self.K
James Kuszmaule2afbe42014-02-17 22:29:59 -0800140 print "B * K unaugmented"
141 print self.B * self.K
142 F = self.A - self.B * self.K
James Kuszmaule2afbe42014-02-17 22:29:59 -0800143 print "A - B * K unaugmented"
144 print F
145 print "eigenvalues"
146 print numpy.linalg.eig(F)[0]
Austin Schuhc8ca2442013-02-23 12:29:33 -0800147
Austin Schuhda825132014-03-02 13:59:59 -0800148 self.rpl = .02
149 self.ipl = 0.004
Austin Schuh3c542312013-02-24 01:53:50 -0800150 self.PlaceObserverPoles([self.rpl + 1j * self.ipl,
Austin Schuh0d9467a2014-02-15 22:36:45 -0800151 self.rpl + 1j * self.ipl,
James Kuszmaule2afbe42014-02-17 22:29:59 -0800152 self.rpl - 1j * self.ipl,
Austin Schuh3c542312013-02-24 01:53:50 -0800153 self.rpl - 1j * self.ipl])
Austin Schuhc8ca2442013-02-23 12:29:33 -0800154
James Kuszmaul92797402014-02-17 14:08:49 -0800155 # The box formed by U_min and U_max must encompass all possible values,
156 # or else Austin's code gets angry.
James Kuszmauld536a402014-02-18 22:32:12 -0800157 self.U_max = numpy.matrix([[12.0], [12.0]])
158 self.U_min = numpy.matrix([[-12.0], [-12.0]])
Austin Schuhc8ca2442013-02-23 12:29:33 -0800159
Austin Schuh2758ade2014-02-22 16:58:21 -0800160 # Compute the steady state velocities for a given applied voltage.
161 # The top and bottom of the claw should spin at the same rate if the
162 # physics is right.
163 X_ss = numpy.matrix([[0], [0], [0.0], [0]])
164
165 U = numpy.matrix([[1.0],[1.0]])
166 A = self.A
167 B = self.B
168 #X_ss[2, 0] = X_ss[2, 0] * A[2, 2] + B[2, 0] * U[0, 0]
169 X_ss[2, 0] = 1 / (1 - A[2, 2]) * B[2, 0] * U[0, 0]
170 #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]
171 #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]
172 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])
173 #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]
174 X_ss[0, 0] = A[0, 2] * X_ss[2, 0] + B[0, 0] * U[0, 0]
175 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]
176
177 print "X_ss", X_ss
178
Austin Schuhfe6d37e2014-03-24 09:45:01 -0700179 self.U_poly = polytope.HPolytope(
180 numpy.matrix([[1, 0],
181 [-1, 0],
182 [0, 1],
183 [0, -1]]),
184 numpy.matrix([[12],
185 [12],
186 [12],
187 [12]]))
188
Austin Schuhc1f68892013-03-16 17:06:27 -0700189 self.InitializeState()
190
191
Austin Schuh3bb9a442014-02-02 16:01:45 -0800192class ClawDeltaU(Claw):
193 def __init__(self, name="Claw"):
194 super(ClawDeltaU, self).__init__(name)
Austin Schuhc1f68892013-03-16 17:06:27 -0700195 A_unaugmented = self.A
196 B_unaugmented = self.B
Austin Schuh0d9467a2014-02-15 22:36:45 -0800197 C_unaugmented = self.C
Austin Schuhc1f68892013-03-16 17:06:27 -0700198
Austin Schuh0d9467a2014-02-15 22:36:45 -0800199 self.A = numpy.matrix([[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, 0.0],
203 [0.0, 0.0, 0.0, 0.0, 1.0]])
204 self.A[0:4, 0:4] = A_unaugmented
205 self.A[0:4, 4] = B_unaugmented[0:4, 0]
Austin Schuhc1f68892013-03-16 17:06:27 -0700206
Austin Schuh0d9467a2014-02-15 22:36:45 -0800207 self.B = numpy.matrix([[0.0, 0.0],
208 [0.0, 0.0],
209 [0.0, 0.0],
210 [0.0, 0.0],
211 [1.0, 0.0]])
212 self.B[0:4, 1] = B_unaugmented[0:4, 1]
Austin Schuhc1f68892013-03-16 17:06:27 -0700213
Austin Schuh0d9467a2014-02-15 22:36:45 -0800214 self.C = numpy.concatenate((C_unaugmented, numpy.matrix([[0.0], [0.0]])),
215 axis=1)
216 self.D = numpy.matrix([[0.0, 0.0],
217 [0.0, 0.0]])
Austin Schuhc1f68892013-03-16 17:06:27 -0700218
Austin Schuh0d9467a2014-02-15 22:36:45 -0800219 #self.PlaceControllerPoles([0.55, 0.35, 0.55, 0.35, 0.80])
220 self.Q = numpy.matrix([[(1.0 / (0.04 ** 2.0)), 0.0, 0.0, 0.0, 0.0],
221 [0.0, (1.0 / (0.01 ** 2)), 0.0, 0.0, 0.0],
222 [0.0, 0.0, 0.01, 0.0, 0.0],
223 [0.0, 0.0, 0.0, 0.08, 0.0],
224 [0.0, 0.0, 0.0, 0.0, (1.0 / (10.0 ** 2))]])
225
226 self.R = numpy.matrix([[0.000001, 0.0],
227 [0.0, 1.0 / (10.0 ** 2.0)]])
228 self.K = controls.dlqr(self.A, self.B, self.Q, self.R)
229
230 self.K = numpy.matrix([[50.0, 0.0, 10.0, 0.0, 1.0],
231 [50.0, 0.0, 10.0, 0.0, 1.0]])
232 #self.K = numpy.matrix([[50.0, -100.0, 0, -10, 0],
233 # [50.0, 100.0, 0, 10, 0]])
234
235 controlability = controls.ctrb(self.A, self.B);
236 print "Rank of augmented controlability matrix.", numpy.linalg.matrix_rank(controlability)
Austin Schuhc1f68892013-03-16 17:06:27 -0700237
238 print "K"
239 print self.K
240 print "Placed controller poles are"
241 print numpy.linalg.eig(self.A - self.B * self.K)[0]
Austin Schuh0d9467a2014-02-15 22:36:45 -0800242 print [numpy.abs(x) for x in numpy.linalg.eig(self.A - self.B * self.K)[0]]
Austin Schuhc1f68892013-03-16 17:06:27 -0700243
244 self.rpl = .05
245 self.ipl = 0.008
Austin Schuh0d9467a2014-02-15 22:36:45 -0800246 self.PlaceObserverPoles([self.rpl + 1j * self.ipl, 0.10, 0.09,
Brian Silverman23a67ca2013-03-16 23:48:50 -0700247 self.rpl - 1j * self.ipl, 0.90])
Austin Schuh0d9467a2014-02-15 22:36:45 -0800248 #print "A is"
249 #print self.A
250 #print "L is"
251 #print self.L
252 #print "C is"
253 #print self.C
254 #print "A - LC is"
255 #print self.A - self.L * self.C
Austin Schuhc1f68892013-03-16 17:06:27 -0700256
Austin Schuh0d9467a2014-02-15 22:36:45 -0800257 #print "Placed observer poles are"
258 #print numpy.linalg.eig(self.A - self.L * self.C)[0]
259
260 self.U_max = numpy.matrix([[12.0], [12.0]])
261 self.U_min = numpy.matrix([[-12.0], [-12.0]])
Austin Schuhc1f68892013-03-16 17:06:27 -0700262
263 self.InitializeState()
264
265
Austin Schuhcda86af2014-02-16 16:16:39 -0800266def FullSeparationPriority(claw, U):
267 bottom_u = U[0, 0]
Austin Schuh2758ade2014-02-22 16:58:21 -0800268 top_u = U[1, 0]
Austin Schuhcda86af2014-02-16 16:16:39 -0800269
Austin Schuhcda86af2014-02-16 16:16:39 -0800270 if bottom_u > claw.U_max[0, 0]:
Austin Schuhcda86af2014-02-16 16:16:39 -0800271 top_u -= bottom_u - claw.U_max[0, 0]
272 if top_u < claw.U_min[1, 0]:
273 top_u = claw.U_min[1, 0]
274
275 bottom_u = claw.U_max[0, 0]
276 if top_u > claw.U_max[1, 0]:
277 bottom_u -= top_u - claw.U_max[1, 0]
278 if bottom_u < claw.U_min[0, 0]:
279 bottom_u = claw.U_min[0, 0]
280
281 top_u = claw.U_max[1, 0]
282 if top_u < claw.U_min[1, 0]:
283 bottom_u -= top_u - claw.U_min[1, 0]
284 if bottom_u > claw.U_max[0, 0]:
285 bottom_u = claw.U_max[0, 0]
286
287 top_u = claw.U_min[1, 0]
288 if bottom_u < claw.U_min[0, 0]:
289 top_u -= bottom_u - claw.U_min[0, 0]
290 if top_u > claw.U_max[1, 0]:
291 top_u = claw.U_max[1, 0]
292
293 bottom_u = claw.U_min[0, 0]
294
Austin Schuh2758ade2014-02-22 16:58:21 -0800295 return numpy.matrix([[bottom_u], [top_u]])
Austin Schuhcda86af2014-02-16 16:16:39 -0800296
Austin Schuhfe6d37e2014-03-24 09:45:01 -0700297def ScaleU(claw, U, K, error):
298 """Clips U as necessary.
299
300 Args:
301 claw: claw object containing moments of inertia and U limits.
302 U: Input matrix to clip as necessary.
303 """
304 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
341 adjusted_pos_error = polydrivetrain.CoerceGoal(pos_poly, numpy.matrix([[position_error[1, 0], -position_error[0, 0]]]), 0.0, position_error)
342
343 return velocity_K * velocity_error + position_K * adjusted_pos_error
344
345 #U = Kpos * poserror + Kvel * velerror
346
347 #scalar = claw.U_max[0, 0] / max(numpy.abs(top_u), numpy.abs(bottom_u))
348
349 #top_u *= scalar
350 #bottom_u *= scalar
351
352 return numpy.matrix([[bottom_u], [top_u]])
353
James Kuszmaulc02a39a2014-02-18 15:45:16 -0800354def AverageUFix(claw, U, preserve_v3=False):
355 """Clips U as necessary.
Austin Schuhcda86af2014-02-16 16:16:39 -0800356
James Kuszmaulc02a39a2014-02-18 15:45:16 -0800357 Args:
358 claw: claw object containing moments of inertia and U limits.
359 U: Input matrix to clip as necessary.
360 preserve_v3: There are two ways to attempt to clip the voltages:
Austin Schuh170fe252014-02-22 15:52:01 -0800361 -If you preserve the imaginary v3, then it will attempt to
James Kuszmaulc02a39a2014-02-18 15:45:16 -0800362 preserve the effect on the separation of the two claws.
363 If it is not able to do this, then it calls itself with preserve_v3
364 set to False.
365 -If you preserve the ratio of the voltage of the bottom and the top,
366 then it will attempt to preserve the ratio of those two. This is
367 equivalent to preserving the ratio of v3 and the bottom voltage.
368 """
369 bottom_u = U[0, 0]
370 top_u = U[1, 0]
371 seperation_u = top_u - bottom_u * claw.J_top / claw.J_bottom
372
Austin Schuh2758ade2014-02-22 16:58:21 -0800373 bottom_bad = bottom_u > claw.U_max[0, 0] or bottom_u < claw.U_min[0, 0]
Austin Schuh170fe252014-02-22 15:52:01 -0800374 top_bad = top_u > claw.U_max[0, 0] or top_u < claw.U_min[0, 0]
375
James Kuszmaulc02a39a2014-02-18 15:45:16 -0800376 scalar = claw.U_max[0, 0] / max(numpy.abs(top_u), numpy.abs(bottom_u))
377 if bottom_bad and preserve_v3:
378 bottom_u *= scalar
379 top_u = seperation_u + bottom_u * claw.J_top / claw.J_bottom
380 if abs(top_u) > claw.U_max[0, 0]:
381 return AverageUFix(claw, U, preserve_v3=False)
382 elif top_bad and preserve_v3:
383 top_u *= scalar
384 bottom_u = (top_u - seperation_u) * claw.J_bottom / claw.J_top
385 if abs(bottom_u) > claw.U_max[0, 0]:
386 return AverageUFix(claw, U, preserve_v3=False)
387 elif (bottom_bad or top_bad) and not preserve_v3:
Austin Schuhcda86af2014-02-16 16:16:39 -0800388 top_u *= scalar
389 bottom_u *= scalar
Austin Schuh2758ade2014-02-22 16:58:21 -0800390 print "Scaling"
Austin Schuhcda86af2014-02-16 16:16:39 -0800391
James Kuszmaulc02a39a2014-02-18 15:45:16 -0800392 return numpy.matrix([[bottom_u], [top_u]])
Austin Schuhcda86af2014-02-16 16:16:39 -0800393
Austin Schuh0d9467a2014-02-15 22:36:45 -0800394def ClipDeltaU(claw, U):
395 delta_u = U[0, 0]
396 top_u = U[1, 0]
397 old_bottom_u = claw.X[4, 0]
398
399 # TODO(austin): Preserve the difference between the top and bottom power.
400 new_unclipped_bottom_u = old_bottom_u + delta_u
401
402 #print "Bottom is", new_unclipped_bottom_u, "Top is", top_u
403 if new_unclipped_bottom_u > claw.U_max[0, 0]:
Austin Schuh0d9467a2014-02-15 22:36:45 -0800404 top_u -= new_unclipped_bottom_u - claw.U_max[0, 0]
405 new_unclipped_bottom_u = claw.U_max[0, 0]
406 if top_u > claw.U_max[1, 0]:
407 new_unclipped_bottom_u -= top_u - claw.U_max[1, 0]
408 top_u = claw.U_max[1, 0]
409 if top_u < claw.U_min[1, 0]:
410 new_unclipped_bottom_u -= top_u - claw.U_min[1, 0]
411 top_u = claw.U_min[1, 0]
412 if new_unclipped_bottom_u < claw.U_min[0, 0]:
413 top_u -= new_unclipped_bottom_u - claw.U_min[0, 0]
414 new_unclipped_bottom_u = claw.U_min[0, 0]
415
416 new_bottom_u = numpy.clip(new_unclipped_bottom_u, claw.U_min[0, 0], claw.U_max[0, 0])
417 new_top_u = numpy.clip(top_u, claw.U_min[1, 0], claw.U_max[1, 0])
418
419 return numpy.matrix([[new_bottom_u - old_bottom_u], [new_top_u]])
Austin Schuhc1f68892013-03-16 17:06:27 -0700420
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700421def run_test(claw, initial_X, goal, max_separation_error=0.01, show_graph=True, iterations=200):
422 """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 -0800423
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700424 The tests themselves are not terribly sophisticated; I just test for
425 whether the goal has been reached and whether the separation goes
426 outside of the initial and goal values by more then max_separation_error.
427 Prints out something for a failure of either condition and returns
428 False if tests fail.
429 Args:
430 claw: claw object to use.
431 initial_X: starting state.
432 goal: goal state.
433 show_graph: Whether or not to display a graph showing the changing
434 states and voltages.
435 iterations: Number of timesteps to run the model for."""
Austin Schuhc8ca2442013-02-23 12:29:33 -0800436
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700437 claw.X = initial_X
438
439 # Various lists for graphing things.
Austin Schuhcda86af2014-02-16 16:16:39 -0800440 t = []
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700441 x_bottom = []
442 x_top = []
443 u_bottom = []
444 u_top = []
445 x_separation = []
Austin Schuhc8ca2442013-02-23 12:29:33 -0800446
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700447 tests_passed = True
448
449 # Bounds which separation should not exceed.
450 lower_bound = (initial_X[1, 0] if initial_X[1, 0] < goal[1, 0]
451 else goal[1, 0]) - max_separation_error
452 upper_bound = (initial_X[1, 0] if initial_X[1, 0] > goal[1, 0]
453 else goal[1, 0]) + max_separation_error
454
455 for i in xrange(iterations):
456 U = claw.K * (goal - claw.X)
457 U = ScaleU(claw, U, claw.K, goal - claw.X)
458 claw.Update(U)
459
460 if claw.X[1, 0] > upper_bound or claw.X[1, 0] < lower_bound:
461 tests_passed = False
462 print "Claw separation was", claw.X[1, 0]
463 print "Should have been between", lower_bound, "and", upper_bound
464
465 t.append(i * claw.dt)
466 x_bottom.append(claw.X[0, 0] * 10.0)
467 x_top.append((claw.X[1, 0] + claw.X[0, 0]) * 10.0)
468 u_bottom.append(U[0, 0])
469 u_top.append(U[1, 0])
470 x_separation.append(claw.X[1, 0] * 10.0)
471
472 if show_graph:
473 pylab.plot(t, x_bottom, label='x bottom * 10')
474 pylab.plot(t, x_top, label='x top * 10')
475 pylab.plot(t, u_bottom, label='u bottom')
476 pylab.plot(t, u_top, label='u top')
477 pylab.plot(t, x_separation, label='separation * 10')
478 pylab.legend()
479 pylab.show()
480
481 # Test to make sure that we are near the goal.
482 if numpy.max(abs(claw.X - goal)) > 1e-4:
483 tests_passed = False
484 print "X was", claw.X, "Expected", goal
485
486 return tests_passed
487
488def main(argv):
489 claw = Claw()
490
491 # Test moving the claw with constant separation.
492 initial_X = numpy.matrix([[-1.0], [0.0], [0.0], [0.0]])
493 R = numpy.matrix([[1.0], [0.0], [0.0], [0.0]])
494 run_test(claw, initial_X, R)
495
496 # Test just changing separation.
497 initial_X = numpy.matrix([[0.0], [0.0], [0.0], [0.0]])
498 R = numpy.matrix([[0.0], [1.0], [0.0], [0.0]])
499 run_test(claw, initial_X, R)
500
501 # Test changing both separation and position at once..
502 initial_X = numpy.matrix([[0.0], [0.0], [0.0], [0.0]])
503 R = numpy.matrix([[1.0], [1.0], [0.0], [0.0]])
504 run_test(claw, initial_X, R)
Austin Schuhc8ca2442013-02-23 12:29:33 -0800505
Austin Schuh3c542312013-02-24 01:53:50 -0800506 # Write the generated constants out to a file.
Austin Schuhcda86af2014-02-16 16:16:39 -0800507 if len(argv) != 3:
508 print "Expected .h file name and .cc file name for the claw."
Austin Schuhc8ca2442013-02-23 12:29:33 -0800509 else:
Austin Schuhcda86af2014-02-16 16:16:39 -0800510 claw = Claw("Claw")
511 loop_writer = control_loop.ControlLoopWriter("Claw", [claw])
Austin Schuh683a0d02013-03-02 01:51:31 -0800512 if argv[1][-3:] == '.cc':
Austin Schuhcda86af2014-02-16 16:16:39 -0800513 loop_writer.Write(argv[2], argv[1])
Austin Schuh683a0d02013-03-02 01:51:31 -0800514 else:
Austin Schuhcda86af2014-02-16 16:16:39 -0800515 loop_writer.Write(argv[1], argv[2])
Austin Schuhc8ca2442013-02-23 12:29:33 -0800516
517if __name__ == '__main__':
518 sys.exit(main(sys.argv))