blob: a827dc9df77165ae0a713c72ee0b7b8d695a49d1 [file] [log] [blame]
Nathan Leongdd728002024-02-03 15:26:53 -08001#include "frc971/control_loops/catapult/catapult.h"
2
3namespace frc971::control_loops::catapult {
4
5const flatbuffers::Offset<
6 frc971::control_loops::PotAndAbsoluteEncoderProfiledJointStatus>
7Catapult::Iterate(const CatapultGoal *catapult_goal,
8 const PotAndAbsolutePosition *position,
9 double battery_voltage, double *catapult_voltage, bool fire,
10 flatbuffers::FlatBufferBuilder *fbb) {
11 const frc971::control_loops::StaticZeroingSingleDOFProfiledSubsystemGoal
12 *return_goal =
13 catapult_goal != nullptr && catapult_goal->has_return_position()
14 ? catapult_goal->return_position()
15 : nullptr;
16
17 const bool catapult_disabled =
18 catapult_.Correct(return_goal, position, catapult_voltage == nullptr);
19
20 if (catapult_disabled) {
21 catapult_state_ = CatapultState::PROFILE;
22 } else if (catapult_.running() && catapult_goal != nullptr && fire &&
23 !last_firing_) {
24 catapult_state_ = CatapultState::FIRING;
25 latched_shot_position = catapult_goal->shot_position();
26 latched_shot_velocity = catapult_goal->shot_velocity();
27 }
28
29 // Don't update last_firing_ if the catapult is disabled, so that we actually
30 // end up firing once it's enabled
31 if (catapult_.running() && !catapult_disabled) {
32 last_firing_ = fire;
33 }
34
35 use_profile_ = true;
36
37 switch (catapult_state_) {
38 case CatapultState::FIRING: {
39 // Select the ball controller. We should only be firing if we have a
40 // ball, or at least should only care about the shot accuracy.
41 catapult_.set_controller_index(0);
42 // Ok, so we've now corrected. Next step is to run the MPC.
43 //
44 // Since there is a unit delay between when we ask for a U and the
45 // hardware applies it, we need to run the optimizer for the position at
46 // the *next* control loop cycle.
47
48 Eigen::Vector3d next_X = catapult_.estimated_state();
49 for (int i = catapult_.controller().plant().coefficients().delayed_u;
50 i > 1; --i) {
51 next_X = catapult_.controller().plant().A() * next_X +
52 catapult_.controller().plant().B() *
53 catapult_.controller().observer().last_U(i - 1);
54 }
55
56 catapult_mpc_.SetState(
57 next_X.block<2, 1>(0, 0),
58 Eigen::Vector2d(latched_shot_position, latched_shot_velocity));
59
60 const bool solved = catapult_mpc_.Solve();
61 current_horizon_ = catapult_mpc_.current_horizon();
62 const bool started = catapult_mpc_.started();
63 if (solved || started) {
64 std::optional<double> solution = catapult_mpc_.Next();
65
66 if (!solution.has_value()) {
67 CHECK_NOTNULL(catapult_voltage);
68 *catapult_voltage = 0.0;
69 if (catapult_mpc_.started()) {
70 ++shot_count_;
71 // Finished the catapult, time to fire.
72 catapult_state_ = CatapultState::RESETTING;
73 }
74 } else {
75 // TODO(austin): Voltage error?
76 CHECK_NOTNULL(catapult_voltage);
77 if (current_horizon_ == 1) {
78 battery_voltage = 12.0;
79 }
80 *catapult_voltage = std::max(
81 0.0, std::min(12.0, (*solution - 0.0 * next_X(2, 0)) * 12.0 /
82 std::max(battery_voltage, 8.0)));
83 use_profile_ = false;
84 }
85 } else {
86 if (!fire) {
87 // Eh, didn't manage to solve before it was time to fire. Give up.
88 catapult_state_ = CatapultState::PROFILE;
89 }
90 }
91
92 if (!use_profile_) {
93 catapult_.ForceGoal(catapult_.estimated_position(),
94 catapult_.estimated_velocity());
95 }
96 }
97 if (catapult_state_ != CatapultState::RESETTING) {
98 break;
99 } else {
100 [[fallthrough]];
101 }
102
103 case CatapultState::RESETTING:
104 if (catapult_.controller().R(1, 0) > 7.0) {
Austin Schuhea834432024-02-20 21:23:13 -0800105 catapult_.mutable_profile()->set_maximum_velocity(7.0);
106 catapult_.mutable_profile()->set_maximum_acceleration(2000.0);
Nathan Leongdd728002024-02-03 15:26:53 -0800107 } else if (catapult_.controller().R(1, 0) > 0.0) {
Austin Schuhea834432024-02-20 21:23:13 -0800108 catapult_.mutable_profile()->set_maximum_velocity(7.0);
109 catapult_.mutable_profile()->set_maximum_acceleration(1000.0);
Nathan Leongdd728002024-02-03 15:26:53 -0800110 } else {
111 catapult_state_ = CatapultState::PROFILE;
112 }
113 [[fallthrough]];
114
115 case CatapultState::PROFILE:
116 break;
117 }
118
119 if (use_profile_) {
120 if (catapult_state_ != CatapultState::FIRING) {
121 catapult_mpc_.Reset();
122 }
123 // Select the controller designed for when we have no ball.
124 catapult_.set_controller_index(1);
125
126 current_horizon_ = 0u;
127 const double output_voltage = catapult_.UpdateController(catapult_disabled);
128 if (catapult_voltage != nullptr) {
129 *catapult_voltage = output_voltage;
130 }
131 }
132
133 catapult_.UpdateObserver(catapult_voltage != nullptr
134 ? (*catapult_voltage * battery_voltage / 12.0)
135 : 0.0);
136
137 return catapult_.MakeStatus(fbb);
138}
139
Austin Schuhea834432024-02-20 21:23:13 -0800140} // namespace frc971::control_loops::catapult