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