blob: e4c71227b3efbb906c92e5035f7c5f7675010a55 [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
Austin Schuhe456f152015-11-27 13:44:39 -08009import gflags
10import glog
11
12FLAGS = gflags.FLAGS
13
14gflags.DEFINE_bool('plot', False, 'If true, plot the loop response.')
Brian Silverman17f503e2015-08-02 18:17:18 -070015
Brian Silverman17f503e2015-08-02 18:17:18 -070016
17class Drivetrain(control_loop.ControlLoop):
18 def __init__(self, name="Drivetrain", left_low=True, right_low=True):
19 super(Drivetrain, self).__init__(name)
Austin Schuh96ce8ae2015-11-26 12:46:02 -080020 # Number of motors per side
21 self.num_motors = 2
Brian Silverman17f503e2015-08-02 18:17:18 -070022 # Stall Torque in N m
Austin Schuh209f1702015-11-29 17:03:00 -080023 self.stall_torque = 2.42 * self.num_motors * 0.60
Brian Silverman17f503e2015-08-02 18:17:18 -070024 # Stall Current in Amps
Austin Schuh96ce8ae2015-11-26 12:46:02 -080025 self.stall_current = 133.0 * self.num_motors
Brian Silverman17f503e2015-08-02 18:17:18 -070026 # Free Speed in RPM. Used number from last year.
Austin Schuh209f1702015-11-29 17:03:00 -080027 self.free_speed = 5500.0
Brian Silverman17f503e2015-08-02 18:17:18 -070028 # Free Current in Amps
Austin Schuh209f1702015-11-29 17:03:00 -080029 self.free_current = 4.7 * self.num_motors
Brian Silverman17f503e2015-08-02 18:17:18 -070030 # Moment of inertia of the drivetrain in kg m^2
Austin Schuh209f1702015-11-29 17:03:00 -080031 self.J = 2.8
Brian Silverman17f503e2015-08-02 18:17:18 -070032 # Mass of the robot, in kg.
33 self.m = 68
34 # Radius of the robot, in meters (from last year).
Austin Schuh209f1702015-11-29 17:03:00 -080035 self.rb = 0.647998644 / 2.0
Brian Silverman17f503e2015-08-02 18:17:18 -070036 # Radius of the wheels, in meters.
37 self.r = .04445
38 # Resistance of the motor, divided by the number of motors.
Austin Schuh96ce8ae2015-11-26 12:46:02 -080039 self.resistance = 12.0 / self.stall_current
Brian Silverman17f503e2015-08-02 18:17:18 -070040 # Motor velocity constant
41 self.Kv = ((self.free_speed / 60.0 * 2.0 * numpy.pi) /
Austin Schuh96ce8ae2015-11-26 12:46:02 -080042 (12.0 - self.resistance * self.free_current))
Brian Silverman17f503e2015-08-02 18:17:18 -070043 # Torque constant
44 self.Kt = self.stall_torque / self.stall_current
45 # Gear ratios
46 self.G_low = 18.0 / 60.0 * 18.0 / 50.0
47 self.G_high = 28.0 / 50.0 * 18.0 / 50.0
48 if left_low:
49 self.Gl = self.G_low
50 else:
51 self.Gl = self.G_high
52 if right_low:
53 self.Gr = self.G_low
54 else:
55 self.Gr = self.G_high
56
57 # Control loop time step
Austin Schuhadf2cde2015-11-08 20:35:16 -080058 self.dt = 0.005
Brian Silverman17f503e2015-08-02 18:17:18 -070059
60 # These describe the way that a given side of a robot will be influenced
61 # by the other side. Units of 1 / kg.
62 self.msp = 1.0 / self.m + self.rb * self.rb / self.J
63 self.msn = 1.0 / self.m - self.rb * self.rb / self.J
64 # The calculations which we will need for A and B.
Austin Schuh96ce8ae2015-11-26 12:46:02 -080065 self.tcl = -self.Kt / self.Kv / (self.Gl * self.Gl * self.resistance * self.r * self.r)
66 self.tcr = -self.Kt / self.Kv / (self.Gr * self.Gr * self.resistance * self.r * self.r)
67 self.mpl = self.Kt / (self.Gl * self.resistance * self.r)
68 self.mpr = self.Kt / (self.Gr * self.resistance * self.r)
Brian Silverman17f503e2015-08-02 18:17:18 -070069
70 # State feedback matrices
71 # X will be of the format
72 # [[positionl], [velocityl], [positionr], velocityr]]
73 self.A_continuous = numpy.matrix(
74 [[0, 1, 0, 0],
75 [0, self.msp * self.tcl, 0, self.msn * self.tcr],
76 [0, 0, 0, 1],
77 [0, self.msn * self.tcl, 0, self.msp * self.tcr]])
78 self.B_continuous = numpy.matrix(
79 [[0, 0],
80 [self.msp * self.mpl, self.msn * self.mpr],
81 [0, 0],
82 [self.msn * self.mpl, self.msp * self.mpr]])
83 self.C = numpy.matrix([[1, 0, 0, 0],
84 [0, 0, 1, 0]])
85 self.D = numpy.matrix([[0, 0],
86 [0, 0]])
87
Brian Silverman17f503e2015-08-02 18:17:18 -070088 self.A, self.B = self.ContinuousToDiscrete(
89 self.A_continuous, self.B_continuous, self.dt)
90
Austin Schuhadf2cde2015-11-08 20:35:16 -080091 q_pos = 0.12
Brian Silverman17f503e2015-08-02 18:17:18 -070092 q_vel = 1.0
93 self.Q = numpy.matrix([[(1.0 / (q_pos ** 2.0)), 0.0, 0.0, 0.0],
94 [0.0, (1.0 / (q_vel ** 2.0)), 0.0, 0.0],
95 [0.0, 0.0, (1.0 / (q_pos ** 2.0)), 0.0],
96 [0.0, 0.0, 0.0, (1.0 / (q_vel ** 2.0))]])
97
98 self.R = numpy.matrix([[(1.0 / (12.0 ** 2.0)), 0.0],
99 [0.0, (1.0 / (12.0 ** 2.0))]])
100 self.K = controls.dlqr(self.A, self.B, self.Q, self.R)
Austin Schuh29659232015-11-26 13:55:30 -0800101
Austin Schuhe456f152015-11-27 13:44:39 -0800102 glog.debug('DT K %s', name)
103 glog.debug(str(self.K))
104 glog.debug(str(numpy.linalg.eig(self.A - self.B * self.K)[0]))
Brian Silverman17f503e2015-08-02 18:17:18 -0700105
106 self.hlp = 0.3
107 self.llp = 0.4
108 self.PlaceObserverPoles([self.hlp, self.hlp, self.llp, self.llp])
109
110 self.U_max = numpy.matrix([[12.0], [12.0]])
111 self.U_min = numpy.matrix([[-12.0], [-12.0]])
112 self.InitializeState()
113
Austin Schuh572ff402015-11-08 12:17:50 -0800114
115class KFDrivetrain(Drivetrain):
116 def __init__(self, name="KFDrivetrain", left_low=True, right_low=True):
117 super(KFDrivetrain, self).__init__(name, left_low, right_low)
118
119 self.unaugmented_A_continuous = self.A_continuous
120 self.unaugmented_B_continuous = self.B_continuous
121
Austin Schuh572ff402015-11-08 12:17:50 -0800122 # The practical voltage applied to the wheels is
123 # V_left = U_left + left_voltage_error
124 #
Austin Schuh2a671df2016-11-26 15:00:06 -0800125 # The states are
Austin Schuh572ff402015-11-08 12:17:50 -0800126 # [left position, left velocity, right position, right velocity,
127 # left voltage error, right voltage error, angular_error]
128 self.A_continuous = numpy.matrix(numpy.zeros((7, 7)))
129 self.B_continuous = numpy.matrix(numpy.zeros((7, 2)))
130 self.A_continuous[0:4,0:4] = self.unaugmented_A_continuous
131 self.A_continuous[0:4,4:6] = self.unaugmented_B_continuous
132 self.B_continuous[0:4,0:2] = self.unaugmented_B_continuous
Austin Schuh209f1702015-11-29 17:03:00 -0800133 self.A_continuous[0,6] = 1
134 self.A_continuous[2,6] = -1
Austin Schuh572ff402015-11-08 12:17:50 -0800135
136 self.A, self.B = self.ContinuousToDiscrete(
137 self.A_continuous, self.B_continuous, self.dt)
138
Austin Schuh209f1702015-11-29 17:03:00 -0800139 self.C = numpy.matrix([[1, 0, 0, 0, 0, 0, 0],
140 [0, 0, 1, 0, 0, 0, 0],
141 [0, -0.5 / self.rb, 0, 0.5 / self.rb, 0, 0, 0]])
Austin Schuh572ff402015-11-08 12:17:50 -0800142
143 self.D = numpy.matrix([[0, 0],
144 [0, 0],
145 [0, 0]])
146
Austin Schuh209f1702015-11-29 17:03:00 -0800147 q_pos = 0.05
148 q_vel = 1.00
149 q_voltage = 10.0
150 q_encoder_uncertainty = 2.00
Austin Schuh572ff402015-11-08 12:17:50 -0800151
152 self.Q = numpy.matrix([[(q_pos ** 2.0), 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
153 [0.0, (q_vel ** 2.0), 0.0, 0.0, 0.0, 0.0, 0.0],
154 [0.0, 0.0, (q_pos ** 2.0), 0.0, 0.0, 0.0, 0.0],
155 [0.0, 0.0, 0.0, (q_vel ** 2.0), 0.0, 0.0, 0.0],
156 [0.0, 0.0, 0.0, 0.0, (q_voltage ** 2.0), 0.0, 0.0],
157 [0.0, 0.0, 0.0, 0.0, 0.0, (q_voltage ** 2.0), 0.0],
Austin Schuh209f1702015-11-29 17:03:00 -0800158 [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, (q_encoder_uncertainty ** 2.0)]])
Austin Schuh572ff402015-11-08 12:17:50 -0800159
Austin Schuh209f1702015-11-29 17:03:00 -0800160 r_pos = 0.0001
161 r_gyro = 0.000001
Austin Schuh572ff402015-11-08 12:17:50 -0800162 self.R = numpy.matrix([[(r_pos ** 2.0), 0.0, 0.0],
163 [0.0, (r_pos ** 2.0), 0.0],
164 [0.0, 0.0, (r_gyro ** 2.0)]])
165
166 # Solving for kf gains.
167 self.KalmanGain, self.Q_steady = controls.kalman(
168 A=self.A, B=self.B, C=self.C, Q=self.Q, R=self.R)
169
170 self.L = self.A * self.KalmanGain
171
172 # We need a nothing controller for the autogen code to be happy.
173 self.K = numpy.matrix(numpy.zeros((self.B.shape[1], self.A.shape[0])))
174
175
Brian Silverman17f503e2015-08-02 18:17:18 -0700176def main(argv):
Austin Schuhe456f152015-11-27 13:44:39 -0800177 argv = FLAGS(argv)
Austin Schuh29659232015-11-26 13:55:30 -0800178
Brian Silverman17f503e2015-08-02 18:17:18 -0700179 # Simulate the response of the system to a step input.
180 drivetrain = Drivetrain()
181 simulated_left = []
182 simulated_right = []
183 for _ in xrange(100):
184 drivetrain.Update(numpy.matrix([[12.0], [12.0]]))
185 simulated_left.append(drivetrain.X[0, 0])
186 simulated_right.append(drivetrain.X[2, 0])
187
Austin Schuhe456f152015-11-27 13:44:39 -0800188 if FLAGS.plot:
Austin Schuh29659232015-11-26 13:55:30 -0800189 pylab.plot(range(100), simulated_left)
190 pylab.plot(range(100), simulated_right)
191 pylab.show()
Brian Silverman17f503e2015-08-02 18:17:18 -0700192
193 # Simulate forwards motion.
194 drivetrain = Drivetrain()
195 close_loop_left = []
196 close_loop_right = []
197 R = numpy.matrix([[1.0], [0.0], [1.0], [0.0]])
198 for _ in xrange(100):
199 U = numpy.clip(drivetrain.K * (R - drivetrain.X_hat),
200 drivetrain.U_min, drivetrain.U_max)
201 drivetrain.UpdateObserver(U)
202 drivetrain.Update(U)
203 close_loop_left.append(drivetrain.X[0, 0])
204 close_loop_right.append(drivetrain.X[2, 0])
205
Austin Schuhe456f152015-11-27 13:44:39 -0800206 if FLAGS.plot:
Austin Schuh29659232015-11-26 13:55:30 -0800207 pylab.plot(range(100), close_loop_left)
208 pylab.plot(range(100), close_loop_right)
209 pylab.show()
Brian Silverman17f503e2015-08-02 18:17:18 -0700210
211 # Try turning in place
212 drivetrain = Drivetrain()
213 close_loop_left = []
214 close_loop_right = []
215 R = numpy.matrix([[-1.0], [0.0], [1.0], [0.0]])
216 for _ in xrange(100):
217 U = numpy.clip(drivetrain.K * (R - drivetrain.X_hat),
218 drivetrain.U_min, drivetrain.U_max)
219 drivetrain.UpdateObserver(U)
220 drivetrain.Update(U)
221 close_loop_left.append(drivetrain.X[0, 0])
222 close_loop_right.append(drivetrain.X[2, 0])
223
Austin Schuhe456f152015-11-27 13:44:39 -0800224 if FLAGS.plot:
Austin Schuh29659232015-11-26 13:55:30 -0800225 pylab.plot(range(100), close_loop_left)
226 pylab.plot(range(100), close_loop_right)
227 pylab.show()
Brian Silverman17f503e2015-08-02 18:17:18 -0700228
229 # Try turning just one side.
230 drivetrain = Drivetrain()
231 close_loop_left = []
232 close_loop_right = []
233 R = numpy.matrix([[0.0], [0.0], [1.0], [0.0]])
234 for _ in xrange(100):
235 U = numpy.clip(drivetrain.K * (R - drivetrain.X_hat),
236 drivetrain.U_min, drivetrain.U_max)
237 drivetrain.UpdateObserver(U)
238 drivetrain.Update(U)
239 close_loop_left.append(drivetrain.X[0, 0])
240 close_loop_right.append(drivetrain.X[2, 0])
241
Austin Schuhe456f152015-11-27 13:44:39 -0800242 if FLAGS.plot:
Austin Schuh29659232015-11-26 13:55:30 -0800243 pylab.plot(range(100), close_loop_left)
244 pylab.plot(range(100), close_loop_right)
245 pylab.show()
Brian Silverman17f503e2015-08-02 18:17:18 -0700246
247 # Write the generated constants out to a file.
Austin Schuh29659232015-11-26 13:55:30 -0800248 drivetrain_low_low = Drivetrain(
249 name="DrivetrainLowLow", left_low=True, right_low=True)
250 drivetrain_low_high = Drivetrain(
251 name="DrivetrainLowHigh", left_low=True, right_low=False)
252 drivetrain_high_low = Drivetrain(
253 name="DrivetrainHighLow", left_low=False, right_low=True)
254 drivetrain_high_high = Drivetrain(
255 name="DrivetrainHighHigh", left_low=False, right_low=False)
Brian Silverman17f503e2015-08-02 18:17:18 -0700256
Austin Schuh29659232015-11-26 13:55:30 -0800257 kf_drivetrain_low_low = KFDrivetrain(
258 name="KFDrivetrainLowLow", left_low=True, right_low=True)
259 kf_drivetrain_low_high = KFDrivetrain(
260 name="KFDrivetrainLowHigh", left_low=True, right_low=False)
261 kf_drivetrain_high_low = KFDrivetrain(
262 name="KFDrivetrainHighLow", left_low=False, right_low=True)
263 kf_drivetrain_high_high = KFDrivetrain(
264 name="KFDrivetrainHighHigh", left_low=False, right_low=False)
Austin Schuh572ff402015-11-08 12:17:50 -0800265
Brian Silverman17f503e2015-08-02 18:17:18 -0700266 if len(argv) != 5:
267 print "Expected .h file name and .cc file name"
268 else:
Austin Schuh572ff402015-11-08 12:17:50 -0800269 namespaces = ['y2014', 'control_loops', 'drivetrain']
Brian Silverman17f503e2015-08-02 18:17:18 -0700270 dog_loop_writer = control_loop.ControlLoopWriter(
271 "Drivetrain", [drivetrain_low_low, drivetrain_low_high,
Austin Schuh572ff402015-11-08 12:17:50 -0800272 drivetrain_high_low, drivetrain_high_high],
273 namespaces = namespaces)
Austin Schuh0e997732015-11-08 15:14:53 -0800274 dog_loop_writer.AddConstant(control_loop.Constant("kDt", "%f",
275 drivetrain_low_low.dt))
Austin Schuh96ce8ae2015-11-26 12:46:02 -0800276 dog_loop_writer.AddConstant(control_loop.Constant("kStallTorque", "%f",
277 drivetrain_low_low.stall_torque))
278 dog_loop_writer.AddConstant(control_loop.Constant("kStallCurrent", "%f",
279 drivetrain_low_low.stall_current))
280 dog_loop_writer.AddConstant(control_loop.Constant("kFreeSpeedRPM", "%f",
281 drivetrain_low_low.free_speed))
282 dog_loop_writer.AddConstant(control_loop.Constant("kFreeCurrent", "%f",
283 drivetrain_low_low.free_current))
284 dog_loop_writer.AddConstant(control_loop.Constant("kJ", "%f",
285 drivetrain_low_low.J))
286 dog_loop_writer.AddConstant(control_loop.Constant("kMass", "%f",
287 drivetrain_low_low.m))
288 dog_loop_writer.AddConstant(control_loop.Constant("kRobotRadius", "%f",
289 drivetrain_low_low.rb))
290 dog_loop_writer.AddConstant(control_loop.Constant("kWheelRadius", "%f",
291 drivetrain_low_low.r))
292 dog_loop_writer.AddConstant(control_loop.Constant("kR", "%f",
293 drivetrain_low_low.resistance))
294 dog_loop_writer.AddConstant(control_loop.Constant("kV", "%f",
295 drivetrain_low_low.Kv))
296 dog_loop_writer.AddConstant(control_loop.Constant("kT", "%f",
297 drivetrain_low_low.Kt))
298
Austin Schuhe456f152015-11-27 13:44:39 -0800299 dog_loop_writer.Write(argv[1], argv[2])
Brian Silverman17f503e2015-08-02 18:17:18 -0700300
Austin Schuh572ff402015-11-08 12:17:50 -0800301 kf_loop_writer = control_loop.ControlLoopWriter(
302 "KFDrivetrain", [kf_drivetrain_low_low, kf_drivetrain_low_high,
Austin Schuhe456f152015-11-27 13:44:39 -0800303 kf_drivetrain_high_low, kf_drivetrain_high_high],
Austin Schuh572ff402015-11-08 12:17:50 -0800304 namespaces = namespaces)
Austin Schuhe456f152015-11-27 13:44:39 -0800305 kf_loop_writer.Write(argv[3], argv[4])
Austin Schuh572ff402015-11-08 12:17:50 -0800306
Brian Silverman17f503e2015-08-02 18:17:18 -0700307if __name__ == '__main__':
308 sys.exit(main(sys.argv))