blob: 07cc43a0f02ed0f95039c75a068b925ce23f879c [file] [log] [blame]
Adam Snaiderc4b3c192015-02-01 01:30:39 +00001#ifndef FRC971_ZEROING_ZEROING_H_
2#define FRC971_ZEROING_ZEROING_H_
3
Philipp Schrader41d82912015-02-15 03:44:23 +00004#include <cstdint>
Adam Snaiderc4b3c192015-02-01 01:30:39 +00005#include <vector>
Philipp Schrader41d82912015-02-15 03:44:23 +00006
Austin Schuh703b8d42015-02-01 14:56:34 -08007#include "frc971/control_loops/control_loops.q.h"
8#include "frc971/constants.h"
Adam Snaiderc4b3c192015-02-01 01:30:39 +00009
Adam Snaiderb4119252015-02-15 01:30:57 +000010// TODO(pschrader): Flag an error if encoder index pulse is not n revolutions
11// away from the last one (i.e. got extra counts from noise, etc..)
12//
13// TODO(pschrader): Flag error if the pot disagrees too much with the encoder
14// after being zeroed.
15//
16// TODO(pschrader): Watch the offset over long periods of time and flag if it
17// gets too far away from the initial value.
18
Adam Snaiderc4b3c192015-02-01 01:30:39 +000019namespace frc971 {
20namespace zeroing {
21
Lee Mracekcdd19ee2019-01-07 10:19:54 -050022template <typename TPosition, typename TZeroingConstants, typename TState>
Neil Balch1049be92017-02-15 23:20:49 -080023class ZeroingEstimator {
24 public:
Lee Mracekcdd19ee2019-01-07 10:19:54 -050025 using Position = TPosition;
26 using ZeroingConstants = TZeroingConstants;
27 using State = TState;
Neil Balch1049be92017-02-15 23:20:49 -080028 virtual ~ZeroingEstimator(){}
29
30 // Returns true if the logic considers the corresponding mechanism to be
Isaac Wilcove0851ffd2017-02-16 04:13:14 +000031 // zeroed.
Neil Balch1049be92017-02-15 23:20:49 -080032 virtual bool zeroed() const = 0;
33
Isaac Wilcove0851ffd2017-02-16 04:13:14 +000034 // Returns the estimated position of the corresponding mechanism.
Neil Balch1049be92017-02-15 23:20:49 -080035 virtual double offset() const = 0;
36
Isaac Wilcove0851ffd2017-02-16 04:13:14 +000037 // Returns true if there has been an error.
Neil Balch1049be92017-02-15 23:20:49 -080038 virtual bool error() const = 0;
Lee Mracekcdd19ee2019-01-07 10:19:54 -050039
40 // Returns true if an offset is ready.
41 virtual bool offset_ready() const = 0;
42
43 // Triggers an internal error. This is used for testing the error
44 // logic.
45 virtual void TriggerError() = 0;
46
47 // Resets the estimator, clearing error and zeroing states.
48 virtual void Reset() = 0;
49
50 // Updates the internal logic with new sensor values
51 virtual void UpdateEstimate(const Position &) = 0;
52
53 // Returns the state of the estimator
54 virtual State GetEstimatorState() const = 0;
Neil Balch1049be92017-02-15 23:20:49 -080055};
56
Brian Silvermanab0b6772017-02-05 16:16:21 -080057// Estimates the position with an incremental encoder with an index pulse and a
58// potentiometer.
Lee Mracekcdd19ee2019-01-07 10:19:54 -050059class PotAndIndexPulseZeroingEstimator : public ZeroingEstimator<PotAndIndexPosition,
60 constants::PotAndIndexPulseZeroingConstants,
61 EstimatorState> {
Adam Snaiderc4b3c192015-02-01 01:30:39 +000062 public:
Campbell Crowley36e93e92017-12-23 14:21:43 -080063 explicit PotAndIndexPulseZeroingEstimator(
Tyler Chatowf8f03112017-02-05 14:31:34 -080064 const constants::PotAndIndexPulseZeroingConstants &constants);
Austin Schuh703b8d42015-02-01 14:56:34 -080065
Adam Snaiderb4119252015-02-15 01:30:57 +000066 // Update the internal logic with the next sensor values.
Lee Mracekcdd19ee2019-01-07 10:19:54 -050067 void UpdateEstimate(const PotAndIndexPosition &info) override;
Adam Snaiderb4119252015-02-15 01:30:57 +000068
69 // Reset the internal logic so it needs to be re-zeroed.
Lee Mracekcdd19ee2019-01-07 10:19:54 -050070 void Reset() override;
Adam Snaiderb4119252015-02-15 01:30:57 +000071
Philipp Schrader53f4b6d2015-02-15 22:32:08 +000072 // Manually trigger an internal error. This is used for testing the error
73 // logic.
Lee Mracekcdd19ee2019-01-07 10:19:54 -050074 void TriggerError() override;
Philipp Schrader53f4b6d2015-02-15 22:32:08 +000075
Neil Balch1049be92017-02-15 23:20:49 -080076 bool error() const override { return error_; }
Philipp Schrader53f4b6d2015-02-15 22:32:08 +000077
Neil Balch1049be92017-02-15 23:20:49 -080078 bool zeroed() const override { return zeroed_; }
Adam Snaiderb4119252015-02-15 01:30:57 +000079
Isaac Wilcove0851ffd2017-02-16 04:13:14 +000080 double offset() const override { return offset_; }
Adam Snaiderb4119252015-02-15 01:30:57 +000081
82 // Returns a number between 0 and 1 that represents the percentage of the
83 // samples being used in the moving average filter. A value of 0.0 means that
84 // no samples are being used. A value of 1.0 means that the filter is using
85 // as many samples as it has room for. For example, after a Reset() this
86 // value returns 0.0. As more samples get added with UpdateEstimate(...) the
87 // return value starts increasing to 1.0.
Austin Schuh703b8d42015-02-01 14:56:34 -080088 double offset_ratio_ready() const {
Austin Schuh5f01f152017-02-11 21:34:08 -080089 return start_pos_samples_.size() /
90 static_cast<double>(constants_.average_filter_size);
Austin Schuh703b8d42015-02-01 14:56:34 -080091 }
92
Austin Schuh7485dbb2016-02-08 00:21:58 -080093 // Returns true if the sample buffer is full.
Lee Mracekcdd19ee2019-01-07 10:19:54 -050094 bool offset_ready() const override {
Austin Schuh5f01f152017-02-11 21:34:08 -080095 return start_pos_samples_.size() == constants_.average_filter_size;
Austin Schuh7485dbb2016-02-08 00:21:58 -080096 }
97
Brian Silverman4f2e2ce2017-02-19 17:49:47 -080098 // Returns information about our current state.
Lee Mracekcdd19ee2019-01-07 10:19:54 -050099 State GetEstimatorState() const override;
Brian Silverman4f2e2ce2017-02-19 17:49:47 -0800100
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000101 private:
Philipp Schradere828be72015-02-15 07:07:37 +0000102 // This function calculates the start position given the internal state and
103 // the provided `latched_encoder' value.
104 double CalculateStartPosition(double start_average,
105 double latched_encoder) const;
106
Austin Schuh5f01f152017-02-11 21:34:08 -0800107 // The zeroing constants used to describe the configuration of the system.
108 const constants::PotAndIndexPulseZeroingConstants constants_;
109
Adam Snaiderb4119252015-02-15 01:30:57 +0000110 // The estimated position.
Austin Schuh5f01f152017-02-11 21:34:08 -0800111 double position_;
Austin Schuhbe133ed2016-03-11 21:23:34 -0800112 // The unzeroed filtered position.
113 double filtered_position_ = 0.0;
Adam Snaiderb4119252015-02-15 01:30:57 +0000114 // The next position in 'start_pos_samples_' to be used to store the next
115 // sample.
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000116 int samples_idx_;
117 // Last 'max_sample_count_' samples for start positions.
118 std::vector<double> start_pos_samples_;
Adam Snaiderb4119252015-02-15 01:30:57 +0000119 // The estimated starting position of the mechanism. We also call this the
120 // 'offset' in some contexts.
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000121 double offset_;
Philipp Schrader41d82912015-02-15 03:44:23 +0000122 // Flag for triggering logic that takes note of the current index pulse count
Philipp Schradere828be72015-02-15 07:07:37 +0000123 // after a reset. See `last_used_index_pulse_count_'.
Philipp Schrader41d82912015-02-15 03:44:23 +0000124 bool wait_for_index_pulse_;
125 // After a reset we keep track of the index pulse count with this. Only after
126 // the index pulse count changes (i.e. increments at least once or wraps
Philipp Schradere828be72015-02-15 07:07:37 +0000127 // around) will we consider the mechanism zeroed. We also use this to store
128 // the most recent `PotAndIndexPosition::index_pulses' value when the start
129 // position was calculated. It helps us calculate the start position only on
130 // index pulses to reject corrupted intermediate data.
131 uint32_t last_used_index_pulse_count_;
Adam Snaiderb4119252015-02-15 01:30:57 +0000132 // Marker to track whether we're fully zeroed yet or not.
133 bool zeroed_;
Philipp Schrader53f4b6d2015-02-15 22:32:08 +0000134 // Marker to track whether an error has occurred. This gets reset to false
135 // whenever Reset() is called.
136 bool error_;
Adam Snaider3cd11c52015-02-16 02:16:09 +0000137 // Stores the position "start_pos" variable the first time the program
138 // is zeroed.
139 double first_start_pos_;
Austin Schuh5f01f152017-02-11 21:34:08 -0800140};
141
Campbell Crowley36e93e92017-12-23 14:21:43 -0800142// Estimates the position with an incremental encoder and a hall effect sensor.
Lee Mracekcdd19ee2019-01-07 10:19:54 -0500143class HallEffectAndPositionZeroingEstimator
144 : public ZeroingEstimator<HallEffectAndPosition,
145 constants::HallEffectZeroingConstants,
146 HallEffectAndPositionEstimatorState> {
Austin Schuh55934032017-03-11 12:45:27 -0800147 public:
Austin Schuh55934032017-03-11 12:45:27 -0800148 explicit HallEffectAndPositionZeroingEstimator(const ZeroingConstants &constants);
149
150 // Update the internal logic with the next sensor values.
Lee Mracekcdd19ee2019-01-07 10:19:54 -0500151 void UpdateEstimate(const Position &info) override;
Austin Schuh55934032017-03-11 12:45:27 -0800152
153 // Reset the internal logic so it needs to be re-zeroed.
Lee Mracekcdd19ee2019-01-07 10:19:54 -0500154 void Reset() override;
Austin Schuh55934032017-03-11 12:45:27 -0800155
156 // Manually trigger an internal error. This is used for testing the error
157 // logic.
Lee Mracekcdd19ee2019-01-07 10:19:54 -0500158 void TriggerError() override;
Austin Schuh55934032017-03-11 12:45:27 -0800159
160 bool error() const override { return error_; }
161
162 bool zeroed() const override { return zeroed_; }
163
164 double offset() const override { return offset_; }
165
Lee Mracekcdd19ee2019-01-07 10:19:54 -0500166 bool offset_ready() const override { return zeroed_; }
167
Austin Schuh55934032017-03-11 12:45:27 -0800168 // Returns information about our current state.
Lee Mracekcdd19ee2019-01-07 10:19:54 -0500169 State GetEstimatorState() const override;
Austin Schuh55934032017-03-11 12:45:27 -0800170
171 private:
172 // Sets the minimum and maximum posedge position values.
173 void StoreEncoderMaxAndMin(const HallEffectAndPosition &info);
174
175 // The zeroing constants used to describe the configuration of the system.
176 const ZeroingConstants constants_;
177
178 // The estimated state of the hall effect.
179 double current_ = 0.0;
180 // The estimated position.
181 double position_ = 0.0;
182 // The smallest and largest positions of the last set of encoder positions
183 // while the hall effect was low.
184 double min_low_position_;
185 double max_low_position_;
186 // If we've seen the hall effect high for enough times without going low, then
187 // we can be sure it isn't a false positive.
188 bool high_long_enough_;
189 size_t cycles_high_;
190
191 bool last_hall_ = false;
192
193 // The estimated starting position of the mechanism. We also call this the
194 // 'offset' in some contexts.
195 double offset_;
196 // Flag for triggering logic that takes note of the current posedge count
197 // after a reset. See `last_used_posedge_count_'.
198 bool initialized_;
199 // After a reset we keep track of the posedge count with this. Only after the
200 // posedge count changes (i.e. increments at least once or wraps around) will
201 // we consider the mechanism zeroed. We also use this to store the most recent
202 // `HallEffectAndPosition::posedge_count' value when the start position
203 // was calculated. It helps us calculate the start position only on posedges
204 // to reject corrupted intermediate data.
205 int32_t last_used_posedge_count_;
206 // Marker to track whether we're fully zeroed yet or not.
207 bool zeroed_;
208 // Marker to track whether an error has occurred. This gets reset to false
209 // whenever Reset() is called.
210 bool error_ = false;
211 // Stores the position "start_pos" variable the first time the program
212 // is zeroed.
213 double first_start_pos_;
214};
215
Austin Schuh5f01f152017-02-11 21:34:08 -0800216// Estimates the position with an absolute encoder which also reports
217// incremental counts, and a potentiometer.
Lee Mracekcdd19ee2019-01-07 10:19:54 -0500218class PotAndAbsEncoderZeroingEstimator
219 : public ZeroingEstimator<PotAndAbsolutePosition,
220 constants::PotAndAbsoluteEncoderZeroingConstants,
221 AbsoluteEstimatorState> {
Austin Schuh5f01f152017-02-11 21:34:08 -0800222 public:
Campbell Crowley36e93e92017-12-23 14:21:43 -0800223 explicit PotAndAbsEncoderZeroingEstimator(
Austin Schuh5f01f152017-02-11 21:34:08 -0800224 const constants::PotAndAbsoluteEncoderZeroingConstants &constants);
225
226 // Resets the internal logic so it needs to be re-zeroed.
Lee Mracekcdd19ee2019-01-07 10:19:54 -0500227 void Reset() override;
Austin Schuh5f01f152017-02-11 21:34:08 -0800228
229 // Updates the sensor values for the zeroing logic.
Lee Mracekcdd19ee2019-01-07 10:19:54 -0500230 void UpdateEstimate(const PotAndAbsolutePosition &info) override;
231
232 void TriggerError() override { error_ = true; }
Austin Schuh5f01f152017-02-11 21:34:08 -0800233
Neil Balch1049be92017-02-15 23:20:49 -0800234 bool zeroed() const override { return zeroed_; }
Austin Schuh5f01f152017-02-11 21:34:08 -0800235
Neil Balch1049be92017-02-15 23:20:49 -0800236 double offset() const override { return offset_; }
Austin Schuh5f01f152017-02-11 21:34:08 -0800237
Neil Balch16275e32017-02-18 16:38:45 -0800238 bool error() const override { return error_; }
Austin Schuh5f01f152017-02-11 21:34:08 -0800239
240 // Returns true if the sample buffer is full.
Lee Mracekcdd19ee2019-01-07 10:19:54 -0500241 bool offset_ready() const override {
Austin Schuh5f01f152017-02-11 21:34:08 -0800242 return relative_to_absolute_offset_samples_.size() ==
243 constants_.average_filter_size &&
244 offset_samples_.size() == constants_.average_filter_size;
245 }
246
Brian Silverman4f2e2ce2017-02-19 17:49:47 -0800247 // Returns information about our current state.
Lee Mracekcdd19ee2019-01-07 10:19:54 -0500248 State GetEstimatorState() const override;
Austin Schuh5f01f152017-02-11 21:34:08 -0800249
250 private:
251 // The zeroing constants used to describe the configuration of the system.
252 const constants::PotAndAbsoluteEncoderZeroingConstants constants_;
Brian Silvermana10d20a2017-02-19 14:28:53 -0800253
Austin Schuh5f01f152017-02-11 21:34:08 -0800254 // True if the mechanism is zeroed.
255 bool zeroed_;
Brian Silvermana10d20a2017-02-19 14:28:53 -0800256 // Marker to track whether an error has occurred.
257 bool error_;
258 // The first valid offset we recorded. This is only set after zeroed_ first
259 // changes to true.
260 double first_offset_;
261
Austin Schuh0e1c2c62017-02-21 02:03:25 -0800262 // The filtered absolute encoder. This is used in the status for calibration.
263 double filtered_absolute_encoder_ = 0.0;
264
Austin Schuh5f01f152017-02-11 21:34:08 -0800265 // Samples of the offset needed to line the relative encoder up with the
266 // absolute encoder.
267 ::std::vector<double> relative_to_absolute_offset_samples_;
268 // Offset between the Pot and Relative encoder position.
269 ::std::vector<double> offset_samples_;
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800270 // Last moving_buffer_size position samples to be used to determine if the
271 // robot is moving.
272 ::std::vector<PotAndAbsolutePosition> buffered_samples_;
273 // Pointer to front of the buffered samples.
274 int buffered_samples_idx_ = 0;
275 // Estimated offset between the pot and relative encoder.
276 double pot_relative_encoder_offset_ = 0;
Austin Schuh5f01f152017-02-11 21:34:08 -0800277 // Estimated start position of the mechanism
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800278 double offset_ = 0;
Austin Schuh5f01f152017-02-11 21:34:08 -0800279 // The next position in 'relative_to_absolute_offset_samples_' and
280 // 'encoder_samples_' to be used to store the next sample.
Austin Schuhddd08f82018-03-02 20:05:29 -0800281 int samples_idx_ = 0;
282
283 size_t nan_samples_ = 0;
Brian Silvermana10d20a2017-02-19 14:28:53 -0800284
Austin Schuh5f01f152017-02-11 21:34:08 -0800285 // The unzeroed filtered position.
286 double filtered_position_ = 0.0;
287 // The filtered position.
288 double position_ = 0.0;
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000289};
290
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000291// Zeros by seeing all the index pulses in the range of motion of the mechanism
292// and using that to figure out which index pulse is which.
Lee Mracekcdd19ee2019-01-07 10:19:54 -0500293class PulseIndexZeroingEstimator : public ZeroingEstimator<IndexPosition,
294 constants::EncoderPlusIndexZeroingConstants,
295 IndexEstimatorState> {
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000296 public:
Campbell Crowley36e93e92017-12-23 14:21:43 -0800297 explicit PulseIndexZeroingEstimator(const ZeroingConstants &constants)
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000298 : constants_(constants) {
299 Reset();
300 }
301
302 // Resets the internal logic so it needs to be re-zeroed.
Lee Mracekcdd19ee2019-01-07 10:19:54 -0500303 void Reset() override;
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000304
305 bool zeroed() const override { return zeroed_; }
306
Austin Schuh6a90cd92017-02-19 20:55:33 -0800307 // It's as ready as it'll ever be...
Lee Mracekcdd19ee2019-01-07 10:19:54 -0500308 bool offset_ready() const override { return true; }
Austin Schuh6a90cd92017-02-19 20:55:33 -0800309
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000310 double offset() const override { return offset_; }
311
312 bool error() const override { return error_; }
313
314 // Updates the internal logic with the next sensor values.
Lee Mracekcdd19ee2019-01-07 10:19:54 -0500315 void UpdateEstimate(const IndexPosition &info) override;
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000316
Brian Silvermanf37839c2017-02-19 18:07:15 -0800317 // Returns information about our current state.
Lee Mracekcdd19ee2019-01-07 10:19:54 -0500318 State GetEstimatorState() const override;
Brian Silvermanf37839c2017-02-19 18:07:15 -0800319
Lee Mracekcdd19ee2019-01-07 10:19:54 -0500320 void TriggerError() override { error_ = true; }
Austin Schuhd93160a2017-03-05 01:00:54 -0800321
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000322 private:
323 // Returns the current real position using the relative encoder offset.
324 double CalculateCurrentPosition(const IndexPosition &info);
325
326 // Sets the minimum and maximum index pulse position values.
327 void StoreIndexPulseMaxAndMin(const IndexPosition &info);
328
329 // Returns the number of index pulses we should have seen so far.
Brian Silvermanf37839c2017-02-19 18:07:15 -0800330 int IndexPulseCount() const;
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000331
332 // Contains the physical constants describing the system.
Philipp Schrader3f5b6182017-03-25 22:36:37 +0000333 const ZeroingConstants constants_;
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000334
335 // The smallest position of all the index pulses.
336 double min_index_position_;
337 // The largest position of all the index pulses.
338 double max_index_position_;
339
340 // The estimated starting position of the mechanism.
341 double offset_;
342 // After a reset we keep track of the index pulse count with this. Only after
343 // the index pulse count changes (i.e. increments at least once or wraps
344 // around) will we consider the mechanism zeroed. We also use this to store
345 // the most recent `PotAndIndexPosition::index_pulses' value when the start
346 // position was calculated. It helps us calculate the start position only on
347 // index pulses to reject corrupted intermediate data.
348 uint32_t last_used_index_pulse_count_;
349
350 // True if we are fully zeroed.
351 bool zeroed_;
352 // Marker to track whether an error has occurred.
353 bool error_;
354
355 // The estimated position.
356 double position_;
357};
358
Adam Snaiderb4119252015-02-15 01:30:57 +0000359} // namespace zeroing
360} // namespace frc971
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000361
362#endif // FRC971_ZEROING_ZEROING_H_