blob: ae6079a6afdd3a3aa25ad9c8cce3caabe46b79f6 [file] [log] [blame]
Austin Schuh82162452022-02-07 22:01:45 -08001#!/usr/bin/python3
2
3from frc971.control_loops.python import control_loop
4from frc971.control_loops.python import controls
5import numpy
6import math
Austin Schuhc3e04282022-02-12 20:00:53 -08007import scipy.optimize
Austin Schuh82162452022-02-07 22:01:45 -08008import sys
9import math
10from y2022.control_loops.python import catapult_lib
11from matplotlib import pylab
12
13import gflags
14import glog
15
16FLAGS = gflags.FLAGS
17
Austin Schuhc3e04282022-02-12 20:00:53 -080018gflags.DEFINE_bool('plot', False, 'If true, plot the loop response.')
Austin Schuh82162452022-02-07 22:01:45 -080019
20ball_mass = 0.25
21ball_diameter = 9.5 * 0.0254
22lever = 17.5 * 0.0254
23
24G = (14.0 / 72.0) * (12.0 / 33.0)
25
26
27def AddResistance(motor, resistance):
28 motor.resistance += resistance
29 return motor
30
Austin Schuhc3e04282022-02-12 20:00:53 -080031
Austin Schuh82162452022-02-07 22:01:45 -080032J_ball = 1.5 * ball_mass * lever * lever
33# Assuming carbon fiber, calculate the mass of the bar.
34M_bar = (1750 * lever * 0.0254 * 0.0254 * (1.0 - (1 - 0.07)**2.0))
35# And the moment of inertia.
36J_bar = 1.0 / 3.0 * M_bar * lever**2.0
37
38# Do the same for a theoretical cup. Assume a 40 thou thick carbon cup.
39M_cup = (1750 * 0.0254 * 0.04 * 2 * math.pi * (ball_diameter / 2.)**2.0)
40J_cup = M_cup * lever**2.0 + M_cup * (ball_diameter / 2.)**2.0
41
42print("J ball", ball_mass * lever * lever)
43print("J bar", J_bar)
44print("bar mass", M_bar)
45print("J cup", J_cup)
46print("cup mass", M_cup)
47
48J = (J_ball + J_bar + J_cup * 1.5)
49print("J", J)
50
Austin Schuhc3e04282022-02-12 20:00:53 -080051kCatapult = catapult_lib.CatapultParams(
Austin Schuh82162452022-02-07 22:01:45 -080052 name='Finisher',
53 motor=AddResistance(control_loop.NMotor(control_loop.Falcon(), 2), 0.03),
54 G=G,
55 J=J,
56 lever=lever,
57 q_pos=0.01,
58 q_vel=10.0,
59 q_voltage=4.0,
60 r_pos=0.01,
61 controller_poles=[.93],
Austin Schuhc3e04282022-02-12 20:00:53 -080062 dt=0.00505)
63
64catapult = catapult_lib.Catapult(kCatapult)
65
66# Ideas for adjusting the cost function:
67#
68# Penalize battery current?
69# Penalize accel/rotor current?
70# Penalize velocity error off destination?
71# Penalize max u
72#
73# Ramp up U cost over time?
74# Once moving, open up saturation bounds
75#
76# We really want our cost function to be robust so that we can tolerate the
77# battery not delivering like we want at the end.
78
79
80def mpc_cost(X_initial, X_final, u_matrix):
81 X = X_initial.copy()
82 cost = 0.0
83 last_u = u_matrix[0]
84 max_u = 0.0
85 for count, u in enumerate(u_matrix):
86 v_prior = X[1, 0]
87 X = catapult.A * X + catapult.B * numpy.matrix([[u]])
88 v = X[1, 0]
89
90 # Smoothness cost on voltage change and voltage.
91 #cost += (u - last_u) ** 2.0
92 #cost += (u - 6.0) ** 2.0
93
94 measured_a = (v - v_prior) / catapult.dt
95 expected_a = 0.0
96
97 # Our good cost!
98 cost_scalar = 0.02 * max(0.0, (20 - (len(u_matrix) - count)) / 20.)
99 cost += ((measured_a - expected_a) * cost_scalar)**2.0
100 cost += (measured_a * 0.010)**2.0
101
102 # Quadratic cost. This delays as long as possible, but approximates a
103 # LQR until saturation.
104 #cost += (u - 0.0) ** 2.0
105 #cost += (0.1 * (X_final[0, 0] - X[0, 0])) ** 2.0
106 #cost += (0.5 * (X_final[1, 0] - X[1, 0])) ** 2.0
107
108 max_u = max(u, max_u)
109 last_u = u
110
111 # Penalize max power usage. This is hard to solve.
112 #cost += max_u * 10
113
114 terminal_cost = (X - X_final).transpose() * numpy.matrix(
115 [[10000.0, 0.0], [0.0, 10000.0]]) * (X - X_final)
116 cost += terminal_cost[0, 0]
117
118 return cost
119
120
121def SolveCatapult(X_initial, X_final, u):
122 """ Solves for the optimal action given a seed, state, and target """
123 def vbat_constraint(z, i):
124 return 12.0 - z[i]
125
126 def forward(z, i):
127 return z[i]
128
129 def mpc_cost2(u_matrix):
130 return mpc_cost(X_initial, X_final, u_matrix)
131
132 constraints = [{
133 'type': 'ineq',
134 'fun': vbat_constraint,
135 'args': (i, )
136 } for i in numpy.arange(len(u))]
137
138 constraints += [{
139 'type': 'ineq',
140 'fun': forward,
141 'args': (i, )
142 } for i in numpy.arange(len(u))]
143
144 result = scipy.optimize.minimize(mpc_cost2,
145 u,
146 method='SLSQP',
147 constraints=constraints)
148 print(result)
149
150 return result.x
151
152
153def CatapultProblem():
154 c = catapult_lib.Catapult(kCatapult)
155
156 kHorizon = 40
157
158 u = [0.0] * kHorizon
159 X_initial = numpy.matrix([[0.0], [0.0]])
160 X_final = numpy.matrix([[2.0], [25.0]])
161
162
163 X_initial = numpy.matrix([[0.0], [0.0]])
164 X = X_initial.copy()
165
166 t_samples = [0.0]
167 x_samples = [0.0]
168 v_samples = [0.0]
169 a_samples = [0.0]
170
171 # Iteratively solve our MPC and simulate it.
172 u_samples = []
173 for i in range(kHorizon):
174 u_horizon = SolveCatapult(X, X_final, u)
175 u_samples.append(u_horizon[0])
176 v_prior = X[1, 0]
177 X = c.A * X + c.B * numpy.matrix([[u_horizon[0]]])
178 v = X[1, 0]
179 t_samples.append(t_samples[-1] + c.dt)
180 x_samples.append(X[0, 0])
181 v_samples.append(X[1, 0])
182 a_samples.append((v - v_prior) / c.dt)
183
184 u = u_horizon[1:]
185
186 print('Final state', X.transpose())
187 print('Final velocity', X[1, 0] * lever)
188 pylab.subplot(2, 1, 1)
189 pylab.plot(t_samples, x_samples, label="x")
190 pylab.plot(t_samples, v_samples, label="v")
191 pylab.plot(t_samples[1:], u_samples, label="u")
192 pylab.legend()
193 pylab.subplot(2, 1, 2)
194 pylab.plot(t_samples, a_samples, label="a")
195 pylab.legend()
196
197 pylab.show()
Austin Schuh82162452022-02-07 22:01:45 -0800198
199
200def main(argv):
201 # Do all our math with a lower voltage so we have headroom.
202 U = numpy.matrix([[9.0]])
Austin Schuhc3e04282022-02-12 20:00:53 -0800203 print(
204 "For G:", G, " max speed ",
205 catapult_lib.MaxSpeed(params=kCatapult,
206 U=U,
207 final_position=math.pi / 2.0))
208
209 CatapultProblem()
Austin Schuh82162452022-02-07 22:01:45 -0800210
211 if FLAGS.plot:
Austin Schuhc3e04282022-02-12 20:00:53 -0800212 catapult_lib.PlotShot(kCatapult, U, final_position=math.pi / 4.0)
Austin Schuh82162452022-02-07 22:01:45 -0800213
214 gs = []
215 speed = []
216 for i in numpy.linspace(0.01, 0.15, 150):
Austin Schuhc3e04282022-02-12 20:00:53 -0800217 kCatapult.G = i
218 gs.append(kCatapult.G)
219 speed.append(
220 catapult_lib.MaxSpeed(params=kCatapult,
221 U=U,
222 final_position=math.pi / 2.0))
223 pylab.plot(gs, speed, label="max_speed")
Austin Schuh82162452022-02-07 22:01:45 -0800224 pylab.show()
225 return 0
226
227
228if __name__ == '__main__':
229 argv = FLAGS(sys.argv)
230 sys.exit(main(argv))