blob: 3395b1defe08ebf188685136cad9f405726b2eff [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
Austin Schuh0d9467a2014-02-15 22:36:45 -080067 controlability = controls.ctrb(self.A, self.B);
Austin Schuhc1f68892013-03-16 17:06:27 -070068
Austin Schuh8a436e82015-02-16 23:31:28 -080069 q_pos = 0.09
70 q_vel = 1.2
Austin Schuh1a387962015-01-31 16:36:20 -080071 self.Q = numpy.matrix([[(1.0 / (q_pos ** 2.0)), 0.0],
72 [0.0, (1.0 / (q_vel ** 2.0))]])
73
74 self.R = numpy.matrix([[(1.0 / (12.0 ** 2.0))]])
75 self.K = controls.dlqr(self.A, self.B, self.Q, self.R)
Austin Schuh1a387962015-01-31 16:36:20 -080076
Austin Schuh8a436e82015-02-16 23:31:28 -080077 print 'K', self.K
78 print 'Poles are', numpy.linalg.eig(self.A - self.B * self.K)[0]
Austin Schuhc1f68892013-03-16 17:06:27 -070079
Austin Schuh8a436e82015-02-16 23:31:28 -080080 self.rpl = 0.30
81 self.ipl = 0.10
Austin Schuh1a387962015-01-31 16:36:20 -080082 self.PlaceObserverPoles([self.rpl + 1j * self.ipl,
83 self.rpl - 1j * self.ipl])
Austin Schuhc1f68892013-03-16 17:06:27 -070084
Austin Schuh1a387962015-01-31 16:36:20 -080085 # The box formed by U_min and U_max must encompass all possible values,
86 # or else Austin's code gets angry.
87 self.U_max = numpy.matrix([[12.0]])
88 self.U_min = numpy.matrix([[-12.0]])
Austin Schuhc1f68892013-03-16 17:06:27 -070089
90 self.InitializeState()
91
Austin Schuhfe6d37e2014-03-24 09:45:01 -070092
Austin Schuh1a387962015-01-31 16:36:20 -080093def run_test(claw, initial_X, goal, max_separation_error=0.01,
94 show_graph=True, iterations=200, controller_claw=None,
95 observer_claw=None):
96 """Runs the claw plant with an initial condition and goal.
Brian Silverman6dd2c532014-03-29 23:34:39 -070097
Austin Schuh1a387962015-01-31 16:36:20 -080098 The tests themselves are not terribly sophisticated; I just test for
James Kuszmaulf63b0ae2014-03-25 16:52:11 -070099 whether the goal has been reached and whether the separation goes
Brian Silverman6dd2c532014-03-29 23:34:39 -0700100 outside of the initial and goal values by more than max_separation_error.
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700101 Prints out something for a failure of either condition and returns
102 False if tests fail.
103 Args:
104 claw: claw object to use.
105 initial_X: starting state.
106 goal: goal state.
107 show_graph: Whether or not to display a graph showing the changing
108 states and voltages.
Austin Schuh1a387962015-01-31 16:36:20 -0800109 iterations: Number of timesteps to run the model for.
110 controller_claw: claw object to get K from, or None if we should
111 use claw.
112 observer_claw: claw object to use for the observer, or None if we should
113 use the actual state.
114 """
Austin Schuhc8ca2442013-02-23 12:29:33 -0800115
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700116 claw.X = initial_X
117
Austin Schuh1a387962015-01-31 16:36:20 -0800118 if controller_claw is None:
119 controller_claw = claw
120
121 if observer_claw is not None:
122 observer_claw.X_hat = initial_X + 0.01
123 observer_claw.X_hat = initial_X
124
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700125 # Various lists for graphing things.
Austin Schuhcda86af2014-02-16 16:16:39 -0800126 t = []
Austin Schuh1a387962015-01-31 16:36:20 -0800127 x = []
128 v = []
129 x_hat = []
130 u = []
Austin Schuhc8ca2442013-02-23 12:29:33 -0800131
Austin Schuh1a387962015-01-31 16:36:20 -0800132 sep_plot_gain = 100.0
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700133
134 for i in xrange(iterations):
Austin Schuh1a387962015-01-31 16:36:20 -0800135 X_hat = claw.X
136 if observer_claw is not None:
137 X_hat = observer_claw.X_hat
138 x_hat.append(observer_claw.X_hat[0, 0])
139 U = controller_claw.K * (goal - X_hat)
140 U[0, 0] = numpy.clip(U[0, 0], -12, 12)
141 x.append(claw.X[0, 0])
142 v.append(claw.X[1, 0])
143 if observer_claw is not None:
144 observer_claw.PredictObserver(U)
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700145 claw.Update(U)
Austin Schuh1a387962015-01-31 16:36:20 -0800146 if observer_claw is not None:
147 observer_claw.Y = claw.Y
148 observer_claw.CorrectObserver(U)
Brian Silvermanb087c0a2014-03-30 12:59:52 -0700149
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700150 t.append(i * claw.dt)
Austin Schuh1a387962015-01-31 16:36:20 -0800151 u.append(U[0, 0])
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700152
153 if show_graph:
Austin Schuh1a387962015-01-31 16:36:20 -0800154 pylab.subplot(2, 1, 1)
155 pylab.plot(t, x, label='x')
156 if observer_claw is not None:
157 pylab.plot(t, x_hat, label='x_hat')
158 pylab.legend()
159
160 pylab.subplot(2, 1, 2)
161 pylab.plot(t, u, label='u')
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700162 pylab.legend()
163 pylab.show()
164
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700165
166def main(argv):
Austin Schuh1a387962015-01-31 16:36:20 -0800167 loaded_mass = 5
168 #loaded_mass = 0
169 claw = Claw(mass=5 + loaded_mass)
170 claw_controller = Claw(mass=5 + 5)
171 observer_claw = Claw(mass=5 + 5)
172 #observer_claw = None
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700173
174 # Test moving the claw with constant separation.
Austin Schuh1a387962015-01-31 16:36:20 -0800175 initial_X = numpy.matrix([[0.0], [0.0]])
176 R = numpy.matrix([[1.0], [0.0]])
177 run_test(claw, initial_X, R, controller_claw=claw_controller,
178 observer_claw=observer_claw)
Brian Silvermanf05948b2014-03-30 00:24:36 -0700179
Austin Schuh3c542312013-02-24 01:53:50 -0800180 # Write the generated constants out to a file.
Austin Schuhcda86af2014-02-16 16:16:39 -0800181 if len(argv) != 3:
182 print "Expected .h file name and .cc file name for the claw."
Austin Schuhc8ca2442013-02-23 12:29:33 -0800183 else:
Austin Schuhcda86af2014-02-16 16:16:39 -0800184 claw = Claw("Claw")
185 loop_writer = control_loop.ControlLoopWriter("Claw", [claw])
Austin Schuh683a0d02013-03-02 01:51:31 -0800186 if argv[1][-3:] == '.cc':
Austin Schuhcda86af2014-02-16 16:16:39 -0800187 loop_writer.Write(argv[2], argv[1])
Austin Schuh683a0d02013-03-02 01:51:31 -0800188 else:
Austin Schuhcda86af2014-02-16 16:16:39 -0800189 loop_writer.Write(argv[1], argv[2])
Austin Schuhc8ca2442013-02-23 12:29:33 -0800190
191if __name__ == '__main__':
192 sys.exit(main(sys.argv))