blob: 99870895d5f95bf8c4de45d55ca059a0bb387127 [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 Schuh6c20f202017-02-18 22:31:44 -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 Schuh6c20f202017-02-18 22:31:44 -080077 glog.debug('Free speed is %f', self.free_speed * numpy.pi * 2.0 / 60.0 / self.G)
Austin Schuhbd947022015-03-01 00:10:01 -080078
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 Schuh6c20f202017-02-18 22:31:44 -080087 glog.debug('K: %s', repr(self.K))
88 glog.debug('Poles are: %s', repr(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 Schuh6c20f202017-02-18 22:31:44 -080095 glog.debug('L is: %s', repr(self.L))
Austin Schuhbd947022015-03-01 00:10:01 -080096
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
Austin Schuh6c20f202017-02-18 22:31:44 -0800108 glog.debug('Kal: %s', repr(self.KalmanGain))
Austin Schuhbd947022015-03-01 00:10:01 -0800109 self.L = self.A * self.KalmanGain
Austin Schuh6c20f202017-02-18 22:31:44 -0800110 glog.debug('KalL is: %s', repr(self.L))
Austin Schuhbd947022015-03-01 00:10:01 -0800111
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,
Austin Schuh6c20f202017-02-18 22:31:44 -0800121 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.
Austin Schuh1a387962015-01-31 16:36:20 -0800134 iterations: Number of timesteps to run the model for.
135 controller_claw: claw object to get K from, or None if we should
136 use claw.
137 observer_claw: claw object to use for the observer, or None if we should
138 use the actual state.
139 """
Austin Schuhc8ca2442013-02-23 12:29:33 -0800140
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700141 claw.X = initial_X
142
Austin Schuh1a387962015-01-31 16:36:20 -0800143 if controller_claw is None:
144 controller_claw = claw
145
146 if observer_claw is not None:
147 observer_claw.X_hat = initial_X + 0.01
148 observer_claw.X_hat = initial_X
149
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700150 # Various lists for graphing things.
Austin Schuhcda86af2014-02-16 16:16:39 -0800151 t = []
Austin Schuh1a387962015-01-31 16:36:20 -0800152 x = []
153 v = []
154 x_hat = []
155 u = []
Austin Schuhc8ca2442013-02-23 12:29:33 -0800156
Austin Schuh1a387962015-01-31 16:36:20 -0800157 sep_plot_gain = 100.0
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700158
159 for i in xrange(iterations):
Austin Schuh1a387962015-01-31 16:36:20 -0800160 X_hat = claw.X
161 if observer_claw is not None:
162 X_hat = observer_claw.X_hat
163 x_hat.append(observer_claw.X_hat[0, 0])
164 U = controller_claw.K * (goal - X_hat)
165 U[0, 0] = numpy.clip(U[0, 0], -12, 12)
166 x.append(claw.X[0, 0])
167 v.append(claw.X[1, 0])
168 if observer_claw is not None:
169 observer_claw.PredictObserver(U)
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700170 claw.Update(U)
Austin Schuh1a387962015-01-31 16:36:20 -0800171 if observer_claw is not None:
172 observer_claw.Y = claw.Y
173 observer_claw.CorrectObserver(U)
Brian Silvermanb087c0a2014-03-30 12:59:52 -0700174
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700175 t.append(i * claw.dt)
Austin Schuh1a387962015-01-31 16:36:20 -0800176 u.append(U[0, 0])
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700177
Austin Schuh6c20f202017-02-18 22:31:44 -0800178 pylab.subplot(2, 1, 1)
179 pylab.plot(t, x, label='x')
180 if observer_claw is not None:
181 pylab.plot(t, x_hat, label='x_hat')
182 pylab.legend()
Austin Schuh1a387962015-01-31 16:36:20 -0800183
Austin Schuh6c20f202017-02-18 22:31:44 -0800184 pylab.subplot(2, 1, 2)
185 pylab.plot(t, u, label='u')
186 pylab.legend()
187 pylab.show()
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700188
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700189
190def main(argv):
Austin Schuh6c20f202017-02-18 22:31:44 -0800191 if FLAGS.plot:
192 loaded_mass = 0
193 #loaded_mass = 0
194 claw = Claw(mass=4 + loaded_mass)
195 claw_controller = Claw(mass=5 + 0)
196 observer_claw = Claw(mass=5 + 0)
197 #observer_claw = None
James Kuszmaulf63b0ae2014-03-25 16:52:11 -0700198
Austin Schuh6c20f202017-02-18 22:31:44 -0800199 # Test moving the claw with constant separation.
200 initial_X = numpy.matrix([[0.0], [0.0]])
201 R = numpy.matrix([[1.0], [0.0]])
202 run_test(claw, initial_X, R, controller_claw=claw_controller,
203 observer_claw=observer_claw)
Brian Silvermanf05948b2014-03-30 00:24:36 -0700204
Austin Schuh3c542312013-02-24 01:53:50 -0800205 # Write the generated constants out to a file.
Austin Schuhcda86af2014-02-16 16:16:39 -0800206 if len(argv) != 3:
Adam Snaidere8e09ba2016-03-05 14:40:52 -0800207 glog.fatal('Expected .h and .cc filename for claw.')
Austin Schuhc8ca2442013-02-23 12:29:33 -0800208 else:
Adam Snaidere8e09ba2016-03-05 14:40:52 -0800209 namespaces = ['y2015', 'control_loops', 'claw']
210 claw = Claw('Claw')
211 loop_writer = control_loop.ControlLoopWriter('Claw', [claw],
212 namespaces=namespaces)
213 loop_writer.Write(argv[1], argv[2])
Austin Schuhc8ca2442013-02-23 12:29:33 -0800214
215if __name__ == '__main__':
Austin Schuh6c20f202017-02-18 22:31:44 -0800216 argv = FLAGS(sys.argv)
217 glog.init()
218 sys.exit(main(argv))