blob: adb40e019b381677a3128f8e80aeb76723fd570e [file] [log] [blame]
Austin Schuh10c2d112016-02-14 13:42:28 -08001#ifndef Y2016_CONTROL_LOOPS_SUPERSTRUCTURE_SUPERSTRUCTURE_CONTROLS_H_
2#define Y2016_CONTROL_LOOPS_SUPERSTRUCTURE_SUPERSTRUCTURE_CONTROLS_H_
3
4#include <memory>
5
6#include "aos/common/controls/control_loop.h"
7#include "frc971/control_loops/state_feedback_loop.h"
Brian Silverman2b1957a2016-02-14 20:29:57 -05008#include "frc971/control_loops/simple_capped_state_feedback_loop.h"
Austin Schuh10c2d112016-02-14 13:42:28 -08009#include "aos/common/util/trapezoid_profile.h"
10
11#include "frc971/zeroing/zeroing.h"
12#include "y2016/control_loops/superstructure/superstructure.q.h"
Austin Schuhf59b6bc2016-03-11 21:26:19 -080013#include "y2016/control_loops/superstructure/integral_arm_plant.h"
Austin Schuh10c2d112016-02-14 13:42:28 -080014
15namespace y2016 {
16namespace control_loops {
17namespace superstructure {
18namespace testing {
19class SuperstructureTest_DisabledGoalTest_Test;
20} // namespace testing
21
Austin Schuhf59b6bc2016-03-11 21:26:19 -080022class ArmControlLoop
23 : public ::frc971::control_loops::SimpleCappedStateFeedbackLoop<6, 2, 2> {
24 public:
25 ArmControlLoop(SimpleCappedStateFeedbackLoop<6, 2, 2> &&loop)
26 : SimpleCappedStateFeedbackLoop<6, 2, 2>(::std::move(loop)) {}
27
28 const Eigen::Matrix<double, 2, 1> ControllerOutput() override {
29 const Eigen::Matrix<double, 2, 1> accelerating_ff =
30 controller(0).Kff * (next_R() - controller(0).plant.A() * R());
31 const Eigen::Matrix<double, 2, 1> accelerating_controller =
32 controller(0).K * error() + accelerating_ff;
33
34 const Eigen::Matrix<double, 2, 1> decelerating_ff =
35 controller(1).Kff * (next_R() - controller(1).plant.A() * R());
36 const Eigen::Matrix<double, 2, 1> decelerating_controller =
37 controller(1).K * error() + decelerating_ff;
38
39 const double bemf_voltage = X_hat(1, 0) / kV_shoulder;
40 bool use_accelerating_controller = true;
41 LOG(DEBUG, "Accelerating at %f, decel %f, bemf %f\n",
42 accelerating_controller(0, 0), accelerating_controller(1, 0),
43 bemf_voltage);
44 if (IsAccelerating(bemf_voltage, accelerating_controller(0, 0))) {
45 use_accelerating_controller = true;
46 } else {
47 use_accelerating_controller = false;
48 }
49 if (use_accelerating_controller) {
50 ff_U_ = accelerating_ff;
51 set_controller_index(0);
52 return accelerating_controller;
53 } else {
54 set_controller_index(1);
55 ff_U_ = decelerating_ff;
56 return decelerating_controller;
57 }
58 }
59
60 private:
Austin Schuhd7e29942016-03-12 21:35:11 -080061 void CapU() override {
62 // U(0)
63 // U(1) = coupling * U(0) + ...
64 // So, when modifying U(0), remove the coupling.
65 if (U(0, 0) > max_voltage(0)) {
66 const double overage_amount = U(0, 0) - max_voltage(0);
67 mutable_U(0, 0) = max_voltage(0);
68 const double coupled_amount =
69 (Kff().block<1, 2>(1, 2) * B().block<2, 1>(2, 0))(0, 0) * overage_amount;
70 LOG(DEBUG, "Removing coupled amount %f\n", coupled_amount);
71 mutable_U(1, 0) += coupled_amount;
72 }
73 if (U(0, 0) < min_voltage(0)) {
74 const double under_amount = U(0, 0) - min_voltage(0);
75 mutable_U(0, 0) = min_voltage(0);
76 const double coupled_amount =
77 (Kff().block<1, 2>(1, 2) * B().block<2, 1>(2, 0))(0, 0) *
78 under_amount;
79 LOG(DEBUG, "Removing coupled amount %f\n", coupled_amount);
80 mutable_U(1, 0) += coupled_amount;
81 }
82
83 // Uncapping U above isn't actually a problem with U for the shoulder.
84 // Reset any change.
85 mutable_U_uncapped(1, 0) = U(1, 0);
86 mutable_U(1, 0) =
87 ::std::min(max_voltage(1), ::std::max(min_voltage(1), U(1, 0)));
88 }
89
Austin Schuhf59b6bc2016-03-11 21:26:19 -080090 bool IsAccelerating(double bemf_voltage, double voltage) {
91 if (bemf_voltage > 0) {
92 return voltage > bemf_voltage;
93 } else {
94 return voltage < bemf_voltage;
95 }
96 }
97};
Diana Vandenberge2843c62016-02-13 17:44:20 -080098
Austin Schuhcb60e292017-01-01 16:07:31 -080099template <int number_of_states, int number_of_axes>
100class ProfiledSubsystem {
Austin Schuh10c2d112016-02-14 13:42:28 -0800101 public:
Austin Schuhcb60e292017-01-01 16:07:31 -0800102 ProfiledSubsystem(
103 ::std::unique_ptr<::frc971::control_loops::SimpleCappedStateFeedbackLoop<
104 number_of_states, number_of_axes, number_of_axes>>
105 loop)
106 : loop_(::std::move(loop)) {
107 zeroed_.fill(false);
108 unprofiled_goal_.setZero();
109 }
110
111 void Reset() {
112 zeroed_.fill(false);
113 DoReset();
114 }
115
116 // Returns the controller.
117 const StateFeedbackLoop<number_of_states, number_of_axes, number_of_axes>
118 &controller() const {
119 return *loop_;
120 }
121
122 int controller_index() const { return loop_->controller_index(); }
123
Austin Schuh10c2d112016-02-14 13:42:28 -0800124 // Returns whether the estimators have been initialized and zeroed.
125 bool initialized() const { return initialized_; }
Austin Schuhcb60e292017-01-01 16:07:31 -0800126
127 bool zeroed() const {
128 for (int i = 0; i < number_of_axes; ++i) {
129 if (!zeroed_[i]) {
130 return false;
131 }
132 }
133 return true;
134 }
135
136 bool zeroed(int index) const { return zeroed_[index]; };
137
138 // Returns the filtered goal.
139 const Eigen::Matrix<double, number_of_states, 1> &goal() const {
140 return loop_->R();
141 }
142 double goal(int row, int col) const { return loop_->R(row, col); }
143
144 // Returns the unprofiled goal.
145 const Eigen::Matrix<double, number_of_states, 1> &unprofiled_goal() const {
146 return unprofiled_goal_;
147 }
148 double unprofiled_goal(int row, int col) const {
149 return unprofiled_goal_(row, col);
150 }
151
152 // Returns the current state estimate.
153 const Eigen::Matrix<double, number_of_states, 1> &X_hat() const {
154 return loop_->X_hat();
155 }
156 double X_hat(int row, int col) const { return loop_->X_hat(row, col); }
157
158 protected:
159 void set_zeroed(int index, bool val) { zeroed_[index] = val; }
160
161 // TODO(austin): It's a bold assumption to assume that we will have the same
162 // number of sensors as axes. So far, that's been fine.
163 ::std::unique_ptr<::frc971::control_loops::SimpleCappedStateFeedbackLoop<
164 number_of_states, number_of_axes, number_of_axes>>
165 loop_;
166
167 // The goal that the profile tries to reach.
168 Eigen::Matrix<double, number_of_states, 1> unprofiled_goal_;
169
170 bool initialized_ = false;
171
172 private:
173 ::std::array<bool, number_of_axes> zeroed_;
174
175 virtual void DoReset() = 0;
176};
177
178class Intake : public ProfiledSubsystem<3, 1> {
179 public:
180 Intake();
181 // Returns whether an error has occured
182 bool error() const { return estimator_.error(); }
183
184 // Updates our estimator with the latest position.
185 void Correct(::frc971::PotAndIndexPosition position);
186 // Runs the controller and profile generator for a cycle.
187 void Update(bool disabled);
188 // Sets the maximum voltage that will be commanded by the loop.
189 void set_max_voltage(double voltage);
190
191 // Forces the current goal to the provided goal, bypassing the profiler.
192 void ForceGoal(double goal);
193 // Sets the unprofiled goal. The profiler will generate a profile to go to
194 // this goal.
195 void set_unprofiled_goal(double unprofiled_goal);
196 // Limits our profiles to a max velocity and acceleration for proper motion.
197 void AdjustProfile(double max_angular_velocity,
198 double max_angular_acceleration);
199
200 // Returns true if we have exceeded any hard limits.
201 bool CheckHardLimits();
202
203 // Returns the current internal estimator state for logging.
204 ::frc971::EstimatorState IntakeEstimatorState();
205
206 // Returns the requested intake voltage.
207 double intake_voltage() const { return loop_->U(0, 0); }
208
209 // Returns the current position.
210 double angle() const { return Y_(0, 0); }
211
212 // For testing:
213 // Triggers an estimator error.
214 void TriggerEstimatorError() { estimator_.TriggerError(); }
215
216 private:
217 // Limits the provided goal to the soft limits. Prints "name" when it fails
218 // to aid debugging.
219 void CapGoal(const char *name, Eigen::Matrix<double, 3, 1> *goal);
220
221 // Resets the internal state.
222 void DoReset() override;
223
224 void UpdateIntakeOffset(double offset);
225
226 ::frc971::zeroing::ZeroingEstimator estimator_;
227 aos::util::TrapezoidProfile profile_;
228
229 // Current measurement.
230 Eigen::Matrix<double, 1, 1> Y_;
231 // Current offset. Y_ = offset_ + raw_sensor;
232 Eigen::Matrix<double, 1, 1> offset_;
233};
234
235class Arm : public ProfiledSubsystem<6, 2> {
236 public:
237 Arm();
Diana Vandenberge2843c62016-02-13 17:44:20 -0800238 // Returns whether an error has occured
239 bool error() const {
240 return shoulder_estimator_.error() || wrist_estimator_.error();
241 }
Austin Schuh10c2d112016-02-14 13:42:28 -0800242
243 // Updates our estimator with the latest position.
244 void Correct(::frc971::PotAndIndexPosition position_shoulder,
245 ::frc971::PotAndIndexPosition position_wrist);
246
247 // Forces the goal to be the provided goal.
248 void ForceGoal(double unprofiled_goal_shoulder, double unprofiled_goal_wrist);
249 // Sets the unprofiled goal. The profiler will generate a profile to go to
250 // this goal.
251 void set_unprofiled_goal(double unprofiled_goal_shoulder,
252 double unprofiled_goal_wrist);
253
254 // Runs the controller and profile generator for a cycle.
255 void Update(bool disabled);
256
257 // Limits our profiles to a max velocity and acceleration for proper motion.
258 void AdjustProfile(double max_angular_velocity_shoulder,
259 double max_angular_acceleration_shoulder,
260 double max_angular_velocity_wrist,
261 double max_angular_acceleration_wrist);
262 void set_max_voltage(double shoulder_max_voltage, double wrist_max_voltage);
263
Austin Schuh9f4e8a72016-02-16 15:28:47 -0800264 void set_shoulder_asymetric_limits(double shoulder_min_voltage,
265 double shoulder_max_voltage) {
266 loop_->set_asymetric_voltage(0, shoulder_min_voltage, shoulder_max_voltage);
267 }
268
Austin Schuh10c2d112016-02-14 13:42:28 -0800269 // Returns true if we have exceeded any hard limits.
270 bool CheckHardLimits();
Austin Schuh10c2d112016-02-14 13:42:28 -0800271
272 // Returns the current internal estimator state for logging.
273 ::frc971::EstimatorState ShoulderEstimatorState();
274 ::frc971::EstimatorState WristEstimatorState();
275
276 // Returns the requested shoulder and wrist voltages.
277 double shoulder_voltage() const { return loop_->U(0, 0); }
278 double wrist_voltage() const { return loop_->U(1, 0); }
279
280 // Returns the current positions.
281 double shoulder_angle() const { return Y_(0, 0); }
282 double wrist_angle() const { return Y_(1, 0) + Y_(0, 0); }
283
Diana Vandenberge2843c62016-02-13 17:44:20 -0800284 // For testing:
285 // Triggers an estimator error.
286 void TriggerEstimatorError() { shoulder_estimator_.TriggerError(); }
287
Austin Schuh10c2d112016-02-14 13:42:28 -0800288 private:
Austin Schuhcb60e292017-01-01 16:07:31 -0800289 // Resets the internal state.
290 void DoReset() override;
291
Austin Schuh10c2d112016-02-14 13:42:28 -0800292 // Limits the provided goal to the soft limits. Prints "name" when it fails
293 // to aid debugging.
294 void CapGoal(const char *name, Eigen::Matrix<double, 6, 1> *goal);
295
296 // Updates the offset
297 void UpdateWristOffset(double offset);
298 void UpdateShoulderOffset(double offset);
299
300 friend class testing::SuperstructureTest_DisabledGoalTest_Test;
Austin Schuh10c2d112016-02-14 13:42:28 -0800301
302 aos::util::TrapezoidProfile shoulder_profile_, wrist_profile_;
303 ::frc971::zeroing::ZeroingEstimator shoulder_estimator_, wrist_estimator_;
304
305 // Current measurement.
306 Eigen::Matrix<double, 2, 1> Y_;
307 // Current offset. Y_ = offset_ + raw_sensor;
308 Eigen::Matrix<double, 2, 1> offset_;
Austin Schuh10c2d112016-02-14 13:42:28 -0800309};
310
311} // namespace superstructure
312} // namespace control_loops
313} // namespace y2016
314
315#endif // Y2016_CONTROL_LOOPS_SUPERSTRUCTURE_SUPERSTRUCTURE_CONTROLS_H_