blob: 6a6bb3e6a9a66360b9916e237020c955e797ff66 [file] [log] [blame]
Brian Silverman17f503e2015-08-02 18:17:18 -07001#!/usr/bin/python
2
Austin Schuha3b42552015-11-27 16:30:12 -08003import gflags
4import glog
Austin Schuh9d4aca82015-11-08 14:41:31 -08005import argparse
Brian Silverman17f503e2015-08-02 18:17:18 -07006import numpy
7import sys
8from matplotlib import pylab
9
Austin Schuha3b42552015-11-27 16:30:12 -080010from frc971.control_loops.python import control_loop
11
12FLAGS = gflags.FLAGS
13
14gflags.DEFINE_bool('plot', False, 'If true, plot the loop response.')
15
Brian Silverman17f503e2015-08-02 18:17:18 -070016class SprungShooter(control_loop.ControlLoop):
Austin Schuha3b42552015-11-27 16:30:12 -080017 def __init__(self, name="RawSprungShooter"):
Brian Silverman17f503e2015-08-02 18:17:18 -070018 super(SprungShooter, self).__init__(name)
19 # Stall Torque in N m
20 self.stall_torque = .4982
21 # Stall Current in Amps
22 self.stall_current = 85
23 # Free Speed in RPM
24 self.free_speed = 19300.0
25 # Free Current in Amps
26 self.free_current = 1.2
27 # Effective mass of the shooter in kg.
28 # This rough estimate should about include the effect of the masses
29 # of the gears. If this number is too low, the eigen values of self.A
30 # will start to become extremely small.
31 self.J = 200
32 # Resistance of the motor, divided by the number of motors.
33 self.R = 12.0 / self.stall_current / 2.0
34 # Motor velocity constant
35 self.Kv = ((self.free_speed / 60.0 * 2.0 * numpy.pi) /
36 (12.0 - self.R * self.free_current))
37 # Torque constant
38 self.Kt = self.stall_torque / self.stall_current
39 # Spring constant for the springs, N/m
40 self.Ks = 2800.0
41 # Maximum extension distance (Distance from the 0 force point on the
42 # spring to the latch position.)
43 self.max_extension = 0.32385
44 # Gear ratio multiplied by radius of final sprocket.
45 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
46
47 # Control loop time step
Austin Schuhadf2cde2015-11-08 20:35:16 -080048 self.dt = 0.005
Brian Silverman17f503e2015-08-02 18:17:18 -070049
50 # State feedback matrices
51 self.A_continuous = numpy.matrix(
52 [[0, 1],
53 [-self.Ks / self.J,
54 -self.Kt / self.Kv / (self.J * self.G * self.G * self.R)]])
55 self.B_continuous = numpy.matrix(
56 [[0],
57 [self.Kt / (self.J * self.G * self.R)]])
58 self.C = numpy.matrix([[1, 0]])
59 self.D = numpy.matrix([[0]])
60
61 self.A, self.B = self.ContinuousToDiscrete(
62 self.A_continuous, self.B_continuous, self.dt)
63
64 self.PlaceControllerPoles([0.45, 0.45])
65
66 self.rpl = .05
67 self.ipl = 0.008
68 self.PlaceObserverPoles([self.rpl,
69 self.rpl])
70
71 self.U_max = numpy.matrix([[12.0]])
72 self.U_min = numpy.matrix([[-12.0]])
73
74 self.InitializeState()
75
76
77class Shooter(SprungShooter):
Austin Schuha3b42552015-11-27 16:30:12 -080078 def __init__(self, name="RawShooter"):
Brian Silverman17f503e2015-08-02 18:17:18 -070079 super(Shooter, self).__init__(name)
80
81 # State feedback matrices
82 self.A_continuous = numpy.matrix(
83 [[0, 1],
84 [0, -self.Kt / self.Kv / (self.J * self.G * self.G * self.R)]])
85 self.B_continuous = numpy.matrix(
86 [[0],
87 [self.Kt / (self.J * self.G * self.R)]])
88
89 self.A, self.B = self.ContinuousToDiscrete(
90 self.A_continuous, self.B_continuous, self.dt)
91
92 self.PlaceControllerPoles([0.45, 0.45])
93
94 self.rpl = .05
95 self.ipl = 0.008
96 self.PlaceObserverPoles([self.rpl,
97 self.rpl])
98
99 self.U_max = numpy.matrix([[12.0]])
100 self.U_min = numpy.matrix([[-12.0]])
101
102 self.InitializeState()
103
104
105class SprungShooterDeltaU(SprungShooter):
Austin Schuha3b42552015-11-27 16:30:12 -0800106 def __init__(self, name="SprungShooter"):
Brian Silverman17f503e2015-08-02 18:17:18 -0700107 super(SprungShooterDeltaU, self).__init__(name)
108 A_unaugmented = self.A
109 B_unaugmented = self.B
110
Austin Schuh6c20f202017-02-18 22:31:44 -0800111 A_continuous_unaugmented = self.A_continuous
112 B_continuous_unaugmented = self.B_continuous
113
114 self.A_continuous = numpy.matrix(numpy.zeros((3, 3)))
115 self.A_continuous[0:2, 0:2] = A_continuous_unaugmented
116 self.A_continuous[0:2, 2] = B_continuous_unaugmented
117
118 self.B_continuous = numpy.matrix(numpy.zeros((3, 1)))
119 self.B_continuous[2, 0] = 1.0 / self.dt
120
Brian Silverman17f503e2015-08-02 18:17:18 -0700121 self.A = numpy.matrix([[0.0, 0.0, 0.0],
122 [0.0, 0.0, 0.0],
123 [0.0, 0.0, 1.0]])
124 self.A[0:2, 0:2] = A_unaugmented
125 self.A[0:2, 2] = B_unaugmented
126
127 self.B = numpy.matrix([[0.0],
128 [0.0],
129 [1.0]])
130
131 self.C = numpy.matrix([[1.0, 0.0, 0.0]])
132 self.D = numpy.matrix([[0.0]])
133
134 self.PlaceControllerPoles([0.50, 0.35, 0.80])
135
Austin Schuha3b42552015-11-27 16:30:12 -0800136 glog.debug('K')
137 glog.debug(str(self.K))
138 glog.debug('Placed controller poles are')
139 glog.debug(str(numpy.linalg.eig(self.A - self.B * self.K)[0]))
Brian Silverman17f503e2015-08-02 18:17:18 -0700140
141 self.rpl = .05
142 self.ipl = 0.008
143 self.PlaceObserverPoles([self.rpl + 1j * self.ipl,
144 self.rpl - 1j * self.ipl, 0.90])
Austin Schuha3b42552015-11-27 16:30:12 -0800145 glog.debug('Placed observer poles are')
146 glog.debug(str(numpy.linalg.eig(self.A - self.L * self.C)[0]))
Brian Silverman17f503e2015-08-02 18:17:18 -0700147
148 self.U_max = numpy.matrix([[12.0]])
149 self.U_min = numpy.matrix([[-12.0]])
150
151 self.InitializeState()
152
153
154class ShooterDeltaU(Shooter):
Austin Schuha3b42552015-11-27 16:30:12 -0800155 def __init__(self, name="Shooter"):
Brian Silverman17f503e2015-08-02 18:17:18 -0700156 super(ShooterDeltaU, self).__init__(name)
157 A_unaugmented = self.A
158 B_unaugmented = self.B
159
Austin Schuh6c20f202017-02-18 22:31:44 -0800160 A_continuous_unaugmented = self.A_continuous
161 B_continuous_unaugmented = self.B_continuous
162
163 self.A_continuous = numpy.matrix(numpy.zeros((3, 3)))
164 self.A_continuous[0:2, 0:2] = A_continuous_unaugmented
165 self.A_continuous[0:2, 2] = B_continuous_unaugmented
166
167 self.B_continuous = numpy.matrix(numpy.zeros((3, 1)))
168 self.B_continuous[2, 0] = 1.0 / self.dt
169
Brian Silverman17f503e2015-08-02 18:17:18 -0700170 self.A = numpy.matrix([[0.0, 0.0, 0.0],
171 [0.0, 0.0, 0.0],
172 [0.0, 0.0, 1.0]])
173 self.A[0:2, 0:2] = A_unaugmented
174 self.A[0:2, 2] = B_unaugmented
175
176 self.B = numpy.matrix([[0.0],
177 [0.0],
178 [1.0]])
179
180 self.C = numpy.matrix([[1.0, 0.0, 0.0]])
181 self.D = numpy.matrix([[0.0]])
182
183 self.PlaceControllerPoles([0.55, 0.45, 0.80])
184
Austin Schuha3b42552015-11-27 16:30:12 -0800185 glog.debug('K')
186 glog.debug(str(self.K))
187 glog.debug('Placed controller poles are')
188 glog.debug(str(numpy.linalg.eig(self.A - self.B * self.K)[0]))
Brian Silverman17f503e2015-08-02 18:17:18 -0700189
190 self.rpl = .05
191 self.ipl = 0.008
192 self.PlaceObserverPoles([self.rpl + 1j * self.ipl,
193 self.rpl - 1j * self.ipl, 0.90])
Austin Schuha3b42552015-11-27 16:30:12 -0800194 glog.debug('Placed observer poles are')
195 glog.debug(str(numpy.linalg.eig(self.A - self.L * self.C)[0]))
Brian Silverman17f503e2015-08-02 18:17:18 -0700196
197 self.U_max = numpy.matrix([[12.0]])
198 self.U_min = numpy.matrix([[-12.0]])
199
200 self.InitializeState()
201
202
203def ClipDeltaU(shooter, old_voltage, delta_u):
204 old_u = old_voltage
205 new_u = numpy.clip(old_u + delta_u, shooter.U_min, shooter.U_max)
206 return new_u - old_u
207
208def main(argv):
Austin Schuha3b42552015-11-27 16:30:12 -0800209 argv = FLAGS(argv)
Austin Schuh9d4aca82015-11-08 14:41:31 -0800210
Brian Silverman17f503e2015-08-02 18:17:18 -0700211 # Simulate the response of the system to a goal.
Austin Schuha3b42552015-11-27 16:30:12 -0800212 sprung_shooter = SprungShooterDeltaU()
213 raw_sprung_shooter = SprungShooter()
Brian Silverman17f503e2015-08-02 18:17:18 -0700214 close_loop_x = []
215 close_loop_u = []
216 goal_position = -0.3
Austin Schuh9d4aca82015-11-08 14:41:31 -0800217 R = numpy.matrix([[goal_position],
218 [0.0],
219 [-sprung_shooter.A[1, 0] / sprung_shooter.A[1, 2] *
220 goal_position]])
Brian Silverman17f503e2015-08-02 18:17:18 -0700221 voltage = numpy.matrix([[0.0]])
222 for _ in xrange(500):
223 U = sprung_shooter.K * (R - sprung_shooter.X_hat)
224 U = ClipDeltaU(sprung_shooter, voltage, U)
225 sprung_shooter.Y = raw_sprung_shooter.Y + 0.01
226 sprung_shooter.UpdateObserver(U)
Brian Silverman4e55e582015-11-10 14:16:37 -0500227 voltage += U
Brian Silverman17f503e2015-08-02 18:17:18 -0700228 raw_sprung_shooter.Update(voltage)
229 close_loop_x.append(raw_sprung_shooter.X[0, 0] * 10)
230 close_loop_u.append(voltage[0, 0])
231
Austin Schuha3b42552015-11-27 16:30:12 -0800232 if FLAGS.plot:
Austin Schuh9d4aca82015-11-08 14:41:31 -0800233 pylab.plot(range(500), close_loop_x)
234 pylab.plot(range(500), close_loop_u)
235 pylab.show()
Brian Silverman17f503e2015-08-02 18:17:18 -0700236
Austin Schuha3b42552015-11-27 16:30:12 -0800237 shooter = ShooterDeltaU()
238 raw_shooter = Shooter()
Brian Silverman17f503e2015-08-02 18:17:18 -0700239 close_loop_x = []
240 close_loop_u = []
241 goal_position = -0.3
242 R = numpy.matrix([[goal_position], [0.0], [-shooter.A[1, 0] / shooter.A[1, 2] * goal_position]])
243 voltage = numpy.matrix([[0.0]])
244 for _ in xrange(500):
245 U = shooter.K * (R - shooter.X_hat)
246 U = ClipDeltaU(shooter, voltage, U)
247 shooter.Y = raw_shooter.Y + 0.01
248 shooter.UpdateObserver(U)
Brian Silverman4e55e582015-11-10 14:16:37 -0500249 voltage += U
Brian Silverman17f503e2015-08-02 18:17:18 -0700250 raw_shooter.Update(voltage)
251 close_loop_x.append(raw_shooter.X[0, 0] * 10)
252 close_loop_u.append(voltage[0, 0])
253
Austin Schuha3b42552015-11-27 16:30:12 -0800254 if FLAGS.plot:
Austin Schuh9d4aca82015-11-08 14:41:31 -0800255 pylab.plot(range(500), close_loop_x)
256 pylab.plot(range(500), close_loop_u)
257 pylab.show()
Brian Silverman17f503e2015-08-02 18:17:18 -0700258
259 # Write the generated constants out to a file.
Austin Schuha3b42552015-11-27 16:30:12 -0800260 unaug_sprung_shooter = SprungShooter("RawSprungShooter")
261 unaug_shooter = Shooter("RawShooter")
Austin Schuh9d4aca82015-11-08 14:41:31 -0800262 namespaces = ['y2014', 'control_loops', 'shooter']
263 unaug_loop_writer = control_loop.ControlLoopWriter("RawShooter",
264 [unaug_sprung_shooter,
265 unaug_shooter],
266 namespaces=namespaces)
Austin Schuha3b42552015-11-27 16:30:12 -0800267 unaug_loop_writer.Write(argv[4], argv[3])
Brian Silverman17f503e2015-08-02 18:17:18 -0700268
Austin Schuha3b42552015-11-27 16:30:12 -0800269 sprung_shooter = SprungShooterDeltaU()
270 shooter = ShooterDeltaU()
Austin Schuh9d4aca82015-11-08 14:41:31 -0800271 loop_writer = control_loop.ControlLoopWriter("Shooter",
272 [sprung_shooter, shooter],
273 namespaces=namespaces)
Brian Silverman17f503e2015-08-02 18:17:18 -0700274
Austin Schuh9d4aca82015-11-08 14:41:31 -0800275 loop_writer.AddConstant(control_loop.Constant("kMaxExtension", "%f",
Brian Silverman17f503e2015-08-02 18:17:18 -0700276 sprung_shooter.max_extension))
Austin Schuh9d4aca82015-11-08 14:41:31 -0800277 loop_writer.AddConstant(control_loop.Constant("kSpringConstant", "%f",
Brian Silverman17f503e2015-08-02 18:17:18 -0700278 sprung_shooter.Ks))
Austin Schuh0e997732015-11-08 15:14:53 -0800279 loop_writer.AddConstant(control_loop.Constant("kDt", "%f",
280 sprung_shooter.dt))
Austin Schuha3b42552015-11-27 16:30:12 -0800281 loop_writer.Write(argv[2], argv[1])
Brian Silverman17f503e2015-08-02 18:17:18 -0700282
283if __name__ == '__main__':
284 sys.exit(main(sys.argv))