blob: 69f2599cdb5433448c42ab4bfd2c3ad928392e5b [file] [log] [blame]
Brian Silverman17f503e2015-08-02 18:17:18 -07001#!/usr/bin/python
2
3import control_loop
4import numpy
5import sys
6from matplotlib import pylab
7
8class SprungShooter(control_loop.ControlLoop):
9 def __init__(self, name="RawSprungShooter"):
10 super(SprungShooter, self).__init__(name)
11 # Stall Torque in N m
12 self.stall_torque = .4982
13 # Stall Current in Amps
14 self.stall_current = 85
15 # Free Speed in RPM
16 self.free_speed = 19300.0
17 # Free Current in Amps
18 self.free_current = 1.2
19 # Effective mass of the shooter in kg.
20 # This rough estimate should about include the effect of the masses
21 # of the gears. If this number is too low, the eigen values of self.A
22 # will start to become extremely small.
23 self.J = 200
24 # Resistance of the motor, divided by the number of motors.
25 self.R = 12.0 / self.stall_current / 2.0
26 # Motor velocity constant
27 self.Kv = ((self.free_speed / 60.0 * 2.0 * numpy.pi) /
28 (12.0 - self.R * self.free_current))
29 # Torque constant
30 self.Kt = self.stall_torque / self.stall_current
31 # Spring constant for the springs, N/m
32 self.Ks = 2800.0
33 # Maximum extension distance (Distance from the 0 force point on the
34 # spring to the latch position.)
35 self.max_extension = 0.32385
36 # Gear ratio multiplied by radius of final sprocket.
37 self.G = 10.0 / 40.0 * 20.0 / 54.0 * 24.0 / 54.0 * 20.0 / 84.0 * 16.0 * (3.0 / 8.0) / (2.0 * numpy.pi) * 0.0254
38
39 # Control loop time step
40 self.dt = 0.01
41
42 # State feedback matrices
43 self.A_continuous = numpy.matrix(
44 [[0, 1],
45 [-self.Ks / self.J,
46 -self.Kt / self.Kv / (self.J * self.G * self.G * self.R)]])
47 self.B_continuous = numpy.matrix(
48 [[0],
49 [self.Kt / (self.J * self.G * self.R)]])
50 self.C = numpy.matrix([[1, 0]])
51 self.D = numpy.matrix([[0]])
52
53 self.A, self.B = self.ContinuousToDiscrete(
54 self.A_continuous, self.B_continuous, self.dt)
55
56 self.PlaceControllerPoles([0.45, 0.45])
57
58 self.rpl = .05
59 self.ipl = 0.008
60 self.PlaceObserverPoles([self.rpl,
61 self.rpl])
62
63 self.U_max = numpy.matrix([[12.0]])
64 self.U_min = numpy.matrix([[-12.0]])
65
66 self.InitializeState()
67
68
69class Shooter(SprungShooter):
70 def __init__(self, name="RawShooter"):
71 super(Shooter, self).__init__(name)
72
73 # State feedback matrices
74 self.A_continuous = numpy.matrix(
75 [[0, 1],
76 [0, -self.Kt / self.Kv / (self.J * self.G * self.G * self.R)]])
77 self.B_continuous = numpy.matrix(
78 [[0],
79 [self.Kt / (self.J * self.G * self.R)]])
80
81 self.A, self.B = self.ContinuousToDiscrete(
82 self.A_continuous, self.B_continuous, self.dt)
83
84 self.PlaceControllerPoles([0.45, 0.45])
85
86 self.rpl = .05
87 self.ipl = 0.008
88 self.PlaceObserverPoles([self.rpl,
89 self.rpl])
90
91 self.U_max = numpy.matrix([[12.0]])
92 self.U_min = numpy.matrix([[-12.0]])
93
94 self.InitializeState()
95
96
97class SprungShooterDeltaU(SprungShooter):
98 def __init__(self, name="SprungShooter"):
99 super(SprungShooterDeltaU, self).__init__(name)
100 A_unaugmented = self.A
101 B_unaugmented = self.B
102
103 self.A = numpy.matrix([[0.0, 0.0, 0.0],
104 [0.0, 0.0, 0.0],
105 [0.0, 0.0, 1.0]])
106 self.A[0:2, 0:2] = A_unaugmented
107 self.A[0:2, 2] = B_unaugmented
108
109 self.B = numpy.matrix([[0.0],
110 [0.0],
111 [1.0]])
112
113 self.C = numpy.matrix([[1.0, 0.0, 0.0]])
114 self.D = numpy.matrix([[0.0]])
115
116 self.PlaceControllerPoles([0.50, 0.35, 0.80])
117
118 print "K"
119 print self.K
120 print "Placed controller poles are"
121 print numpy.linalg.eig(self.A - self.B * self.K)[0]
122
123 self.rpl = .05
124 self.ipl = 0.008
125 self.PlaceObserverPoles([self.rpl + 1j * self.ipl,
126 self.rpl - 1j * self.ipl, 0.90])
127 print "Placed observer poles are"
128 print numpy.linalg.eig(self.A - self.L * self.C)[0]
129
130 self.U_max = numpy.matrix([[12.0]])
131 self.U_min = numpy.matrix([[-12.0]])
132
133 self.InitializeState()
134
135
136class ShooterDeltaU(Shooter):
137 def __init__(self, name="Shooter"):
138 super(ShooterDeltaU, self).__init__(name)
139 A_unaugmented = self.A
140 B_unaugmented = self.B
141
142 self.A = numpy.matrix([[0.0, 0.0, 0.0],
143 [0.0, 0.0, 0.0],
144 [0.0, 0.0, 1.0]])
145 self.A[0:2, 0:2] = A_unaugmented
146 self.A[0:2, 2] = B_unaugmented
147
148 self.B = numpy.matrix([[0.0],
149 [0.0],
150 [1.0]])
151
152 self.C = numpy.matrix([[1.0, 0.0, 0.0]])
153 self.D = numpy.matrix([[0.0]])
154
155 self.PlaceControllerPoles([0.55, 0.45, 0.80])
156
157 print "K"
158 print self.K
159 print "Placed controller poles are"
160 print numpy.linalg.eig(self.A - self.B * self.K)[0]
161
162 self.rpl = .05
163 self.ipl = 0.008
164 self.PlaceObserverPoles([self.rpl + 1j * self.ipl,
165 self.rpl - 1j * self.ipl, 0.90])
166 print "Placed observer poles are"
167 print numpy.linalg.eig(self.A - self.L * self.C)[0]
168
169 self.U_max = numpy.matrix([[12.0]])
170 self.U_min = numpy.matrix([[-12.0]])
171
172 self.InitializeState()
173
174
175def ClipDeltaU(shooter, old_voltage, delta_u):
176 old_u = old_voltage
177 new_u = numpy.clip(old_u + delta_u, shooter.U_min, shooter.U_max)
178 return new_u - old_u
179
180def main(argv):
181 # Simulate the response of the system to a goal.
182 sprung_shooter = SprungShooterDeltaU()
183 raw_sprung_shooter = SprungShooter()
184 close_loop_x = []
185 close_loop_u = []
186 goal_position = -0.3
187 R = numpy.matrix([[goal_position], [0.0], [-sprung_shooter.A[1, 0] / sprung_shooter.A[1, 2] * goal_position]])
188 voltage = numpy.matrix([[0.0]])
189 for _ in xrange(500):
190 U = sprung_shooter.K * (R - sprung_shooter.X_hat)
191 U = ClipDeltaU(sprung_shooter, voltage, U)
192 sprung_shooter.Y = raw_sprung_shooter.Y + 0.01
193 sprung_shooter.UpdateObserver(U)
194 voltage += U;
195 raw_sprung_shooter.Update(voltage)
196 close_loop_x.append(raw_sprung_shooter.X[0, 0] * 10)
197 close_loop_u.append(voltage[0, 0])
198
199 pylab.plot(range(500), close_loop_x)
200 pylab.plot(range(500), close_loop_u)
201 pylab.show()
202
203 shooter = ShooterDeltaU()
204 raw_shooter = Shooter()
205 close_loop_x = []
206 close_loop_u = []
207 goal_position = -0.3
208 R = numpy.matrix([[goal_position], [0.0], [-shooter.A[1, 0] / shooter.A[1, 2] * goal_position]])
209 voltage = numpy.matrix([[0.0]])
210 for _ in xrange(500):
211 U = shooter.K * (R - shooter.X_hat)
212 U = ClipDeltaU(shooter, voltage, U)
213 shooter.Y = raw_shooter.Y + 0.01
214 shooter.UpdateObserver(U)
215 voltage += U;
216 raw_shooter.Update(voltage)
217 close_loop_x.append(raw_shooter.X[0, 0] * 10)
218 close_loop_u.append(voltage[0, 0])
219
220 pylab.plot(range(500), close_loop_x)
221 pylab.plot(range(500), close_loop_u)
222 pylab.show()
223
224 # Write the generated constants out to a file.
225 if len(argv) != 5:
226 print "Expected .h file name and .cc file name for"
227 print "both the plant and unaugmented plant"
228 else:
229 unaug_sprung_shooter = SprungShooter("RawSprungShooter")
230 unaug_shooter = Shooter("RawShooter")
231 unaug_loop_writer = control_loop.ControlLoopWriter("RawShooter",
232 [unaug_sprung_shooter,
233 unaug_shooter])
234 if argv[3][-3:] == '.cc':
235 unaug_loop_writer.Write(argv[4], argv[3])
236 else:
237 unaug_loop_writer.Write(argv[3], argv[4])
238
239 sprung_shooter = SprungShooterDeltaU()
240 shooter = ShooterDeltaU()
241 loop_writer = control_loop.ControlLoopWriter("Shooter", [sprung_shooter,
242 shooter])
243
244 loop_writer.AddConstant(control_loop.Constant("kMaxExtension", "%f",
245 sprung_shooter.max_extension))
246 loop_writer.AddConstant(control_loop.Constant("kSpringConstant", "%f",
247 sprung_shooter.Ks))
248 if argv[1][-3:] == '.cc':
249 loop_writer.Write(argv[2], argv[1])
250 else:
251 loop_writer.Write(argv[1], argv[2])
252
253if __name__ == '__main__':
254 sys.exit(main(sys.argv))