blob: b1244f343e87eaeef7febcfc36047e7db5238d63 [file] [log] [blame]
Campbell Crowley33e0e3d2017-12-27 17:55:40 -08001#!/usr/bin/python
2
3from frc971.control_loops.python import control_loop
4from frc971.control_loops.python import controls
5import numpy
6import sys
7from matplotlib import pylab
8import glog
9
10class DrivetrainParams(object):
Diana Burgessd0180f12018-03-21 21:24:17 -070011 def __init__(self,
12 J,
13 mass,
14 robot_radius,
15 wheel_radius,
16 G_high,
17 G_low,
Austin Schuhbcce26a2018-03-26 23:41:24 -070018 q_pos_low=0.12,
19 q_pos_high=0.14,
20 q_vel_low=1.0,
21 q_vel_high=0.95,
Diana Burgessd0180f12018-03-21 21:24:17 -070022 efficiency=0.60,
23 has_imu=False,
24 force=False,
25 kf_q_voltage=10.0,
26 motor_type=control_loop.CIM(),
27 num_motors=2,
28 dt=0.00505,
29 controller_poles=[0.90, 0.90],
Michael Schuh95fbcc52018-03-10 20:57:20 -080030 observer_poles=[0.02, 0.02],
31 robot_cg_offset=0.0):
Diana Burgessd0180f12018-03-21 21:24:17 -070032 """Defines all constants of a drivetrain.
Campbell Crowley33e0e3d2017-12-27 17:55:40 -080033
Diana Burgessd0180f12018-03-21 21:24:17 -070034 Args:
35 J: float, Moment of inertia of drivetrain in kg m^2
36 mass: float, Mass of the robot in kg.
37 robot_radius: float, Radius of the robot, in meters (requires tuning by
38 hand).
39 wheel_radius: float, Radius of the wheels, in meters.
40 G_high: float, Gear ratio for high gear.
41 G_low: float, Gear ratio for low gear.
42 dt: float, Control loop time step.
43 q_pos_low: float, q position low gear.
44 q_pos_high: float, q position high gear.
45 q_vel_low: float, q velocity low gear.
46 q_vel_high: float, q velocity high gear.
47 efficiency: float, gear box effiency.
48 has_imu: bool, true if imu is present.
49 force: bool, true if force.
50 kf_q_voltage: float
51 motor_type: object, class of values defining the motor in drivetrain.
52 num_motors: int, number of motors on one side of drivetrain.
53 controller_poles: array, An array of poles. (See control_loop.py)
54 observer_poles: array, An array of poles. (See control_loop.py)
Michael Schuh95fbcc52018-03-10 20:57:20 -080055 robot_cg_offset: offset in meters of CG from robot center to left side
Diana Burgessd0180f12018-03-21 21:24:17 -070056 """
Campbell Crowley33e0e3d2017-12-27 17:55:40 -080057
Diana Burgessd0180f12018-03-21 21:24:17 -070058 self.J = J
59 self.mass = mass
60 self.robot_radius = robot_radius
Michael Schuh95fbcc52018-03-10 20:57:20 -080061 self.robot_cg_offset = robot_cg_offset
Diana Burgessd0180f12018-03-21 21:24:17 -070062 self.wheel_radius = wheel_radius
63 self.G_high = G_high
64 self.G_low = G_low
65 self.dt = dt
66 self.q_pos_low = q_pos_low
67 self.q_pos_high = q_pos_high
68 self.q_vel_low = q_vel_low
69 self.q_vel_high = q_vel_high
70 self.efficiency = efficiency
71 self.has_imu = has_imu
72 self.kf_q_voltage = kf_q_voltage
73 self.motor_type = motor_type
74 self.force = force
75 self.num_motors = num_motors
76 self.controller_poles = controller_poles
77 self.observer_poles = observer_poles
78
Campbell Crowley33e0e3d2017-12-27 17:55:40 -080079
80class Drivetrain(control_loop.ControlLoop):
Diana Burgessd0180f12018-03-21 21:24:17 -070081 def __init__(self,
82 drivetrain_params,
83 name="Drivetrain",
84 left_low=True,
85 right_low=True):
86 """Defines a base drivetrain for a robot.
Campbell Crowley33e0e3d2017-12-27 17:55:40 -080087
Diana Burgessd0180f12018-03-21 21:24:17 -070088 Args:
89 drivetrain_params: DrivetrainParams, class of values defining the drivetrain.
90 name: string, Name of this drivetrain.
91 left_low: bool, Whether the left is in high gear.
92 right_low: bool, Whether the right is in high gear.
93 """
94 super(Drivetrain, self).__init__(name)
Campbell Crowley33e0e3d2017-12-27 17:55:40 -080095
Diana Burgessd0180f12018-03-21 21:24:17 -070096 # Moment of inertia of the drivetrain in kg m^2
97 self.J = drivetrain_params.J
98 # Mass of the robot, in kg.
99 self.mass = drivetrain_params.mass
100 # Radius of the robot, in meters (requires tuning by hand)
101 self.robot_radius = drivetrain_params.robot_radius
102 # Radius of the wheels, in meters.
103 self.r = drivetrain_params.wheel_radius
104 self.has_imu = drivetrain_params.has_imu
Michael Schuh95fbcc52018-03-10 20:57:20 -0800105 # Offset in meters of the CG from the center of the robot to the left side
106 # of the robot. Since the arm is on the right side, the offset will
107 # likely be a negative number.
108 self.robot_cg_offset = drivetrain_params.robot_cg_offset
109 # Distance from the left side of the robot to the Center of Gravity
110 self.robot_radius_l = drivetrain_params.robot_radius - self.robot_cg_offset
111 # Distance from the right side of the robot to the Center of Gravity
112 self.robot_radius_r = drivetrain_params.robot_radius + self.robot_cg_offset
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800113
Diana Burgessd0180f12018-03-21 21:24:17 -0700114 # Gear ratios
115 self.G_low = drivetrain_params.G_low
116 self.G_high = drivetrain_params.G_high
117 if left_low:
118 self.Gl = self.G_low
119 else:
120 self.Gl = self.G_high
121 if right_low:
122 self.Gr = self.G_low
123 else:
124 self.Gr = self.G_high
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800125
Diana Burgessd0180f12018-03-21 21:24:17 -0700126 # Control loop time step
127 self.dt = drivetrain_params.dt
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800128
Diana Burgessd0180f12018-03-21 21:24:17 -0700129 self.efficiency = drivetrain_params.efficiency
130 self.force = drivetrain_params.force
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800131
Diana Burgessd0180f12018-03-21 21:24:17 -0700132 self.BuildDrivetrain(drivetrain_params.motor_type,
133 drivetrain_params.num_motors)
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800134
Diana Burgessd0180f12018-03-21 21:24:17 -0700135 if left_low or right_low:
136 q_pos = drivetrain_params.q_pos_low
137 q_vel = drivetrain_params.q_vel_low
138 else:
139 q_pos = drivetrain_params.q_pos_high
140 q_vel = drivetrain_params.q_vel_high
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800141
Diana Burgessd0180f12018-03-21 21:24:17 -0700142 self.BuildDrivetrainController(q_pos, q_vel)
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800143
Diana Burgessd0180f12018-03-21 21:24:17 -0700144 self.InitializeState()
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800145
Diana Burgessd0180f12018-03-21 21:24:17 -0700146 def BuildDrivetrain(self, motor, num_motors_per_side):
147 self.motor = motor
148 # Number of motors per side
149 self.num_motors = num_motors_per_side
150 # Stall Torque in N m
151 self.stall_torque = motor.stall_torque * self.num_motors * self.efficiency
152 # Stall Current in Amps
153 self.stall_current = motor.stall_current * self.num_motors
154 # Free Speed in rad/s
155 self.free_speed = motor.free_speed
156 # Free Current in Amps
157 self.free_current = motor.free_current * self.num_motors
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800158
Diana Burgessd0180f12018-03-21 21:24:17 -0700159 # Effective motor resistance in ohms.
160 self.resistance = 12.0 / self.stall_current
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800161
Diana Burgessd0180f12018-03-21 21:24:17 -0700162 # Resistance of the motor, divided by the number of motors.
163 # Motor velocity constant
164 self.Kv = (self.free_speed /
165 (12.0 - self.resistance * self.free_current))
166 # Torque constant
167 self.Kt = self.stall_torque / self.stall_current
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800168
Diana Burgessd0180f12018-03-21 21:24:17 -0700169 # These describe the way that a given side of a robot will be influenced
170 # by the other side. Units of 1 / kg.
Michael Schuh95fbcc52018-03-10 20:57:20 -0800171 self.mspl = 1.0 / self.mass + self.robot_radius_l * self.robot_radius_l / self.J
172 self.mspr = 1.0 / self.mass + self.robot_radius_r * self.robot_radius_r / self.J
173 self.msnl = self.robot_radius_r / ( self.robot_radius_l * self.mass ) - \
174 self.robot_radius_l * self.robot_radius_r / self.J
175 self.msnr = self.robot_radius_l / ( self.robot_radius_r * self.mass ) - \
176 self.robot_radius_l * self.robot_radius_r / self.J
Diana Burgessd0180f12018-03-21 21:24:17 -0700177 # The calculations which we will need for A and B.
178 self.tcl = self.Kt / self.Kv / (
179 self.Gl * self.Gl * self.resistance * self.r * self.r)
180 self.tcr = self.Kt / self.Kv / (
181 self.Gr * self.Gr * self.resistance * self.r * self.r)
182 self.mpl = self.Kt / (self.Gl * self.resistance * self.r)
183 self.mpr = self.Kt / (self.Gr * self.resistance * self.r)
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800184
Diana Burgessd0180f12018-03-21 21:24:17 -0700185 # State feedback matrices
186 # X will be of the format
187 # [[positionl], [velocityl], [positionr], velocityr]]
188 self.A_continuous = numpy.matrix(
Michael Schuh95fbcc52018-03-10 20:57:20 -0800189 [[0, 1, 0, 0],
190 [0, -self.mspl * self.tcl, 0, -self.msnr * self.tcr],
191 [0, 0, 0, 1],
192 [0, -self.msnl * self.tcl, 0, -self.mspr * self.tcr]])
Diana Burgessd0180f12018-03-21 21:24:17 -0700193 self.B_continuous = numpy.matrix(
Michael Schuh95fbcc52018-03-10 20:57:20 -0800194 [[0, 0],
195 [self.mspl * self.mpl, self.msnr * self.mpr],
196 [0, 0],
197 [self.msnl * self.mpl, self.mspr * self.mpr]])
Diana Burgessd0180f12018-03-21 21:24:17 -0700198 self.C = numpy.matrix([[1, 0, 0, 0], [0, 0, 1, 0]])
199 self.D = numpy.matrix([[0, 0], [0, 0]])
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800200
Diana Burgessd0180f12018-03-21 21:24:17 -0700201 self.A, self.B = self.ContinuousToDiscrete(self.A_continuous,
202 self.B_continuous, self.dt)
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800203
Diana Burgessd0180f12018-03-21 21:24:17 -0700204 def BuildDrivetrainController(self, q_pos, q_vel):
205 # Tune the LQR controller
206 self.Q = numpy.matrix([[(1.0 / (q_pos**2.0)), 0.0, 0.0,
207 0.0], [0.0, (1.0 / (q_vel**2.0)), 0.0, 0.0],
208 [0.0, 0.0, (1.0 / (q_pos**2.0)),
209 0.0], [0.0, 0.0, 0.0, (1.0 / (q_vel**2.0))]])
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800210
Diana Burgessd0180f12018-03-21 21:24:17 -0700211 self.R = numpy.matrix([[(1.0 / (12.0**2.0)), 0.0],
212 [0.0, (1.0 / (12.0**2.0))]])
213 self.K = controls.dlqr(self.A, self.B, self.Q, self.R)
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800214
Diana Burgessd0180f12018-03-21 21:24:17 -0700215 glog.debug('DT q_pos %f q_vel %s %s', q_pos, q_vel, self._name)
216 glog.debug(str(numpy.linalg.eig(self.A - self.B * self.K)[0]))
217 glog.debug('K %s', repr(self.K))
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800218
Diana Burgessd0180f12018-03-21 21:24:17 -0700219 self.hlp = 0.3
220 self.llp = 0.4
221 self.PlaceObserverPoles([self.hlp, self.hlp, self.llp, self.llp])
222
223 self.U_max = numpy.matrix([[12.0], [12.0]])
224 self.U_min = numpy.matrix([[-12.0], [-12.0]])
225
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800226
227class KFDrivetrain(Drivetrain):
Diana Burgessd0180f12018-03-21 21:24:17 -0700228 def __init__(self,
229 drivetrain_params,
230 name="KFDrivetrain",
231 left_low=True,
232 right_low=True):
233 """Kalman filter values of a drivetrain.
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800234
Diana Burgessd0180f12018-03-21 21:24:17 -0700235 Args:
236 drivetrain_params: DrivetrainParams, class of values defining the drivetrain.
237 name: string, Name of this drivetrain.
238 left_low: bool, Whether the left is in high gear.
239 right_low: bool, Whether the right is in high gear.
240 """
241 super(KFDrivetrain, self).__init__(drivetrain_params, name, left_low,
242 right_low)
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800243
Diana Burgessd0180f12018-03-21 21:24:17 -0700244 self.unaugmented_A_continuous = self.A_continuous
245 self.unaugmented_B_continuous = self.B_continuous
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800246
Diana Burgessd0180f12018-03-21 21:24:17 -0700247 # The practical voltage applied to the wheels is
248 # V_left = U_left + left_voltage_error
249 #
250 # The states are
251 # [left position, left velocity, right position, right velocity,
252 # left voltage error, right voltage error, angular_error]
253 #
254 # The left and right positions are filtered encoder positions and are not
255 # adjusted for heading error.
256 # The turn velocity as computed by the left and right velocities is
257 # adjusted by the gyro velocity.
258 # The angular_error is the angular velocity error between the wheel speed
259 # and the gyro speed.
260 self.A_continuous = numpy.matrix(numpy.zeros((7, 7)))
261 self.B_continuous = numpy.matrix(numpy.zeros((7, 2)))
262 self.A_continuous[0:4, 0:4] = self.unaugmented_A_continuous
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800263
Diana Burgessd0180f12018-03-21 21:24:17 -0700264 if self.force:
265 self.A_continuous[0:4, 4:6] = numpy.matrix(
Michael Schuh95fbcc52018-03-10 20:57:20 -0800266 [[0.0, 0.0], [self.mspl, self.msnl], [0.0, 0.0],
267 [self.msnr, self.mspr]])
Diana Burgessd0180f12018-03-21 21:24:17 -0700268 q_voltage = drivetrain_params.kf_q_voltage * self.mpl
269 else:
270 self.A_continuous[0:4, 4:6] = self.unaugmented_B_continuous
271 q_voltage = drivetrain_params.kf_q_voltage
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800272
Diana Burgessd0180f12018-03-21 21:24:17 -0700273 self.B_continuous[0:4, 0:2] = self.unaugmented_B_continuous
274 self.A_continuous[0, 6] = 1
275 self.A_continuous[2, 6] = -1
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800276
Diana Burgessd0180f12018-03-21 21:24:17 -0700277 self.A, self.B = self.ContinuousToDiscrete(self.A_continuous,
278 self.B_continuous, self.dt)
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800279
Diana Burgessd0180f12018-03-21 21:24:17 -0700280 if self.has_imu:
281 self.C = numpy.matrix(
282 [[1, 0, 0, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0, 0], [
283 0, -0.5 / drivetrain_params.robot_radius, 0,
284 0.5 / drivetrain_params.robot_radius, 0, 0, 0
285 ], [0, 0, 0, 0, 0, 0, 0]])
286 gravity = 9.8
287 self.C[3, 0:6] = 0.5 * (
288 self.A_continuous[1, 0:6] + self.A_continuous[3, 0:6]
289 ) / gravity
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800290
Diana Burgessd0180f12018-03-21 21:24:17 -0700291 self.D = numpy.matrix([[0, 0], [0, 0], [0, 0], [
292 0.5 *
293 (self.B_continuous[1, 0] + self.B_continuous[3, 0]) / gravity,
294 0.5 *
295 (self.B_continuous[1, 1] + self.B_continuous[3, 1]) / gravity
296 ]])
297 else:
298 self.C = numpy.matrix(
299 [[1, 0, 0, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0, 0], [
300 0, -0.5 / drivetrain_params.robot_radius, 0,
301 0.5 / drivetrain_params.robot_radius, 0, 0, 0
302 ]])
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800303
Diana Burgessd0180f12018-03-21 21:24:17 -0700304 self.D = numpy.matrix([[0, 0], [0, 0], [0, 0]])
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800305
Diana Burgessd0180f12018-03-21 21:24:17 -0700306 q_pos = 0.05
307 q_vel = 1.00
308 q_encoder_uncertainty = 2.00
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800309
Diana Burgessd0180f12018-03-21 21:24:17 -0700310 self.Q = numpy.matrix(
311 [[(q_pos**2.0), 0.0, 0.0, 0.0, 0.0, 0.0,
312 0.0], [0.0, (q_vel**2.0), 0.0, 0.0, 0.0, 0.0,
313 0.0], [0.0, 0.0, (q_pos**2.0), 0.0, 0.0, 0.0,
314 0.0], [0.0, 0.0, 0.0, (q_vel**2.0), 0.0, 0.0, 0.0],
315 [0.0, 0.0, 0.0, 0.0, (q_voltage**2.0), 0.0,
316 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, (q_voltage**2.0), 0.0],
317 [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, (q_encoder_uncertainty**2.0)]])
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800318
Diana Burgessd0180f12018-03-21 21:24:17 -0700319 r_pos = 0.0001
320 r_gyro = 0.000001
321 if self.has_imu:
322 r_accelerometer = 7.0
323 self.R = numpy.matrix([[(r_pos**2.0), 0.0, 0.0, 0.0],
324 [0.0, (r_pos**2.0), 0.0, 0.0],
325 [0.0, 0.0, (r_gyro**2.0), 0.0],
326 [0.0, 0.0, 0.0, (r_accelerometer**2.0)]])
327 else:
328 self.R = numpy.matrix([[(r_pos**2.0), 0.0, 0.0],
329 [0.0, (r_pos**2.0), 0.0],
330 [0.0, 0.0, (r_gyro**2.0)]])
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800331
Diana Burgessd0180f12018-03-21 21:24:17 -0700332 # Solving for kf gains.
333 self.KalmanGain, self.Q_steady = controls.kalman(
334 A=self.A, B=self.B, C=self.C, Q=self.Q, R=self.R)
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800335
Diana Burgessd0180f12018-03-21 21:24:17 -0700336 if not self.has_imu:
337 self.KalmanGain = numpy.hstack((self.KalmanGain, numpy.matrix(numpy.zeros((7, 1)))))
338 self.C = numpy.vstack((self.C, numpy.matrix(numpy.zeros((1, 7)))))
339 self.D = numpy.vstack((self.D, numpy.matrix(numpy.zeros((1, 2)))))
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800340
Diana Burgessd0180f12018-03-21 21:24:17 -0700341 self.L = self.A * self.KalmanGain
342
343 unaug_K = self.K
344
345 # Implement a nice closed loop controller for use by the closed loop
346 # controller.
347 self.K = numpy.matrix(numpy.zeros((self.B.shape[1], self.A.shape[0])))
348 self.K[0:2, 0:4] = unaug_K
349 if self.force:
350 self.K[0, 4] = 1.0 / self.mpl
351 self.K[1, 5] = 1.0 / self.mpr
352 else:
353 self.K[0, 4] = 1.0
354 self.K[1, 5] = 1.0
355
356 self.Qff = numpy.matrix(numpy.zeros((4, 4)))
357 qff_pos = 0.005
358 qff_vel = 1.00
359 self.Qff[0, 0] = 1.0 / qff_pos**2.0
360 self.Qff[1, 1] = 1.0 / qff_vel**2.0
361 self.Qff[2, 2] = 1.0 / qff_pos**2.0
362 self.Qff[3, 3] = 1.0 / qff_vel**2.0
363 self.Kff = numpy.matrix(numpy.zeros((2, 7)))
364 self.Kff[0:2, 0:4] = controls.TwoStateFeedForwards(
365 self.B[0:4, :], self.Qff)
366
367 self.InitializeState()
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800368
369
370def WriteDrivetrain(drivetrain_files, kf_drivetrain_files, year_namespace,
Austin Schuhbcce26a2018-03-26 23:41:24 -0700371 drivetrain_params, scalar_type='double'):
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800372
Diana Burgessd0180f12018-03-21 21:24:17 -0700373 # Write the generated constants out to a file.
374 drivetrain_low_low = Drivetrain(
375 name="DrivetrainLowLow",
376 left_low=True,
377 right_low=True,
378 drivetrain_params=drivetrain_params)
379 drivetrain_low_high = Drivetrain(
380 name="DrivetrainLowHigh",
381 left_low=True,
382 right_low=False,
383 drivetrain_params=drivetrain_params)
384 drivetrain_high_low = Drivetrain(
385 name="DrivetrainHighLow",
386 left_low=False,
387 right_low=True,
388 drivetrain_params=drivetrain_params)
389 drivetrain_high_high = Drivetrain(
390 name="DrivetrainHighHigh",
391 left_low=False,
392 right_low=False,
393 drivetrain_params=drivetrain_params)
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800394
Diana Burgessd0180f12018-03-21 21:24:17 -0700395 kf_drivetrain_low_low = KFDrivetrain(
396 name="KFDrivetrainLowLow",
397 left_low=True,
398 right_low=True,
399 drivetrain_params=drivetrain_params)
400 kf_drivetrain_low_high = KFDrivetrain(
401 name="KFDrivetrainLowHigh",
402 left_low=True,
403 right_low=False,
404 drivetrain_params=drivetrain_params)
405 kf_drivetrain_high_low = KFDrivetrain(
406 name="KFDrivetrainHighLow",
407 left_low=False,
408 right_low=True,
409 drivetrain_params=drivetrain_params)
410 kf_drivetrain_high_high = KFDrivetrain(
411 name="KFDrivetrainHighHigh",
412 left_low=False,
413 right_low=False,
414 drivetrain_params=drivetrain_params)
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800415
Austin Schuhbcce26a2018-03-26 23:41:24 -0700416 if isinstance(year_namespace, list):
417 namespaces = year_namespace
418 else:
419 namespaces = [year_namespace, 'control_loops', 'drivetrain']
Diana Burgessd0180f12018-03-21 21:24:17 -0700420 dog_loop_writer = control_loop.ControlLoopWriter(
421 "Drivetrain", [
422 drivetrain_low_low, drivetrain_low_high, drivetrain_high_low,
423 drivetrain_high_high
424 ],
Austin Schuhbcce26a2018-03-26 23:41:24 -0700425 namespaces=namespaces,
426 scalar_type=scalar_type)
Diana Burgessd0180f12018-03-21 21:24:17 -0700427 dog_loop_writer.AddConstant(
428 control_loop.Constant("kDt", "%f", drivetrain_low_low.dt))
429 dog_loop_writer.AddConstant(
430 control_loop.Constant("kStallTorque", "%f",
431 drivetrain_low_low.stall_torque))
432 dog_loop_writer.AddConstant(
433 control_loop.Constant("kStallCurrent", "%f",
434 drivetrain_low_low.stall_current))
435 dog_loop_writer.AddConstant(
436 control_loop.Constant("kFreeSpeed", "%f",
437 drivetrain_low_low.free_speed))
438 dog_loop_writer.AddConstant(
439 control_loop.Constant("kFreeCurrent", "%f",
440 drivetrain_low_low.free_current))
441 dog_loop_writer.AddConstant(
442 control_loop.Constant("kJ", "%f", drivetrain_low_low.J))
443 dog_loop_writer.AddConstant(
444 control_loop.Constant("kMass", "%f", drivetrain_low_low.mass))
445 dog_loop_writer.AddConstant(
446 control_loop.Constant("kRobotRadius", "%f",
447 drivetrain_low_low.robot_radius))
448 dog_loop_writer.AddConstant(
449 control_loop.Constant("kWheelRadius", "%f", drivetrain_low_low.r))
450 dog_loop_writer.AddConstant(
451 control_loop.Constant("kR", "%f", drivetrain_low_low.resistance))
452 dog_loop_writer.AddConstant(
453 control_loop.Constant("kV", "%f", drivetrain_low_low.Kv))
454 dog_loop_writer.AddConstant(
455 control_loop.Constant("kT", "%f", drivetrain_low_low.Kt))
456 dog_loop_writer.AddConstant(
457 control_loop.Constant("kLowGearRatio", "%f", drivetrain_low_low.G_low))
458 dog_loop_writer.AddConstant(
459 control_loop.Constant("kHighGearRatio", "%f",
460 drivetrain_high_high.G_high))
461 dog_loop_writer.AddConstant(
462 control_loop.Constant(
463 "kHighOutputRatio", "%f",
464 drivetrain_high_high.G_high * drivetrain_high_high.r))
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800465
Diana Burgessd0180f12018-03-21 21:24:17 -0700466 dog_loop_writer.Write(drivetrain_files[0], drivetrain_files[1])
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800467
Diana Burgessd0180f12018-03-21 21:24:17 -0700468 kf_loop_writer = control_loop.ControlLoopWriter(
469 "KFDrivetrain", [
470 kf_drivetrain_low_low, kf_drivetrain_low_high,
471 kf_drivetrain_high_low, kf_drivetrain_high_high
472 ],
Austin Schuhbcce26a2018-03-26 23:41:24 -0700473 namespaces=namespaces,
474 scalar_type=scalar_type)
Diana Burgessd0180f12018-03-21 21:24:17 -0700475 kf_loop_writer.Write(kf_drivetrain_files[0], kf_drivetrain_files[1])
476
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800477
478def PlotDrivetrainMotions(drivetrain_params):
Diana Burgessd0180f12018-03-21 21:24:17 -0700479 # Simulate the response of the system to a step input.
480 drivetrain = Drivetrain(
481 left_low=False, right_low=False, drivetrain_params=drivetrain_params)
482 simulated_left = []
483 simulated_right = []
484 for _ in xrange(100):
485 drivetrain.Update(numpy.matrix([[12.0], [12.0]]))
486 simulated_left.append(drivetrain.X[0, 0])
487 simulated_right.append(drivetrain.X[2, 0])
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800488
Diana Burgessd0180f12018-03-21 21:24:17 -0700489 pylab.rc('lines', linewidth=4)
490 pylab.plot(range(100), simulated_left, label='left position')
491 pylab.plot(range(100), simulated_right, 'r--', label='right position')
492 pylab.suptitle('Acceleration Test\n12 Volt Step Input')
493 pylab.legend(loc='lower right')
494 pylab.show()
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800495
Diana Burgessd0180f12018-03-21 21:24:17 -0700496 # Simulate forwards motion.
497 drivetrain = Drivetrain(
498 left_low=False, right_low=False, drivetrain_params=drivetrain_params)
499 close_loop_left = []
500 close_loop_right = []
501 left_power = []
502 right_power = []
503 R = numpy.matrix([[1.0], [0.0], [1.0], [0.0]])
504 for _ in xrange(300):
505 U = numpy.clip(drivetrain.K * (R - drivetrain.X_hat), drivetrain.U_min,
506 drivetrain.U_max)
507 drivetrain.UpdateObserver(U)
508 drivetrain.Update(U)
509 close_loop_left.append(drivetrain.X[0, 0])
510 close_loop_right.append(drivetrain.X[2, 0])
511 left_power.append(U[0, 0])
512 right_power.append(U[1, 0])
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800513
Diana Burgessd0180f12018-03-21 21:24:17 -0700514 pylab.plot(range(300), close_loop_left, label='left position')
515 pylab.plot(range(300), close_loop_right, 'm--', label='right position')
516 pylab.plot(range(300), left_power, label='left power')
517 pylab.plot(range(300), right_power, '--', label='right power')
518 pylab.suptitle('Linear Move\nLeft and Right Position going to 1')
519 pylab.legend()
520 pylab.show()
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800521
Diana Burgessd0180f12018-03-21 21:24:17 -0700522 # Try turning in place
523 drivetrain = Drivetrain(drivetrain_params=drivetrain_params)
524 close_loop_left = []
525 close_loop_right = []
526 R = numpy.matrix([[-1.0], [0.0], [1.0], [0.0]])
527 for _ in xrange(200):
528 U = numpy.clip(drivetrain.K * (R - drivetrain.X_hat), drivetrain.U_min,
529 drivetrain.U_max)
530 drivetrain.UpdateObserver(U)
531 drivetrain.Update(U)
532 close_loop_left.append(drivetrain.X[0, 0])
533 close_loop_right.append(drivetrain.X[2, 0])
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800534
Diana Burgessd0180f12018-03-21 21:24:17 -0700535 pylab.plot(range(200), close_loop_left, label='left position')
536 pylab.plot(range(200), close_loop_right, label='right position')
537 pylab.suptitle(
538 'Angular Move\nLeft position going to -1 and right position going to 1'
539 )
540 pylab.legend(loc='center right')
541 pylab.show()
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800542
Diana Burgessd0180f12018-03-21 21:24:17 -0700543 # Try turning just one side.
544 drivetrain = Drivetrain(drivetrain_params=drivetrain_params)
545 close_loop_left = []
546 close_loop_right = []
547 R = numpy.matrix([[0.0], [0.0], [1.0], [0.0]])
548 for _ in xrange(300):
549 U = numpy.clip(drivetrain.K * (R - drivetrain.X_hat), drivetrain.U_min,
550 drivetrain.U_max)
551 drivetrain.UpdateObserver(U)
552 drivetrain.Update(U)
553 close_loop_left.append(drivetrain.X[0, 0])
554 close_loop_right.append(drivetrain.X[2, 0])
Campbell Crowley33e0e3d2017-12-27 17:55:40 -0800555
Diana Burgessd0180f12018-03-21 21:24:17 -0700556 pylab.plot(range(300), close_loop_left, label='left position')
557 pylab.plot(range(300), close_loop_right, label='right position')
558 pylab.suptitle(
559 'Pivot\nLeft position not changing and right position going to 1')
560 pylab.legend(loc='center right')
561 pylab.show()