blob: 86a261d607ec8156db10c911603b1e750315250e [file] [log] [blame]
Austin Schuhc8ca2442013-02-23 12:29:33 -08001#!/usr/bin/python
2
Adam Snaidere8e09ba2016-03-05 14:40:52 -08003from frc971.control_loops.python import control_loop
4from frc971.control_loops.python import controls
5from frc971.control_loops.python import polytope
Austin Schuhc8ca2442013-02-23 12:29:33 -08006import numpy
Austin Schuhc8ca2442013-02-23 12:29:33 -08007import sys
Austin Schuh1a387962015-01-31 16:36:20 -08008import matplotlib
Austin Schuhc8ca2442013-02-23 12:29:33 -08009from matplotlib import pylab
Adam Snaidere8e09ba2016-03-05 14:40:52 -080010import glog
11import gflags
12
13FLAGS = gflags.FLAGS
14
15try:
16 gflags.DEFINE_bool('plot', False, 'If true, plot the loop response.')
17except gflags.DuplicateFlagError:
18 pass
Austin Schuhc8ca2442013-02-23 12:29:33 -080019
Austin Schuh3bb9a442014-02-02 16:01:45 -080020class Claw(control_loop.ControlLoop):
Austin Schuh1a387962015-01-31 16:36:20 -080021 def __init__(self, name="Claw", mass=None):
Austin Schuh3bb9a442014-02-02 16:01:45 -080022 super(Claw, self).__init__(name)
Austin Schuhc8ca2442013-02-23 12:29:33 -080023 # Stall Torque in N m
Austin Schuh1a387962015-01-31 16:36:20 -080024 self.stall_torque = 0.476
Austin Schuhc8ca2442013-02-23 12:29:33 -080025 # Stall Current in Amps
Austin Schuh1a387962015-01-31 16:36:20 -080026 self.stall_current = 80.730
James Kuszmaul92797402014-02-17 14:08:49 -080027 # Free Speed in RPM
Austin Schuh1a387962015-01-31 16:36:20 -080028 self.free_speed = 13906.0
Austin Schuh3c542312013-02-24 01:53:50 -080029 # Free Current in Amps
Austin Schuh1a387962015-01-31 16:36:20 -080030 self.free_current = 5.820
31 # Mass of the claw
32 if mass is None:
33 self.mass = 5.0
34 else:
35 self.mass = mass
James Kuszmaul3ac190a2014-03-26 20:11:04 -070036
Austin Schuhc8ca2442013-02-23 12:29:33 -080037 # Resistance of the motor
James Kuszmaul92797402014-02-17 14:08:49 -080038 self.R = 12.0 / self.stall_current
Austin Schuhc8ca2442013-02-23 12:29:33 -080039 # Motor velocity constant
Austin Schuh3c542312013-02-24 01:53:50 -080040 self.Kv = ((self.free_speed / 60.0 * 2.0 * numpy.pi) /
Austin Schuh1a387962015-01-31 16:36:20 -080041 (12.0 - self.R * self.free_current))
Austin Schuhc8ca2442013-02-23 12:29:33 -080042 # Torque constant
43 self.Kt = self.stall_torque / self.stall_current
44 # Gear ratio
Austin Schuh1a387962015-01-31 16:36:20 -080045 self.G = (56.0 / 12.0) * (54.0 / 14.0) * (64.0 / 14.0) * (72.0 / 18.0)
46 # Claw length
47 self.r = 18 * 0.0254
Austin Schuhc8ca2442013-02-23 12:29:33 -080048
Austin Schuh1a387962015-01-31 16:36:20 -080049 self.J = self.r * self.mass
50
51 # Control loop time step
52 self.dt = 0.005
53
54 # State is [position, velocity]
55 # Input is [Voltage]
56
57 C1 = self.G * self.G * self.Kt / (self.R * self.J * self.Kv)
58 C2 = self.Kt * self.G / (self.J * self.R)
James Kuszmaule2afbe42014-02-17 22:29:59 -080059
Austin Schuhc8ca2442013-02-23 12:29:33 -080060 self.A_continuous = numpy.matrix(
James Kuszmaule2afbe42014-02-17 22:29:59 -080061 [[0, 1],
Austin Schuh1a387962015-01-31 16:36:20 -080062 [0, -C1]])
James Kuszmaule2afbe42014-02-17 22:29:59 -080063
Austin Schuh1a387962015-01-31 16:36:20 -080064 # Start with the unmodified input
Austin Schuhc8ca2442013-02-23 12:29:33 -080065 self.B_continuous = numpy.matrix(
James Kuszmaule2afbe42014-02-17 22:29:59 -080066 [[0],
Austin Schuh1a387962015-01-31 16:36:20 -080067 [C2]])
James Kuszmaule2afbe42014-02-17 22:29:59 -080068
Austin Schuh1a387962015-01-31 16:36:20 -080069 self.C = numpy.matrix([[1, 0]])
70 self.D = numpy.matrix([[0]])
Austin Schuhc8ca2442013-02-23 12:29:33 -080071
Austin Schuhc1f68892013-03-16 17:06:27 -070072 self.A, self.B = self.ContinuousToDiscrete(
73 self.A_continuous, self.B_continuous, self.dt)
Austin Schuhc8ca2442013-02-23 12:29:33 -080074
Brian Silvermane18cf502015-11-28 23:04:43 +000075 controllability = controls.ctrb(self.A, self.B)
Austin Schuhc1f68892013-03-16 17:06:27 -070076
Austin Schuhbd947022015-03-01 00:10:01 -080077 print "Free speed is", self.free_speed * numpy.pi * 2.0 / 60.0 / self.G
78
79 q_pos = 0.15
80 q_vel = 2.5
Austin Schuh1a387962015-01-31 16:36:20 -080081 self.Q = numpy.matrix([[(1.0 / (q_pos ** 2.0)), 0.0],
82 [0.0, (1.0 / (q_vel ** 2.0))]])
83
84 self.R = numpy.matrix([[(1.0 / (12.0 ** 2.0))]])
85 self.K = controls.dlqr(self.A, self.B, self.Q, self.R)
Austin Schuh1a387962015-01-31 16:36:20 -080086
Austin Schuh8a436e82015-02-16 23:31:28 -080087 print 'K', self.K
88 print 'Poles are', numpy.linalg.eig(self.A - self.B * self.K)[0]
Austin Schuhc1f68892013-03-16 17:06:27 -070089
Austin Schuh8a436e82015-02-16 23:31:28 -080090 self.rpl = 0.30
91 self.ipl = 0.10
Austin Schuh1a387962015-01-31 16:36:20 -080092 self.PlaceObserverPoles([self.rpl + 1j * self.ipl,
93 self.rpl - 1j * self.ipl])
Austin Schuhc1f68892013-03-16 17:06:27 -070094
Austin Schuhbd947022015-03-01 00:10:01 -080095 print 'L is', self.L
96
Brian Silverman73c298d2015-03-30 10:30:12 -040097 q_pos = 0.05
98 q_vel = 2.65
Austin Schuhbd947022015-03-01 00:10:01 -080099 self.Q = numpy.matrix([[(q_pos ** 2.0), 0.0],
100 [0.0, (q_vel ** 2.0)]])
101
Brian Silverman73c298d2015-03-30 10:30:12 -0400102 r_volts = 0.025
Austin Schuhbd947022015-03-01 00:10:01 -0800103 self.R = numpy.matrix([[(r_volts ** 2.0)]])
104
105 self.KalmanGain, self.Q_steady = controls.kalman(
106 A=self.A, B=self.B, C=self.C, Q=self.Q, R=self.R)
107
108 print 'Kal', self.KalmanGain
109 self.L = self.A * self.KalmanGain
110 print 'KalL is', self.L
111
Austin Schuh1a387962015-01-31 16:36:20 -0800112 # The box formed by U_min and U_max must encompass all possible values,
113 # or else Austin's code gets angry.
114 self.U_max = numpy.matrix([[12.0]])
115 self.U_min = numpy.matrix([[-12.0]])
Austin Schuhc1f68892013-03-16 17:06:27 -0700116
117 self.InitializeState()
118
Austin Schuhfe6d37e2014-03-24 09:45:01 -0700119
Austin Schuh1a387962015-01-31 16:36:20 -0800120def run_test(claw, initial_X, goal, max_separation_error=0.01,
Adam Snaidere8e09ba2016-03-05 14:40:52 -0800121 show_graph=False, iterations=200, controller_claw=None,
Austin Schuh1a387962015-01-31 16:36:20 -0800122 observer_claw=None):
123 """Runs the claw plant with an initial condition and goal.
Brian Silverman6dd2c532014-03-29 23:34:39 -0700124
Austin Schuh1a387962015-01-31 16:36:20 -0800125 The tests themselves are not terribly sophisticated; I just test for
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700126 whether the goal has been reached and whether the separation goes
Brian Silverman6dd2c532014-03-29 23:34:39 -0700127 outside of the initial and goal values by more than max_separation_error.
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700128 Prints out something for a failure of either condition and returns
129 False if tests fail.
130 Args:
131 claw: claw object to use.
132 initial_X: starting state.
133 goal: goal state.
134 show_graph: Whether or not to display a graph showing the changing
135 states and voltages.
Austin Schuh1a387962015-01-31 16:36:20 -0800136 iterations: Number of timesteps to run the model for.
137 controller_claw: claw object to get K from, or None if we should
138 use claw.
139 observer_claw: claw object to use for the observer, or None if we should
140 use the actual state.
141 """
Austin Schuhc8ca2442013-02-23 12:29:33 -0800142
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700143 claw.X = initial_X
144
Austin Schuh1a387962015-01-31 16:36:20 -0800145 if controller_claw is None:
146 controller_claw = claw
147
148 if observer_claw is not None:
149 observer_claw.X_hat = initial_X + 0.01
150 observer_claw.X_hat = initial_X
151
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700152 # Various lists for graphing things.
Austin Schuhcda86af2014-02-16 16:16:39 -0800153 t = []
Austin Schuh1a387962015-01-31 16:36:20 -0800154 x = []
155 v = []
156 x_hat = []
157 u = []
Austin Schuhc8ca2442013-02-23 12:29:33 -0800158
Austin Schuh1a387962015-01-31 16:36:20 -0800159 sep_plot_gain = 100.0
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700160
161 for i in xrange(iterations):
Austin Schuh1a387962015-01-31 16:36:20 -0800162 X_hat = claw.X
163 if observer_claw is not None:
164 X_hat = observer_claw.X_hat
165 x_hat.append(observer_claw.X_hat[0, 0])
166 U = controller_claw.K * (goal - X_hat)
167 U[0, 0] = numpy.clip(U[0, 0], -12, 12)
168 x.append(claw.X[0, 0])
169 v.append(claw.X[1, 0])
170 if observer_claw is not None:
171 observer_claw.PredictObserver(U)
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700172 claw.Update(U)
Austin Schuh1a387962015-01-31 16:36:20 -0800173 if observer_claw is not None:
174 observer_claw.Y = claw.Y
175 observer_claw.CorrectObserver(U)
Brian Silvermanb087c0a2014-03-30 12:59:52 -0700176
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700177 t.append(i * claw.dt)
Austin Schuh1a387962015-01-31 16:36:20 -0800178 u.append(U[0, 0])
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700179
180 if show_graph:
Austin Schuh1a387962015-01-31 16:36:20 -0800181 pylab.subplot(2, 1, 1)
182 pylab.plot(t, x, label='x')
183 if observer_claw is not None:
184 pylab.plot(t, x_hat, label='x_hat')
185 pylab.legend()
186
187 pylab.subplot(2, 1, 2)
188 pylab.plot(t, u, label='u')
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700189 pylab.legend()
190 pylab.show()
191
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700192
193def main(argv):
Austin Schuhbd947022015-03-01 00:10:01 -0800194 loaded_mass = 0
Austin Schuh1a387962015-01-31 16:36:20 -0800195 #loaded_mass = 0
Austin Schuhbd947022015-03-01 00:10:01 -0800196 claw = Claw(mass=4 + loaded_mass)
197 claw_controller = Claw(mass=5 + 0)
198 observer_claw = Claw(mass=5 + 0)
Austin Schuh1a387962015-01-31 16:36:20 -0800199 #observer_claw = None
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700200
201 # Test moving the claw with constant separation.
Austin Schuh1a387962015-01-31 16:36:20 -0800202 initial_X = numpy.matrix([[0.0], [0.0]])
203 R = numpy.matrix([[1.0], [0.0]])
204 run_test(claw, initial_X, R, controller_claw=claw_controller,
205 observer_claw=observer_claw)
Brian Silvermanf05948b2014-03-30 00:24:36 -0700206
Austin Schuh3c542312013-02-24 01:53:50 -0800207 # Write the generated constants out to a file.
Austin Schuhcda86af2014-02-16 16:16:39 -0800208 if len(argv) != 3:
Adam Snaidere8e09ba2016-03-05 14:40:52 -0800209 glog.fatal('Expected .h and .cc filename for claw.')
Austin Schuhc8ca2442013-02-23 12:29:33 -0800210 else:
Adam Snaidere8e09ba2016-03-05 14:40:52 -0800211 namespaces = ['y2015', 'control_loops', 'claw']
212 claw = Claw('Claw')
213 loop_writer = control_loop.ControlLoopWriter('Claw', [claw],
214 namespaces=namespaces)
215 loop_writer.Write(argv[1], argv[2])
Austin Schuhc8ca2442013-02-23 12:29:33 -0800216
217if __name__ == '__main__':
218 sys.exit(main(sys.argv))