blob: 1372c25beebef45d9a833529349d9ecf945d7933 [file] [log] [blame]
Brian Silverman17f503e2015-08-02 18:17:18 -07001#!/usr/bin/python
2
Austin Schuh572ff402015-11-08 12:17:50 -08003from frc971.control_loops.python import control_loop
4from frc971.control_loops.python import controls
Brian Silverman17f503e2015-08-02 18:17:18 -07005import numpy
6import sys
7from matplotlib import pylab
8
9
10class CIM(control_loop.ControlLoop):
11 def __init__(self):
12 super(CIM, self).__init__("CIM")
13 # Stall Torque in N m
14 self.stall_torque = 2.42
15 # Stall Current in Amps
16 self.stall_current = 133
17 # Free Speed in RPM
18 self.free_speed = 4650.0
19 # Free Current in Amps
20 self.free_current = 2.7
21 # Moment of inertia of the CIM in kg m^2
22 self.J = 0.0001
23 # Resistance of the motor, divided by 2 to account for the 2 motors
24 self.R = 12.0 / self.stall_current
25 # Motor velocity constant
26 self.Kv = ((self.free_speed / 60.0 * 2.0 * numpy.pi) /
27 (12.0 - self.R * self.free_current))
28 # Torque constant
29 self.Kt = self.stall_torque / self.stall_current
30 # Control loop time step
Austin Schuhedc317c2015-11-08 14:07:42 -080031 self.dt = 0.010
Brian Silverman17f503e2015-08-02 18:17:18 -070032
33 # State feedback matrices
34 self.A_continuous = numpy.matrix(
35 [[-self.Kt / self.Kv / (self.J * self.R)]])
36 self.B_continuous = numpy.matrix(
37 [[self.Kt / (self.J * self.R)]])
38 self.C = numpy.matrix([[1]])
39 self.D = numpy.matrix([[0]])
40
41 self.A, self.B = self.ContinuousToDiscrete(self.A_continuous,
42 self.B_continuous, self.dt)
43
44 self.PlaceControllerPoles([0.01])
45 self.PlaceObserverPoles([0.01])
46
47 self.U_max = numpy.matrix([[12.0]])
48 self.U_min = numpy.matrix([[-12.0]])
49
50 self.InitializeState()
51
52
53class Drivetrain(control_loop.ControlLoop):
54 def __init__(self, name="Drivetrain", left_low=True, right_low=True):
55 super(Drivetrain, self).__init__(name)
56 # Stall Torque in N m
57 self.stall_torque = 2.42
58 # Stall Current in Amps
59 self.stall_current = 133.0
60 # Free Speed in RPM. Used number from last year.
61 self.free_speed = 4650.0
62 # Free Current in Amps
63 self.free_current = 2.7
64 # Moment of inertia of the drivetrain in kg m^2
65 # Just borrowed from last year.
66 self.J = 4.5
67 # Mass of the robot, in kg.
68 self.m = 68
69 # Radius of the robot, in meters (from last year).
70 self.rb = 0.617998644 / 2.0
71 # Radius of the wheels, in meters.
72 self.r = .04445
73 # Resistance of the motor, divided by the number of motors.
Austin Schuh572ff402015-11-08 12:17:50 -080074 self.R = 12.0 / self.stall_current / 2
Brian Silverman17f503e2015-08-02 18:17:18 -070075 # Motor velocity constant
76 self.Kv = ((self.free_speed / 60.0 * 2.0 * numpy.pi) /
77 (12.0 - self.R * self.free_current))
78 # Torque constant
79 self.Kt = self.stall_torque / self.stall_current
80 # Gear ratios
81 self.G_low = 18.0 / 60.0 * 18.0 / 50.0
82 self.G_high = 28.0 / 50.0 * 18.0 / 50.0
83 if left_low:
84 self.Gl = self.G_low
85 else:
86 self.Gl = self.G_high
87 if right_low:
88 self.Gr = self.G_low
89 else:
90 self.Gr = self.G_high
91
92 # Control loop time step
Austin Schuh86f895e2015-11-08 13:40:51 -080093 self.dt = 0.010
Brian Silverman17f503e2015-08-02 18:17:18 -070094
95 # These describe the way that a given side of a robot will be influenced
96 # by the other side. Units of 1 / kg.
97 self.msp = 1.0 / self.m + self.rb * self.rb / self.J
98 self.msn = 1.0 / self.m - self.rb * self.rb / self.J
99 # The calculations which we will need for A and B.
100 self.tcl = -self.Kt / self.Kv / (self.Gl * self.Gl * self.R * self.r * self.r)
101 self.tcr = -self.Kt / self.Kv / (self.Gr * self.Gr * self.R * self.r * self.r)
102 self.mpl = self.Kt / (self.Gl * self.R * self.r)
103 self.mpr = self.Kt / (self.Gr * self.R * self.r)
104
105 # State feedback matrices
106 # X will be of the format
107 # [[positionl], [velocityl], [positionr], velocityr]]
108 self.A_continuous = numpy.matrix(
109 [[0, 1, 0, 0],
110 [0, self.msp * self.tcl, 0, self.msn * self.tcr],
111 [0, 0, 0, 1],
112 [0, self.msn * self.tcl, 0, self.msp * self.tcr]])
113 self.B_continuous = numpy.matrix(
114 [[0, 0],
115 [self.msp * self.mpl, self.msn * self.mpr],
116 [0, 0],
117 [self.msn * self.mpl, self.msp * self.mpr]])
118 self.C = numpy.matrix([[1, 0, 0, 0],
119 [0, 0, 1, 0]])
120 self.D = numpy.matrix([[0, 0],
121 [0, 0]])
122
Brian Silverman17f503e2015-08-02 18:17:18 -0700123 self.A, self.B = self.ContinuousToDiscrete(
124 self.A_continuous, self.B_continuous, self.dt)
125
126 # Poles from last year.
127 self.hp = 0.65
128 self.lp = 0.83
129 self.PlaceControllerPoles([self.hp, self.lp, self.hp, self.lp])
Austin Schuh572ff402015-11-08 12:17:50 -0800130 #print self.K
Brian Silverman17f503e2015-08-02 18:17:18 -0700131 q_pos = 0.07
132 q_vel = 1.0
133 self.Q = numpy.matrix([[(1.0 / (q_pos ** 2.0)), 0.0, 0.0, 0.0],
134 [0.0, (1.0 / (q_vel ** 2.0)), 0.0, 0.0],
135 [0.0, 0.0, (1.0 / (q_pos ** 2.0)), 0.0],
136 [0.0, 0.0, 0.0, (1.0 / (q_vel ** 2.0))]])
137
138 self.R = numpy.matrix([[(1.0 / (12.0 ** 2.0)), 0.0],
139 [0.0, (1.0 / (12.0 ** 2.0))]])
140 self.K = controls.dlqr(self.A, self.B, self.Q, self.R)
Austin Schuh572ff402015-11-08 12:17:50 -0800141 #print self.A
142 #print self.B
143 #print self.K
144 #print numpy.linalg.eig(self.A - self.B * self.K)[0]
Brian Silverman17f503e2015-08-02 18:17:18 -0700145
146 self.hlp = 0.3
147 self.llp = 0.4
148 self.PlaceObserverPoles([self.hlp, self.hlp, self.llp, self.llp])
149
150 self.U_max = numpy.matrix([[12.0], [12.0]])
151 self.U_min = numpy.matrix([[-12.0], [-12.0]])
152 self.InitializeState()
153
Austin Schuh572ff402015-11-08 12:17:50 -0800154
155class KFDrivetrain(Drivetrain):
156 def __init__(self, name="KFDrivetrain", left_low=True, right_low=True):
157 super(KFDrivetrain, self).__init__(name, left_low, right_low)
158
159 self.unaugmented_A_continuous = self.A_continuous
160 self.unaugmented_B_continuous = self.B_continuous
161
162 # The states are
163 # The practical voltage applied to the wheels is
164 # V_left = U_left + left_voltage_error
165 #
166 # [left position, left velocity, right position, right velocity,
167 # left voltage error, right voltage error, angular_error]
168 self.A_continuous = numpy.matrix(numpy.zeros((7, 7)))
169 self.B_continuous = numpy.matrix(numpy.zeros((7, 2)))
170 self.A_continuous[0:4,0:4] = self.unaugmented_A_continuous
171 self.A_continuous[0:4,4:6] = self.unaugmented_B_continuous
172 self.B_continuous[0:4,0:2] = self.unaugmented_B_continuous
173
174 self.A, self.B = self.ContinuousToDiscrete(
175 self.A_continuous, self.B_continuous, self.dt)
176
177 self.C = numpy.matrix([[1, 0, 0, 0, 0, 0, self.rb],
178 [0, 0, 1, 0, 0, 0, -self.rb],
179 [0, -2.0 / self.rb, 0, 2.0 / self.rb, 0, 0, 0]])
180
181 self.D = numpy.matrix([[0, 0],
182 [0, 0],
183 [0, 0]])
184
185 q_pos = 0.08
186 q_vel = 0.40
187 q_voltage = 6.0
188 q_gyro = 0.1
189
190 self.Q = numpy.matrix([[(q_pos ** 2.0), 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
191 [0.0, (q_vel ** 2.0), 0.0, 0.0, 0.0, 0.0, 0.0],
192 [0.0, 0.0, (q_pos ** 2.0), 0.0, 0.0, 0.0, 0.0],
193 [0.0, 0.0, 0.0, (q_vel ** 2.0), 0.0, 0.0, 0.0],
194 [0.0, 0.0, 0.0, 0.0, (q_voltage ** 2.0), 0.0, 0.0],
195 [0.0, 0.0, 0.0, 0.0, 0.0, (q_voltage ** 2.0), 0.0],
196 [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, (q_gyro ** 2.0)]])
197
198 r_pos = 0.05
199 r_gyro = 0.001
200 self.R = numpy.matrix([[(r_pos ** 2.0), 0.0, 0.0],
201 [0.0, (r_pos ** 2.0), 0.0],
202 [0.0, 0.0, (r_gyro ** 2.0)]])
203
204 # Solving for kf gains.
205 self.KalmanGain, self.Q_steady = controls.kalman(
206 A=self.A, B=self.B, C=self.C, Q=self.Q, R=self.R)
207
208 self.L = self.A * self.KalmanGain
209
210 # We need a nothing controller for the autogen code to be happy.
211 self.K = numpy.matrix(numpy.zeros((self.B.shape[1], self.A.shape[0])))
212
213
Brian Silverman17f503e2015-08-02 18:17:18 -0700214def main(argv):
215 # Simulate the response of the system to a step input.
216 drivetrain = Drivetrain()
217 simulated_left = []
218 simulated_right = []
219 for _ in xrange(100):
220 drivetrain.Update(numpy.matrix([[12.0], [12.0]]))
221 simulated_left.append(drivetrain.X[0, 0])
222 simulated_right.append(drivetrain.X[2, 0])
223
224 #pylab.plot(range(100), simulated_left)
225 #pylab.plot(range(100), simulated_right)
226 #pylab.show()
227
228 # Simulate forwards motion.
229 drivetrain = Drivetrain()
230 close_loop_left = []
231 close_loop_right = []
232 R = numpy.matrix([[1.0], [0.0], [1.0], [0.0]])
233 for _ in xrange(100):
234 U = numpy.clip(drivetrain.K * (R - drivetrain.X_hat),
235 drivetrain.U_min, drivetrain.U_max)
236 drivetrain.UpdateObserver(U)
237 drivetrain.Update(U)
238 close_loop_left.append(drivetrain.X[0, 0])
239 close_loop_right.append(drivetrain.X[2, 0])
240
241 #pylab.plot(range(100), close_loop_left)
242 #pylab.plot(range(100), close_loop_right)
243 #pylab.show()
244
245 # Try turning in place
246 drivetrain = Drivetrain()
247 close_loop_left = []
248 close_loop_right = []
249 R = numpy.matrix([[-1.0], [0.0], [1.0], [0.0]])
250 for _ in xrange(100):
251 U = numpy.clip(drivetrain.K * (R - drivetrain.X_hat),
252 drivetrain.U_min, drivetrain.U_max)
253 drivetrain.UpdateObserver(U)
254 drivetrain.Update(U)
255 close_loop_left.append(drivetrain.X[0, 0])
256 close_loop_right.append(drivetrain.X[2, 0])
257
258 #pylab.plot(range(100), close_loop_left)
259 #pylab.plot(range(100), close_loop_right)
260 #pylab.show()
261
262 # Try turning just one side.
263 drivetrain = Drivetrain()
264 close_loop_left = []
265 close_loop_right = []
266 R = numpy.matrix([[0.0], [0.0], [1.0], [0.0]])
267 for _ in xrange(100):
268 U = numpy.clip(drivetrain.K * (R - drivetrain.X_hat),
269 drivetrain.U_min, drivetrain.U_max)
270 drivetrain.UpdateObserver(U)
271 drivetrain.Update(U)
272 close_loop_left.append(drivetrain.X[0, 0])
273 close_loop_right.append(drivetrain.X[2, 0])
274
275 #pylab.plot(range(100), close_loop_left)
276 #pylab.plot(range(100), close_loop_right)
277 #pylab.show()
278
279 # Write the generated constants out to a file.
Brian Silverman17f503e2015-08-02 18:17:18 -0700280 drivetrain_low_low = Drivetrain(name="DrivetrainLowLow", left_low=True, right_low=True)
281 drivetrain_low_high = Drivetrain(name="DrivetrainLowHigh", left_low=True, right_low=False)
282 drivetrain_high_low = Drivetrain(name="DrivetrainHighLow", left_low=False, right_low=True)
283 drivetrain_high_high = Drivetrain(name="DrivetrainHighHigh", left_low=False, right_low=False)
284
Austin Schuh572ff402015-11-08 12:17:50 -0800285 kf_drivetrain_low_low = KFDrivetrain(name="KFDrivetrainLowLow", left_low=True, right_low=True)
286 kf_drivetrain_low_high = KFDrivetrain(name="KFDrivetrainLowHigh", left_low=True, right_low=False)
287 kf_drivetrain_high_low = KFDrivetrain(name="KFDrivetrainHighLow", left_low=False, right_low=True)
288 kf_drivetrain_high_high = KFDrivetrain(name="KFDrivetrainHighHigh", left_low=False, right_low=False)
289
Brian Silverman17f503e2015-08-02 18:17:18 -0700290 if len(argv) != 5:
291 print "Expected .h file name and .cc file name"
292 else:
Austin Schuh572ff402015-11-08 12:17:50 -0800293 namespaces = ['y2014', 'control_loops', 'drivetrain']
Brian Silverman17f503e2015-08-02 18:17:18 -0700294 dog_loop_writer = control_loop.ControlLoopWriter(
295 "Drivetrain", [drivetrain_low_low, drivetrain_low_high,
Austin Schuh572ff402015-11-08 12:17:50 -0800296 drivetrain_high_low, drivetrain_high_high],
297 namespaces = namespaces)
Brian Silverman17f503e2015-08-02 18:17:18 -0700298 if argv[1][-3:] == '.cc':
299 dog_loop_writer.Write(argv[2], argv[1])
300 else:
301 dog_loop_writer.Write(argv[1], argv[2])
302
Austin Schuh572ff402015-11-08 12:17:50 -0800303 kf_loop_writer = control_loop.ControlLoopWriter(
304 "KFDrivetrain", [kf_drivetrain_low_low, kf_drivetrain_low_high,
305 kf_drivetrain_high_low, kf_drivetrain_high_high],
306 namespaces = namespaces)
307 if argv[3][-3:] == '.cc':
308 kf_loop_writer.Write(argv[4], argv[3])
309 else:
310 kf_loop_writer.Write(argv[3], argv[4])
311
Brian Silverman17f503e2015-08-02 18:17:18 -0700312if __name__ == '__main__':
313 sys.exit(main(sys.argv))