blob: cfb7e3508e801566c854750a148bde6885dabac0 [file] [log] [blame]
Brian Silverman17f503e2015-08-02 18:17:18 -07001#!/usr/bin/python
2
Austin Schuh9d4aca82015-11-08 14:41:31 -08003from frc971.control_loops.python import control_loop
4import argparse
Brian Silverman17f503e2015-08-02 18:17:18 -07005import numpy
6import sys
7from matplotlib import pylab
8
9class SprungShooter(control_loop.ControlLoop):
10 def __init__(self, name="RawSprungShooter"):
11 super(SprungShooter, self).__init__(name)
12 # Stall Torque in N m
13 self.stall_torque = .4982
14 # Stall Current in Amps
15 self.stall_current = 85
16 # Free Speed in RPM
17 self.free_speed = 19300.0
18 # Free Current in Amps
19 self.free_current = 1.2
20 # Effective mass of the shooter in kg.
21 # This rough estimate should about include the effect of the masses
22 # of the gears. If this number is too low, the eigen values of self.A
23 # will start to become extremely small.
24 self.J = 200
25 # Resistance of the motor, divided by the number of motors.
26 self.R = 12.0 / self.stall_current / 2.0
27 # Motor velocity constant
28 self.Kv = ((self.free_speed / 60.0 * 2.0 * numpy.pi) /
29 (12.0 - self.R * self.free_current))
30 # Torque constant
31 self.Kt = self.stall_torque / self.stall_current
32 # Spring constant for the springs, N/m
33 self.Ks = 2800.0
34 # Maximum extension distance (Distance from the 0 force point on the
35 # spring to the latch position.)
36 self.max_extension = 0.32385
37 # Gear ratio multiplied by radius of final sprocket.
38 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
39
40 # Control loop time step
41 self.dt = 0.01
42
43 # State feedback matrices
44 self.A_continuous = numpy.matrix(
45 [[0, 1],
46 [-self.Ks / self.J,
47 -self.Kt / self.Kv / (self.J * self.G * self.G * self.R)]])
48 self.B_continuous = numpy.matrix(
49 [[0],
50 [self.Kt / (self.J * self.G * self.R)]])
51 self.C = numpy.matrix([[1, 0]])
52 self.D = numpy.matrix([[0]])
53
54 self.A, self.B = self.ContinuousToDiscrete(
55 self.A_continuous, self.B_continuous, self.dt)
56
57 self.PlaceControllerPoles([0.45, 0.45])
58
59 self.rpl = .05
60 self.ipl = 0.008
61 self.PlaceObserverPoles([self.rpl,
62 self.rpl])
63
64 self.U_max = numpy.matrix([[12.0]])
65 self.U_min = numpy.matrix([[-12.0]])
66
67 self.InitializeState()
68
69
70class Shooter(SprungShooter):
71 def __init__(self, name="RawShooter"):
72 super(Shooter, self).__init__(name)
73
74 # State feedback matrices
75 self.A_continuous = numpy.matrix(
76 [[0, 1],
77 [0, -self.Kt / self.Kv / (self.J * self.G * self.G * self.R)]])
78 self.B_continuous = numpy.matrix(
79 [[0],
80 [self.Kt / (self.J * self.G * self.R)]])
81
82 self.A, self.B = self.ContinuousToDiscrete(
83 self.A_continuous, self.B_continuous, self.dt)
84
85 self.PlaceControllerPoles([0.45, 0.45])
86
87 self.rpl = .05
88 self.ipl = 0.008
89 self.PlaceObserverPoles([self.rpl,
90 self.rpl])
91
92 self.U_max = numpy.matrix([[12.0]])
93 self.U_min = numpy.matrix([[-12.0]])
94
95 self.InitializeState()
96
97
98class SprungShooterDeltaU(SprungShooter):
99 def __init__(self, name="SprungShooter"):
100 super(SprungShooterDeltaU, self).__init__(name)
101 A_unaugmented = self.A
102 B_unaugmented = self.B
103
104 self.A = numpy.matrix([[0.0, 0.0, 0.0],
105 [0.0, 0.0, 0.0],
106 [0.0, 0.0, 1.0]])
107 self.A[0:2, 0:2] = A_unaugmented
108 self.A[0:2, 2] = B_unaugmented
109
110 self.B = numpy.matrix([[0.0],
111 [0.0],
112 [1.0]])
113
114 self.C = numpy.matrix([[1.0, 0.0, 0.0]])
115 self.D = numpy.matrix([[0.0]])
116
117 self.PlaceControllerPoles([0.50, 0.35, 0.80])
118
119 print "K"
120 print self.K
121 print "Placed controller poles are"
122 print numpy.linalg.eig(self.A - self.B * self.K)[0]
123
124 self.rpl = .05
125 self.ipl = 0.008
126 self.PlaceObserverPoles([self.rpl + 1j * self.ipl,
127 self.rpl - 1j * self.ipl, 0.90])
128 print "Placed observer poles are"
129 print numpy.linalg.eig(self.A - self.L * self.C)[0]
130
131 self.U_max = numpy.matrix([[12.0]])
132 self.U_min = numpy.matrix([[-12.0]])
133
134 self.InitializeState()
135
136
137class ShooterDeltaU(Shooter):
138 def __init__(self, name="Shooter"):
139 super(ShooterDeltaU, self).__init__(name)
140 A_unaugmented = self.A
141 B_unaugmented = self.B
142
143 self.A = numpy.matrix([[0.0, 0.0, 0.0],
144 [0.0, 0.0, 0.0],
145 [0.0, 0.0, 1.0]])
146 self.A[0:2, 0:2] = A_unaugmented
147 self.A[0:2, 2] = B_unaugmented
148
149 self.B = numpy.matrix([[0.0],
150 [0.0],
151 [1.0]])
152
153 self.C = numpy.matrix([[1.0, 0.0, 0.0]])
154 self.D = numpy.matrix([[0.0]])
155
156 self.PlaceControllerPoles([0.55, 0.45, 0.80])
157
158 print "K"
159 print self.K
160 print "Placed controller poles are"
161 print numpy.linalg.eig(self.A - self.B * self.K)[0]
162
163 self.rpl = .05
164 self.ipl = 0.008
165 self.PlaceObserverPoles([self.rpl + 1j * self.ipl,
166 self.rpl - 1j * self.ipl, 0.90])
167 print "Placed observer poles are"
168 print numpy.linalg.eig(self.A - self.L * self.C)[0]
169
170 self.U_max = numpy.matrix([[12.0]])
171 self.U_min = numpy.matrix([[-12.0]])
172
173 self.InitializeState()
174
175
176def ClipDeltaU(shooter, old_voltage, delta_u):
177 old_u = old_voltage
178 new_u = numpy.clip(old_u + delta_u, shooter.U_min, shooter.U_max)
179 return new_u - old_u
180
181def main(argv):
Austin Schuh9d4aca82015-11-08 14:41:31 -0800182 parser = argparse.ArgumentParser(description='Calculate shooter.')
183 parser.add_argument('--plot', action='store_true', default=False, help='If true, plot')
184 parser.add_argument('shootercc')
185 parser.add_argument('shooterh')
186 parser.add_argument('unaugmented_shootercc')
187 parser.add_argument('unaugmented_shooterh')
188
189 args = parser.parse_args(argv[1:])
190
Brian Silverman17f503e2015-08-02 18:17:18 -0700191 # Simulate the response of the system to a goal.
192 sprung_shooter = SprungShooterDeltaU()
193 raw_sprung_shooter = SprungShooter()
194 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)
207 voltage += U;
208 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 Schuh9d4aca82015-11-08 14:41:31 -0800212 if args.plot:
213 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
217 shooter = ShooterDeltaU()
218 raw_shooter = Shooter()
219 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)
229 voltage += U;
230 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 Schuh9d4aca82015-11-08 14:41:31 -0800234 if args.plot:
235 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 Schuh9d4aca82015-11-08 14:41:31 -0800240 unaug_sprung_shooter = SprungShooter("RawSprungShooter")
241 unaug_shooter = Shooter("RawShooter")
242 namespaces = ['y2014', 'control_loops', 'shooter']
243 unaug_loop_writer = control_loop.ControlLoopWriter("RawShooter",
244 [unaug_sprung_shooter,
245 unaug_shooter],
246 namespaces=namespaces)
247 unaug_loop_writer.Write(args.unaugmented_shooterh,
248 args.unaugmented_shootercc)
Brian Silverman17f503e2015-08-02 18:17:18 -0700249
Austin Schuh9d4aca82015-11-08 14:41:31 -0800250 sprung_shooter = SprungShooterDeltaU()
251 shooter = ShooterDeltaU()
252 loop_writer = control_loop.ControlLoopWriter("Shooter",
253 [sprung_shooter, shooter],
254 namespaces=namespaces)
Brian Silverman17f503e2015-08-02 18:17:18 -0700255
Austin Schuh9d4aca82015-11-08 14:41:31 -0800256 loop_writer.AddConstant(control_loop.Constant("kMaxExtension", "%f",
Brian Silverman17f503e2015-08-02 18:17:18 -0700257 sprung_shooter.max_extension))
Austin Schuh9d4aca82015-11-08 14:41:31 -0800258 loop_writer.AddConstant(control_loop.Constant("kSpringConstant", "%f",
Brian Silverman17f503e2015-08-02 18:17:18 -0700259 sprung_shooter.Ks))
Austin Schuh9d4aca82015-11-08 14:41:31 -0800260 loop_writer.Write(args.shooterh, args.shootercc)
Brian Silverman17f503e2015-08-02 18:17:18 -0700261
262if __name__ == '__main__':
263 sys.exit(main(sys.argv))