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