blob: aa8087faa36f079907e14babaf67955c65fe784a [file] [log] [blame]
Maxwell Henderson34242992024-01-07 12:39:11 -08001#include "frc971/control_loops/flywheel/flywheel_controller.h"
Sabina Davisedf89472020-02-17 15:27:37 -08002
3#include <chrono>
4
5#include "aos/logging/logging.h"
Stephan Pleinesf63bde82024-01-13 15:59:33 -08006
7namespace frc971::control_loops::flywheel {
Sabina Davisedf89472020-02-17 15:27:37 -08008
Austin Schuhe8ca06a2020-03-07 22:27:39 -08009// Class to current limit battery current for a flywheel controller.
10class CurrentLimitedStateFeedbackController
11 : public StateFeedbackLoop<3, 1, 1, double,
12 StateFeedbackHybridPlant<3, 1, 1>,
13 HybridKalman<3, 1, 1>> {
14 public:
15 // Builds a CurrentLimitedStateFeedbackController given the coefficients, bemf
16 // coefficient (units of radians/sec / volt), and motor resistance in ohms.
17 CurrentLimitedStateFeedbackController(
18 StateFeedbackLoop<3, 1, 1, double, StateFeedbackHybridPlant<3, 1, 1>,
19 HybridKalman<3, 1, 1>> &&other,
20 double bemf, double resistance)
21 : StateFeedbackLoop(std::move(other)),
22 bemf_(bemf),
23 resistance_(resistance) {}
24
Austin Schuh80476772021-03-06 20:17:36 -080025 double resistance() const { return resistance_; }
26 double bemf() const { return bemf_; }
27
Austin Schuhe8ca06a2020-03-07 22:27:39 -080028 void CapU() override {
29 const double bemf_voltage = X_hat(1) / bemf_;
30 // Solve the system of equations:
31 //
32 // motor_current = (u - bemf_voltage) / resistance
33 // battery_current = ((u - bemf_voltage) / resistance) * u / 12.0
34 // 0.0 = u * u - u * bemf_voltage - max_current * 12.0 * resistance
35 //
36 // And we have a quadratic!
37 const double a = 1;
38 const double b = -bemf_voltage;
Austin Schuh242d7132021-11-07 17:39:48 -080039 const double c_positive = -70.0 * 12.0 * resistance_;
Austin Schuhc044dda2021-11-14 21:02:49 -080040 const double c_negative = -25.0 * 12.0 * resistance_;
Austin Schuhe8ca06a2020-03-07 22:27:39 -080041
42 // Root is always positive.
Austin Schuh242d7132021-11-07 17:39:48 -080043 const double root_positive = std::sqrt(b * b - 4.0 * a * c_positive);
44 const double root_negative = std::sqrt(b * b - 4.0 * a * c_negative);
45 const double upper_limit = (-b + root_positive) / (2.0 * a);
46 const double lower_limit = (-b - root_negative) / (2.0 * a);
Austin Schuhe8ca06a2020-03-07 22:27:39 -080047
48 // Limit to the battery voltage and the current limit voltage.
49 mutable_U(0, 0) = std::clamp(U(0, 0), lower_limit, upper_limit);
Maxwell Henderson7a3c99b2024-01-08 15:41:09 -080050
51 double lower_clamp = (std::abs(R(1)) > 50.0) ? 1.0 : 0.0;
52 double upper_clamp = 12.0;
53
54 if (R(1) < 0.0) {
55 // If R(1) is negative, swap and invert.
56 std::swap(lower_clamp, upper_clamp);
57 lower_clamp *= -1.0;
58 upper_clamp *= -1.0;
Austin Schuh242d7132021-11-07 17:39:48 -080059 }
Maxwell Henderson7a3c99b2024-01-08 15:41:09 -080060
61 mutable_U(0, 0) = std::clamp(U(0, 0), lower_clamp, upper_clamp);
Austin Schuhe8ca06a2020-03-07 22:27:39 -080062 }
63
64 private:
65 double bemf_ = 0.0;
66 double resistance_ = 0.0;
67};
68
Austin Schuh43b9ae92020-02-29 23:08:38 -080069FlywheelController::FlywheelController(
70 StateFeedbackLoop<3, 1, 1, double, StateFeedbackHybridPlant<3, 1, 1>,
Austin Schuhe8ca06a2020-03-07 22:27:39 -080071 HybridKalman<3, 1, 1>> &&loop,
72 double bemf, double resistance)
73 : loop_(new CurrentLimitedStateFeedbackController(std::move(loop), bemf,
74 resistance)) {
Sabina Davis0f31d3f2020-02-20 20:41:00 -080075 history_.fill(std::pair<double, ::aos::monotonic_clock::time_point>(
76 0, ::aos::monotonic_clock::epoch()));
Sabina Davisedf89472020-02-17 15:27:37 -080077 Y_.setZero();
78}
79
80void FlywheelController::set_goal(double angular_velocity_goal) {
81 loop_->mutable_next_R() << 0.0, angular_velocity_goal, 0.0;
82 last_goal_ = angular_velocity_goal;
83}
84
Sabina Davis0f31d3f2020-02-20 20:41:00 -080085void FlywheelController::set_position(
86 double current_position,
87 const aos::monotonic_clock::time_point position_timestamp) {
Austin Schuh43b9ae92020-02-29 23:08:38 -080088 // Project time forwards.
89 const int newest_history_position =
90 ((history_position_ == 0) ? kHistoryLength : history_position_) - 1;
91
92 if (!first_) {
93 loop_->UpdateObserver(
94 loop_->U(),
95 position_timestamp - std::get<1>(history_[newest_history_position]));
96 } else {
97 first_ = false;
98 }
99
Sabina Davisedf89472020-02-17 15:27:37 -0800100 // Update position in the model.
101 Y_ << current_position;
102
103 // Add the position to the history.
Sabina Davis0f31d3f2020-02-20 20:41:00 -0800104 history_[history_position_] =
105 std::pair<double, ::aos::monotonic_clock::time_point>(current_position,
106 position_timestamp);
Sabina Davisedf89472020-02-17 15:27:37 -0800107 history_position_ = (history_position_ + 1) % kHistoryLength;
Austin Schuh43b9ae92020-02-29 23:08:38 -0800108
109 loop_->Correct(Y_);
Sabina Davisedf89472020-02-17 15:27:37 -0800110}
111
112double FlywheelController::voltage() const { return loop_->U(0, 0); }
113
Austin Schuh80476772021-03-06 20:17:36 -0800114double FlywheelController::current() const {
115 return ((voltage() - (velocity() / loop_->bemf())) / (loop_->resistance())) *
116 voltage() / 12.0;
117}
118
Sabina Davisedf89472020-02-17 15:27:37 -0800119void FlywheelController::Update(bool disabled) {
120 loop_->mutable_R() = loop_->next_R();
Maxwell Henderson7a3c99b2024-01-08 15:41:09 -0800121
122 if (std::abs(loop_->R(1, 0)) < 1.0) {
Sabina Davisedf89472020-02-17 15:27:37 -0800123 // Kill power at low angular velocities.
124 disabled = true;
125 }
126
Austin Schuh43b9ae92020-02-29 23:08:38 -0800127 loop_->UpdateController(disabled);
Sabina Davisedf89472020-02-17 15:27:37 -0800128}
129
130flatbuffers::Offset<FlywheelControllerStatus> FlywheelController::SetStatus(
131 flatbuffers::FlatBufferBuilder *fbb) {
132 // Compute the oldest point in the history.
Austin Schuh43b9ae92020-02-29 23:08:38 -0800133 const int oldest_history_position = history_position_;
134 const int newest_history_position =
Sabina Davisedf89472020-02-17 15:27:37 -0800135 ((history_position_ == 0) ? kHistoryLength : history_position_) - 1;
Austin Schuh80476772021-03-06 20:17:36 -0800136 const int second_newest_history_position =
137 ((newest_history_position == 0) ? kHistoryLength
138 : newest_history_position) -
139 1;
Sabina Davisedf89472020-02-17 15:27:37 -0800140
Sabina Davis0f31d3f2020-02-20 20:41:00 -0800141 const double total_loop_time = ::aos::time::DurationInSeconds(
Austin Schuh43b9ae92020-02-29 23:08:38 -0800142 std::get<1>(history_[newest_history_position]) -
Sabina Davis0f31d3f2020-02-20 20:41:00 -0800143 std::get<1>(history_[oldest_history_position]));
144
145 const double distance_traveled =
Austin Schuh43b9ae92020-02-29 23:08:38 -0800146 std::get<0>(history_[newest_history_position]) -
Sabina Davis0f31d3f2020-02-20 20:41:00 -0800147 std::get<0>(history_[oldest_history_position]);
148
Austin Schuh80476772021-03-06 20:17:36 -0800149 const double last_loop_time = ::aos::time::DurationInSeconds(
150 std::get<1>(history_[newest_history_position]) -
151 std::get<1>(history_[second_newest_history_position]));
152
153 const double last_distance_traveled =
154 std::get<0>(history_[newest_history_position]) -
155 std::get<0>(history_[second_newest_history_position]);
156
Sabina Davisedf89472020-02-17 15:27:37 -0800157 // Compute the distance moved over that time period.
Sabina Davis0f31d3f2020-02-20 20:41:00 -0800158 avg_angular_velocity_ = (distance_traveled) / (total_loop_time);
Sabina Davisedf89472020-02-17 15:27:37 -0800159
160 FlywheelControllerStatusBuilder builder(*fbb);
161
Sabina Davis0f31d3f2020-02-20 20:41:00 -0800162 builder.add_avg_angular_velocity(avg_angular_velocity_);
Austin Schuh80476772021-03-06 20:17:36 -0800163 builder.add_dt_angular_velocity(last_distance_traveled / last_loop_time);
Sabina Davisedf89472020-02-17 15:27:37 -0800164 builder.add_angular_velocity(loop_->X_hat(1, 0));
Austin Schuh43b9ae92020-02-29 23:08:38 -0800165 builder.add_voltage_error(loop_->X_hat(2, 0));
Austin Schuh80476772021-03-06 20:17:36 -0800166 builder.add_commanded_current(current());
Sabina Davisedf89472020-02-17 15:27:37 -0800167 builder.add_angular_velocity_goal(last_goal_);
168 return builder.Finish();
169}
170
Austin Schuh80476772021-03-06 20:17:36 -0800171FlywheelController::~FlywheelController() {}
172
173double FlywheelController::velocity() const { return loop_->X_hat(1, 0); }
174
Stephan Pleinesf63bde82024-01-13 15:59:33 -0800175} // namespace frc971::control_loops::flywheel