blob: 6bd7aa572bcddf338b700342945d480b0b8d3d2a [file] [log] [blame]
Austin Schuhd5ccb862017-03-11 22:06:36 -08001#ifndef Y2017_CONTROL_LOOPS_SUPERSTRUCTURE_COLUMN_COLUMN_H_
2#define Y2017_CONTROL_LOOPS_SUPERSTRUCTURE_COLUMN_COLUMN_H_
3
4#include <array>
5#include <chrono>
6#include <memory>
7#include <utility>
8
9#include "Eigen/Dense"
10
11#include "frc971/constants.h"
12#include "frc971/control_loops/profiled_subsystem.h"
13#include "frc971/control_loops/state_feedback_loop.h"
14#include "y2017/constants.h"
15#include "y2017/control_loops/superstructure/column/column_zeroing.h"
16#include "y2017/control_loops/superstructure/intake/intake.h"
17#include "y2017/control_loops/superstructure/superstructure.q.h"
18
19namespace y2017 {
20namespace control_loops {
21namespace superstructure {
22namespace column {
23
24class ColumnProfiledSubsystem
25 : public ::frc971::control_loops::ProfiledSubsystem<
26 6, 1, ColumnZeroingEstimator, 2, 2> {
27 public:
28 ColumnProfiledSubsystem(
29 ::std::unique_ptr<
30 ::frc971::control_loops::SimpleCappedStateFeedbackLoop<6, 2, 2>>
31 loop,
32 const ::y2017::constants::Values::Column &zeroing_constants,
33 const ::frc971::constants::Range &range, double default_angular_velocity,
34 double default_angular_acceleration);
35
36 // Updates our estimator with the latest position.
37 void Correct(const ColumnPosition &position);
38 // Runs the controller and profile generator for a cycle.
39 void Update(bool disabled);
40
41 // Fills out the ProfiledJointStatus structure with the current state.
42 template <class StatusType>
43 void PopulateTurretStatus(StatusType *status);
44
45 // Forces the current goal to the provided goal, bypassing the profiler.
46 void ForceGoal(double goal_velocity, double goal);
47 // Sets the unprofiled goal. The profiler will generate a profile to go to
48 // this goal.
49 void set_indexer_unprofiled_goal(double goal_velocity);
50 void set_turret_unprofiled_goal(double unprofiled_goal);
51 void set_unprofiled_goal(double goal_velocity, double unprofiled_goal);
52 // Limits our profiles to a max velocity and acceleration for proper motion.
53 void AdjustProfile(const ::frc971::ProfileParameters &profile_parameters);
54 void AdjustProfile(double max_angular_velocity,
55 double max_angular_acceleration);
56
57 // Returns true if we have exceeded any hard limits.
58 bool CheckHardLimits();
59
60 // Returns the requested voltage.
61 double indexer_voltage() const { return loop_->U(0, 0); }
62 double uncapped_indexer_voltage() const { return loop_->U_uncapped(0, 0); }
63 double turret_voltage() const { return loop_->U(1, 0); }
64
65 // Returns the current Y.
66 const ::Eigen::Matrix<double, 2, 1> Y() const { return Y_; }
67 double Y(int i, int j) const { return Y_(i, j); }
68
69 // Returns the current indexer position.
70 double indexer_position() const { return Y_(0, 0); }
71
72 bool saturated() const { return saturated_; }
73
74 // Returns the current turret position.
75 double turret_position() const { return Y_(1, 0) + Y_(0, 0); }
76
77 // For testing:
78 // Triggers an estimator error.
79 void TriggerEstimatorError() { estimators_[0].TriggerError(); }
80
81 const ::frc971::constants::Range &range() const { return range_; }
82
83 bool IsIndexerStuck() const;
84 double IndexerStuckVoltage() const;
85 void PartialIndexerReset();
86 void PartialTurretReset();
87 void PopulateIndexerStatus(IndexerStatus *status);
88
89 void AddOffset(double indexer_offset_delta, double turret_offset_delta);
90
91 protected:
92 // Limits the provided goal to the soft limits. Prints "name" when it fails
93 // to aid debugging.
94 virtual void CapGoal(const char *name, Eigen::Matrix<double, 6, 1> *goal);
95
96 private:
97 void UpdateOffset(double indexer_offset, double turret_offset);
98
99 ::std::unique_ptr<StateFeedbackLoop<6, 2, 2>> stuck_indexer_detector_;
100
101 // History array for calculating a filtered angular velocity.
102 static constexpr int kHistoryLength = 5;
103 ::std::array<double, kHistoryLength> indexer_history_;
104 ptrdiff_t indexer_history_position_ = 0;
105
106 double indexer_error_ = 0.0;
107 double indexer_dt_velocity_ = 0.0;
108 double indexer_last_position_ = 0.0;
109 double indexer_average_angular_velocity_ = 0.0;
110 double indexer_position_error_ = 0.0;
111 bool indexer_ready_ = false;
112
113 bool saturated_ = false;
114
115 Eigen::Matrix<double, 6, 1> X_hat_current_;
116 Eigen::Matrix<double, 6, 1> stuck_indexer_X_hat_current_;
117
118 aos::util::TrapezoidProfile profile_;
119
120 // Current measurement.
121 Eigen::Matrix<double, 2, 1> Y_;
122 // Current offset. Y_ = offset_ + raw_sensor;
123 Eigen::Matrix<double, 2, 1> offset_;
124
125 const ::frc971::constants::Range range_;
126
127 const double default_velocity_;
128 const double default_acceleration_;
129
130 double turret_last_position_ = 0;
131};
132
133template <typename StatusType>
134void ColumnProfiledSubsystem::PopulateTurretStatus(StatusType *status) {
135 status->zeroed = zeroed();
136 status->state = -1;
137 // We don't know, so default to the bad case.
138 status->estopped = true;
139
140 status->position = X_hat(2, 0);
141 status->velocity = X_hat(3, 0);
142 status->goal_position = goal(2, 0);
143 status->goal_velocity = goal(3, 0);
144 status->unprofiled_goal_position = unprofiled_goal(2, 0);
145 status->unprofiled_goal_velocity = unprofiled_goal(3, 0);
146 status->voltage_error = X_hat(5, 0);
147 status->calculated_velocity =
148 (turret_position() - turret_last_position_) /
149 ::std::chrono::duration_cast<::std::chrono::duration<double>>(
150 ::aos::controls::kLoopFrequency)
151 .count();
152
153 status->estimator_state = EstimatorState(0);
154
155 Eigen::Matrix<double, 6, 1> error = controller().error();
156 status->position_power = controller().controller().K(0, 0) * error(0, 0);
157 status->velocity_power = controller().controller().K(0, 1) * error(1, 0);
158}
159
160class Column {
161 public:
162 Column();
163 double goal(int row, int col) const {
164 return profiled_subsystem_.goal(row, col);
165 }
166
167 double turret_position() const {
168 return profiled_subsystem_.turret_position();
169 }
170
171 void set_freeze(bool freeze) { freeze_ = freeze; }
172
173 // The zeroing and operating voltages.
174 static constexpr double kZeroingVoltage = 5.0;
175 static constexpr double kOperatingVoltage = 12.0;
176 static constexpr double kIntakeZeroingMinDistance = 0.08;
177 static constexpr double kIntakeTolerance = 0.005;
178 static constexpr double kStuckZeroingTrackingError = 0.02;
179 static constexpr double kTurretNearZero = 0.1;
180
181 void Iterate(const control_loops::IndexerGoal *unsafe_indexer_goal,
182 const control_loops::TurretGoal *unsafe_turret_goal,
183 const ColumnPosition *position, double *indexer_output,
184 double *turret_output, IndexerStatus *indexer_status,
185 TurretProfiledSubsystemStatus *turret_status,
186 intake::Intake *intake);
187
188 void Reset();
189
190 enum class State : int32_t {
191 UNINITIALIZED = 0,
192 ZEROING_UNINITIALIZED = 1,
193 ZEROING_POSITIVE = 2,
194 ZEROING_NEGATIVE = 3,
195 RUNNING = 4,
196 ESTOP = 5,
197 };
198
199 enum class IndexerState : int32_t {
200 // The system is running correctly, no stuck condition detected.
201 RUNNING = 0,
202 // The system is reversing to unjam.
203 REVERSING = 1
204 };
205
206 State state() const { return state_; }
207 IndexerState indexer_state() const { return indexer_state_; }
208
209 private:
210 State state_ = State::UNINITIALIZED;
211
212 IndexerState indexer_state_ = IndexerState::RUNNING;
213
214 bool freeze_ = false;
215
216 // The last time that we transitioned from RUNNING to REVERSING or the
217 // reverse. Used to implement the timeouts.
218 ::aos::monotonic_clock::time_point last_transition_time_ =
219 ::aos::monotonic_clock::min_time;
220
221 ColumnProfiledSubsystem profiled_subsystem_;
222};
223
224} // namespace column
225} // namespace superstructure
226} // namespace control_loops
227} // namespace y2017
228
229#endif // Y2017_CONTROL_LOOPS_SUPERSTRUCTURE_COLUMN_COLUMN_H_