blob: 1a25f95d2e63c928a94234227273fbb2cda04245 [file] [log] [blame]
Austin Schuhc8ca2442013-02-23 12:29:33 -08001#!/usr/bin/python
2
Austin Schuh3c542312013-02-24 01:53:50 -08003import control_loop
Austin Schuh0d9467a2014-02-15 22:36:45 -08004import controls
Austin Schuhfe6d37e2014-03-24 09:45:01 -07005import polytope
6import polydrivetrain
Austin Schuhc8ca2442013-02-23 12:29:33 -08007import numpy
Austin Schuhc8ca2442013-02-23 12:29:33 -08008import sys
Austin Schuh1a387962015-01-31 16:36:20 -08009import matplotlib
Austin Schuhc8ca2442013-02-23 12:29:33 -080010from matplotlib import pylab
Austin Schuhc8ca2442013-02-23 12:29:33 -080011
Austin Schuh3bb9a442014-02-02 16:01:45 -080012class Claw(control_loop.ControlLoop):
Austin Schuh1a387962015-01-31 16:36:20 -080013 def __init__(self, name="Claw", mass=None):
Austin Schuh3bb9a442014-02-02 16:01:45 -080014 super(Claw, self).__init__(name)
Austin Schuhc8ca2442013-02-23 12:29:33 -080015 # Stall Torque in N m
Austin Schuh1a387962015-01-31 16:36:20 -080016 self.stall_torque = 0.476
Austin Schuhc8ca2442013-02-23 12:29:33 -080017 # Stall Current in Amps
Austin Schuh1a387962015-01-31 16:36:20 -080018 self.stall_current = 80.730
James Kuszmaul92797402014-02-17 14:08:49 -080019 # Free Speed in RPM
Austin Schuh1a387962015-01-31 16:36:20 -080020 self.free_speed = 13906.0
Austin Schuh3c542312013-02-24 01:53:50 -080021 # Free Current in Amps
Austin Schuh1a387962015-01-31 16:36:20 -080022 self.free_current = 5.820
23 # Mass of the claw
24 if mass is None:
25 self.mass = 5.0
26 else:
27 self.mass = mass
James Kuszmaul3ac190a2014-03-26 20:11:04 -070028
Austin Schuhc8ca2442013-02-23 12:29:33 -080029 # Resistance of the motor
James Kuszmaul92797402014-02-17 14:08:49 -080030 self.R = 12.0 / self.stall_current
Austin Schuhc8ca2442013-02-23 12:29:33 -080031 # Motor velocity constant
Austin Schuh3c542312013-02-24 01:53:50 -080032 self.Kv = ((self.free_speed / 60.0 * 2.0 * numpy.pi) /
Austin Schuh1a387962015-01-31 16:36:20 -080033 (12.0 - self.R * self.free_current))
Austin Schuhc8ca2442013-02-23 12:29:33 -080034 # Torque constant
35 self.Kt = self.stall_torque / self.stall_current
36 # Gear ratio
Austin Schuh1a387962015-01-31 16:36:20 -080037 self.G = (56.0 / 12.0) * (54.0 / 14.0) * (64.0 / 14.0) * (72.0 / 18.0)
38 # Claw length
39 self.r = 18 * 0.0254
Austin Schuhc8ca2442013-02-23 12:29:33 -080040
Austin Schuh1a387962015-01-31 16:36:20 -080041 self.J = self.r * self.mass
42
43 # Control loop time step
44 self.dt = 0.005
45
46 # State is [position, velocity]
47 # Input is [Voltage]
48
49 C1 = self.G * self.G * self.Kt / (self.R * self.J * self.Kv)
50 C2 = self.Kt * self.G / (self.J * self.R)
James Kuszmaule2afbe42014-02-17 22:29:59 -080051
Austin Schuhc8ca2442013-02-23 12:29:33 -080052 self.A_continuous = numpy.matrix(
James Kuszmaule2afbe42014-02-17 22:29:59 -080053 [[0, 1],
Austin Schuh1a387962015-01-31 16:36:20 -080054 [0, -C1]])
James Kuszmaule2afbe42014-02-17 22:29:59 -080055
Austin Schuh1a387962015-01-31 16:36:20 -080056 # Start with the unmodified input
Austin Schuhc8ca2442013-02-23 12:29:33 -080057 self.B_continuous = numpy.matrix(
James Kuszmaule2afbe42014-02-17 22:29:59 -080058 [[0],
Austin Schuh1a387962015-01-31 16:36:20 -080059 [C2]])
James Kuszmaule2afbe42014-02-17 22:29:59 -080060
Austin Schuh1a387962015-01-31 16:36:20 -080061 self.C = numpy.matrix([[1, 0]])
62 self.D = numpy.matrix([[0]])
Austin Schuhc8ca2442013-02-23 12:29:33 -080063
Austin Schuhc1f68892013-03-16 17:06:27 -070064 self.A, self.B = self.ContinuousToDiscrete(
65 self.A_continuous, self.B_continuous, self.dt)
Austin Schuhc8ca2442013-02-23 12:29:33 -080066
James Kuszmaulc02a39a2014-02-18 15:45:16 -080067 print self.A
Austin Schuh0d9467a2014-02-15 22:36:45 -080068
69 controlability = controls.ctrb(self.A, self.B);
70 print "Rank of augmented controlability matrix.", numpy.linalg.matrix_rank(controlability)
Austin Schuhc1f68892013-03-16 17:06:27 -070071
Austin Schuh1a387962015-01-31 16:36:20 -080072 q_pos = 0.03
73 q_vel = 0.5
74 self.Q = numpy.matrix([[(1.0 / (q_pos ** 2.0)), 0.0],
75 [0.0, (1.0 / (q_vel ** 2.0))]])
76
77 self.R = numpy.matrix([[(1.0 / (12.0 ** 2.0))]])
78 self.K = controls.dlqr(self.A, self.B, self.Q, self.R)
Austin Schuhc1f68892013-03-16 17:06:27 -070079 print self.K
Austin Schuh1a387962015-01-31 16:36:20 -080080
Austin Schuhc1f68892013-03-16 17:06:27 -070081 print numpy.linalg.eig(self.A - self.B * self.K)[0]
82
Austin Schuh1a387962015-01-31 16:36:20 -080083 self.rpl = 0.20
84 self.ipl = 0.05
85 self.PlaceObserverPoles([self.rpl + 1j * self.ipl,
86 self.rpl - 1j * self.ipl])
Austin Schuhc1f68892013-03-16 17:06:27 -070087
Austin Schuh1a387962015-01-31 16:36:20 -080088 # The box formed by U_min and U_max must encompass all possible values,
89 # or else Austin's code gets angry.
90 self.U_max = numpy.matrix([[12.0]])
91 self.U_min = numpy.matrix([[-12.0]])
Austin Schuhc1f68892013-03-16 17:06:27 -070092
93 self.InitializeState()
94
Austin Schuhfe6d37e2014-03-24 09:45:01 -070095
Austin Schuh1a387962015-01-31 16:36:20 -080096def run_test(claw, initial_X, goal, max_separation_error=0.01,
97 show_graph=True, iterations=200, controller_claw=None,
98 observer_claw=None):
99 """Runs the claw plant with an initial condition and goal.
Brian Silverman6dd2c532014-03-29 23:34:39 -0700100
Austin Schuh1a387962015-01-31 16:36:20 -0800101 The tests themselves are not terribly sophisticated; I just test for
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700102 whether the goal has been reached and whether the separation goes
Brian Silverman6dd2c532014-03-29 23:34:39 -0700103 outside of the initial and goal values by more than max_separation_error.
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700104 Prints out something for a failure of either condition and returns
105 False if tests fail.
106 Args:
107 claw: claw object to use.
108 initial_X: starting state.
109 goal: goal state.
110 show_graph: Whether or not to display a graph showing the changing
111 states and voltages.
Austin Schuh1a387962015-01-31 16:36:20 -0800112 iterations: Number of timesteps to run the model for.
113 controller_claw: claw object to get K from, or None if we should
114 use claw.
115 observer_claw: claw object to use for the observer, or None if we should
116 use the actual state.
117 """
Austin Schuhc8ca2442013-02-23 12:29:33 -0800118
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700119 claw.X = initial_X
120
Austin Schuh1a387962015-01-31 16:36:20 -0800121 if controller_claw is None:
122 controller_claw = claw
123
124 if observer_claw is not None:
125 observer_claw.X_hat = initial_X + 0.01
126 observer_claw.X_hat = initial_X
127
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700128 # Various lists for graphing things.
Austin Schuhcda86af2014-02-16 16:16:39 -0800129 t = []
Austin Schuh1a387962015-01-31 16:36:20 -0800130 x = []
131 v = []
132 x_hat = []
133 u = []
Austin Schuhc8ca2442013-02-23 12:29:33 -0800134
Austin Schuh1a387962015-01-31 16:36:20 -0800135 sep_plot_gain = 100.0
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700136
137 for i in xrange(iterations):
Austin Schuh1a387962015-01-31 16:36:20 -0800138 X_hat = claw.X
139 if observer_claw is not None:
140 X_hat = observer_claw.X_hat
141 x_hat.append(observer_claw.X_hat[0, 0])
142 U = controller_claw.K * (goal - X_hat)
143 U[0, 0] = numpy.clip(U[0, 0], -12, 12)
144 x.append(claw.X[0, 0])
145 v.append(claw.X[1, 0])
146 if observer_claw is not None:
147 observer_claw.PredictObserver(U)
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700148 claw.Update(U)
Austin Schuh1a387962015-01-31 16:36:20 -0800149 if observer_claw is not None:
150 observer_claw.Y = claw.Y
151 observer_claw.CorrectObserver(U)
Brian Silvermanb087c0a2014-03-30 12:59:52 -0700152
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700153 t.append(i * claw.dt)
Austin Schuh1a387962015-01-31 16:36:20 -0800154 u.append(U[0, 0])
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700155
156 if show_graph:
Austin Schuh1a387962015-01-31 16:36:20 -0800157 pylab.subplot(2, 1, 1)
158 pylab.plot(t, x, label='x')
159 if observer_claw is not None:
160 pylab.plot(t, x_hat, label='x_hat')
161 pylab.legend()
162
163 pylab.subplot(2, 1, 2)
164 pylab.plot(t, u, label='u')
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700165 pylab.legend()
166 pylab.show()
167
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700168
169def main(argv):
Austin Schuh1a387962015-01-31 16:36:20 -0800170 loaded_mass = 5
171 #loaded_mass = 0
172 claw = Claw(mass=5 + loaded_mass)
173 claw_controller = Claw(mass=5 + 5)
174 observer_claw = Claw(mass=5 + 5)
175 #observer_claw = None
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700176
177 # Test moving the claw with constant separation.
Austin Schuh1a387962015-01-31 16:36:20 -0800178 initial_X = numpy.matrix([[0.0], [0.0]])
179 R = numpy.matrix([[1.0], [0.0]])
180 run_test(claw, initial_X, R, controller_claw=claw_controller,
181 observer_claw=observer_claw)
Brian Silvermanf05948b2014-03-30 00:24:36 -0700182
Austin Schuh3c542312013-02-24 01:53:50 -0800183 # Write the generated constants out to a file.
Austin Schuhcda86af2014-02-16 16:16:39 -0800184 if len(argv) != 3:
185 print "Expected .h file name and .cc file name for the claw."
Austin Schuhc8ca2442013-02-23 12:29:33 -0800186 else:
Austin Schuhcda86af2014-02-16 16:16:39 -0800187 claw = Claw("Claw")
188 loop_writer = control_loop.ControlLoopWriter("Claw", [claw])
Austin Schuh683a0d02013-03-02 01:51:31 -0800189 if argv[1][-3:] == '.cc':
Austin Schuhcda86af2014-02-16 16:16:39 -0800190 loop_writer.Write(argv[2], argv[1])
Austin Schuh683a0d02013-03-02 01:51:31 -0800191 else:
Austin Schuhcda86af2014-02-16 16:16:39 -0800192 loop_writer.Write(argv[1], argv[2])
Austin Schuhc8ca2442013-02-23 12:29:33 -0800193
194if __name__ == '__main__':
195 sys.exit(main(sys.argv))