blob: 342fb8397e9ad34c66b4bc73f03f3b7871181678 [file] [log] [blame]
Austin Schuh473a5652017-02-05 01:30:42 -08001#ifndef FRC971_CONTROL_LOOPS_PROFILED_SUBSYSTEM_H_
2#define FRC971_CONTROL_LOOPS_PROFILED_SUBSYSTEM_H_
3
4#include <array>
5#include <memory>
6#include <utility>
7
8#include "Eigen/Dense"
9
10#include "aos/common/controls/control_loop.h"
11#include "aos/common/util/trapezoid_profile.h"
12#include "frc971/control_loops/simple_capped_state_feedback_loop.h"
13#include "frc971/control_loops/state_feedback_loop.h"
14#include "frc971/zeroing/zeroing.h"
15
16namespace frc971 {
17namespace control_loops {
18
19template <int number_of_states, int number_of_axes>
20class ProfiledSubsystem {
21 public:
22 ProfiledSubsystem(
23 ::std::unique_ptr<::frc971::control_loops::SimpleCappedStateFeedbackLoop<
24 number_of_states, number_of_axes, number_of_axes>>
25 loop,
26 ::std::array<::frc971::zeroing::ZeroingEstimator, number_of_axes>
27 &&estimators)
28 : loop_(::std::move(loop)), estimators_(::std::move(estimators)) {
29 zeroed_.fill(false);
30 unprofiled_goal_.setZero();
31 }
32
33 // Returns whether an error has occured
34 bool error() const {
35 for (const auto &estimator : estimators_) {
36 if (estimator.error()) {
37 return true;
38 }
39 }
40 return false;
41 }
42
43 void Reset() {
44 zeroed_.fill(false);
45 for (auto &estimator : estimators_) {
46 estimator.Reset();
47 }
48 }
49
50 // Returns the controller.
51 const StateFeedbackLoop<number_of_states, number_of_axes, number_of_axes>
52 &controller() const {
53 return *loop_;
54 }
55
56 int controller_index() const { return loop_->controller_index(); }
57
58 // Returns whether the estimators have been initialized and zeroed.
59 bool initialized() const { return initialized_; }
60
61 bool zeroed() const {
62 for (int i = 0; i < number_of_axes; ++i) {
63 if (!zeroed_[i]) {
64 return false;
65 }
66 }
67 return true;
68 }
69
70 bool zeroed(int index) const { return zeroed_[index]; };
71
72 // Returns the filtered goal.
73 const Eigen::Matrix<double, number_of_states, 1> &goal() const {
74 return loop_->R();
75 }
76 double goal(int row, int col) const { return loop_->R(row, col); }
77
78 // Returns the unprofiled goal.
79 const Eigen::Matrix<double, number_of_states, 1> &unprofiled_goal() const {
80 return unprofiled_goal_;
81 }
82 double unprofiled_goal(int row, int col) const {
83 return unprofiled_goal_(row, col);
84 }
85
86 // Returns the current state estimate.
87 const Eigen::Matrix<double, number_of_states, 1> &X_hat() const {
88 return loop_->X_hat();
89 }
90 double X_hat(int row, int col) const { return loop_->X_hat(row, col); }
91
92 // Returns the current internal estimator state for logging.
93 ::frc971::EstimatorState EstimatorState(int index) {
94 ::frc971::EstimatorState estimator_state;
95 ::frc971::zeroing::PopulateEstimatorState(estimators_[index],
96 &estimator_state);
97
98 return estimator_state;
99 }
100
101 // Sets the maximum voltage that will be commanded by the loop.
102 void set_max_voltage(::std::array<double, number_of_axes> voltages) {
103 for (int i = 0; i < number_of_axes; ++i) {
104 loop_->set_max_voltage(i, voltages[i]);
105 }
106 }
107
108 protected:
109 void set_zeroed(int index, bool val) { zeroed_[index] = val; }
110
111 // TODO(austin): It's a bold assumption to assume that we will have the same
112 // number of sensors as axes. So far, that's been fine.
113 ::std::unique_ptr<::frc971::control_loops::SimpleCappedStateFeedbackLoop<
114 number_of_states, number_of_axes, number_of_axes>>
115 loop_;
116
117 // The goal that the profile tries to reach.
118 Eigen::Matrix<double, number_of_states, 1> unprofiled_goal_;
119
120 bool initialized_ = false;
121
122 ::std::array<::frc971::zeroing::ZeroingEstimator, number_of_axes> estimators_;
123
124 private:
125 ::std::array<bool, number_of_axes> zeroed_;
126};
127
128class SingleDOFProfiledSubsystem
129 : public ::frc971::control_loops::ProfiledSubsystem<3, 1> {
130 public:
131 SingleDOFProfiledSubsystem(
132 ::std::unique_ptr<SimpleCappedStateFeedbackLoop<3, 1, 1>> loop,
133 const ::frc971::constants::ZeroingConstants &estimator,
134 const ::frc971::constants::Range &range, double default_angular_velocity,
135 double default_angular_acceleration);
136
137 // Updates our estimator with the latest position.
138 void Correct(::frc971::PotAndIndexPosition position);
139 // Runs the controller and profile generator for a cycle.
140 void Update(bool disabled);
141
142 // Forces the current goal to the provided goal, bypassing the profiler.
143 void ForceGoal(double goal);
144 // Sets the unprofiled goal. The profiler will generate a profile to go to
145 // this goal.
146 void set_unprofiled_goal(double unprofiled_goal);
147 // Limits our profiles to a max velocity and acceleration for proper motion.
148 void AdjustProfile(double max_angular_velocity,
149 double max_angular_acceleration);
150
151 // Returns true if we have exceeded any hard limits.
152 bool CheckHardLimits();
153
154 // Returns the requested intake voltage.
155 double intake_voltage() const { return loop_->U(0, 0); }
156
157 // Returns the current position.
158 double angle() const { return Y_(0, 0); }
159
160 // For testing:
161 // Triggers an estimator error.
162 void TriggerEstimatorError() { estimators_[0].TriggerError(); }
163
164 private:
165 // Limits the provided goal to the soft limits. Prints "name" when it fails
166 // to aid debugging.
167 void CapGoal(const char *name, Eigen::Matrix<double, 3, 1> *goal);
168
169 void UpdateOffset(double offset);
170
171 aos::util::TrapezoidProfile profile_;
172
173 // Current measurement.
174 Eigen::Matrix<double, 1, 1> Y_;
175 // Current offset. Y_ = offset_ + raw_sensor;
176 Eigen::Matrix<double, 1, 1> offset_;
177
178 const ::frc971::constants::Range range_;
179
180 const double default_velocity_;
181 const double default_acceleration_;
182};
183
184} // namespace control_loops
185} // namespace frc971
186
187#endif // FRC971_CONTROL_LOOPS_PROFILED_SUBSYSTEM_H_