blob: b1f378e495de8672d62d0c4a41bfeabe2845a5c2 [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
13 self.stall_torque = 1.4
14 # Stall Current in Amps
15 self.stall_current = 86
16 # Free Speed in RPM
17 self.free_speed = 6200.0
Austin Schuh3c542312013-02-24 01:53:50 -080018 # Free Current in Amps
19 self.free_current = 1.5
Austin Schuh3bb9a442014-02-02 16:01:45 -080020 # Moment of inertia of the claw in kg m^2
Austin Schuh683a0d02013-03-02 01:51:31 -080021 # TODO(aschuh): Measure this in reality. It doesn't seem high enough.
22 # James measured 0.51, but that can't be right given what I am seeing.
Brian Silverman0f637382013-03-03 17:44:46 -080023 self.J = 2.0
Austin Schuhc8ca2442013-02-23 12:29:33 -080024 # Resistance of the motor
25 self.R = 12.0 / self.stall_current + 0.024 + .003
26 # 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
32 self.G = 1.0 / ((84.0 / 20.0) * (50.0 / 14.0) * (40.0 / 14.0) * (40.0 / 12.0))
33 # 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]
38 # Input is [bottom power, top power]
39 # Motor time constant.
40 self.motor_timeconstant = self.Kt / self.Kv / (self.J * self.G * self.G * self.R)
Austin Schuhc8ca2442013-02-23 12:29:33 -080041 # State feedback matrices
42 self.A_continuous = numpy.matrix(
Austin Schuh0d9467a2014-02-15 22:36:45 -080043 [[0, 0, 1, 0],
44 [0, 0, 0, 1],
45 [0, 0, -self.motor_timeconstant, 0],
46 [0, 0, 0, -self.motor_timeconstant]])
47
48 self.motor_feedforward = self.Kt / (self.J * self.G * self.R)
49
Austin Schuhc8ca2442013-02-23 12:29:33 -080050 self.B_continuous = numpy.matrix(
Austin Schuh0d9467a2014-02-15 22:36:45 -080051 [[0, 0],
52 [0, 0],
53 [self.motor_feedforward, 0],
54 [-self.motor_feedforward, self.motor_feedforward]])
55 self.C = numpy.matrix([[1, 0, 0, 0],
56 [1, 1, 0, 0]])
57 self.D = numpy.matrix([[0, 0],
58 [0, 0]])
Austin Schuhc8ca2442013-02-23 12:29:33 -080059
Austin Schuhc1f68892013-03-16 17:06:27 -070060 self.A, self.B = self.ContinuousToDiscrete(
61 self.A_continuous, self.B_continuous, self.dt)
Austin Schuhc8ca2442013-02-23 12:29:33 -080062
Austin Schuh0d9467a2014-02-15 22:36:45 -080063 #controlability = controls.ctrb(self.A, self.B);
64 #print "Rank of controlability matrix.", numpy.linalg.matrix_rank(controlability)
65
66 self.PlaceControllerPoles([0.85, 0.45, 0.45, 0.85])
Austin Schuhc8ca2442013-02-23 12:29:33 -080067
68 self.rpl = .05
69 self.ipl = 0.008
Austin Schuh3c542312013-02-24 01:53:50 -080070 self.PlaceObserverPoles([self.rpl + 1j * self.ipl,
Austin Schuh0d9467a2014-02-15 22:36:45 -080071 self.rpl - 1j * self.ipl,
72 self.rpl + 1j * self.ipl,
Austin Schuh3c542312013-02-24 01:53:50 -080073 self.rpl - 1j * self.ipl])
Austin Schuhc8ca2442013-02-23 12:29:33 -080074
Austin Schuh0d9467a2014-02-15 22:36:45 -080075 self.U_max = numpy.matrix([[12.0], [12.0]])
76 self.U_min = numpy.matrix([[-12.0], [-12.0]])
Austin Schuhc8ca2442013-02-23 12:29:33 -080077
Austin Schuhc1f68892013-03-16 17:06:27 -070078 self.InitializeState()
79
80
Austin Schuh3bb9a442014-02-02 16:01:45 -080081class ClawDeltaU(Claw):
82 def __init__(self, name="Claw"):
83 super(ClawDeltaU, self).__init__(name)
Austin Schuhc1f68892013-03-16 17:06:27 -070084 A_unaugmented = self.A
85 B_unaugmented = self.B
Austin Schuh0d9467a2014-02-15 22:36:45 -080086 C_unaugmented = self.C
Austin Schuhc1f68892013-03-16 17:06:27 -070087
Austin Schuh0d9467a2014-02-15 22:36:45 -080088 self.A = numpy.matrix([[0.0, 0.0, 0.0, 0.0, 0.0],
89 [0.0, 0.0, 0.0, 0.0, 0.0],
90 [0.0, 0.0, 0.0, 0.0, 0.0],
91 [0.0, 0.0, 0.0, 0.0, 0.0],
92 [0.0, 0.0, 0.0, 0.0, 1.0]])
93 self.A[0:4, 0:4] = A_unaugmented
94 self.A[0:4, 4] = B_unaugmented[0:4, 0]
Austin Schuhc1f68892013-03-16 17:06:27 -070095
Austin Schuh0d9467a2014-02-15 22:36:45 -080096 self.B = numpy.matrix([[0.0, 0.0],
97 [0.0, 0.0],
98 [0.0, 0.0],
99 [0.0, 0.0],
100 [1.0, 0.0]])
101 self.B[0:4, 1] = B_unaugmented[0:4, 1]
Austin Schuhc1f68892013-03-16 17:06:27 -0700102
Austin Schuh0d9467a2014-02-15 22:36:45 -0800103 self.C = numpy.concatenate((C_unaugmented, numpy.matrix([[0.0], [0.0]])),
104 axis=1)
105 self.D = numpy.matrix([[0.0, 0.0],
106 [0.0, 0.0]])
Austin Schuhc1f68892013-03-16 17:06:27 -0700107
Austin Schuh0d9467a2014-02-15 22:36:45 -0800108 #self.PlaceControllerPoles([0.55, 0.35, 0.55, 0.35, 0.80])
109 self.Q = numpy.matrix([[(1.0 / (0.04 ** 2.0)), 0.0, 0.0, 0.0, 0.0],
110 [0.0, (1.0 / (0.01 ** 2)), 0.0, 0.0, 0.0],
111 [0.0, 0.0, 0.01, 0.0, 0.0],
112 [0.0, 0.0, 0.0, 0.08, 0.0],
113 [0.0, 0.0, 0.0, 0.0, (1.0 / (10.0 ** 2))]])
114
115 self.R = numpy.matrix([[0.000001, 0.0],
116 [0.0, 1.0 / (10.0 ** 2.0)]])
117 self.K = controls.dlqr(self.A, self.B, self.Q, self.R)
118
119 self.K = numpy.matrix([[50.0, 0.0, 10.0, 0.0, 1.0],
120 [50.0, 0.0, 10.0, 0.0, 1.0]])
121 #self.K = numpy.matrix([[50.0, -100.0, 0, -10, 0],
122 # [50.0, 100.0, 0, 10, 0]])
123
124 controlability = controls.ctrb(self.A, self.B);
125 print "Rank of augmented controlability matrix.", numpy.linalg.matrix_rank(controlability)
Austin Schuhc1f68892013-03-16 17:06:27 -0700126
127 print "K"
128 print self.K
129 print "Placed controller poles are"
130 print numpy.linalg.eig(self.A - self.B * self.K)[0]
Austin Schuh0d9467a2014-02-15 22:36:45 -0800131 print [numpy.abs(x) for x in numpy.linalg.eig(self.A - self.B * self.K)[0]]
Austin Schuhc1f68892013-03-16 17:06:27 -0700132
133 self.rpl = .05
134 self.ipl = 0.008
Austin Schuh0d9467a2014-02-15 22:36:45 -0800135 self.PlaceObserverPoles([self.rpl + 1j * self.ipl, 0.10, 0.09,
Brian Silverman23a67ca2013-03-16 23:48:50 -0700136 self.rpl - 1j * self.ipl, 0.90])
Austin Schuh0d9467a2014-02-15 22:36:45 -0800137 #print "A is"
138 #print self.A
139 #print "L is"
140 #print self.L
141 #print "C is"
142 #print self.C
143 #print "A - LC is"
144 #print self.A - self.L * self.C
Austin Schuhc1f68892013-03-16 17:06:27 -0700145
Austin Schuh0d9467a2014-02-15 22:36:45 -0800146 #print "Placed observer poles are"
147 #print numpy.linalg.eig(self.A - self.L * self.C)[0]
148
149 self.U_max = numpy.matrix([[12.0], [12.0]])
150 self.U_min = numpy.matrix([[-12.0], [-12.0]])
Austin Schuhc1f68892013-03-16 17:06:27 -0700151
152 self.InitializeState()
153
154
Austin Schuh0d9467a2014-02-15 22:36:45 -0800155def ClipDeltaU(claw, U):
156 delta_u = U[0, 0]
157 top_u = U[1, 0]
158 old_bottom_u = claw.X[4, 0]
159
160 # TODO(austin): Preserve the difference between the top and bottom power.
161 new_unclipped_bottom_u = old_bottom_u + delta_u
162
163 #print "Bottom is", new_unclipped_bottom_u, "Top is", top_u
164 if new_unclipped_bottom_u > claw.U_max[0, 0]:
165 #print "Bottom is too big. Was", new_unclipped_bottom_u, "changing top by", new_unclipped_bottom_u - claw.U_max[0, 0]
166 top_u -= new_unclipped_bottom_u - claw.U_max[0, 0]
167 new_unclipped_bottom_u = claw.U_max[0, 0]
168 if top_u > claw.U_max[1, 0]:
169 new_unclipped_bottom_u -= top_u - claw.U_max[1, 0]
170 top_u = claw.U_max[1, 0]
171 if top_u < claw.U_min[1, 0]:
172 new_unclipped_bottom_u -= top_u - claw.U_min[1, 0]
173 top_u = claw.U_min[1, 0]
174 if new_unclipped_bottom_u < claw.U_min[0, 0]:
175 top_u -= new_unclipped_bottom_u - claw.U_min[0, 0]
176 new_unclipped_bottom_u = claw.U_min[0, 0]
177
178 new_bottom_u = numpy.clip(new_unclipped_bottom_u, claw.U_min[0, 0], claw.U_max[0, 0])
179 new_top_u = numpy.clip(top_u, claw.U_min[1, 0], claw.U_max[1, 0])
180
181 return numpy.matrix([[new_bottom_u - old_bottom_u], [new_top_u]])
Austin Schuhc1f68892013-03-16 17:06:27 -0700182
Austin Schuhc8ca2442013-02-23 12:29:33 -0800183def main(argv):
Austin Schuh3c542312013-02-24 01:53:50 -0800184 # Simulate the response of the system to a step input.
Austin Schuh0d9467a2014-02-15 22:36:45 -0800185 #claw = ClawDeltaU()
186 #simulated_x = []
187 #for _ in xrange(100):
188 # claw.Update(numpy.matrix([[12.0]]))
189 # simulated_x.append(claw.X[0, 0])
Austin Schuhc8ca2442013-02-23 12:29:33 -0800190
Austin Schuh0d9467a2014-02-15 22:36:45 -0800191 #pylab.plot(range(100), simulated_x)
192 #pylab.show()
Austin Schuhc8ca2442013-02-23 12:29:33 -0800193
Austin Schuh3c542312013-02-24 01:53:50 -0800194 # Simulate the closed loop response of the system to a step input.
Austin Schuh3bb9a442014-02-02 16:01:45 -0800195 top_claw = ClawDeltaU("TopClaw")
Austin Schuh0d9467a2014-02-15 22:36:45 -0800196 close_loop_x_bottom = []
197 close_loop_x_sep = []
198 close_loop_u_bottom = []
199 close_loop_u_top = []
200 R = numpy.matrix([[1.0], [0.0], [0.0], [0.0], [0.0]])
201 top_claw.X[0, 0] = 0
202 for _ in xrange(50):
203 #print "Error is", (R - top_claw.X_hat)
204 U = top_claw.K * (R - top_claw.X_hat)
Austin Schuh3bb9a442014-02-02 16:01:45 -0800205 U = ClipDeltaU(top_claw, U)
206 top_claw.UpdateObserver(U)
207 top_claw.Update(U)
Austin Schuh0d9467a2014-02-15 22:36:45 -0800208 close_loop_x_bottom.append(top_claw.X[0, 0] * 10)
209 close_loop_u_bottom.append(top_claw.X[4, 0])
210 close_loop_x_sep.append(top_claw.X[1, 0] * 10)
211 close_loop_u_top.append(U[1, 0])
Austin Schuhc8ca2442013-02-23 12:29:33 -0800212
Austin Schuh0d9467a2014-02-15 22:36:45 -0800213 pylab.plot(range(50), close_loop_x_bottom, label='x bottom')
214 pylab.plot(range(50), close_loop_u_bottom, label='u bottom')
215 pylab.plot(range(50), close_loop_x_sep, label='seperation')
216 pylab.plot(range(50), close_loop_u_top, label='u top')
217 pylab.legend()
Austin Schuhfa033692013-02-24 01:00:55 -0800218 pylab.show()
Austin Schuhc8ca2442013-02-23 12:29:33 -0800219
Austin Schuh3c542312013-02-24 01:53:50 -0800220 # Write the generated constants out to a file.
Austin Schuh3bb9a442014-02-02 16:01:45 -0800221 if len(argv) != 9:
Austin Schuhc1f68892013-03-16 17:06:27 -0700222 print "Expected .h file name and .cc file name for"
223 print "both the plant and unaugmented plant"
Austin Schuhc8ca2442013-02-23 12:29:33 -0800224 else:
Austin Schuh3bb9a442014-02-02 16:01:45 -0800225 top_unaug_claw = Claw("RawTopClaw")
226 top_unaug_loop_writer = control_loop.ControlLoopWriter("RawTopClaw",
227 [top_unaug_claw])
Austin Schuh683a0d02013-03-02 01:51:31 -0800228 if argv[1][-3:] == '.cc':
Austin Schuh3bb9a442014-02-02 16:01:45 -0800229 top_unaug_loop_writer.Write(argv[2], argv[1])
Austin Schuh683a0d02013-03-02 01:51:31 -0800230 else:
Austin Schuh3bb9a442014-02-02 16:01:45 -0800231 top_unaug_loop_writer.Write(argv[1], argv[2])
232
233 top_loop_writer = control_loop.ControlLoopWriter("TopClaw", [top_claw])
234 if argv[3][-3:] == '.cc':
235 top_loop_writer.Write(argv[4], argv[3])
236 else:
237 top_loop_writer.Write(argv[3], argv[4])
238
239 bottom_claw = ClawDeltaU("BottomClaw")
240 bottom_unaug_claw = Claw("RawBottomClaw")
241 bottom_unaug_loop_writer = control_loop.ControlLoopWriter(
242 "RawBottomClaw", [bottom_unaug_claw])
243 if argv[5][-3:] == '.cc':
244 bottom_unaug_loop_writer.Write(argv[6], argv[5])
245 else:
246 bottom_unaug_loop_writer.Write(argv[5], argv[6])
247
248 bottom_loop_writer = control_loop.ControlLoopWriter("BottomClaw",
249 [bottom_claw])
250 if argv[7][-3:] == '.cc':
251 bottom_loop_writer.Write(argv[8], argv[7])
252 else:
253 bottom_loop_writer.Write(argv[7], argv[8])
Austin Schuhc8ca2442013-02-23 12:29:33 -0800254
255if __name__ == '__main__':
256 sys.exit(main(sys.argv))