blob: 6b8c224681757736f73de0bc58941b3aba313714 [file] [log] [blame]
joe2d92e852014-01-25 14:31:24 -08001#!/usr/bin/python
2
3import control_loop
4import numpy
5import sys
6from matplotlib import pylab
7
Austin Schuhd34569d2014-02-18 20:26:38 -08008class SprungShooter(control_loop.ControlLoop):
9 def __init__(self, name="RawSprungShooter"):
10 super(SprungShooter, self).__init__(name)
joe2d92e852014-01-25 14:31:24 -080011 # Stall Torque in N m
James Kuszmaul49d0e6c2014-02-03 19:46:17 -080012 self.stall_torque = .4982
joe2d92e852014-01-25 14:31:24 -080013 # 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
James Kuszmaul49d0e6c2014-02-03 19:46:17 -080018 self.free_current = 1.2
James Kuszmaul6682f342014-02-14 10:21:04 -050019 # 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.
Austin Schuh06cbbf12014-02-22 02:07:31 -080023 self.J = 200
James Kuszmaul49d0e6c2014-02-03 19:46:17 -080024 # Resistance of the motor, divided by the number of motors.
25 self.R = 12.0 / self.stall_current / 2.0
joe2d92e852014-01-25 14:31:24 -080026 # Motor velocity constant
27 self.Kv = ((self.free_speed / 60.0 * 2.0 * numpy.pi) /
James Kuszmaule1755b32014-02-13 06:27:48 -080028 (12.0 - self.R * self.free_current))
joe2d92e852014-01-25 14:31:24 -080029 # Torque constant
30 self.Kt = self.stall_torque / self.stall_current
James Kuszmaul49d0e6c2014-02-03 19:46:17 -080031 # Spring constant for the springs, N/m
James Kuszmaule1755b32014-02-13 06:27:48 -080032 self.Ks = 2800.0
33 # Gear ratio multiplied by radius of final sprocket.
Austin Schuhd34569d2014-02-18 20:26:38 -080034 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
35
joe2d92e852014-01-25 14:31:24 -080036 # Control loop time step
37 self.dt = 0.01
38
39 # State feedback matrices
40 self.A_continuous = numpy.matrix(
41 [[0, 1],
James Kuszmaule1755b32014-02-13 06:27:48 -080042 [-self.Ks / self.J,
James Kuszmaul49d0e6c2014-02-03 19:46:17 -080043 -self.Kt / self.Kv / (self.J * self.G * self.G * self.R)]])
joe2d92e852014-01-25 14:31:24 -080044 self.B_continuous = numpy.matrix(
45 [[0],
46 [self.Kt / (self.J * self.G * self.R)]])
47 self.C = numpy.matrix([[1, 0]])
48 self.D = numpy.matrix([[0]])
49
50 self.A, self.B = self.ContinuousToDiscrete(
51 self.A_continuous, self.B_continuous, self.dt)
52
James Kuszmaul6682f342014-02-14 10:21:04 -050053 self.PlaceControllerPoles([0.45, 0.45])
joe2d92e852014-01-25 14:31:24 -080054
55 self.rpl = .05
56 self.ipl = 0.008
James Kuszmaule1755b32014-02-13 06:27:48 -080057 self.PlaceObserverPoles([self.rpl,
58 self.rpl])
joe2d92e852014-01-25 14:31:24 -080059
60 self.U_max = numpy.matrix([[12.0]])
61 self.U_min = numpy.matrix([[-12.0]])
62
63 self.InitializeState()
64
65
Austin Schuhd34569d2014-02-18 20:26:38 -080066class Shooter(SprungShooter):
67 def __init__(self, name="RawShooter"):
68 super(Shooter, self).__init__(name)
69
70 # State feedback matrices
71 self.A_continuous = numpy.matrix(
72 [[0, 1],
73 [0, -self.Kt / self.Kv / (self.J * self.G * self.G * self.R)]])
74 self.B_continuous = numpy.matrix(
75 [[0],
76 [self.Kt / (self.J * self.G * self.R)]])
77
78 self.A, self.B = self.ContinuousToDiscrete(
79 self.A_continuous, self.B_continuous, self.dt)
80
81 self.PlaceControllerPoles([0.45, 0.45])
82
83 self.rpl = .05
84 self.ipl = 0.008
85 self.PlaceObserverPoles([self.rpl,
86 self.rpl])
87
88 self.U_max = numpy.matrix([[12.0]])
89 self.U_min = numpy.matrix([[-12.0]])
90
91 self.InitializeState()
92
93
94class SprungShooterDeltaU(SprungShooter):
95 def __init__(self, name="SprungShooter"):
96 super(SprungShooterDeltaU, self).__init__(name)
97 A_unaugmented = self.A
98 B_unaugmented = self.B
99
100 self.A = numpy.matrix([[0.0, 0.0, 0.0],
101 [0.0, 0.0, 0.0],
102 [0.0, 0.0, 1.0]])
103 self.A[0:2, 0:2] = A_unaugmented
104 self.A[0:2, 2] = B_unaugmented
105
106 self.B = numpy.matrix([[0.0],
107 [0.0],
108 [1.0]])
109
110 self.C = numpy.matrix([[1.0, 0.0, 0.0]])
111 self.D = numpy.matrix([[0.0]])
112
Austin Schuh06cbbf12014-02-22 02:07:31 -0800113 self.PlaceControllerPoles([0.50, 0.35, 0.80])
Austin Schuhd34569d2014-02-18 20:26:38 -0800114
115 print "K"
116 print self.K
117 print "Placed controller poles are"
118 print numpy.linalg.eig(self.A - self.B * self.K)[0]
119
120 self.rpl = .05
121 self.ipl = 0.008
122 self.PlaceObserverPoles([self.rpl + 1j * self.ipl,
123 self.rpl - 1j * self.ipl, 0.90])
124 print "Placed observer poles are"
125 print numpy.linalg.eig(self.A - self.L * self.C)[0]
126
127 self.U_max = numpy.matrix([[12.0]])
128 self.U_min = numpy.matrix([[-12.0]])
129
130 self.InitializeState()
131
132
joe2d92e852014-01-25 14:31:24 -0800133class ShooterDeltaU(Shooter):
134 def __init__(self, name="Shooter"):
135 super(ShooterDeltaU, self).__init__(name)
136 A_unaugmented = self.A
137 B_unaugmented = self.B
138
139 self.A = numpy.matrix([[0.0, 0.0, 0.0],
140 [0.0, 0.0, 0.0],
141 [0.0, 0.0, 1.0]])
142 self.A[0:2, 0:2] = A_unaugmented
143 self.A[0:2, 2] = B_unaugmented
144
145 self.B = numpy.matrix([[0.0],
146 [0.0],
147 [1.0]])
148
149 self.C = numpy.matrix([[1.0, 0.0, 0.0]])
150 self.D = numpy.matrix([[0.0]])
151
James Kuszmaul49d0e6c2014-02-03 19:46:17 -0800152 self.PlaceControllerPoles([0.55, 0.45, 0.80])
joe2d92e852014-01-25 14:31:24 -0800153
154 print "K"
155 print self.K
156 print "Placed controller poles are"
157 print numpy.linalg.eig(self.A - self.B * self.K)[0]
158
159 self.rpl = .05
160 self.ipl = 0.008
161 self.PlaceObserverPoles([self.rpl + 1j * self.ipl,
162 self.rpl - 1j * self.ipl, 0.90])
163 print "Placed observer poles are"
164 print numpy.linalg.eig(self.A - self.L * self.C)[0]
165
166 self.U_max = numpy.matrix([[12.0]])
167 self.U_min = numpy.matrix([[-12.0]])
168
169 self.InitializeState()
170
171
Austin Schuhd34569d2014-02-18 20:26:38 -0800172def ClipDeltaU(shooter, old_voltage, delta_u):
173 old_u = old_voltage
joe2d92e852014-01-25 14:31:24 -0800174 new_u = numpy.clip(old_u + delta_u, shooter.U_min, shooter.U_max)
175 return new_u - old_u
176
177def main(argv):
James Kuszmaul49d0e6c2014-02-03 19:46:17 -0800178 # Simulate the response of the system to a goal.
Austin Schuhd34569d2014-02-18 20:26:38 -0800179 sprung_shooter = SprungShooterDeltaU()
180 raw_sprung_shooter = SprungShooter()
joe2d92e852014-01-25 14:31:24 -0800181 close_loop_x = []
182 close_loop_u = []
Austin Schuhd34569d2014-02-18 20:26:38 -0800183 goal_position = -0.3
184 R = numpy.matrix([[goal_position], [0.0], [-sprung_shooter.A[1, 0] / sprung_shooter.A[1, 2] * goal_position]])
185 voltage = numpy.matrix([[0.0]])
James Kuszmaul6682f342014-02-14 10:21:04 -0500186 for _ in xrange(500):
Austin Schuhd34569d2014-02-18 20:26:38 -0800187 U = sprung_shooter.K * (R - sprung_shooter.X_hat)
188 U = ClipDeltaU(sprung_shooter, voltage, U)
189 sprung_shooter.Y = raw_sprung_shooter.Y + 0.01
190 sprung_shooter.UpdateObserver(U)
191 voltage += U;
192 raw_sprung_shooter.Update(voltage)
193 close_loop_x.append(raw_sprung_shooter.X[0, 0] * 10)
194 close_loop_u.append(voltage[0, 0])
195
196 pylab.plot(range(500), close_loop_x)
197 pylab.plot(range(500), close_loop_u)
198 pylab.show()
199
200 shooter = ShooterDeltaU()
201 raw_shooter = Shooter()
202 close_loop_x = []
203 close_loop_u = []
204 goal_position = -0.3
205 R = numpy.matrix([[goal_position], [0.0], [-shooter.A[1, 0] / shooter.A[1, 2] * goal_position]])
206 voltage = numpy.matrix([[0.0]])
207 for _ in xrange(500):
208 U = shooter.K * (R - shooter.X_hat)
209 U = ClipDeltaU(shooter, voltage, U)
210 shooter.Y = raw_shooter.Y + 0.01
joe2d92e852014-01-25 14:31:24 -0800211 shooter.UpdateObserver(U)
Austin Schuhd34569d2014-02-18 20:26:38 -0800212 voltage += U;
213 raw_shooter.Update(voltage)
214 close_loop_x.append(raw_shooter.X[0, 0] * 10)
215 close_loop_u.append(voltage[0, 0])
joe2d92e852014-01-25 14:31:24 -0800216
James Kuszmaul6682f342014-02-14 10:21:04 -0500217 pylab.plot(range(500), close_loop_x)
218 pylab.plot(range(500), close_loop_u)
joe2d92e852014-01-25 14:31:24 -0800219 pylab.show()
220
221 # Write the generated constants out to a file.
joe93778a62014-02-15 13:22:14 -0800222 if len(argv) != 5:
joe2d92e852014-01-25 14:31:24 -0800223 print "Expected .h file name and .cc file name for"
224 print "both the plant and unaugmented plant"
225 else:
Austin Schuhd34569d2014-02-18 20:26:38 -0800226 unaug_sprung_shooter = SprungShooter("RawSprungShooter")
joe2d92e852014-01-25 14:31:24 -0800227 unaug_shooter = Shooter("RawShooter")
228 unaug_loop_writer = control_loop.ControlLoopWriter("RawShooter",
Austin Schuhd34569d2014-02-18 20:26:38 -0800229 [unaug_sprung_shooter,
230 unaug_shooter])
joe93778a62014-02-15 13:22:14 -0800231 if argv[3][-3:] == '.cc':
232 unaug_loop_writer.Write(argv[4], argv[3])
233 else:
234 unaug_loop_writer.Write(argv[3], argv[4])
joe2d92e852014-01-25 14:31:24 -0800235
Austin Schuhd34569d2014-02-18 20:26:38 -0800236 sprung_shooter = SprungShooterDeltaU()
Austin Schuh30537882014-02-18 01:07:23 -0800237 shooter = ShooterDeltaU()
Ben Fredrickson1b45f782014-02-23 07:44:36 +0000238 loop_writer = control_loop.ControlLoopWriter("Shooter", [sprung_shooter,
239 shooter],
240 write_constants=True)
joe2d92e852014-01-25 14:31:24 -0800241 if argv[1][-3:] == '.cc':
242 loop_writer.Write(argv[2], argv[1])
243 else:
244 loop_writer.Write(argv[1], argv[2])
245
246if __name__ == '__main__':
247 sys.exit(main(sys.argv))