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