blob: 4231887d68169b3a41038b09abe270320bce384f [file] [log] [blame]
Comran Morshede9b12922015-11-04 19:46:48 +00001#!/usr/bin/python
2
Campbell Crowley9c3ecfd2015-12-31 17:04:30 -08003from frc971.control_loops.python import control_loop
4from frc971.control_loops.python import controls
Comran Morshede9b12922015-11-04 19:46:48 +00005import numpy
6import sys
7from matplotlib import pylab
8
Campbell Crowley9c3ecfd2015-12-31 17:04:30 -08009import glog
Comran Morshede9b12922015-11-04 19:46:48 +000010
11class CIM(control_loop.ControlLoop):
12 def __init__(self):
13 super(CIM, self).__init__("CIM")
14 # Stall Torque in N m
15 self.stall_torque = 2.42
16 # Stall Current in Amps
17 self.stall_current = 133
18 # Free Speed in RPM
19 self.free_speed = 4650.0
20 # Free Current in Amps
21 self.free_current = 2.7
22 # Moment of inertia of the CIM in kg m^2
23 self.J = 0.0001
24 # Resistance of the motor, divided by 2 to account for the 2 motors
25 self.R = 12.0 / self.stall_current
26 # Motor velocity constant
27 self.Kv = ((self.free_speed / 60.0 * 2.0 * numpy.pi) /
28 (12.0 - self.R * self.free_current))
29 # Torque constant
30 self.Kt = self.stall_torque / self.stall_current
31 # Control loop time step
32 self.dt = 0.005
33
34 # State feedback matrices
35 self.A_continuous = numpy.matrix(
36 [[-self.Kt / self.Kv / (self.J * self.R)]])
37 self.B_continuous = numpy.matrix(
38 [[self.Kt / (self.J * self.R)]])
39 self.C = numpy.matrix([[1]])
40 self.D = numpy.matrix([[0]])
41
42 self.A, self.B = self.ContinuousToDiscrete(self.A_continuous,
43 self.B_continuous, self.dt)
44
45 self.PlaceControllerPoles([0.01])
46 self.PlaceObserverPoles([0.01])
47
48 self.U_max = numpy.matrix([[12.0]])
49 self.U_min = numpy.matrix([[-12.0]])
50
51 self.InitializeState()
52
53
54class Drivetrain(control_loop.ControlLoop):
55 def __init__(self, name="Drivetrain", left_low=True, right_low=True):
56 super(Drivetrain, self).__init__(name)
57 # Stall Torque in N m
58 self.stall_torque = 2.42
59 # Stall Current in Amps
Comran Morshed41ed7c22015-11-04 21:03:37 +000060 self.stall_current = 133.0
Comran Morshede9b12922015-11-04 19:46:48 +000061 # Free Speed in RPM. Used number from last year.
62 self.free_speed = 4650.0
63 # Free Current in Amps
64 self.free_current = 2.7
65 # Moment of inertia of the drivetrain in kg m^2
66 # Just borrowed from last year.
Comran Morshed41ed7c22015-11-04 21:03:37 +000067 self.J = 10
Comran Morshede9b12922015-11-04 19:46:48 +000068 # Mass of the robot, in kg.
Comran Morshed41ed7c22015-11-04 21:03:37 +000069 self.m = 68
Comran Morshede9b12922015-11-04 19:46:48 +000070 # Radius of the robot, in meters (from last year).
Comran Morshed41ed7c22015-11-04 21:03:37 +000071 self.rb = 0.9603 / 2.0
Comran Morshede9b12922015-11-04 19:46:48 +000072 # Radius of the wheels, in meters.
Comran Morshed41ed7c22015-11-04 21:03:37 +000073 self.r = 0.0508
Comran Morshede9b12922015-11-04 19:46:48 +000074 # Resistance of the motor, divided by the number of motors.
Comran Morshed41ed7c22015-11-04 21:03:37 +000075 self.R = 12.0 / self.stall_current / 2
Comran Morshede9b12922015-11-04 19:46:48 +000076 # Motor velocity constant
77 self.Kv = ((self.free_speed / 60.0 * 2.0 * numpy.pi) /
78 (12.0 - self.R * self.free_current))
79 # Torque constant
80 self.Kt = self.stall_torque / self.stall_current
81 # Gear ratios
Comran Morshed41ed7c22015-11-04 21:03:37 +000082 self.G_const = 18.0 / 44.0 * 18.0 / 60.0
83
84 self.G_low = self.G_const
85 self.G_high = self.G_const
86
Comran Morshede9b12922015-11-04 19:46:48 +000087 if left_low:
88 self.Gl = self.G_low
89 else:
90 self.Gl = self.G_high
91 if right_low:
92 self.Gr = self.G_low
93 else:
94 self.Gr = self.G_high
Comran Morshed41ed7c22015-11-04 21:03:37 +000095
Comran Morshede9b12922015-11-04 19:46:48 +000096 # Control loop time step
Comran Morshed41ed7c22015-11-04 21:03:37 +000097 self.dt = 0.005
Comran Morshede9b12922015-11-04 19:46:48 +000098
99 # These describe the way that a given side of a robot will be influenced
100 # by the other side. Units of 1 / kg.
101 self.msp = 1.0 / self.m + self.rb * self.rb / self.J
102 self.msn = 1.0 / self.m - self.rb * self.rb / self.J
103 # The calculations which we will need for A and B.
104 self.tcl = -self.Kt / self.Kv / (self.Gl * self.Gl * self.R * self.r * self.r)
105 self.tcr = -self.Kt / self.Kv / (self.Gr * self.Gr * self.R * self.r * self.r)
106 self.mpl = self.Kt / (self.Gl * self.R * self.r)
107 self.mpr = self.Kt / (self.Gr * self.R * self.r)
108
109 # State feedback matrices
110 # X will be of the format
111 # [[positionl], [velocityl], [positionr], velocityr]]
112 self.A_continuous = numpy.matrix(
113 [[0, 1, 0, 0],
114 [0, self.msp * self.tcl, 0, self.msn * self.tcr],
115 [0, 0, 0, 1],
116 [0, self.msn * self.tcl, 0, self.msp * self.tcr]])
117 self.B_continuous = numpy.matrix(
118 [[0, 0],
119 [self.msp * self.mpl, self.msn * self.mpr],
120 [0, 0],
121 [self.msn * self.mpl, self.msp * self.mpr]])
122 self.C = numpy.matrix([[1, 0, 0, 0],
123 [0, 0, 1, 0]])
124 self.D = numpy.matrix([[0, 0],
125 [0, 0]])
126
Campbell Crowley9c3ecfd2015-12-31 17:04:30 -0800127 #glog.debug('THE NUMBER I WANT %s', str(numpy.linalg.inv(self.A_continuous) * -self.B_continuous * numpy.matrix([[12.0], [12.0]])))
Comran Morshede9b12922015-11-04 19:46:48 +0000128 self.A, self.B = self.ContinuousToDiscrete(
129 self.A_continuous, self.B_continuous, self.dt)
130
131 # Poles from last year.
132 self.hp = 0.65
133 self.lp = 0.83
134 self.PlaceControllerPoles([self.hp, self.lp, self.hp, self.lp])
Campbell Crowley9c3ecfd2015-12-31 17:04:30 -0800135 glog.info('K %s', str(self.K))
Comran Morshede9b12922015-11-04 19:46:48 +0000136 q_pos = 0.07
137 q_vel = 1.0
138 self.Q = numpy.matrix([[(1.0 / (q_pos ** 2.0)), 0.0, 0.0, 0.0],
139 [0.0, (1.0 / (q_vel ** 2.0)), 0.0, 0.0],
140 [0.0, 0.0, (1.0 / (q_pos ** 2.0)), 0.0],
141 [0.0, 0.0, 0.0, (1.0 / (q_vel ** 2.0))]])
142
143 self.R = numpy.matrix([[(1.0 / (12.0 ** 2.0)), 0.0],
144 [0.0, (1.0 / (12.0 ** 2.0))]])
145 self.K = controls.dlqr(self.A, self.B, self.Q, self.R)
Campbell Crowley9c3ecfd2015-12-31 17:04:30 -0800146 glog.info('A %s', str(self.A))
147 glog.info('B %s', str(self.B))
148 glog.info('K %s', str(self.K))
149 glog.info('Poles are %s', str(numpy.linalg.eig(self.A - self.B * self.K)[0]))
Comran Morshede9b12922015-11-04 19:46:48 +0000150
151 self.hlp = 0.3
152 self.llp = 0.4
153 self.PlaceObserverPoles([self.hlp, self.hlp, self.llp, self.llp])
154
155 self.U_max = numpy.matrix([[12.0], [12.0]])
156 self.U_min = numpy.matrix([[-12.0], [-12.0]])
157 self.InitializeState()
158
159def main(argv):
160 # Simulate the response of the system to a step input.
161 drivetrain = Drivetrain()
162 simulated_left = []
163 simulated_right = []
164 for _ in xrange(100):
165 drivetrain.Update(numpy.matrix([[12.0], [12.0]]))
166 simulated_left.append(drivetrain.X[0, 0])
167 simulated_right.append(drivetrain.X[2, 0])
168
169 #pylab.plot(range(100), simulated_left)
170 #pylab.plot(range(100), simulated_right)
171 #pylab.show()
172
173 # Simulate forwards motion.
174 drivetrain = Drivetrain()
175 close_loop_left = []
176 close_loop_right = []
177 R = numpy.matrix([[1.0], [0.0], [1.0], [0.0]])
178 for _ in xrange(100):
179 U = numpy.clip(drivetrain.K * (R - drivetrain.X_hat),
180 drivetrain.U_min, drivetrain.U_max)
181 drivetrain.UpdateObserver(U)
182 drivetrain.Update(U)
183 close_loop_left.append(drivetrain.X[0, 0])
184 close_loop_right.append(drivetrain.X[2, 0])
185
186 #pylab.plot(range(100), close_loop_left)
187 #pylab.plot(range(100), close_loop_right)
188 #pylab.show()
189
190 # Try turning in place
191 drivetrain = Drivetrain()
192 close_loop_left = []
193 close_loop_right = []
194 R = numpy.matrix([[-1.0], [0.0], [1.0], [0.0]])
195 for _ in xrange(100):
196 U = numpy.clip(drivetrain.K * (R - drivetrain.X_hat),
197 drivetrain.U_min, drivetrain.U_max)
198 drivetrain.UpdateObserver(U)
199 drivetrain.Update(U)
200 close_loop_left.append(drivetrain.X[0, 0])
201 close_loop_right.append(drivetrain.X[2, 0])
202
203 #pylab.plot(range(100), close_loop_left)
204 #pylab.plot(range(100), close_loop_right)
205 #pylab.show()
206
207 # Try turning just one side.
208 drivetrain = Drivetrain()
209 close_loop_left = []
210 close_loop_right = []
211 R = numpy.matrix([[0.0], [0.0], [1.0], [0.0]])
212 for _ in xrange(100):
213 U = numpy.clip(drivetrain.K * (R - drivetrain.X_hat),
214 drivetrain.U_min, drivetrain.U_max)
215 drivetrain.UpdateObserver(U)
216 drivetrain.Update(U)
217 close_loop_left.append(drivetrain.X[0, 0])
218 close_loop_right.append(drivetrain.X[2, 0])
219
220 #pylab.plot(range(100), close_loop_left)
221 #pylab.plot(range(100), close_loop_right)
222 #pylab.show()
223
224 # Write the generated constants out to a file.
Campbell Crowley9c3ecfd2015-12-31 17:04:30 -0800225 glog.info('Output one')
Comran Morshede9b12922015-11-04 19:46:48 +0000226 drivetrain_low_low = Drivetrain(name="DrivetrainLowLow", left_low=True, right_low=True)
227 drivetrain_low_high = Drivetrain(name="DrivetrainLowHigh", left_low=True, right_low=False)
228 drivetrain_high_low = Drivetrain(name="DrivetrainHighLow", left_low=False, right_low=True)
229 drivetrain_high_high = Drivetrain(name="DrivetrainHighHigh", left_low=False, right_low=False)
230
Campbell Crowley9c3ecfd2015-12-31 17:04:30 -0800231 if len(argv) != 3:
232 glog.fatal('Expected .h file name and .cc file name')
Comran Morshede9b12922015-11-04 19:46:48 +0000233 else:
234 dog_loop_writer = control_loop.ControlLoopWriter(
235 "Drivetrain", [drivetrain_low_low, drivetrain_low_high,
236 drivetrain_high_low, drivetrain_high_high],
Campbell Crowley9c3ecfd2015-12-31 17:04:30 -0800237 namespaces=['y2014_bot3', 'control_loops', 'drivetrain'])
238 dog_loop_writer.Write(argv[1], argv[2])
Comran Morshede9b12922015-11-04 19:46:48 +0000239
240if __name__ == '__main__':
241 sys.exit(main(sys.argv))