blob: adea0e87a4ad9318bc8f6488d9fb5f5435d8eda1 [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"
Maxwell Henderson34242992024-01-07 12:39:11 -08006namespace frc971 {
Sabina Davisedf89472020-02-17 15:27:37 -08007namespace control_loops {
Maxwell Henderson34242992024-01-07 12:39:11 -08008namespace flywheel {
Sabina Davisedf89472020-02-17 15:27:37 -08009
Austin Schuhe8ca06a2020-03-07 22:27:39 -080010// Class to current limit battery current for a flywheel controller.
11class CurrentLimitedStateFeedbackController
12 : public StateFeedbackLoop<3, 1, 1, double,
13 StateFeedbackHybridPlant<3, 1, 1>,
14 HybridKalman<3, 1, 1>> {
15 public:
16 // Builds a CurrentLimitedStateFeedbackController given the coefficients, bemf
17 // coefficient (units of radians/sec / volt), and motor resistance in ohms.
18 CurrentLimitedStateFeedbackController(
19 StateFeedbackLoop<3, 1, 1, double, StateFeedbackHybridPlant<3, 1, 1>,
20 HybridKalman<3, 1, 1>> &&other,
21 double bemf, double resistance)
22 : StateFeedbackLoop(std::move(other)),
23 bemf_(bemf),
24 resistance_(resistance) {}
25
Austin Schuh80476772021-03-06 20:17:36 -080026 double resistance() const { return resistance_; }
27 double bemf() const { return bemf_; }
28
Austin Schuhe8ca06a2020-03-07 22:27:39 -080029 void CapU() override {
30 const double bemf_voltage = X_hat(1) / bemf_;
31 // Solve the system of equations:
32 //
33 // motor_current = (u - bemf_voltage) / resistance
34 // battery_current = ((u - bemf_voltage) / resistance) * u / 12.0
35 // 0.0 = u * u - u * bemf_voltage - max_current * 12.0 * resistance
36 //
37 // And we have a quadratic!
38 const double a = 1;
39 const double b = -bemf_voltage;
Austin Schuh242d7132021-11-07 17:39:48 -080040 const double c_positive = -70.0 * 12.0 * resistance_;
Austin Schuhc044dda2021-11-14 21:02:49 -080041 const double c_negative = -25.0 * 12.0 * resistance_;
Austin Schuhe8ca06a2020-03-07 22:27:39 -080042
43 // Root is always positive.
Austin Schuh242d7132021-11-07 17:39:48 -080044 const double root_positive = std::sqrt(b * b - 4.0 * a * c_positive);
45 const double root_negative = std::sqrt(b * b - 4.0 * a * c_negative);
46 const double upper_limit = (-b + root_positive) / (2.0 * a);
47 const double lower_limit = (-b - root_negative) / (2.0 * a);
Austin Schuhe8ca06a2020-03-07 22:27:39 -080048
49 // Limit to the battery voltage and the current limit voltage.
50 mutable_U(0, 0) = std::clamp(U(0, 0), lower_limit, upper_limit);
Austin Schuhc044dda2021-11-14 21:02:49 -080051 if (R(1) > 50.0) {
52 mutable_U(0, 0) = std::clamp(U(0, 0), 1.0, 12.0);
Austin Schuh242d7132021-11-07 17:39:48 -080053 } else {
54 mutable_U(0, 0) = std::clamp(U(0, 0), 0.0, 12.0);
55 }
Austin Schuhe8ca06a2020-03-07 22:27:39 -080056 }
57
58 private:
59 double bemf_ = 0.0;
60 double resistance_ = 0.0;
61};
62
Austin Schuh43b9ae92020-02-29 23:08:38 -080063FlywheelController::FlywheelController(
64 StateFeedbackLoop<3, 1, 1, double, StateFeedbackHybridPlant<3, 1, 1>,
Austin Schuhe8ca06a2020-03-07 22:27:39 -080065 HybridKalman<3, 1, 1>> &&loop,
66 double bemf, double resistance)
67 : loop_(new CurrentLimitedStateFeedbackController(std::move(loop), bemf,
68 resistance)) {
Sabina Davis0f31d3f2020-02-20 20:41:00 -080069 history_.fill(std::pair<double, ::aos::monotonic_clock::time_point>(
70 0, ::aos::monotonic_clock::epoch()));
Sabina Davisedf89472020-02-17 15:27:37 -080071 Y_.setZero();
72}
73
74void FlywheelController::set_goal(double angular_velocity_goal) {
75 loop_->mutable_next_R() << 0.0, angular_velocity_goal, 0.0;
76 last_goal_ = angular_velocity_goal;
77}
78
Sabina Davis0f31d3f2020-02-20 20:41:00 -080079void FlywheelController::set_position(
80 double current_position,
81 const aos::monotonic_clock::time_point position_timestamp) {
Austin Schuh43b9ae92020-02-29 23:08:38 -080082 // Project time forwards.
83 const int newest_history_position =
84 ((history_position_ == 0) ? kHistoryLength : history_position_) - 1;
85
86 if (!first_) {
87 loop_->UpdateObserver(
88 loop_->U(),
89 position_timestamp - std::get<1>(history_[newest_history_position]));
90 } else {
91 first_ = false;
92 }
93
Sabina Davisedf89472020-02-17 15:27:37 -080094 // Update position in the model.
95 Y_ << current_position;
96
97 // Add the position to the history.
Sabina Davis0f31d3f2020-02-20 20:41:00 -080098 history_[history_position_] =
99 std::pair<double, ::aos::monotonic_clock::time_point>(current_position,
100 position_timestamp);
Sabina Davisedf89472020-02-17 15:27:37 -0800101 history_position_ = (history_position_ + 1) % kHistoryLength;
Austin Schuh43b9ae92020-02-29 23:08:38 -0800102
103 loop_->Correct(Y_);
Sabina Davisedf89472020-02-17 15:27:37 -0800104}
105
106double FlywheelController::voltage() const { return loop_->U(0, 0); }
107
Austin Schuh80476772021-03-06 20:17:36 -0800108double FlywheelController::current() const {
109 return ((voltage() - (velocity() / loop_->bemf())) / (loop_->resistance())) *
110 voltage() / 12.0;
111}
112
Sabina Davisedf89472020-02-17 15:27:37 -0800113void FlywheelController::Update(bool disabled) {
114 loop_->mutable_R() = loop_->next_R();
115 if (loop_->R(1, 0) < 1.0) {
116 // Kill power at low angular velocities.
117 disabled = true;
118 }
119
Austin Schuh43b9ae92020-02-29 23:08:38 -0800120 loop_->UpdateController(disabled);
Sabina Davisedf89472020-02-17 15:27:37 -0800121}
122
123flatbuffers::Offset<FlywheelControllerStatus> FlywheelController::SetStatus(
124 flatbuffers::FlatBufferBuilder *fbb) {
125 // Compute the oldest point in the history.
Austin Schuh43b9ae92020-02-29 23:08:38 -0800126 const int oldest_history_position = history_position_;
127 const int newest_history_position =
Sabina Davisedf89472020-02-17 15:27:37 -0800128 ((history_position_ == 0) ? kHistoryLength : history_position_) - 1;
Austin Schuh80476772021-03-06 20:17:36 -0800129 const int second_newest_history_position =
130 ((newest_history_position == 0) ? kHistoryLength
131 : newest_history_position) -
132 1;
Sabina Davisedf89472020-02-17 15:27:37 -0800133
Sabina Davis0f31d3f2020-02-20 20:41:00 -0800134 const double total_loop_time = ::aos::time::DurationInSeconds(
Austin Schuh43b9ae92020-02-29 23:08:38 -0800135 std::get<1>(history_[newest_history_position]) -
Sabina Davis0f31d3f2020-02-20 20:41:00 -0800136 std::get<1>(history_[oldest_history_position]));
137
138 const double distance_traveled =
Austin Schuh43b9ae92020-02-29 23:08:38 -0800139 std::get<0>(history_[newest_history_position]) -
Sabina Davis0f31d3f2020-02-20 20:41:00 -0800140 std::get<0>(history_[oldest_history_position]);
141
Austin Schuh80476772021-03-06 20:17:36 -0800142 const double last_loop_time = ::aos::time::DurationInSeconds(
143 std::get<1>(history_[newest_history_position]) -
144 std::get<1>(history_[second_newest_history_position]));
145
146 const double last_distance_traveled =
147 std::get<0>(history_[newest_history_position]) -
148 std::get<0>(history_[second_newest_history_position]);
149
Sabina Davisedf89472020-02-17 15:27:37 -0800150 // Compute the distance moved over that time period.
Sabina Davis0f31d3f2020-02-20 20:41:00 -0800151 avg_angular_velocity_ = (distance_traveled) / (total_loop_time);
Sabina Davisedf89472020-02-17 15:27:37 -0800152
153 FlywheelControllerStatusBuilder builder(*fbb);
154
Sabina Davis0f31d3f2020-02-20 20:41:00 -0800155 builder.add_avg_angular_velocity(avg_angular_velocity_);
Austin Schuh80476772021-03-06 20:17:36 -0800156 builder.add_dt_angular_velocity(last_distance_traveled / last_loop_time);
Sabina Davisedf89472020-02-17 15:27:37 -0800157 builder.add_angular_velocity(loop_->X_hat(1, 0));
Austin Schuh43b9ae92020-02-29 23:08:38 -0800158 builder.add_voltage_error(loop_->X_hat(2, 0));
Austin Schuh80476772021-03-06 20:17:36 -0800159 builder.add_commanded_current(current());
Sabina Davisedf89472020-02-17 15:27:37 -0800160 builder.add_angular_velocity_goal(last_goal_);
161 return builder.Finish();
162}
163
Austin Schuh80476772021-03-06 20:17:36 -0800164FlywheelController::~FlywheelController() {}
165
166double FlywheelController::velocity() const { return loop_->X_hat(1, 0); }
167
Maxwell Henderson34242992024-01-07 12:39:11 -0800168} // namespace flywheel
Sabina Davisedf89472020-02-17 15:27:37 -0800169} // namespace control_loops
Maxwell Henderson34242992024-01-07 12:39:11 -0800170} // namespace frc971