blob: 0adeedecfde65b26a4b4f8f9eb033def9d152207 [file] [log] [blame]
Comran Morshed0d6cf9b2015-06-17 19:29:57 +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 Morshed0d6cf9b2015-06-17 19:29:57 +00005import numpy
6import sys
7from matplotlib import pylab
8
Campbell Crowley9c3ecfd2015-12-31 17:04:30 -08009import glog
Comran Morshed0d6cf9b2015-06-17 19:29:57 +000010
Comran Morshed0d6cf9b2015-06-17 19:29:57 +000011
12class Drivetrain(control_loop.ControlLoop):
13 def __init__(self, name="Drivetrain", left_low=True, right_low=True):
14 super(Drivetrain, self).__init__(name)
15 # Stall Torque in N m
16 self.stall_torque = 2.42
17 # Stall Current in Amps
18 self.stall_current = 133.0
19 # Free Speed in RPM. Used number from last year.
20 self.free_speed = 4650.0
21 # Free Current in Amps
22 self.free_current = 2.7
23 # Moment of inertia of the drivetrain in kg m^2
24 # Just borrowed from last year.
Austin Schuh4c6a84d2015-09-14 21:34:08 +000025 self.J = 10
Comran Morshed0d6cf9b2015-06-17 19:29:57 +000026 # Mass of the robot, in kg.
27 self.m = 68
28 # Radius of the robot, in meters (from last year).
29 self.rb = 0.9603 / 2.0
30 # Radius of the wheels, in meters.
Austin Schuh511a67b2015-09-12 13:47:12 -070031 self.r = 0.0508
Comran Morshed0d6cf9b2015-06-17 19:29:57 +000032 # Resistance of the motor, divided by the number of motors.
33 self.R = 12.0 / self.stall_current / 2
34 # Motor velocity constant
35 self.Kv = ((self.free_speed / 60.0 * 2.0 * numpy.pi) /
36 (12.0 - self.R * self.free_current))
37 # Torque constant
38 self.Kt = self.stall_torque / self.stall_current
39 # Gear ratios
Austin Schuh511a67b2015-09-12 13:47:12 -070040 self.G_const = 18.0 / 44.0 * 18.0 / 60.0
Comran Morshed0d6cf9b2015-06-17 19:29:57 +000041
42 self.G_low = self.G_const
43 self.G_high = self.G_const
44
45 if left_low:
46 self.Gl = self.G_low
47 else:
48 self.Gl = self.G_high
49 if right_low:
50 self.Gr = self.G_low
51 else:
52 self.Gr = self.G_high
53
54 # Control loop time step
55 self.dt = 0.005
56
57 # These describe the way that a given side of a robot will be influenced
58 # by the other side. Units of 1 / kg.
59 self.msp = 1.0 / self.m + self.rb * self.rb / self.J
60 self.msn = 1.0 / self.m - self.rb * self.rb / self.J
61 # The calculations which we will need for A and B.
62 self.tcl = -self.Kt / self.Kv / (self.Gl * self.Gl * self.R * self.r * self.r)
63 self.tcr = -self.Kt / self.Kv / (self.Gr * self.Gr * self.R * self.r * self.r)
64 self.mpl = self.Kt / (self.Gl * self.R * self.r)
65 self.mpr = self.Kt / (self.Gr * self.R * self.r)
66
67 # State feedback matrices
68 # X will be of the format
69 # [[positionl], [velocityl], [positionr], velocityr]]
70 self.A_continuous = numpy.matrix(
71 [[0, 1, 0, 0],
72 [0, self.msp * self.tcl, 0, self.msn * self.tcr],
73 [0, 0, 0, 1],
74 [0, self.msn * self.tcl, 0, self.msp * self.tcr]])
75 self.B_continuous = numpy.matrix(
76 [[0, 0],
77 [self.msp * self.mpl, self.msn * self.mpr],
78 [0, 0],
79 [self.msn * self.mpl, self.msp * self.mpr]])
80 self.C = numpy.matrix([[1, 0, 0, 0],
81 [0, 0, 1, 0]])
82 self.D = numpy.matrix([[0, 0],
83 [0, 0]])
84
Campbell Crowley9c3ecfd2015-12-31 17:04:30 -080085 #glog.debug('THE NUMBER I WANT %s', str(numpy.linalg.inv(self.A_continuous) * -self.B_continuous * numpy.matrix([[12.0], [12.0]])))
Comran Morshed0d6cf9b2015-06-17 19:29:57 +000086 self.A, self.B = self.ContinuousToDiscrete(
87 self.A_continuous, self.B_continuous, self.dt)
88
89 # Poles from last year.
90 self.hp = 0.65
91 self.lp = 0.83
92 self.PlaceControllerPoles([self.hp, self.lp, self.hp, self.lp])
Campbell Crowley9c3ecfd2015-12-31 17:04:30 -080093 glog.info('K %s', str(self.K))
Comran Morshed0d6cf9b2015-06-17 19:29:57 +000094 q_pos = 0.07
95 q_vel = 1.0
96 self.Q = numpy.matrix([[(1.0 / (q_pos ** 2.0)), 0.0, 0.0, 0.0],
97 [0.0, (1.0 / (q_vel ** 2.0)), 0.0, 0.0],
98 [0.0, 0.0, (1.0 / (q_pos ** 2.0)), 0.0],
99 [0.0, 0.0, 0.0, (1.0 / (q_vel ** 2.0))]])
100
101 self.R = numpy.matrix([[(1.0 / (12.0 ** 2.0)), 0.0],
102 [0.0, (1.0 / (12.0 ** 2.0))]])
103 self.K = controls.dlqr(self.A, self.B, self.Q, self.R)
Campbell Crowley9c3ecfd2015-12-31 17:04:30 -0800104 glog.info('A %s', str(self.A))
105 glog.info('B %s', str(self.B))
106 glog.info('K %s', str(self.K))
107 glog.info('Poles are %s', str(numpy.linalg.eig(self.A - self.B * self.K)[0]))
Comran Morshed0d6cf9b2015-06-17 19:29:57 +0000108
109 self.hlp = 0.3
110 self.llp = 0.4
111 self.PlaceObserverPoles([self.hlp, self.hlp, self.llp, self.llp])
112
113 self.U_max = numpy.matrix([[12.0], [12.0]])
114 self.U_min = numpy.matrix([[-12.0], [-12.0]])
115 self.InitializeState()
116
117def main(argv):
118 # Simulate the response of the system to a step input.
119 drivetrain = Drivetrain()
120 simulated_left = []
121 simulated_right = []
122 for _ in xrange(100):
123 drivetrain.Update(numpy.matrix([[12.0], [12.0]]))
124 simulated_left.append(drivetrain.X[0, 0])
125 simulated_right.append(drivetrain.X[2, 0])
126
127 #pylab.plot(range(100), simulated_left)
128 #pylab.plot(range(100), simulated_right)
129 #pylab.show()
130
131 # Simulate forwards motion.
132 drivetrain = Drivetrain()
133 close_loop_left = []
134 close_loop_right = []
135 R = numpy.matrix([[1.0], [0.0], [1.0], [0.0]])
136 for _ in xrange(100):
137 U = numpy.clip(drivetrain.K * (R - drivetrain.X_hat),
138 drivetrain.U_min, drivetrain.U_max)
139 drivetrain.UpdateObserver(U)
140 drivetrain.Update(U)
141 close_loop_left.append(drivetrain.X[0, 0])
142 close_loop_right.append(drivetrain.X[2, 0])
143
144 #pylab.plot(range(100), close_loop_left)
145 #pylab.plot(range(100), close_loop_right)
146 #pylab.show()
147
148 # Try turning in place
149 drivetrain = Drivetrain()
150 close_loop_left = []
151 close_loop_right = []
152 R = numpy.matrix([[-1.0], [0.0], [1.0], [0.0]])
153 for _ in xrange(100):
154 U = numpy.clip(drivetrain.K * (R - drivetrain.X_hat),
155 drivetrain.U_min, drivetrain.U_max)
156 drivetrain.UpdateObserver(U)
157 drivetrain.Update(U)
158 close_loop_left.append(drivetrain.X[0, 0])
159 close_loop_right.append(drivetrain.X[2, 0])
160
161 #pylab.plot(range(100), close_loop_left)
162 #pylab.plot(range(100), close_loop_right)
163 #pylab.show()
164
165 # Try turning just one side.
166 drivetrain = Drivetrain()
167 close_loop_left = []
168 close_loop_right = []
169 R = numpy.matrix([[0.0], [0.0], [1.0], [0.0]])
170 for _ in xrange(100):
171 U = numpy.clip(drivetrain.K * (R - drivetrain.X_hat),
172 drivetrain.U_min, drivetrain.U_max)
173 drivetrain.UpdateObserver(U)
174 drivetrain.Update(U)
175 close_loop_left.append(drivetrain.X[0, 0])
176 close_loop_right.append(drivetrain.X[2, 0])
177
178 #pylab.plot(range(100), close_loop_left)
179 #pylab.plot(range(100), close_loop_right)
180 #pylab.show()
181
182 # Write the generated constants out to a file.
Campbell Crowley9c3ecfd2015-12-31 17:04:30 -0800183 glog.info('Output one')
Comran Morshed0d6cf9b2015-06-17 19:29:57 +0000184 drivetrain_low_low = Drivetrain(name="DrivetrainLowLow", left_low=True, right_low=True)
185 drivetrain_low_high = Drivetrain(name="DrivetrainLowHigh", left_low=True, right_low=False)
186 drivetrain_high_low = Drivetrain(name="DrivetrainHighLow", left_low=False, right_low=True)
187 drivetrain_high_high = Drivetrain(name="DrivetrainHighHigh", left_low=False, right_low=False)
188
Campbell Crowley9c3ecfd2015-12-31 17:04:30 -0800189 if len(argv) != 3:
190 glog.fatal('Expected .h file name and .cc file name %s', str(len(argv)))
Comran Morshed0d6cf9b2015-06-17 19:29:57 +0000191 else:
192 dog_loop_writer = control_loop.ControlLoopWriter(
193 "Drivetrain", [drivetrain_low_low, drivetrain_low_high,
Jasmine Zhoudde7a772015-09-11 23:08:52 -0700194 drivetrain_high_low, drivetrain_high_high],
Campbell Crowley9c3ecfd2015-12-31 17:04:30 -0800195 namespaces=['y2015_bot3', 'control_loops', 'drivetrain'])
196 dog_loop_writer.Write(argv[1], argv[2])
Comran Morshed0d6cf9b2015-06-17 19:29:57 +0000197
198if __name__ == '__main__':
199 sys.exit(main(sys.argv))