blob: a4767d1d6d36e7315a5d7b8939f15737a618e279 [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
111 self.A = numpy.matrix([[0.0, 0.0, 0.0],
112 [0.0, 0.0, 0.0],
113 [0.0, 0.0, 1.0]])
114 self.A[0:2, 0:2] = A_unaugmented
115 self.A[0:2, 2] = B_unaugmented
116
117 self.B = numpy.matrix([[0.0],
118 [0.0],
119 [1.0]])
120
121 self.C = numpy.matrix([[1.0, 0.0, 0.0]])
122 self.D = numpy.matrix([[0.0]])
123
124 self.PlaceControllerPoles([0.50, 0.35, 0.80])
125
Austin Schuha3b42552015-11-27 16:30:12 -0800126 glog.debug('K')
127 glog.debug(str(self.K))
128 glog.debug('Placed controller poles are')
129 glog.debug(str(numpy.linalg.eig(self.A - self.B * self.K)[0]))
Brian Silverman17f503e2015-08-02 18:17:18 -0700130
131 self.rpl = .05
132 self.ipl = 0.008
133 self.PlaceObserverPoles([self.rpl + 1j * self.ipl,
134 self.rpl - 1j * self.ipl, 0.90])
Austin Schuha3b42552015-11-27 16:30:12 -0800135 glog.debug('Placed observer poles are')
136 glog.debug(str(numpy.linalg.eig(self.A - self.L * self.C)[0]))
Brian Silverman17f503e2015-08-02 18:17:18 -0700137
138 self.U_max = numpy.matrix([[12.0]])
139 self.U_min = numpy.matrix([[-12.0]])
140
141 self.InitializeState()
142
143
144class ShooterDeltaU(Shooter):
Austin Schuha3b42552015-11-27 16:30:12 -0800145 def __init__(self, name="Shooter"):
Brian Silverman17f503e2015-08-02 18:17:18 -0700146 super(ShooterDeltaU, self).__init__(name)
147 A_unaugmented = self.A
148 B_unaugmented = self.B
149
150 self.A = numpy.matrix([[0.0, 0.0, 0.0],
151 [0.0, 0.0, 0.0],
152 [0.0, 0.0, 1.0]])
153 self.A[0:2, 0:2] = A_unaugmented
154 self.A[0:2, 2] = B_unaugmented
155
156 self.B = numpy.matrix([[0.0],
157 [0.0],
158 [1.0]])
159
160 self.C = numpy.matrix([[1.0, 0.0, 0.0]])
161 self.D = numpy.matrix([[0.0]])
162
163 self.PlaceControllerPoles([0.55, 0.45, 0.80])
164
Austin Schuha3b42552015-11-27 16:30:12 -0800165 glog.debug('K')
166 glog.debug(str(self.K))
167 glog.debug('Placed controller poles are')
168 glog.debug(str(numpy.linalg.eig(self.A - self.B * self.K)[0]))
Brian Silverman17f503e2015-08-02 18:17:18 -0700169
170 self.rpl = .05
171 self.ipl = 0.008
172 self.PlaceObserverPoles([self.rpl + 1j * self.ipl,
173 self.rpl - 1j * self.ipl, 0.90])
Austin Schuha3b42552015-11-27 16:30:12 -0800174 glog.debug('Placed observer poles are')
175 glog.debug(str(numpy.linalg.eig(self.A - self.L * self.C)[0]))
Brian Silverman17f503e2015-08-02 18:17:18 -0700176
177 self.U_max = numpy.matrix([[12.0]])
178 self.U_min = numpy.matrix([[-12.0]])
179
180 self.InitializeState()
181
182
183def ClipDeltaU(shooter, old_voltage, delta_u):
184 old_u = old_voltage
185 new_u = numpy.clip(old_u + delta_u, shooter.U_min, shooter.U_max)
186 return new_u - old_u
187
188def main(argv):
Austin Schuha3b42552015-11-27 16:30:12 -0800189 argv = FLAGS(argv)
Austin Schuh9d4aca82015-11-08 14:41:31 -0800190
Brian Silverman17f503e2015-08-02 18:17:18 -0700191 # Simulate the response of the system to a goal.
Austin Schuha3b42552015-11-27 16:30:12 -0800192 sprung_shooter = SprungShooterDeltaU()
193 raw_sprung_shooter = SprungShooter()
Brian Silverman17f503e2015-08-02 18:17:18 -0700194 close_loop_x = []
195 close_loop_u = []
196 goal_position = -0.3
Austin Schuh9d4aca82015-11-08 14:41:31 -0800197 R = numpy.matrix([[goal_position],
198 [0.0],
199 [-sprung_shooter.A[1, 0] / sprung_shooter.A[1, 2] *
200 goal_position]])
Brian Silverman17f503e2015-08-02 18:17:18 -0700201 voltage = numpy.matrix([[0.0]])
202 for _ in xrange(500):
203 U = sprung_shooter.K * (R - sprung_shooter.X_hat)
204 U = ClipDeltaU(sprung_shooter, voltage, U)
205 sprung_shooter.Y = raw_sprung_shooter.Y + 0.01
206 sprung_shooter.UpdateObserver(U)
Brian Silverman4e55e582015-11-10 14:16:37 -0500207 voltage += U
Brian Silverman17f503e2015-08-02 18:17:18 -0700208 raw_sprung_shooter.Update(voltage)
209 close_loop_x.append(raw_sprung_shooter.X[0, 0] * 10)
210 close_loop_u.append(voltage[0, 0])
211
Austin Schuha3b42552015-11-27 16:30:12 -0800212 if FLAGS.plot:
Austin Schuh9d4aca82015-11-08 14:41:31 -0800213 pylab.plot(range(500), close_loop_x)
214 pylab.plot(range(500), close_loop_u)
215 pylab.show()
Brian Silverman17f503e2015-08-02 18:17:18 -0700216
Austin Schuha3b42552015-11-27 16:30:12 -0800217 shooter = ShooterDeltaU()
218 raw_shooter = Shooter()
Brian Silverman17f503e2015-08-02 18:17:18 -0700219 close_loop_x = []
220 close_loop_u = []
221 goal_position = -0.3
222 R = numpy.matrix([[goal_position], [0.0], [-shooter.A[1, 0] / shooter.A[1, 2] * goal_position]])
223 voltage = numpy.matrix([[0.0]])
224 for _ in xrange(500):
225 U = shooter.K * (R - shooter.X_hat)
226 U = ClipDeltaU(shooter, voltage, U)
227 shooter.Y = raw_shooter.Y + 0.01
228 shooter.UpdateObserver(U)
Brian Silverman4e55e582015-11-10 14:16:37 -0500229 voltage += U
Brian Silverman17f503e2015-08-02 18:17:18 -0700230 raw_shooter.Update(voltage)
231 close_loop_x.append(raw_shooter.X[0, 0] * 10)
232 close_loop_u.append(voltage[0, 0])
233
Austin Schuha3b42552015-11-27 16:30:12 -0800234 if FLAGS.plot:
Austin Schuh9d4aca82015-11-08 14:41:31 -0800235 pylab.plot(range(500), close_loop_x)
236 pylab.plot(range(500), close_loop_u)
237 pylab.show()
Brian Silverman17f503e2015-08-02 18:17:18 -0700238
239 # Write the generated constants out to a file.
Austin Schuha3b42552015-11-27 16:30:12 -0800240 unaug_sprung_shooter = SprungShooter("RawSprungShooter")
241 unaug_shooter = Shooter("RawShooter")
Austin Schuh9d4aca82015-11-08 14:41:31 -0800242 namespaces = ['y2014', 'control_loops', 'shooter']
243 unaug_loop_writer = control_loop.ControlLoopWriter("RawShooter",
244 [unaug_sprung_shooter,
245 unaug_shooter],
246 namespaces=namespaces)
Austin Schuha3b42552015-11-27 16:30:12 -0800247 unaug_loop_writer.Write(argv[4], argv[3])
Brian Silverman17f503e2015-08-02 18:17:18 -0700248
Austin Schuha3b42552015-11-27 16:30:12 -0800249 sprung_shooter = SprungShooterDeltaU()
250 shooter = ShooterDeltaU()
Austin Schuh9d4aca82015-11-08 14:41:31 -0800251 loop_writer = control_loop.ControlLoopWriter("Shooter",
252 [sprung_shooter, shooter],
253 namespaces=namespaces)
Brian Silverman17f503e2015-08-02 18:17:18 -0700254
Austin Schuh9d4aca82015-11-08 14:41:31 -0800255 loop_writer.AddConstant(control_loop.Constant("kMaxExtension", "%f",
Brian Silverman17f503e2015-08-02 18:17:18 -0700256 sprung_shooter.max_extension))
Austin Schuh9d4aca82015-11-08 14:41:31 -0800257 loop_writer.AddConstant(control_loop.Constant("kSpringConstant", "%f",
Brian Silverman17f503e2015-08-02 18:17:18 -0700258 sprung_shooter.Ks))
Austin Schuh0e997732015-11-08 15:14:53 -0800259 loop_writer.AddConstant(control_loop.Constant("kDt", "%f",
260 sprung_shooter.dt))
Austin Schuha3b42552015-11-27 16:30:12 -0800261 loop_writer.Write(argv[2], argv[1])
Brian Silverman17f503e2015-08-02 18:17:18 -0700262
263if __name__ == '__main__':
264 sys.exit(main(sys.argv))