blob: d30e8f5758405cd8f00e1820eed009523f695136 [file] [log] [blame]
Austin Schuh1d731362022-02-22 13:57:55 -08001#ifndef Y2022_CONTROL_LOOPS_SUPERSTRUCTURE_CATAPULT_CATAPULT_H_
2#define Y2022_CONTROL_LOOPS_SUPERSTRUCTURE_CATAPULT_CATAPULT_H_
3
4#include "Eigen/Dense"
Austin Schuh1d731362022-02-22 13:57:55 -08005#include "glog/logging.h"
Philipp Schrader790cb542023-07-05 21:06:52 -07006
7#include "frc971/control_loops/state_feedback_loop.h"
James Kuszmaulec635d22023-08-12 18:39:24 -07008#include "frc971/zeroing/pot_and_absolute_encoder.h"
Austin Schuh1d731362022-02-22 13:57:55 -08009#include "osqp++.h"
Austin Schuh39f26f62022-02-24 21:34:46 -080010#include "y2022/constants.h"
11#include "y2022/control_loops/superstructure/superstructure_goal_generated.h"
12#include "y2022/control_loops/superstructure/superstructure_position_generated.h"
13#include "y2022/control_loops/superstructure/superstructure_status_generated.h"
Austin Schuh1d731362022-02-22 13:57:55 -080014
15namespace y2022 {
16namespace control_loops {
17namespace superstructure {
18namespace catapult {
19
20// MPC problem for a specified horizon. This contains all the state for the
21// solver, setters to modify the current and target state, and a way to fetch
22// the solution.
23class MPCProblem {
24 public:
25 MPCProblem(size_t horizon,
26 Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> P,
27 Eigen::Matrix<double, Eigen::Dynamic, 1> accel_q,
28 Eigen::Matrix<double, 2, 2> Af,
29 Eigen::Matrix<double, Eigen::Dynamic, 2> final_q);
30
31 MPCProblem(MPCProblem const &) = delete;
32 void operator=(MPCProblem const &x) = delete;
33
34 // Sets the current and final state. This keeps the problem in tact and
35 // doesn't recreate it, so it will be fast.
36 void SetState(Eigen::Matrix<double, 2, 1> X_initial,
37 Eigen::Matrix<double, 2, 1> X_final);
38
39 // Solves our problem.
40 bool Solve();
41
42 double solve_time() const { return solve_time_; }
43
44 // Returns the solution that the solver found when Solve was last called.
45 double U(size_t i) const { return solver_.primal_solution()(i); }
46
47 // Returns the number of U's to be solved.
48 size_t horizon() const { return horizon_; }
49
50 // Warm starts the optimizer with the provided solution to make it solve
51 // faster.
52 void WarmStart(const MPCProblem &p);
53
54 private:
55 // The number of u's to solve for.
56 const size_t horizon_;
57
58 // The problem statement variables needed by SetState to update q.
59 const Eigen::Matrix<double, Eigen::Dynamic, 1> accel_q_;
60 const Eigen::Matrix<double, 2, 2> Af_;
61 const Eigen::Matrix<double, Eigen::Dynamic, 2> final_q_;
62
63 Eigen::Matrix<double, 2, 1> X_initial_;
64 Eigen::Matrix<double, 2, 1> X_final_;
65
Austin Schuh39f26f62022-02-24 21:34:46 -080066 Eigen::Matrix<double, Eigen::Dynamic, 1> objective_vector_;
67
Austin Schuh1d731362022-02-22 13:57:55 -080068 // Solver state.
69 osqp::OsqpInstance instance_;
70 osqp::OsqpSolver solver_;
71 osqp::OsqpSettings settings_;
72
73 double solve_time_ = 0;
74};
75
76// Decently efficient problem generator for multiple horizons given a max
77// horizon to solve for.
78//
79// The math is documented in mpc.tex
80class CatapultProblemGenerator {
81 public:
82 // Builds a problem generator for the specified max horizon and caches a lot
83 // of the state.
84 CatapultProblemGenerator(size_t horizon);
85
86 // Returns the maximum horizon.
87 size_t horizon() const { return horizon_; }
88
89 // Makes a problem for the specificed horizon.
90 std::unique_ptr<MPCProblem> MakeProblem(size_t horizon);
91
92 // Returns the P and Q matrices for the problem statement.
93 // cost = 0.5 X.T P X + q.T X
94 const Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> P(size_t horizon);
95 const Eigen::Matrix<double, Eigen::Dynamic, 1> q(
96 size_t horizon, Eigen::Matrix<double, 2, 1> X_initial,
97 Eigen::Matrix<double, 2, 1> X_final);
98
99 private:
100 const Eigen::Matrix<double, Eigen::Dynamic, 1> accel_q(size_t horizon);
101
102 const Eigen::Matrix<double, 2, 2> Af(size_t horizon);
103 const Eigen::Matrix<double, 2, Eigen::Dynamic> Bf(size_t horizon);
104 const Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> Pi(
105 size_t horizon);
106
107 // These functions are used in the constructor to build up the matrices below.
108 Eigen::Matrix<double, Eigen::Dynamic, 2> MakeAs();
109 Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> MakeBs();
110 Eigen::Matrix<double, Eigen::Dynamic, 1> Makem();
111 Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> MakeM();
112 Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> MakeW();
113 Eigen::Matrix<double, Eigen::Dynamic, 1> Makew();
114 Eigen::DiagonalMatrix<double, Eigen::Dynamic> MakePi();
115 Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> MakeP();
116
117 const StateFeedbackPlant<2, 1, 1> plant_;
118 const size_t horizon_;
119
120 const Eigen::DiagonalMatrix<double, 2> Q_final_;
121
122 const Eigen::Matrix<double, Eigen::Dynamic, 2> As_;
123 const Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> Bs_;
124 const Eigen::Matrix<double, Eigen::Dynamic, 1> m_;
125 const Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> M_;
126
127 const Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> W_;
128 const Eigen::Matrix<double, Eigen::Dynamic, 1> w_;
129 const Eigen::DiagonalMatrix<double, Eigen::Dynamic> Pi_;
130
131 const Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> WM_;
132 const Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> Wmpw_;
133};
134
Austin Schuh39f26f62022-02-24 21:34:46 -0800135// A class to hold all the state needed to manage the catapult MPC solvers for
136// repeated shots.
137//
138// The solver may take a couple of cycles to get everything converged and ready.
139// The flow is as follows:
140// 1) Reset() the state for the new problem.
141// 2) Update to the current state with SetState()
142// 3) Call Solve(). This will return true if it is ready to be executed, false
143// if it needs more iterations to fully converge.
144// 4) Next() returns the current optimal control output and advances the
145// pointers to the next problem.
146// 5) Go back to 2 for the next cycle.
147class CatapultController {
148 public:
149 CatapultController(size_t horizon);
150
151 // Starts back over at the first controller.
152 void Reset();
153
154 // Updates our current and final states for the current controller.
155 void SetState(Eigen::Matrix<double, 2, 1> X_initial,
156 Eigen::Matrix<double, 2, 1> X_final);
157
158 // Solves! Returns true if the solution converged and osqp was happy.
159 bool Solve();
160
161 // Returns the time in seconds it last took Solve to run.
162 double solve_time() const { return solve_time_; }
163
Austin Schuh41472552022-03-13 18:09:41 -0700164 // Returns the horizon of the current controller.
165 size_t current_horizon() const {
166 if (current_controller_ >= problems_.size()) {
167 return 0u;
168 } else {
169 return problems_[current_controller_]->horizon();
170 }
171 }
172
Austin Schuh39f26f62022-02-24 21:34:46 -0800173 // Returns the controller value if there is a controller to run, or nullopt if
174 // we finished the last controller. Advances the controller pointer to the
175 // next controller and warms up the next controller.
176 std::optional<double> Next();
177
178 // Returns true if Next has been called and a controller has been used. Reset
179 // starts over.
180 bool started() const { return current_controller_ != 0u; }
181
182 private:
183 CatapultProblemGenerator generator_;
184
185 std::vector<std::unique_ptr<MPCProblem>> problems_;
186
187 size_t current_controller_ = 0;
188 double solve_time_ = 0.0;
189};
190
191// Class to handle transitioning between both the profiled subsystem and the MPC
192// for shooting.
193class Catapult {
194 public:
195 Catapult(const constants::Values &values)
Austin Schuh41472552022-03-13 18:09:41 -0700196 : catapult_(values.catapult.subsystem_params), catapult_mpc_(30) {}
Austin Schuh39f26f62022-02-24 21:34:46 -0800197
198 using PotAndAbsoluteEncoderSubsystem =
199 ::frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystem<
200 ::frc971::zeroing::PotAndAbsoluteEncoderZeroingEstimator,
201 ::frc971::control_loops::PotAndAbsoluteEncoderProfiledJointStatus>;
202
203 // Resets all state for when WPILib restarts.
204 void Reset() { catapult_.Reset(); }
205
Ravago Jones5da06352022-03-04 20:26:24 -0800206 void Estop() { catapult_.Estop(); }
207
Austin Schuh39f26f62022-02-24 21:34:46 -0800208 bool zeroed() const { return catapult_.zeroed(); }
209 bool estopped() const { return catapult_.estopped(); }
210 double solve_time() const { return catapult_mpc_.solve_time(); }
211
Austin Schuh41472552022-03-13 18:09:41 -0700212 uint8_t mpc_horizon() const { return current_horizon_; }
213
Austin Schuh39f26f62022-02-24 21:34:46 -0800214 bool mpc_active() const { return !use_profile_; }
215
Austin Schuh80fc2752022-02-25 13:33:56 -0800216 // Returns the number of shots taken.
217 int shot_count() const { return shot_count_; }
218
Ravago Jones5da06352022-03-04 20:26:24 -0800219 // Returns the estimated position
220 double estimated_position() const { return catapult_.estimated_position(); }
221
Austin Schuh39f26f62022-02-24 21:34:46 -0800222 // Runs either the MPC or the profiled subsystem depending on if we are
223 // shooting or not. Returns the status.
224 const flatbuffers::Offset<
225 frc971::control_loops::PotAndAbsoluteEncoderProfiledJointStatus>
Ravago Jones3283ce02022-03-09 19:31:29 -0800226 Iterate(const CatapultGoal *unsafe_goal, const Position *position,
Austin Schuh97410d72022-03-12 15:37:23 -0800227 double battery_voltage, double *catapult_voltage, bool fire,
Ravago Jones5da06352022-03-04 20:26:24 -0800228 flatbuffers::FlatBufferBuilder *fbb);
Austin Schuh39f26f62022-02-24 21:34:46 -0800229
230 private:
Austin Schuh39f26f62022-02-24 21:34:46 -0800231 PotAndAbsoluteEncoderSubsystem catapult_;
232
233 catapult::CatapultController catapult_mpc_;
234
235 enum CatapultState { PROFILE, FIRING, RESETTING };
236
237 CatapultState catapult_state_ = CatapultState::PROFILE;
238
Ravago Jones3283ce02022-03-09 19:31:29 -0800239 double latched_shot_position = 0.0;
240 double latched_shot_velocity = 0.0;
241
Austin Schuh39f26f62022-02-24 21:34:46 -0800242 bool last_firing_ = false;
243 bool use_profile_ = true;
Austin Schuh80fc2752022-02-25 13:33:56 -0800244
245 int shot_count_ = 0;
Austin Schuh41472552022-03-13 18:09:41 -0700246 uint8_t current_horizon_ = 0u;
Austin Schuh39f26f62022-02-24 21:34:46 -0800247};
248
Austin Schuh1d731362022-02-22 13:57:55 -0800249} // namespace catapult
250} // namespace superstructure
251} // namespace control_loops
252} // namespace y2022
253
254#endif // Y2022_CONTROL_LOOPS_SUPERSTRUCTURE_CATAPULT_CATAPULT_H_