blob: d4cc8380924120dc86464b0f230f7206e061db3f [file] [log] [blame]
joe2d92e852014-01-25 14:31:24 -08001#!/usr/bin/python
2
3import control_loop
4import numpy
5import sys
6from matplotlib import pylab
7
8class Shooter(control_loop.ControlLoop):
9 def __init__(self, name="RawShooter"):
10 super(Shooter, self).__init__(name)
11 # Stall Torque in N m
James Kuszmaul49d0e6c2014-02-03 19:46:17 -080012 self.stall_torque = .4982
joe2d92e852014-01-25 14:31:24 -080013 # Stall Current in Amps
14 self.stall_current = 85
15 # Free Speed in RPM
16 self.free_speed = 19300.0
17 # Free Current in Amps
James Kuszmaul49d0e6c2014-02-03 19:46:17 -080018 self.free_current = 1.2
James Kuszmaul6682f342014-02-14 10:21:04 -050019 # Effective mass of the shooter in kg.
20 # This rough estimate should about include the effect of the masses
21 # of the gears. If this number is too low, the eigen values of self.A
22 # will start to become extremely small.
23 self.J = 12
James Kuszmaul49d0e6c2014-02-03 19:46:17 -080024 # Resistance of the motor, divided by the number of motors.
25 self.R = 12.0 / self.stall_current / 2.0
joe2d92e852014-01-25 14:31:24 -080026 # Motor velocity constant
27 self.Kv = ((self.free_speed / 60.0 * 2.0 * numpy.pi) /
James Kuszmaule1755b32014-02-13 06:27:48 -080028 (12.0 - self.R * self.free_current))
joe2d92e852014-01-25 14:31:24 -080029 # Torque constant
30 self.Kt = self.stall_torque / self.stall_current
James Kuszmaul49d0e6c2014-02-03 19:46:17 -080031 # Spring constant for the springs, N/m
James Kuszmaule1755b32014-02-13 06:27:48 -080032 self.Ks = 2800.0
33 # Gear ratio multiplied by radius of final sprocket.
34 self.G = 10.0 / 40.0 * 20.0 / 54.0 * 24.0 / 54.0 * 20.0 / 84.0 * 0.0182
joe2d92e852014-01-25 14:31:24 -080035 # Control loop time step
36 self.dt = 0.01
37
James Kuszmaul49d0e6c2014-02-03 19:46:17 -080038
joe2d92e852014-01-25 14:31:24 -080039 # State feedback matrices
James Kuszmaul49d0e6c2014-02-03 19:46:17 -080040 # TODO(james): Make this work with origins other than at kx = 0.
joe2d92e852014-01-25 14:31:24 -080041 self.A_continuous = numpy.matrix(
42 [[0, 1],
James Kuszmaule1755b32014-02-13 06:27:48 -080043 [-self.Ks / self.J,
James Kuszmaul49d0e6c2014-02-03 19:46:17 -080044 -self.Kt / self.Kv / (self.J * self.G * self.G * self.R)]])
joe2d92e852014-01-25 14:31:24 -080045 self.B_continuous = numpy.matrix(
46 [[0],
47 [self.Kt / (self.J * self.G * self.R)]])
48 self.C = numpy.matrix([[1, 0]])
49 self.D = numpy.matrix([[0]])
50
51 self.A, self.B = self.ContinuousToDiscrete(
52 self.A_continuous, self.B_continuous, self.dt)
53
James Kuszmaul6682f342014-02-14 10:21:04 -050054 self.PlaceControllerPoles([0.45, 0.45])
joe2d92e852014-01-25 14:31:24 -080055
56 self.rpl = .05
57 self.ipl = 0.008
James Kuszmaule1755b32014-02-13 06:27:48 -080058 self.PlaceObserverPoles([self.rpl,
59 self.rpl])
joe2d92e852014-01-25 14:31:24 -080060
61 self.U_max = numpy.matrix([[12.0]])
62 self.U_min = numpy.matrix([[-12.0]])
63
64 self.InitializeState()
65
66
67class ShooterDeltaU(Shooter):
68 def __init__(self, name="Shooter"):
69 super(ShooterDeltaU, self).__init__(name)
70 A_unaugmented = self.A
71 B_unaugmented = self.B
72
73 self.A = numpy.matrix([[0.0, 0.0, 0.0],
74 [0.0, 0.0, 0.0],
75 [0.0, 0.0, 1.0]])
76 self.A[0:2, 0:2] = A_unaugmented
77 self.A[0:2, 2] = B_unaugmented
78
79 self.B = numpy.matrix([[0.0],
80 [0.0],
81 [1.0]])
82
83 self.C = numpy.matrix([[1.0, 0.0, 0.0]])
84 self.D = numpy.matrix([[0.0]])
85
James Kuszmaul49d0e6c2014-02-03 19:46:17 -080086 self.PlaceControllerPoles([0.55, 0.45, 0.80])
joe2d92e852014-01-25 14:31:24 -080087
88 print "K"
89 print self.K
90 print "Placed controller poles are"
91 print numpy.linalg.eig(self.A - self.B * self.K)[0]
92
93 self.rpl = .05
94 self.ipl = 0.008
95 self.PlaceObserverPoles([self.rpl + 1j * self.ipl,
96 self.rpl - 1j * self.ipl, 0.90])
97 print "Placed observer poles are"
98 print numpy.linalg.eig(self.A - self.L * self.C)[0]
99
100 self.U_max = numpy.matrix([[12.0]])
101 self.U_min = numpy.matrix([[-12.0]])
102
103 self.InitializeState()
104
105
106def ClipDeltaU(shooter, delta_u):
107 old_u = numpy.matrix([[shooter.X[2, 0]]])
108 new_u = numpy.clip(old_u + delta_u, shooter.U_min, shooter.U_max)
109 return new_u - old_u
110
111def main(argv):
112 # Simulate the response of the system to a step input.
James Kuszmaul49d0e6c2014-02-03 19:46:17 -0800113 shooter = Shooter()
joe2d92e852014-01-25 14:31:24 -0800114 simulated_x = []
James Kuszmaule1755b32014-02-13 06:27:48 -0800115 for _ in xrange(2000):
James Kuszmaul6682f342014-02-14 10:21:04 -0500116 U = 2.0
James Kuszmaule1755b32014-02-13 06:27:48 -0800117 shooter.Update(numpy.matrix([[U]]))
joe2d92e852014-01-25 14:31:24 -0800118 simulated_x.append(shooter.X[0, 0])
119
James Kuszmaule1755b32014-02-13 06:27:48 -0800120 pylab.plot(range(2000), simulated_x)
joe2d92e852014-01-25 14:31:24 -0800121 pylab.show()
122
James Kuszmaul49d0e6c2014-02-03 19:46:17 -0800123 # Simulate the response of the system to a goal.
124 shooter = Shooter()
joe2d92e852014-01-25 14:31:24 -0800125 close_loop_x = []
126 close_loop_u = []
James Kuszmaul6682f342014-02-14 10:21:04 -0500127 R = numpy.matrix([[0.3], [0.0]])
128 for _ in xrange(500):
129 augment = (-numpy.linalg.lstsq(shooter.B_continuous, numpy.identity(
James Kuszmaul49d0e6c2014-02-03 19:46:17 -0800130 shooter.B_continuous.shape[0]))[0] *
131 shooter.A_continuous * R)
James Kuszmaul6682f342014-02-14 10:21:04 -0500132 U = numpy.clip(shooter.K * (R - shooter.X_hat) + augment,
James Kuszmaul49d0e6c2014-02-03 19:46:17 -0800133 shooter.U_min, shooter.U_max)
134#U = ClipDeltaU(shooter, U)
joe2d92e852014-01-25 14:31:24 -0800135 shooter.UpdateObserver(U)
136 shooter.Update(U)
137 close_loop_x.append(shooter.X[0, 0] * 10)
James Kuszmaul49d0e6c2014-02-03 19:46:17 -0800138 close_loop_u.append(U[0, 0])
joe2d92e852014-01-25 14:31:24 -0800139
James Kuszmaul6682f342014-02-14 10:21:04 -0500140 pylab.plot(range(500), close_loop_x)
141 pylab.plot(range(500), close_loop_u)
joe2d92e852014-01-25 14:31:24 -0800142 pylab.show()
143
144 # Write the generated constants out to a file.
145 if len(argv) != 3:
146 print "Expected .h file name and .cc file name for"
147 print "both the plant and unaugmented plant"
148 else:
149 unaug_shooter = Shooter("RawShooter")
150 unaug_loop_writer = control_loop.ControlLoopWriter("RawShooter",
151 [unaug_shooter])
152 #if argv[3][-3:] == '.cc':
153 # unaug_loop_writer.Write(argv[4], argv[3])
154 #else:
155 # unaug_loop_writer.Write(argv[3], argv[4])
156
157 loop_writer = control_loop.ControlLoopWriter("Shooter", [shooter])
158 if argv[1][-3:] == '.cc':
159 loop_writer.Write(argv[2], argv[1])
160 else:
161 loop_writer.Write(argv[1], argv[2])
162
163if __name__ == '__main__':
164 sys.exit(main(sys.argv))