blob: dc821479be20ad56bae7589fdfc106347c4b617e [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);
Maxwell Henderson7a3c99b2024-01-08 15:41:09 -080051
52 double lower_clamp = (std::abs(R(1)) > 50.0) ? 1.0 : 0.0;
53 double upper_clamp = 12.0;
54
55 if (R(1) < 0.0) {
56 // If R(1) is negative, swap and invert.
57 std::swap(lower_clamp, upper_clamp);
58 lower_clamp *= -1.0;
59 upper_clamp *= -1.0;
Austin Schuh242d7132021-11-07 17:39:48 -080060 }
Maxwell Henderson7a3c99b2024-01-08 15:41:09 -080061
62 mutable_U(0, 0) = std::clamp(U(0, 0), lower_clamp, upper_clamp);
Austin Schuhe8ca06a2020-03-07 22:27:39 -080063 }
64
65 private:
66 double bemf_ = 0.0;
67 double resistance_ = 0.0;
68};
69
Austin Schuh43b9ae92020-02-29 23:08:38 -080070FlywheelController::FlywheelController(
71 StateFeedbackLoop<3, 1, 1, double, StateFeedbackHybridPlant<3, 1, 1>,
Austin Schuhe8ca06a2020-03-07 22:27:39 -080072 HybridKalman<3, 1, 1>> &&loop,
73 double bemf, double resistance)
74 : loop_(new CurrentLimitedStateFeedbackController(std::move(loop), bemf,
75 resistance)) {
Sabina Davis0f31d3f2020-02-20 20:41:00 -080076 history_.fill(std::pair<double, ::aos::monotonic_clock::time_point>(
77 0, ::aos::monotonic_clock::epoch()));
Sabina Davisedf89472020-02-17 15:27:37 -080078 Y_.setZero();
79}
80
81void FlywheelController::set_goal(double angular_velocity_goal) {
82 loop_->mutable_next_R() << 0.0, angular_velocity_goal, 0.0;
83 last_goal_ = angular_velocity_goal;
84}
85
Sabina Davis0f31d3f2020-02-20 20:41:00 -080086void FlywheelController::set_position(
87 double current_position,
88 const aos::monotonic_clock::time_point position_timestamp) {
Austin Schuh43b9ae92020-02-29 23:08:38 -080089 // Project time forwards.
90 const int newest_history_position =
91 ((history_position_ == 0) ? kHistoryLength : history_position_) - 1;
92
93 if (!first_) {
94 loop_->UpdateObserver(
95 loop_->U(),
96 position_timestamp - std::get<1>(history_[newest_history_position]));
97 } else {
98 first_ = false;
99 }
100
Sabina Davisedf89472020-02-17 15:27:37 -0800101 // Update position in the model.
102 Y_ << current_position;
103
104 // Add the position to the history.
Sabina Davis0f31d3f2020-02-20 20:41:00 -0800105 history_[history_position_] =
106 std::pair<double, ::aos::monotonic_clock::time_point>(current_position,
107 position_timestamp);
Sabina Davisedf89472020-02-17 15:27:37 -0800108 history_position_ = (history_position_ + 1) % kHistoryLength;
Austin Schuh43b9ae92020-02-29 23:08:38 -0800109
110 loop_->Correct(Y_);
Sabina Davisedf89472020-02-17 15:27:37 -0800111}
112
113double FlywheelController::voltage() const { return loop_->U(0, 0); }
114
Austin Schuh80476772021-03-06 20:17:36 -0800115double FlywheelController::current() const {
116 return ((voltage() - (velocity() / loop_->bemf())) / (loop_->resistance())) *
117 voltage() / 12.0;
118}
119
Sabina Davisedf89472020-02-17 15:27:37 -0800120void FlywheelController::Update(bool disabled) {
121 loop_->mutable_R() = loop_->next_R();
Maxwell Henderson7a3c99b2024-01-08 15:41:09 -0800122
123 if (std::abs(loop_->R(1, 0)) < 1.0) {
Sabina Davisedf89472020-02-17 15:27:37 -0800124 // Kill power at low angular velocities.
125 disabled = true;
126 }
127
Austin Schuh43b9ae92020-02-29 23:08:38 -0800128 loop_->UpdateController(disabled);
Sabina Davisedf89472020-02-17 15:27:37 -0800129}
130
131flatbuffers::Offset<FlywheelControllerStatus> FlywheelController::SetStatus(
132 flatbuffers::FlatBufferBuilder *fbb) {
133 // Compute the oldest point in the history.
Austin Schuh43b9ae92020-02-29 23:08:38 -0800134 const int oldest_history_position = history_position_;
135 const int newest_history_position =
Sabina Davisedf89472020-02-17 15:27:37 -0800136 ((history_position_ == 0) ? kHistoryLength : history_position_) - 1;
Austin Schuh80476772021-03-06 20:17:36 -0800137 const int second_newest_history_position =
138 ((newest_history_position == 0) ? kHistoryLength
139 : newest_history_position) -
140 1;
Sabina Davisedf89472020-02-17 15:27:37 -0800141
Sabina Davis0f31d3f2020-02-20 20:41:00 -0800142 const double total_loop_time = ::aos::time::DurationInSeconds(
Austin Schuh43b9ae92020-02-29 23:08:38 -0800143 std::get<1>(history_[newest_history_position]) -
Sabina Davis0f31d3f2020-02-20 20:41:00 -0800144 std::get<1>(history_[oldest_history_position]));
145
146 const double distance_traveled =
Austin Schuh43b9ae92020-02-29 23:08:38 -0800147 std::get<0>(history_[newest_history_position]) -
Sabina Davis0f31d3f2020-02-20 20:41:00 -0800148 std::get<0>(history_[oldest_history_position]);
149
Austin Schuh80476772021-03-06 20:17:36 -0800150 const double last_loop_time = ::aos::time::DurationInSeconds(
151 std::get<1>(history_[newest_history_position]) -
152 std::get<1>(history_[second_newest_history_position]));
153
154 const double last_distance_traveled =
155 std::get<0>(history_[newest_history_position]) -
156 std::get<0>(history_[second_newest_history_position]);
157
Sabina Davisedf89472020-02-17 15:27:37 -0800158 // Compute the distance moved over that time period.
Sabina Davis0f31d3f2020-02-20 20:41:00 -0800159 avg_angular_velocity_ = (distance_traveled) / (total_loop_time);
Sabina Davisedf89472020-02-17 15:27:37 -0800160
161 FlywheelControllerStatusBuilder builder(*fbb);
162
Sabina Davis0f31d3f2020-02-20 20:41:00 -0800163 builder.add_avg_angular_velocity(avg_angular_velocity_);
Austin Schuh80476772021-03-06 20:17:36 -0800164 builder.add_dt_angular_velocity(last_distance_traveled / last_loop_time);
Sabina Davisedf89472020-02-17 15:27:37 -0800165 builder.add_angular_velocity(loop_->X_hat(1, 0));
Austin Schuh43b9ae92020-02-29 23:08:38 -0800166 builder.add_voltage_error(loop_->X_hat(2, 0));
Austin Schuh80476772021-03-06 20:17:36 -0800167 builder.add_commanded_current(current());
Sabina Davisedf89472020-02-17 15:27:37 -0800168 builder.add_angular_velocity_goal(last_goal_);
169 return builder.Finish();
170}
171
Austin Schuh80476772021-03-06 20:17:36 -0800172FlywheelController::~FlywheelController() {}
173
174double FlywheelController::velocity() const { return loop_->X_hat(1, 0); }
175
Maxwell Henderson34242992024-01-07 12:39:11 -0800176} // namespace flywheel
Sabina Davisedf89472020-02-17 15:27:37 -0800177} // namespace control_loops
Maxwell Henderson34242992024-01-07 12:39:11 -0800178} // namespace frc971