blob: fe1b4616afe8da74de30c8277f00a084c2c45318 [file] [log] [blame]
Adam Snaiderc4b3c192015-02-01 01:30:39 +00001#ifndef FRC971_ZEROING_ZEROING_H_
2#define FRC971_ZEROING_ZEROING_H_
3
Austin Schuh66c59ba2019-01-26 20:34:35 -08004#include <algorithm>
5#include <cmath>
Philipp Schrader41d82912015-02-15 03:44:23 +00006#include <cstdint>
Adam Snaiderc4b3c192015-02-01 01:30:39 +00007#include <vector>
Philipp Schrader41d82912015-02-15 03:44:23 +00008
Alex Perrycb7da4b2019-08-28 19:35:56 -07009#include "flatbuffers/flatbuffers.h"
Tyler Chatow3c47f3c2020-01-29 20:45:23 -080010#include "frc971/constants.h"
11#include "frc971/control_loops/control_loops_generated.h"
Alex Perrycb7da4b2019-08-28 19:35:56 -070012
Adam Snaiderb4119252015-02-15 01:30:57 +000013// TODO(pschrader): Flag an error if encoder index pulse is not n revolutions
14// away from the last one (i.e. got extra counts from noise, etc..)
15//
16// TODO(pschrader): Flag error if the pot disagrees too much with the encoder
17// after being zeroed.
18//
19// TODO(pschrader): Watch the offset over long periods of time and flag if it
20// gets too far away from the initial value.
21
Adam Snaiderc4b3c192015-02-01 01:30:39 +000022namespace frc971 {
23namespace zeroing {
24
Lee Mracekcdd19ee2019-01-07 10:19:54 -050025template <typename TPosition, typename TZeroingConstants, typename TState>
Neil Balch1049be92017-02-15 23:20:49 -080026class ZeroingEstimator {
27 public:
Lee Mracekcdd19ee2019-01-07 10:19:54 -050028 using Position = TPosition;
29 using ZeroingConstants = TZeroingConstants;
30 using State = TState;
Tyler Chatow3c47f3c2020-01-29 20:45:23 -080031 virtual ~ZeroingEstimator() {}
Neil Balch1049be92017-02-15 23:20:49 -080032
33 // Returns true if the logic considers the corresponding mechanism to be
Isaac Wilcove0851ffd2017-02-16 04:13:14 +000034 // zeroed.
Neil Balch1049be92017-02-15 23:20:49 -080035 virtual bool zeroed() const = 0;
36
Isaac Wilcove0851ffd2017-02-16 04:13:14 +000037 // Returns the estimated position of the corresponding mechanism.
Neil Balch1049be92017-02-15 23:20:49 -080038 virtual double offset() const = 0;
39
Isaac Wilcove0851ffd2017-02-16 04:13:14 +000040 // Returns true if there has been an error.
Neil Balch1049be92017-02-15 23:20:49 -080041 virtual bool error() const = 0;
Lee Mracekcdd19ee2019-01-07 10:19:54 -050042
43 // Returns true if an offset is ready.
44 virtual bool offset_ready() const = 0;
45
46 // Triggers an internal error. This is used for testing the error
47 // logic.
48 virtual void TriggerError() = 0;
49
50 // Resets the estimator, clearing error and zeroing states.
51 virtual void Reset() = 0;
52
53 // Updates the internal logic with new sensor values
54 virtual void UpdateEstimate(const Position &) = 0;
55
56 // Returns the state of the estimator
Alex Perrycb7da4b2019-08-28 19:35:56 -070057 virtual flatbuffers::Offset<State> GetEstimatorState(
58 flatbuffers::FlatBufferBuilder *fbb) const = 0;
Neil Balch1049be92017-02-15 23:20:49 -080059};
60
Brian Silvermanab0b6772017-02-05 16:16:21 -080061// Estimates the position with an incremental encoder with an index pulse and a
62// potentiometer.
Tyler Chatow3c47f3c2020-01-29 20:45:23 -080063class PotAndIndexPulseZeroingEstimator
64 : public ZeroingEstimator<PotAndIndexPosition,
65 constants::PotAndIndexPulseZeroingConstants,
66 EstimatorState> {
Adam Snaiderc4b3c192015-02-01 01:30:39 +000067 public:
Campbell Crowley36e93e92017-12-23 14:21:43 -080068 explicit PotAndIndexPulseZeroingEstimator(
Tyler Chatowf8f03112017-02-05 14:31:34 -080069 const constants::PotAndIndexPulseZeroingConstants &constants);
Austin Schuh703b8d42015-02-01 14:56:34 -080070
Adam Snaiderb4119252015-02-15 01:30:57 +000071 // Update the internal logic with the next sensor values.
Lee Mracekcdd19ee2019-01-07 10:19:54 -050072 void UpdateEstimate(const PotAndIndexPosition &info) override;
Adam Snaiderb4119252015-02-15 01:30:57 +000073
74 // Reset the internal logic so it needs to be re-zeroed.
Lee Mracekcdd19ee2019-01-07 10:19:54 -050075 void Reset() override;
Adam Snaiderb4119252015-02-15 01:30:57 +000076
Philipp Schrader53f4b6d2015-02-15 22:32:08 +000077 // Manually trigger an internal error. This is used for testing the error
78 // logic.
Lee Mracekcdd19ee2019-01-07 10:19:54 -050079 void TriggerError() override;
Philipp Schrader53f4b6d2015-02-15 22:32:08 +000080
Neil Balch1049be92017-02-15 23:20:49 -080081 bool error() const override { return error_; }
Philipp Schrader53f4b6d2015-02-15 22:32:08 +000082
Neil Balch1049be92017-02-15 23:20:49 -080083 bool zeroed() const override { return zeroed_; }
Adam Snaiderb4119252015-02-15 01:30:57 +000084
Isaac Wilcove0851ffd2017-02-16 04:13:14 +000085 double offset() const override { return offset_; }
Adam Snaiderb4119252015-02-15 01:30:57 +000086
87 // Returns a number between 0 and 1 that represents the percentage of the
88 // samples being used in the moving average filter. A value of 0.0 means that
89 // no samples are being used. A value of 1.0 means that the filter is using
90 // as many samples as it has room for. For example, after a Reset() this
91 // value returns 0.0. As more samples get added with UpdateEstimate(...) the
92 // return value starts increasing to 1.0.
Austin Schuh703b8d42015-02-01 14:56:34 -080093 double offset_ratio_ready() const {
Austin Schuh5f01f152017-02-11 21:34:08 -080094 return start_pos_samples_.size() /
95 static_cast<double>(constants_.average_filter_size);
Austin Schuh703b8d42015-02-01 14:56:34 -080096 }
97
Austin Schuh7485dbb2016-02-08 00:21:58 -080098 // Returns true if the sample buffer is full.
Lee Mracekcdd19ee2019-01-07 10:19:54 -050099 bool offset_ready() const override {
Austin Schuh5f01f152017-02-11 21:34:08 -0800100 return start_pos_samples_.size() == constants_.average_filter_size;
Austin Schuh7485dbb2016-02-08 00:21:58 -0800101 }
102
Brian Silverman4f2e2ce2017-02-19 17:49:47 -0800103 // Returns information about our current state.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700104 virtual flatbuffers::Offset<State> GetEstimatorState(
105 flatbuffers::FlatBufferBuilder *fbb) const override;
Brian Silverman4f2e2ce2017-02-19 17:49:47 -0800106
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000107 private:
Philipp Schradere828be72015-02-15 07:07:37 +0000108 // This function calculates the start position given the internal state and
109 // the provided `latched_encoder' value.
110 double CalculateStartPosition(double start_average,
111 double latched_encoder) const;
112
Austin Schuh5f01f152017-02-11 21:34:08 -0800113 // The zeroing constants used to describe the configuration of the system.
114 const constants::PotAndIndexPulseZeroingConstants constants_;
115
Adam Snaiderb4119252015-02-15 01:30:57 +0000116 // The estimated position.
Austin Schuh5f01f152017-02-11 21:34:08 -0800117 double position_;
Austin Schuhbe133ed2016-03-11 21:23:34 -0800118 // The unzeroed filtered position.
119 double filtered_position_ = 0.0;
Adam Snaiderb4119252015-02-15 01:30:57 +0000120 // The next position in 'start_pos_samples_' to be used to store the next
121 // sample.
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000122 int samples_idx_;
123 // Last 'max_sample_count_' samples for start positions.
124 std::vector<double> start_pos_samples_;
Adam Snaiderb4119252015-02-15 01:30:57 +0000125 // The estimated starting position of the mechanism. We also call this the
126 // 'offset' in some contexts.
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000127 double offset_;
Philipp Schrader41d82912015-02-15 03:44:23 +0000128 // Flag for triggering logic that takes note of the current index pulse count
Philipp Schradere828be72015-02-15 07:07:37 +0000129 // after a reset. See `last_used_index_pulse_count_'.
Philipp Schrader41d82912015-02-15 03:44:23 +0000130 bool wait_for_index_pulse_;
131 // After a reset we keep track of the index pulse count with this. Only after
132 // the index pulse count changes (i.e. increments at least once or wraps
Philipp Schradere828be72015-02-15 07:07:37 +0000133 // around) will we consider the mechanism zeroed. We also use this to store
134 // the most recent `PotAndIndexPosition::index_pulses' value when the start
135 // position was calculated. It helps us calculate the start position only on
136 // index pulses to reject corrupted intermediate data.
137 uint32_t last_used_index_pulse_count_;
Adam Snaiderb4119252015-02-15 01:30:57 +0000138 // Marker to track whether we're fully zeroed yet or not.
139 bool zeroed_;
Philipp Schrader53f4b6d2015-02-15 22:32:08 +0000140 // Marker to track whether an error has occurred. This gets reset to false
141 // whenever Reset() is called.
142 bool error_;
Adam Snaider3cd11c52015-02-16 02:16:09 +0000143 // Stores the position "start_pos" variable the first time the program
144 // is zeroed.
145 double first_start_pos_;
Austin Schuh5f01f152017-02-11 21:34:08 -0800146};
147
Campbell Crowley36e93e92017-12-23 14:21:43 -0800148// Estimates the position with an incremental encoder and a hall effect sensor.
Lee Mracekcdd19ee2019-01-07 10:19:54 -0500149class HallEffectAndPositionZeroingEstimator
150 : public ZeroingEstimator<HallEffectAndPosition,
Tyler Chatow3c47f3c2020-01-29 20:45:23 -0800151 constants::HallEffectZeroingConstants,
152 HallEffectAndPositionEstimatorState> {
Austin Schuh55934032017-03-11 12:45:27 -0800153 public:
Tyler Chatow3c47f3c2020-01-29 20:45:23 -0800154 explicit HallEffectAndPositionZeroingEstimator(
155 const ZeroingConstants &constants);
Austin Schuh55934032017-03-11 12:45:27 -0800156
157 // Update the internal logic with the next sensor values.
Lee Mracekcdd19ee2019-01-07 10:19:54 -0500158 void UpdateEstimate(const Position &info) override;
Austin Schuh55934032017-03-11 12:45:27 -0800159
160 // Reset the internal logic so it needs to be re-zeroed.
Lee Mracekcdd19ee2019-01-07 10:19:54 -0500161 void Reset() override;
Austin Schuh55934032017-03-11 12:45:27 -0800162
163 // Manually trigger an internal error. This is used for testing the error
164 // logic.
Lee Mracekcdd19ee2019-01-07 10:19:54 -0500165 void TriggerError() override;
Austin Schuh55934032017-03-11 12:45:27 -0800166
167 bool error() const override { return error_; }
168
169 bool zeroed() const override { return zeroed_; }
170
171 double offset() const override { return offset_; }
172
Lee Mracekcdd19ee2019-01-07 10:19:54 -0500173 bool offset_ready() const override { return zeroed_; }
174
Austin Schuh55934032017-03-11 12:45:27 -0800175 // Returns information about our current state.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700176 virtual flatbuffers::Offset<State> GetEstimatorState(
177 flatbuffers::FlatBufferBuilder *fbb) const override;
Austin Schuh55934032017-03-11 12:45:27 -0800178
179 private:
180 // Sets the minimum and maximum posedge position values.
181 void StoreEncoderMaxAndMin(const HallEffectAndPosition &info);
182
183 // The zeroing constants used to describe the configuration of the system.
184 const ZeroingConstants constants_;
185
186 // The estimated state of the hall effect.
187 double current_ = 0.0;
188 // The estimated position.
189 double position_ = 0.0;
190 // The smallest and largest positions of the last set of encoder positions
191 // while the hall effect was low.
192 double min_low_position_;
193 double max_low_position_;
194 // If we've seen the hall effect high for enough times without going low, then
195 // we can be sure it isn't a false positive.
196 bool high_long_enough_;
197 size_t cycles_high_;
198
199 bool last_hall_ = false;
200
201 // The estimated starting position of the mechanism. We also call this the
202 // 'offset' in some contexts.
203 double offset_;
204 // Flag for triggering logic that takes note of the current posedge count
205 // after a reset. See `last_used_posedge_count_'.
206 bool initialized_;
207 // After a reset we keep track of the posedge count with this. Only after the
208 // posedge count changes (i.e. increments at least once or wraps around) will
209 // we consider the mechanism zeroed. We also use this to store the most recent
210 // `HallEffectAndPosition::posedge_count' value when the start position
211 // was calculated. It helps us calculate the start position only on posedges
212 // to reject corrupted intermediate data.
213 int32_t last_used_posedge_count_;
214 // Marker to track whether we're fully zeroed yet or not.
215 bool zeroed_;
216 // Marker to track whether an error has occurred. This gets reset to false
217 // whenever Reset() is called.
218 bool error_ = false;
219 // Stores the position "start_pos" variable the first time the program
220 // is zeroed.
221 double first_start_pos_;
222};
223
Austin Schuh66c59ba2019-01-26 20:34:35 -0800224// Class to encapsulate the logic to decide when we are moving and which samples
225// are safe to use.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700226template <typename Position, typename PositionBuffer>
Austin Schuh66c59ba2019-01-26 20:34:35 -0800227class MoveDetector {
228 public:
229 MoveDetector(size_t filter_size) {
230 buffered_samples_.reserve(filter_size);
231 Reset();
232 }
233
234 // Clears all the state.
235 void Reset() {
236 buffered_samples_.clear();
237 buffered_samples_idx_ = 0;
238 }
239
240 // Updates the detector with a new sample. Returns true if we are moving.
241 // buffer_size is the number of samples in the moving buffer, and
242 // zeroing_threshold is the max amount we can move within the period specified
243 // by buffer_size.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700244 bool Update(const PositionBuffer &position_buffer, size_t buffer_size,
Austin Schuh66c59ba2019-01-26 20:34:35 -0800245 double zeroing_threshold) {
246 bool moving = true;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700247 Position position(position_buffer);
Austin Schuh66c59ba2019-01-26 20:34:35 -0800248 if (buffered_samples_.size() < buffer_size) {
249 // Not enough samples to start determining if the robot is moving or not,
250 // don't use the samples yet.
251 buffered_samples_.push_back(position);
252 } else {
253 // Have enough samples to start determining if the robot is moving or not.
254 buffered_samples_[buffered_samples_idx_] = position;
255 const auto minmax_value = ::std::minmax_element(
256 buffered_samples_.begin(), buffered_samples_.end(),
257 [](const Position &left, const Position &right) {
258 return left.encoder < right.encoder;
259 });
260 const double min_value = minmax_value.first->encoder;
261 const double max_value = minmax_value.second->encoder;
262
263 if (::std::abs(max_value - min_value) < zeroing_threshold) {
264 // Robot isn't moving, use middle sample to determine offsets.
265 moving = false;
266 }
267 }
268 buffered_samples_idx_ = (buffered_samples_idx_ + 1) % buffer_size;
269 return moving;
270 }
271
272 // Returns a safe sample if we aren't moving as reported by Update.
273 const Position &GetSample() const {
274 // The robot is not moving, use the middle sample to determine offsets.
275 // The middle sample makes it so that we don't use the samples from the
276 // beginning or end of periods when the robot is moving.
277 const int middle_index =
278 (buffered_samples_idx_ + (buffered_samples_.size() / 2)) %
279 buffered_samples_.size();
280 return buffered_samples_[middle_index];
281 }
282
283 private:
284 // The last buffer_size samples.
285 ::std::vector<Position> buffered_samples_;
286 // The index to place the next sample in.
287 size_t buffered_samples_idx_;
288};
289
Austin Schuh5f01f152017-02-11 21:34:08 -0800290// Estimates the position with an absolute encoder which also reports
291// incremental counts, and a potentiometer.
Austin Schuh72db9a12019-01-21 18:02:51 -0800292class PotAndAbsoluteEncoderZeroingEstimator
Lee Mracekcdd19ee2019-01-07 10:19:54 -0500293 : public ZeroingEstimator<PotAndAbsolutePosition,
Austin Schuh72db9a12019-01-21 18:02:51 -0800294 constants::PotAndAbsoluteEncoderZeroingConstants,
295 PotAndAbsoluteEncoderEstimatorState> {
Austin Schuh5f01f152017-02-11 21:34:08 -0800296 public:
Austin Schuh72db9a12019-01-21 18:02:51 -0800297 explicit PotAndAbsoluteEncoderZeroingEstimator(
Austin Schuh5f01f152017-02-11 21:34:08 -0800298 const constants::PotAndAbsoluteEncoderZeroingConstants &constants);
299
300 // Resets the internal logic so it needs to be re-zeroed.
Lee Mracekcdd19ee2019-01-07 10:19:54 -0500301 void Reset() override;
Austin Schuh5f01f152017-02-11 21:34:08 -0800302
303 // Updates the sensor values for the zeroing logic.
Lee Mracekcdd19ee2019-01-07 10:19:54 -0500304 void UpdateEstimate(const PotAndAbsolutePosition &info) override;
305
306 void TriggerError() override { error_ = true; }
Austin Schuh5f01f152017-02-11 21:34:08 -0800307
Neil Balch1049be92017-02-15 23:20:49 -0800308 bool zeroed() const override { return zeroed_; }
Austin Schuh5f01f152017-02-11 21:34:08 -0800309
Neil Balch1049be92017-02-15 23:20:49 -0800310 double offset() const override { return offset_; }
Austin Schuh5f01f152017-02-11 21:34:08 -0800311
Neil Balch16275e32017-02-18 16:38:45 -0800312 bool error() const override { return error_; }
Austin Schuh5f01f152017-02-11 21:34:08 -0800313
314 // Returns true if the sample buffer is full.
Lee Mracekcdd19ee2019-01-07 10:19:54 -0500315 bool offset_ready() const override {
Austin Schuh5f01f152017-02-11 21:34:08 -0800316 return relative_to_absolute_offset_samples_.size() ==
317 constants_.average_filter_size &&
318 offset_samples_.size() == constants_.average_filter_size;
319 }
320
Brian Silverman4f2e2ce2017-02-19 17:49:47 -0800321 // Returns information about our current state.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700322 virtual flatbuffers::Offset<State> GetEstimatorState(
323 flatbuffers::FlatBufferBuilder *fbb) const override;
Austin Schuh5f01f152017-02-11 21:34:08 -0800324
325 private:
Alex Perrycb7da4b2019-08-28 19:35:56 -0700326 struct PositionStruct {
327 PositionStruct(const PotAndAbsolutePosition &position_buffer)
328 : absolute_encoder(position_buffer.absolute_encoder()),
329 encoder(position_buffer.encoder()),
330 pot(position_buffer.pot()) {}
331 double absolute_encoder;
332 double encoder;
333 double pot;
334 };
335
Austin Schuh5f01f152017-02-11 21:34:08 -0800336 // The zeroing constants used to describe the configuration of the system.
337 const constants::PotAndAbsoluteEncoderZeroingConstants constants_;
Brian Silvermana10d20a2017-02-19 14:28:53 -0800338
Austin Schuh5f01f152017-02-11 21:34:08 -0800339 // True if the mechanism is zeroed.
340 bool zeroed_;
Brian Silvermana10d20a2017-02-19 14:28:53 -0800341 // Marker to track whether an error has occurred.
342 bool error_;
343 // The first valid offset we recorded. This is only set after zeroed_ first
344 // changes to true.
345 double first_offset_;
346
Austin Schuh0e1c2c62017-02-21 02:03:25 -0800347 // The filtered absolute encoder. This is used in the status for calibration.
348 double filtered_absolute_encoder_ = 0.0;
349
Austin Schuh5f01f152017-02-11 21:34:08 -0800350 // Samples of the offset needed to line the relative encoder up with the
351 // absolute encoder.
352 ::std::vector<double> relative_to_absolute_offset_samples_;
353 // Offset between the Pot and Relative encoder position.
354 ::std::vector<double> offset_samples_;
Austin Schuh66c59ba2019-01-26 20:34:35 -0800355
Alex Perrycb7da4b2019-08-28 19:35:56 -0700356 MoveDetector<PositionStruct, PotAndAbsolutePosition> move_detector_;
Austin Schuh66c59ba2019-01-26 20:34:35 -0800357
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800358 // Estimated offset between the pot and relative encoder.
359 double pot_relative_encoder_offset_ = 0;
Austin Schuh5f01f152017-02-11 21:34:08 -0800360 // Estimated start position of the mechanism
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800361 double offset_ = 0;
Austin Schuh5f01f152017-02-11 21:34:08 -0800362 // The next position in 'relative_to_absolute_offset_samples_' and
363 // 'encoder_samples_' to be used to store the next sample.
Austin Schuhddd08f82018-03-02 20:05:29 -0800364 int samples_idx_ = 0;
365
366 size_t nan_samples_ = 0;
Brian Silvermana10d20a2017-02-19 14:28:53 -0800367
Austin Schuh5f01f152017-02-11 21:34:08 -0800368 // The unzeroed filtered position.
369 double filtered_position_ = 0.0;
370 // The filtered position.
371 double position_ = 0.0;
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000372};
373
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000374// Zeros by seeing all the index pulses in the range of motion of the mechanism
375// and using that to figure out which index pulse is which.
Tyler Chatow3c47f3c2020-01-29 20:45:23 -0800376class PulseIndexZeroingEstimator
377 : public ZeroingEstimator<IndexPosition,
378 constants::EncoderPlusIndexZeroingConstants,
379 IndexEstimatorState> {
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000380 public:
Campbell Crowley36e93e92017-12-23 14:21:43 -0800381 explicit PulseIndexZeroingEstimator(const ZeroingConstants &constants)
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000382 : constants_(constants) {
383 Reset();
384 }
385
386 // Resets the internal logic so it needs to be re-zeroed.
Lee Mracekcdd19ee2019-01-07 10:19:54 -0500387 void Reset() override;
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000388
389 bool zeroed() const override { return zeroed_; }
390
Austin Schuh6a90cd92017-02-19 20:55:33 -0800391 // It's as ready as it'll ever be...
Lee Mracekcdd19ee2019-01-07 10:19:54 -0500392 bool offset_ready() const override { return true; }
Austin Schuh6a90cd92017-02-19 20:55:33 -0800393
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000394 double offset() const override { return offset_; }
395
396 bool error() const override { return error_; }
397
398 // Updates the internal logic with the next sensor values.
Lee Mracekcdd19ee2019-01-07 10:19:54 -0500399 void UpdateEstimate(const IndexPosition &info) override;
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000400
Brian Silvermanf37839c2017-02-19 18:07:15 -0800401 // Returns information about our current state.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700402 virtual flatbuffers::Offset<State> GetEstimatorState(
403 flatbuffers::FlatBufferBuilder *fbb) const override;
Brian Silvermanf37839c2017-02-19 18:07:15 -0800404
Lee Mracekcdd19ee2019-01-07 10:19:54 -0500405 void TriggerError() override { error_ = true; }
Austin Schuhd93160a2017-03-05 01:00:54 -0800406
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000407 private:
408 // Returns the current real position using the relative encoder offset.
409 double CalculateCurrentPosition(const IndexPosition &info);
410
411 // Sets the minimum and maximum index pulse position values.
412 void StoreIndexPulseMaxAndMin(const IndexPosition &info);
413
414 // Returns the number of index pulses we should have seen so far.
Brian Silvermanf37839c2017-02-19 18:07:15 -0800415 int IndexPulseCount() const;
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000416
417 // Contains the physical constants describing the system.
Philipp Schrader3f5b6182017-03-25 22:36:37 +0000418 const ZeroingConstants constants_;
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000419
420 // The smallest position of all the index pulses.
421 double min_index_position_;
422 // The largest position of all the index pulses.
423 double max_index_position_;
424
425 // The estimated starting position of the mechanism.
426 double offset_;
427 // After a reset we keep track of the index pulse count with this. Only after
428 // the index pulse count changes (i.e. increments at least once or wraps
429 // around) will we consider the mechanism zeroed. We also use this to store
430 // the most recent `PotAndIndexPosition::index_pulses' value when the start
431 // position was calculated. It helps us calculate the start position only on
432 // index pulses to reject corrupted intermediate data.
433 uint32_t last_used_index_pulse_count_;
434
435 // True if we are fully zeroed.
436 bool zeroed_;
437 // Marker to track whether an error has occurred.
438 bool error_;
439
440 // The estimated position.
441 double position_;
442};
443
Austin Schuhd82068e2019-01-26 20:05:42 -0800444// Estimates the position with an absolute encoder which also reports
445// incremental counts. The absolute encoder can't spin more than one
446// revolution.
447class AbsoluteEncoderZeroingEstimator
448 : public ZeroingEstimator<AbsolutePosition,
449 constants::AbsoluteEncoderZeroingConstants,
450 AbsoluteEncoderEstimatorState> {
451 public:
452 explicit AbsoluteEncoderZeroingEstimator(
453 const constants::AbsoluteEncoderZeroingConstants &constants);
454
455 // Resets the internal logic so it needs to be re-zeroed.
456 void Reset() override;
457
458 // Updates the sensor values for the zeroing logic.
459 void UpdateEstimate(const AbsolutePosition &info) override;
460
461 void TriggerError() override { error_ = true; }
462
463 bool zeroed() const override { return zeroed_; }
464
465 double offset() const override { return offset_; }
466
467 bool error() const override { return error_; }
468
469 // Returns true if the sample buffer is full.
470 bool offset_ready() const override {
471 return relative_to_absolute_offset_samples_.size() ==
472 constants_.average_filter_size;
473 }
474
475 // Returns information about our current state.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700476 virtual flatbuffers::Offset<State> GetEstimatorState(
477 flatbuffers::FlatBufferBuilder *fbb) const override;
Austin Schuhd82068e2019-01-26 20:05:42 -0800478
479 private:
Alex Perrycb7da4b2019-08-28 19:35:56 -0700480 struct PositionStruct {
481 PositionStruct(const AbsolutePosition &position_buffer)
482 : absolute_encoder(position_buffer.absolute_encoder()),
483 encoder(position_buffer.encoder()) {}
484 double absolute_encoder;
485 double encoder;
486 };
487
Austin Schuhd82068e2019-01-26 20:05:42 -0800488 // The zeroing constants used to describe the configuration of the system.
489 const constants::AbsoluteEncoderZeroingConstants constants_;
490
491 // True if the mechanism is zeroed.
492 bool zeroed_;
493 // Marker to track whether an error has occurred.
494 bool error_;
495 // The first valid offset we recorded. This is only set after zeroed_ first
496 // changes to true.
497 double first_offset_;
498
499 // The filtered absolute encoder. This is used in the status for calibration.
500 double filtered_absolute_encoder_ = 0.0;
501
502 // Samples of the offset needed to line the relative encoder up with the
503 // absolute encoder.
504 ::std::vector<double> relative_to_absolute_offset_samples_;
505
Alex Perrycb7da4b2019-08-28 19:35:56 -0700506 MoveDetector<PositionStruct, AbsolutePosition> move_detector_;
Austin Schuhd82068e2019-01-26 20:05:42 -0800507
508 // Estimated start position of the mechanism
509 double offset_ = 0;
510 // The next position in 'relative_to_absolute_offset_samples_' and
511 // 'encoder_samples_' to be used to store the next sample.
512 int samples_idx_ = 0;
513
514 // Number of NANs we've seen in a row.
515 size_t nan_samples_ = 0;
516
517 // The filtered position.
518 double position_ = 0.0;
519};
520
Tyler Chatow3c47f3c2020-01-29 20:45:23 -0800521class RelativeEncoderZeroingEstimator
522 : public ZeroingEstimator<RelativePosition, void,
523 RelativeEncoderEstimatorState> {
524 public:
525 explicit RelativeEncoderZeroingEstimator() {}
526
527 // Update position with new position from encoder
528 void UpdateEstimate(const RelativePosition &position) override {
529 position_ = position.encoder();
530 }
531
532 // We alre always zeroed
533 bool zeroed() const override { return true; }
534
535 // Starting position of the joint
536 double offset() const override { return 0; }
537
538 // Has an error occured? Note: Only triggered by TriggerError()
539 bool error() const override { return error_; }
540
541 // Offset is always ready, since we are always zeroed.
542 bool offset_ready() const override { return true; }
543
544 void TriggerError() override { error_ = true; }
545
546 void Reset() override { error_ = false; }
547
548 flatbuffers::Offset<State> GetEstimatorState(
549 flatbuffers::FlatBufferBuilder *fbb) const override {
550 State::Builder builder(*fbb);
551 builder.add_error(error_);
552 builder.add_position(position_);
553 return builder.Finish();
554 }
555
556 private:
557 // Position from encoder relative to start
558 double position_ = 0;
559 bool error_ = false;
560};
561
Adam Snaiderb4119252015-02-15 01:30:57 +0000562} // namespace zeroing
563} // namespace frc971
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000564
565#endif // FRC971_ZEROING_ZEROING_H_