blob: 9287dae34d25e37a7305686eb6bc4fba78e5b333 [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
Tyler Chatow6738c362019-02-16 14:12:30 -080016
Brian Silverman17f503e2015-08-02 18:17:18 -070017class SprungShooter(control_loop.ControlLoop):
Brian Silverman17f503e2015-08-02 18:17:18 -070018
Tyler Chatow6738c362019-02-16 14:12:30 -080019 def __init__(self, name="RawSprungShooter"):
20 super(SprungShooter, self).__init__(name)
21 # Stall Torque in N m
22 self.stall_torque = .4982
23 # Stall Current in Amps
24 self.stall_current = 85
25 # Free Speed in RPM
26 self.free_speed = 19300.0
27 # Free Current in Amps
28 self.free_current = 1.2
29 # Effective mass of the shooter in kg.
30 # This rough estimate should about include the effect of the masses
31 # of the gears. If this number is too low, the eigen values of self.A
32 # will start to become extremely small.
33 self.J = 200
34 # Resistance of the motor, divided by the number of motors.
35 self.R = 12.0 / self.stall_current / 2.0
36 # Motor velocity constant
37 self.Kv = ((self.free_speed / 60.0 * 2.0 * numpy.pi) /
38 (12.0 - self.R * self.free_current))
39 # Torque constant
40 self.Kt = self.stall_torque / self.stall_current
41 # Spring constant for the springs, N/m
42 self.Ks = 2800.0
43 # Maximum extension distance (Distance from the 0 force point on the
44 # spring to the latch position.)
45 self.max_extension = 0.32385
46 # Gear ratio multiplied by radius of final sprocket.
47 self.G = 10.0 / 40.0 * 20.0 / 54.0 * 24.0 / 54.0 * 20.0 / 84.0 * 16.0 * (
48 3.0 / 8.0) / (2.0 * numpy.pi) * 0.0254
Brian Silverman17f503e2015-08-02 18:17:18 -070049
Tyler Chatow6738c362019-02-16 14:12:30 -080050 # Control loop time step
51 self.dt = 0.005
Brian Silverman17f503e2015-08-02 18:17:18 -070052
Tyler Chatow6738c362019-02-16 14:12:30 -080053 # State feedback matrices
54 self.A_continuous = numpy.matrix(
55 [[0, 1],
56 [
57 -self.Ks / self.J,
58 -self.Kt / self.Kv / (self.J * self.G * self.G * self.R)
59 ]])
60 self.B_continuous = numpy.matrix(
61 [[0], [self.Kt / (self.J * self.G * self.R)]])
62 self.C = numpy.matrix([[1, 0]])
63 self.D = numpy.matrix([[0]])
Brian Silverman17f503e2015-08-02 18:17:18 -070064
Tyler Chatow6738c362019-02-16 14:12:30 -080065 self.A, self.B = self.ContinuousToDiscrete(self.A_continuous,
66 self.B_continuous, self.dt)
Brian Silverman17f503e2015-08-02 18:17:18 -070067
Tyler Chatow6738c362019-02-16 14:12:30 -080068 self.PlaceControllerPoles([0.45, 0.45])
Brian Silverman17f503e2015-08-02 18:17:18 -070069
Tyler Chatow6738c362019-02-16 14:12:30 -080070 self.rpl = .05
71 self.ipl = 0.008
72 self.PlaceObserverPoles([self.rpl, self.rpl])
Brian Silverman17f503e2015-08-02 18:17:18 -070073
Tyler Chatow6738c362019-02-16 14:12:30 -080074 self.U_max = numpy.matrix([[12.0]])
75 self.U_min = numpy.matrix([[-12.0]])
76
77 self.InitializeState()
Brian Silverman17f503e2015-08-02 18:17:18 -070078
79
80class Shooter(SprungShooter):
Brian Silverman17f503e2015-08-02 18:17:18 -070081
Tyler Chatow6738c362019-02-16 14:12:30 -080082 def __init__(self, name="RawShooter"):
83 super(Shooter, self).__init__(name)
Brian Silverman17f503e2015-08-02 18:17:18 -070084
Tyler Chatow6738c362019-02-16 14:12:30 -080085 # State feedback matrices
86 self.A_continuous = numpy.matrix(
87 [[0, 1],
88 [0, -self.Kt / self.Kv / (self.J * self.G * self.G * self.R)]])
89 self.B_continuous = numpy.matrix(
90 [[0], [self.Kt / (self.J * self.G * self.R)]])
Brian Silverman17f503e2015-08-02 18:17:18 -070091
Tyler Chatow6738c362019-02-16 14:12:30 -080092 self.A, self.B = self.ContinuousToDiscrete(self.A_continuous,
93 self.B_continuous, self.dt)
Brian Silverman17f503e2015-08-02 18:17:18 -070094
Tyler Chatow6738c362019-02-16 14:12:30 -080095 self.PlaceControllerPoles([0.45, 0.45])
Brian Silverman17f503e2015-08-02 18:17:18 -070096
Tyler Chatow6738c362019-02-16 14:12:30 -080097 self.rpl = .05
98 self.ipl = 0.008
99 self.PlaceObserverPoles([self.rpl, self.rpl])
Brian Silverman17f503e2015-08-02 18:17:18 -0700100
Tyler Chatow6738c362019-02-16 14:12:30 -0800101 self.U_max = numpy.matrix([[12.0]])
102 self.U_min = numpy.matrix([[-12.0]])
103
104 self.InitializeState()
Brian Silverman17f503e2015-08-02 18:17:18 -0700105
106
107class SprungShooterDeltaU(SprungShooter):
Brian Silverman17f503e2015-08-02 18:17:18 -0700108
Tyler Chatow6738c362019-02-16 14:12:30 -0800109 def __init__(self, name="SprungShooter"):
110 super(SprungShooterDeltaU, self).__init__(name)
111 A_unaugmented = self.A
112 B_unaugmented = self.B
Austin Schuh6c20f202017-02-18 22:31:44 -0800113
Tyler Chatow6738c362019-02-16 14:12:30 -0800114 A_continuous_unaugmented = self.A_continuous
115 B_continuous_unaugmented = self.B_continuous
Austin Schuh6c20f202017-02-18 22:31:44 -0800116
Tyler Chatow6738c362019-02-16 14:12:30 -0800117 self.A_continuous = numpy.matrix(numpy.zeros((3, 3)))
118 self.A_continuous[0:2, 0:2] = A_continuous_unaugmented
119 self.A_continuous[0:2, 2] = B_continuous_unaugmented
Austin Schuh6c20f202017-02-18 22:31:44 -0800120
Tyler Chatow6738c362019-02-16 14:12:30 -0800121 self.B_continuous = numpy.matrix(numpy.zeros((3, 1)))
122 self.B_continuous[2, 0] = 1.0 / self.dt
Brian Silverman17f503e2015-08-02 18:17:18 -0700123
Tyler Chatow6738c362019-02-16 14:12:30 -0800124 self.A = numpy.matrix([[0.0, 0.0, 0.0], [0.0, 0.0, 0.0],
125 [0.0, 0.0, 1.0]])
126 self.A[0:2, 0:2] = A_unaugmented
127 self.A[0:2, 2] = B_unaugmented
Brian Silverman17f503e2015-08-02 18:17:18 -0700128
Tyler Chatow6738c362019-02-16 14:12:30 -0800129 self.B = numpy.matrix([[0.0], [0.0], [1.0]])
Brian Silverman17f503e2015-08-02 18:17:18 -0700130
Tyler Chatow6738c362019-02-16 14:12:30 -0800131 self.C = numpy.matrix([[1.0, 0.0, 0.0]])
132 self.D = numpy.matrix([[0.0]])
Brian Silverman17f503e2015-08-02 18:17:18 -0700133
Tyler Chatow6738c362019-02-16 14:12:30 -0800134 self.PlaceControllerPoles([0.50, 0.35, 0.80])
Brian Silverman17f503e2015-08-02 18:17:18 -0700135
Tyler Chatow6738c362019-02-16 14:12:30 -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
Tyler Chatow6738c362019-02-16 14:12:30 -0800141 self.rpl = .05
142 self.ipl = 0.008
143 self.PlaceObserverPoles(
144 [self.rpl + 1j * self.ipl, self.rpl - 1j * self.ipl, 0.90])
145 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
Tyler Chatow6738c362019-02-16 14:12:30 -0800148 self.U_max = numpy.matrix([[12.0]])
149 self.U_min = numpy.matrix([[-12.0]])
150
151 self.InitializeState()
Brian Silverman17f503e2015-08-02 18:17:18 -0700152
153
154class ShooterDeltaU(Shooter):
Brian Silverman17f503e2015-08-02 18:17:18 -0700155
Tyler Chatow6738c362019-02-16 14:12:30 -0800156 def __init__(self, name="Shooter"):
157 super(ShooterDeltaU, self).__init__(name)
158 A_unaugmented = self.A
159 B_unaugmented = self.B
Austin Schuh6c20f202017-02-18 22:31:44 -0800160
Tyler Chatow6738c362019-02-16 14:12:30 -0800161 A_continuous_unaugmented = self.A_continuous
162 B_continuous_unaugmented = self.B_continuous
Austin Schuh6c20f202017-02-18 22:31:44 -0800163
Tyler Chatow6738c362019-02-16 14:12:30 -0800164 self.A_continuous = numpy.matrix(numpy.zeros((3, 3)))
165 self.A_continuous[0:2, 0:2] = A_continuous_unaugmented
166 self.A_continuous[0:2, 2] = B_continuous_unaugmented
Austin Schuh6c20f202017-02-18 22:31:44 -0800167
Tyler Chatow6738c362019-02-16 14:12:30 -0800168 self.B_continuous = numpy.matrix(numpy.zeros((3, 1)))
169 self.B_continuous[2, 0] = 1.0 / self.dt
Brian Silverman17f503e2015-08-02 18:17:18 -0700170
Tyler Chatow6738c362019-02-16 14:12:30 -0800171 self.A = numpy.matrix([[0.0, 0.0, 0.0], [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
Brian Silverman17f503e2015-08-02 18:17:18 -0700175
Tyler Chatow6738c362019-02-16 14:12:30 -0800176 self.B = numpy.matrix([[0.0], [0.0], [1.0]])
Brian Silverman17f503e2015-08-02 18:17:18 -0700177
Tyler Chatow6738c362019-02-16 14:12:30 -0800178 self.C = numpy.matrix([[1.0, 0.0, 0.0]])
179 self.D = numpy.matrix([[0.0]])
Brian Silverman17f503e2015-08-02 18:17:18 -0700180
Tyler Chatow6738c362019-02-16 14:12:30 -0800181 self.PlaceControllerPoles([0.55, 0.45, 0.80])
Brian Silverman17f503e2015-08-02 18:17:18 -0700182
Tyler Chatow6738c362019-02-16 14:12:30 -0800183 glog.debug('K')
184 glog.debug(str(self.K))
185 glog.debug('Placed controller poles are')
186 glog.debug(str(numpy.linalg.eig(self.A - self.B * self.K)[0]))
Brian Silverman17f503e2015-08-02 18:17:18 -0700187
Tyler Chatow6738c362019-02-16 14:12:30 -0800188 self.rpl = .05
189 self.ipl = 0.008
190 self.PlaceObserverPoles(
191 [self.rpl + 1j * self.ipl, self.rpl - 1j * self.ipl, 0.90])
192 glog.debug('Placed observer poles are')
193 glog.debug(str(numpy.linalg.eig(self.A - self.L * self.C)[0]))
Brian Silverman17f503e2015-08-02 18:17:18 -0700194
Tyler Chatow6738c362019-02-16 14:12:30 -0800195 self.U_max = numpy.matrix([[12.0]])
196 self.U_min = numpy.matrix([[-12.0]])
197
198 self.InitializeState()
Brian Silverman17f503e2015-08-02 18:17:18 -0700199
200
201def ClipDeltaU(shooter, old_voltage, delta_u):
Tyler Chatow6738c362019-02-16 14:12:30 -0800202 old_u = old_voltage
203 new_u = numpy.clip(old_u + delta_u, shooter.U_min, shooter.U_max)
204 return new_u - old_u
205
Brian Silverman17f503e2015-08-02 18:17:18 -0700206
207def main(argv):
Tyler Chatow6738c362019-02-16 14:12:30 -0800208 argv = FLAGS(argv)
Austin Schuh9d4aca82015-11-08 14:41:31 -0800209
Tyler Chatow6738c362019-02-16 14:12:30 -0800210 # Simulate the response of the system to a goal.
211 sprung_shooter = SprungShooterDeltaU()
212 raw_sprung_shooter = SprungShooter()
213 close_loop_x = []
214 close_loop_u = []
215 goal_position = -0.3
216 R = numpy.matrix(
217 [[goal_position], [0.0],
218 [-sprung_shooter.A[1, 0] / sprung_shooter.A[1, 2] * goal_position]])
219 voltage = numpy.matrix([[0.0]])
220 for _ in xrange(500):
221 U = sprung_shooter.K * (R - sprung_shooter.X_hat)
222 U = ClipDeltaU(sprung_shooter, voltage, U)
223 sprung_shooter.Y = raw_sprung_shooter.Y + 0.01
224 sprung_shooter.UpdateObserver(U)
225 voltage += U
226 raw_sprung_shooter.Update(voltage)
227 close_loop_x.append(raw_sprung_shooter.X[0, 0] * 10)
228 close_loop_u.append(voltage[0, 0])
Brian Silverman17f503e2015-08-02 18:17:18 -0700229
Tyler Chatow6738c362019-02-16 14:12:30 -0800230 if FLAGS.plot:
231 pylab.plot(range(500), close_loop_x)
232 pylab.plot(range(500), close_loop_u)
233 pylab.show()
Brian Silverman17f503e2015-08-02 18:17:18 -0700234
Tyler Chatow6738c362019-02-16 14:12:30 -0800235 shooter = ShooterDeltaU()
236 raw_shooter = Shooter()
237 close_loop_x = []
238 close_loop_u = []
239 goal_position = -0.3
240 R = numpy.matrix([[goal_position], [0.0],
241 [-shooter.A[1, 0] / shooter.A[1, 2] * goal_position]])
242 voltage = numpy.matrix([[0.0]])
243 for _ in xrange(500):
244 U = shooter.K * (R - shooter.X_hat)
245 U = ClipDeltaU(shooter, voltage, U)
246 shooter.Y = raw_shooter.Y + 0.01
247 shooter.UpdateObserver(U)
248 voltage += U
249 raw_shooter.Update(voltage)
250 close_loop_x.append(raw_shooter.X[0, 0] * 10)
251 close_loop_u.append(voltage[0, 0])
Brian Silverman17f503e2015-08-02 18:17:18 -0700252
Tyler Chatow6738c362019-02-16 14:12:30 -0800253 if FLAGS.plot:
254 pylab.plot(range(500), close_loop_x)
255 pylab.plot(range(500), close_loop_u)
256 pylab.show()
Brian Silverman17f503e2015-08-02 18:17:18 -0700257
Tyler Chatow6738c362019-02-16 14:12:30 -0800258 # Write the generated constants out to a file.
259 unaug_sprung_shooter = SprungShooter("RawSprungShooter")
260 unaug_shooter = Shooter("RawShooter")
261 namespaces = ['y2014', 'control_loops', 'shooter']
262 unaug_loop_writer = control_loop.ControlLoopWriter(
263 "RawShooter", [unaug_sprung_shooter, unaug_shooter],
264 namespaces=namespaces)
265 unaug_loop_writer.Write(argv[4], argv[3])
Brian Silverman17f503e2015-08-02 18:17:18 -0700266
Tyler Chatow6738c362019-02-16 14:12:30 -0800267 sprung_shooter = SprungShooterDeltaU()
268 shooter = ShooterDeltaU()
269 loop_writer = control_loop.ControlLoopWriter(
270 "Shooter", [sprung_shooter, shooter], namespaces=namespaces)
Brian Silverman17f503e2015-08-02 18:17:18 -0700271
Tyler Chatow6738c362019-02-16 14:12:30 -0800272 loop_writer.AddConstant(
273 control_loop.Constant("kMaxExtension", "%f",
274 sprung_shooter.max_extension))
275 loop_writer.AddConstant(
276 control_loop.Constant("kSpringConstant", "%f", sprung_shooter.Ks))
277 loop_writer.AddConstant(
278 control_loop.Constant("kDt", "%f", sprung_shooter.dt))
279 loop_writer.Write(argv[2], argv[1])
280
Brian Silverman17f503e2015-08-02 18:17:18 -0700281
282if __name__ == '__main__':
Tyler Chatow6738c362019-02-16 14:12:30 -0800283 sys.exit(main(sys.argv))