blob: dc9caa109d2c56ebdd27946583cd463f0cb4ed03 [file] [log] [blame]
Austin Schuh1a387962015-01-31 16:36:20 -08001#!/usr/bin/python
2
Austin Schuh88af0852016-12-04 20:31:32 -08003from frc971.control_loops.python import control_loop
4from frc971.control_loops.python import controls
5from frc971.control_loops.python import polytope
Austin Schuh1a387962015-01-31 16:36:20 -08006import numpy
7import sys
8import matplotlib
9from matplotlib import pylab
10
11class Elevator(control_loop.ControlLoop):
12 def __init__(self, name="Elevator", mass=None):
13 super(Elevator, self).__init__(name)
14 # Stall Torque in N m
15 self.stall_torque = 0.476
16 # Stall Current in Amps
17 self.stall_current = 80.730
18 # Free Speed in RPM
19 self.free_speed = 13906.0
20 # Free Current in Amps
21 self.free_current = 5.820
22 # Mass of the elevator
23 if mass is None:
24 self.mass = 13.0
25 else:
26 self.mass = mass
27
28 # Resistance of the motor
29 self.R = 12.0 / self.stall_current
30 # Motor velocity constant
31 self.Kv = ((self.free_speed / 60.0 * 2.0 * numpy.pi) /
32 (12.0 - self.R * self.free_current))
33 # Torque constant
34 self.Kt = self.stall_torque / self.stall_current
35 # Gear ratio
36 self.G = (56.0 / 12.0) * (84.0 / 14.0)
37 # Pulley diameter
38 self.r = 32 * 0.005 / numpy.pi / 2.0
39 # Control loop time step
40 self.dt = 0.005
41
42 # Elevator left/right spring constant (N/m)
Austin Schuhbfb8b242015-02-16 15:45:22 -080043 self.spring = 800.0
Austin Schuh1a387962015-01-31 16:36:20 -080044
45 # State is [average position, average velocity,
46 # position difference/2, velocity difference/2]
Austin Schuh703b8d42015-02-01 14:56:34 -080047 # Input is [V_left, V_right]
Austin Schuh1a387962015-01-31 16:36:20 -080048
49 C1 = self.spring / (self.mass * 0.5)
50 C2 = self.Kt * self.G / (self.mass * 0.5 * self.r * self.R)
51 C3 = self.G * self.G * self.Kt / (
52 self.R * self.r * self.r * self.mass * 0.5 * self.Kv)
53
54 self.A_continuous = numpy.matrix(
55 [[0, 1, 0, 0],
56 [0, -C3, 0, 0],
57 [0, 0, 0, 1],
58 [0, 0, -C1 * 2.0, -C3]])
59
60 print "Full speed is", C2 / C3 * 12.0
61
62 # Start with the unmodified input
63 self.B_continuous = numpy.matrix(
64 [[0, 0],
65 [C2 / 2.0, C2 / 2.0],
66 [0, 0],
67 [C2 / 2.0, -C2 / 2.0]])
68
69 self.C = numpy.matrix([[1, 0, 1, 0],
70 [1, 0, -1, 0]])
71 self.D = numpy.matrix([[0, 0],
72 [0, 0]])
73
74 self.A, self.B = self.ContinuousToDiscrete(
75 self.A_continuous, self.B_continuous, self.dt)
76
77 print self.A
78
Brian Silvermane18cf502015-11-28 23:04:43 +000079 controllability = controls.ctrb(self.A, self.B)
80 print "Rank of augmented controllability matrix.", numpy.linalg.matrix_rank(
81 controllability)
Austin Schuh1a387962015-01-31 16:36:20 -080082
83 q_pos = 0.02
84 q_vel = 0.400
85 q_pos_diff = 0.01
86 q_vel_diff = 0.45
87 self.Q = numpy.matrix([[(1.0 / (q_pos ** 2.0)), 0.0, 0.0, 0.0],
88 [0.0, (1.0 / (q_vel ** 2.0)), 0.0, 0.0],
89 [0.0, 0.0, (1.0 / (q_pos_diff ** 2.0)), 0.0],
90 [0.0, 0.0, 0.0, (1.0 / (q_vel_diff ** 2.0))]])
91
92 self.R = numpy.matrix([[(1.0 / (12.0 ** 2.0)), 0.0],
93 [0.0, 1.0 / (12.0 ** 2.0)]])
94 self.K = controls.dlqr(self.A, self.B, self.Q, self.R)
95 print self.K
96
97 print numpy.linalg.eig(self.A - self.B * self.K)[0]
98
99 self.rpl = 0.20
100 self.ipl = 0.05
101 self.PlaceObserverPoles([self.rpl + 1j * self.ipl,
102 self.rpl + 1j * self.ipl,
103 self.rpl - 1j * self.ipl,
104 self.rpl - 1j * self.ipl])
105
106 # The box formed by U_min and U_max must encompass all possible values,
107 # or else Austin's code gets angry.
108 self.U_max = numpy.matrix([[12.0], [12.0]])
109 self.U_min = numpy.matrix([[-12.0], [-12.0]])
110
111 self.InitializeState()
112
113
114def CapU(U):
115 if U[0, 0] - U[1, 0] > 24:
116 return numpy.matrix([[12], [-12]])
117 elif U[0, 0] - U[1, 0] < -24:
118 return numpy.matrix([[-12], [12]])
119 else:
120 max_u = max(U[0, 0], U[1, 0])
121 min_u = min(U[0, 0], U[1, 0])
122 if max_u > 12:
123 return U - (max_u - 12)
124 if min_u < -12:
125 return U - (min_u + 12)
126 return U
127
128
129def run_test(elevator, initial_X, goal, max_separation_error=0.01,
Austin Schuh88af0852016-12-04 20:31:32 -0800130 show_graph=False, iterations=200, controller_elevator=None,
Austin Schuh1a387962015-01-31 16:36:20 -0800131 observer_elevator=None):
132 """Runs the elevator plant with an initial condition and goal.
133
134 The tests themselves are not terribly sophisticated; I just test for
135 whether the goal has been reached and whether the separation goes
136 outside of the initial and goal values by more than max_separation_error.
137 Prints out something for a failure of either condition and returns
138 False if tests fail.
139 Args:
140 elevator: elevator object to use.
141 initial_X: starting state.
142 goal: goal state.
143 show_graph: Whether or not to display a graph showing the changing
144 states and voltages.
145 iterations: Number of timesteps to run the model for.
146 controller_elevator: elevator object to get K from, or None if we should
147 use elevator.
148 observer_elevator: elevator object to use for the observer, or None if we
149 should use the actual state.
150 """
151
152 elevator.X = initial_X
153
154 if controller_elevator is None:
155 controller_elevator = elevator
156
157 if observer_elevator is not None:
158 observer_elevator.X_hat = initial_X + 0.01
159 observer_elevator.X_hat = initial_X
160
161 # Various lists for graphing things.
162 t = []
163 x_avg = []
164 x_sep = []
165 x_hat_avg = []
166 x_hat_sep = []
167 v_avg = []
168 v_sep = []
169 u_left = []
170 u_right = []
171
172 sep_plot_gain = 100.0
173
174 for i in xrange(iterations):
175 X_hat = elevator.X
176 if observer_elevator is not None:
177 X_hat = observer_elevator.X_hat
178 x_hat_avg.append(observer_elevator.X_hat[0, 0])
179 x_hat_sep.append(observer_elevator.X_hat[2, 0] * sep_plot_gain)
180 U = controller_elevator.K * (goal - X_hat)
181 U = CapU(U)
182 x_avg.append(elevator.X[0, 0])
183 v_avg.append(elevator.X[1, 0])
184 x_sep.append(elevator.X[2, 0] * sep_plot_gain)
185 v_sep.append(elevator.X[3, 0])
186 if observer_elevator is not None:
187 observer_elevator.PredictObserver(U)
188 elevator.Update(U)
189 if observer_elevator is not None:
190 observer_elevator.Y = elevator.Y
191 observer_elevator.CorrectObserver(U)
192
193 t.append(i * elevator.dt)
194 u_left.append(U[0, 0])
195 u_right.append(U[1, 0])
196
197 print numpy.linalg.inv(elevator.A)
198 print "delta time is ", elevator.dt
199 print "Velocity at t=0 is ", x_avg[0], v_avg[0], x_sep[0], v_sep[0]
200 print "Velocity at t=1+dt is ", x_avg[1], v_avg[1], x_sep[1], v_sep[1]
201
202 if show_graph:
203 pylab.subplot(2, 1, 1)
204 pylab.plot(t, x_avg, label='x avg')
205 pylab.plot(t, x_sep, label='x sep')
206 if observer_elevator is not None:
207 pylab.plot(t, x_hat_avg, label='x_hat avg')
208 pylab.plot(t, x_hat_sep, label='x_hat sep')
209 pylab.legend()
210
211 pylab.subplot(2, 1, 2)
212 pylab.plot(t, u_left, label='u left')
213 pylab.plot(t, u_right, label='u right')
214 pylab.legend()
215 pylab.show()
216
217
218def main(argv):
219 loaded_mass = 25
220 #loaded_mass = 0
221 elevator = Elevator(mass=13 + loaded_mass)
222 elevator_controller = Elevator(mass=13 + 15)
223 observer_elevator = Elevator(mass=13 + 15)
224 #observer_elevator = None
225
226 # Test moving the elevator with constant separation.
227 initial_X = numpy.matrix([[0.0], [0.0], [0.01], [0.0]])
228 #initial_X = numpy.matrix([[0.0], [0.0], [0.00], [0.0]])
229 R = numpy.matrix([[1.0], [0.0], [0.0], [0.0]])
230 run_test(elevator, initial_X, R, controller_elevator=elevator_controller,
231 observer_elevator=observer_elevator)
232
233 # Write the generated constants out to a file.
234 if len(argv) != 3:
235 print "Expected .h file name and .cc file name for the elevator."
236 else:
Austin Schuh88af0852016-12-04 20:31:32 -0800237 namespaces = ['y2015', 'control_loops', 'fridge']
Austin Schuh1a387962015-01-31 16:36:20 -0800238 elevator = Elevator("Elevator")
Austin Schuh88af0852016-12-04 20:31:32 -0800239 loop_writer = control_loop.ControlLoopWriter("Elevator", [elevator],
240 namespaces=namespaces)
241 if argv[1][-3:] == '.cc':
242 loop_writer.Write(argv[2], argv[1])
243 else:
244 loop_writer.Write(argv[1], argv[2])
Austin Schuh1a387962015-01-31 16:36:20 -0800245
246if __name__ == '__main__':
247 sys.exit(main(sys.argv))