blob: 5cab5dbb94ab0b0cb317cbb50278e8ad3df514cb [file] [log] [blame]
Adam Snaiderc4b3c192015-02-01 01:30:39 +00001#include "frc971/zeroing/zeroing.h"
Adam Snaiderb4119252015-02-15 01:30:57 +00002
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -08003#include <algorithm>
Isaac Wilcove0851ffd2017-02-16 04:13:14 +00004#include <cmath>
5#include <limits>
Austin Schuha8f88d42019-01-26 12:33:54 -08006#include <numeric>
Isaac Wilcove0851ffd2017-02-16 04:13:14 +00007#include <vector>
Adam Snaiderc4b3c192015-02-01 01:30:39 +00008
Austin Schuh5f01f152017-02-11 21:34:08 -08009#include "frc971/zeroing/wrap.h"
10
Adam Snaiderc4b3c192015-02-01 01:30:39 +000011namespace frc971 {
12namespace zeroing {
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -080013namespace {
14
15bool compare_encoder(const PotAndAbsolutePosition &left,
16 const PotAndAbsolutePosition &right) {
17 return left.encoder < right.encoder;
18}
19
20} // namespace
Adam Snaiderc4b3c192015-02-01 01:30:39 +000021
Tyler Chatowf8f03112017-02-05 14:31:34 -080022PotAndIndexPulseZeroingEstimator::PotAndIndexPulseZeroingEstimator(
Austin Schuh5f01f152017-02-11 21:34:08 -080023 const constants::PotAndIndexPulseZeroingConstants &constants)
24 : constants_(constants) {
25 start_pos_samples_.reserve(constants_.average_filter_size);
Adam Snaiderb4119252015-02-15 01:30:57 +000026 Reset();
Austin Schuh703b8d42015-02-01 14:56:34 -080027}
28
Tyler Chatowf8f03112017-02-05 14:31:34 -080029void PotAndIndexPulseZeroingEstimator::Reset() {
Adam Snaiderc4b3c192015-02-01 01:30:39 +000030 samples_idx_ = 0;
Isaac Wilcove0851ffd2017-02-16 04:13:14 +000031 offset_ = 0;
Adam Snaiderb4119252015-02-15 01:30:57 +000032 start_pos_samples_.clear();
33 zeroed_ = false;
Philipp Schrader41d82912015-02-15 03:44:23 +000034 wait_for_index_pulse_ = true;
Philipp Schradere828be72015-02-15 07:07:37 +000035 last_used_index_pulse_count_ = 0;
Philipp Schrader53f4b6d2015-02-15 22:32:08 +000036 error_ = false;
37}
38
Tyler Chatowf8f03112017-02-05 14:31:34 -080039void PotAndIndexPulseZeroingEstimator::TriggerError() {
Philipp Schrader53f4b6d2015-02-15 22:32:08 +000040 if (!error_) {
41 LOG(ERROR, "Manually triggered zeroing error.\n");
42 error_ = true;
43 }
Philipp Schradere828be72015-02-15 07:07:37 +000044}
45
Tyler Chatowf8f03112017-02-05 14:31:34 -080046double PotAndIndexPulseZeroingEstimator::CalculateStartPosition(
47 double start_average, double latched_encoder) const {
Philipp Schradere828be72015-02-15 07:07:37 +000048 // We calculate an aproximation of the value of the last index position.
49 // Also account for index pulses not lining up with integer multiples of the
50 // index_diff.
Austin Schuh5f01f152017-02-11 21:34:08 -080051 double index_pos =
52 start_average + latched_encoder - constants_.measured_index_position;
Philipp Schradere828be72015-02-15 07:07:37 +000053 // We round index_pos to the closest valid value of the index.
Austin Schuh5f01f152017-02-11 21:34:08 -080054 double accurate_index_pos = (round(index_pos / constants_.index_difference)) *
55 constants_.index_difference;
Philipp Schradere828be72015-02-15 07:07:37 +000056 // Now we reverse the first calculation to get the accurate start position.
Austin Schuh5f01f152017-02-11 21:34:08 -080057 return accurate_index_pos - latched_encoder +
58 constants_.measured_index_position;
Adam Snaiderc4b3c192015-02-01 01:30:39 +000059}
60
Tyler Chatowf8f03112017-02-05 14:31:34 -080061void PotAndIndexPulseZeroingEstimator::UpdateEstimate(
62 const PotAndIndexPosition &info) {
Philipp Schrader41d82912015-02-15 03:44:23 +000063 // We want to make sure that we encounter at least one index pulse while
64 // zeroing. So we take the index pulse count from the first sample after
65 // reset and wait for that count to change before we consider ourselves
66 // zeroed.
67 if (wait_for_index_pulse_) {
Philipp Schradere828be72015-02-15 07:07:37 +000068 last_used_index_pulse_count_ = info.index_pulses;
Philipp Schrader41d82912015-02-15 03:44:23 +000069 wait_for_index_pulse_ = false;
70 }
71
Austin Schuh5f01f152017-02-11 21:34:08 -080072 if (start_pos_samples_.size() < constants_.average_filter_size) {
Adam Snaiderc4b3c192015-02-01 01:30:39 +000073 start_pos_samples_.push_back(info.pot - info.encoder);
74 } else {
75 start_pos_samples_[samples_idx_] = info.pot - info.encoder;
76 }
Adam Snaiderb4119252015-02-15 01:30:57 +000077
78 // Drop the oldest sample when we run this function the next time around.
Austin Schuh5f01f152017-02-11 21:34:08 -080079 samples_idx_ = (samples_idx_ + 1) % constants_.average_filter_size;
Adam Snaiderc4b3c192015-02-01 01:30:39 +000080
Adam Snaiderb4119252015-02-15 01:30:57 +000081 double sample_sum = 0.0;
82
Adam Snaiderc4b3c192015-02-01 01:30:39 +000083 for (size_t i = 0; i < start_pos_samples_.size(); ++i) {
Adam Snaiderb4119252015-02-15 01:30:57 +000084 sample_sum += start_pos_samples_[i];
Adam Snaiderc4b3c192015-02-01 01:30:39 +000085 }
86
87 // Calculates the average of the starting position.
Adam Snaiderb4119252015-02-15 01:30:57 +000088 double start_average = sample_sum / start_pos_samples_.size();
89
90 // If there are no index pulses to use or we don't have enough samples yet to
91 // have a well-filtered starting position then we use the filtered value as
92 // our best guess.
Austin Schuh7485dbb2016-02-08 00:21:58 -080093 if (!zeroed_ &&
94 (info.index_pulses == last_used_index_pulse_count_ || !offset_ready())) {
Isaac Wilcove0851ffd2017-02-16 04:13:14 +000095 offset_ = start_average;
Philipp Schradere828be72015-02-15 07:07:37 +000096 } else if (!zeroed_ || last_used_index_pulse_count_ != info.index_pulses) {
97 // Note the accurate start position and the current index pulse count so
98 // that we only run this logic once per index pulse. That should be more
99 // resilient to corrupted intermediate data.
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000100 offset_ = CalculateStartPosition(start_average, info.latched_encoder);
Philipp Schradere828be72015-02-15 07:07:37 +0000101 last_used_index_pulse_count_ = info.index_pulses;
Austin Schuh7485dbb2016-02-08 00:21:58 -0800102
103 // TODO(austin): Reject encoder positions which have x% error rather than
104 // rounding to the closest index pulse.
105
Adam Snaider3cd11c52015-02-16 02:16:09 +0000106 // Save the first starting position.
107 if (!zeroed_) {
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000108 first_start_pos_ = offset_;
Adam Snaider3cd11c52015-02-16 02:16:09 +0000109 LOG(INFO, "latching start position %f\n", first_start_pos_);
110 }
Adam Snaiderb4119252015-02-15 01:30:57 +0000111
112 // Now that we have an accurate starting position we can consider ourselves
113 // zeroed.
Austin Schuh703b8d42015-02-01 14:56:34 -0800114 zeroed_ = true;
Adam Snaider3cd11c52015-02-16 02:16:09 +0000115 // Throw an error if first_start_pos is bigger/smaller than
Austin Schuh5f01f152017-02-11 21:34:08 -0800116 // constants_.allowable_encoder_error * index_diff + start_pos.
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000117 if (::std::abs(first_start_pos_ - offset_) >
Austin Schuh5f01f152017-02-11 21:34:08 -0800118 constants_.allowable_encoder_error * constants_.index_difference) {
Adam Snaider3cd11c52015-02-16 02:16:09 +0000119 if (!error_) {
120 LOG(ERROR,
121 "Encoder ticks out of range since last index pulse. first start "
Austin Schuh1c85bc82016-04-03 21:36:31 -0700122 "position: %f recent starting position: %f, allowable error: %f\n",
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000123 first_start_pos_, offset_,
Austin Schuh5f01f152017-02-11 21:34:08 -0800124 constants_.allowable_encoder_error * constants_.index_difference);
Adam Snaider3cd11c52015-02-16 02:16:09 +0000125 error_ = true;
126 }
127 }
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000128 }
Adam Snaiderb4119252015-02-15 01:30:57 +0000129
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000130 position_ = offset_ + info.encoder;
Austin Schuhbe133ed2016-03-11 21:23:34 -0800131 filtered_position_ = start_average + info.encoder;
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000132}
133
Brian Silverman4f2e2ce2017-02-19 17:49:47 -0800134PotAndIndexPulseZeroingEstimator::State
135PotAndIndexPulseZeroingEstimator::GetEstimatorState() const {
136 State r;
137 r.error = error_;
138 r.zeroed = zeroed_;
139 r.position = position_;
140 r.pot_position = filtered_position_;
141 return r;
142}
143
Austin Schuh55934032017-03-11 12:45:27 -0800144HallEffectAndPositionZeroingEstimator::HallEffectAndPositionZeroingEstimator(
145 const ZeroingConstants &constants)
146 : constants_(constants) {
147 Reset();
148}
149
150void HallEffectAndPositionZeroingEstimator::Reset() {
151 offset_ = 0.0;
152 min_low_position_ = ::std::numeric_limits<double>::max();
153 max_low_position_ = ::std::numeric_limits<double>::lowest();
154 zeroed_ = false;
155 initialized_ = false;
156 last_used_posedge_count_ = 0;
157 cycles_high_ = 0;
158 high_long_enough_ = false;
159 first_start_pos_ = 0.0;
160 error_ = false;
161 current_ = 0.0;
162 first_start_pos_ = 0.0;
163}
164
165void HallEffectAndPositionZeroingEstimator::TriggerError() {
166 if (!error_) {
167 LOG(ERROR, "Manually triggered zeroing error.\n");
168 error_ = true;
169 }
170}
171
172void HallEffectAndPositionZeroingEstimator::StoreEncoderMaxAndMin(
173 const HallEffectAndPosition &info) {
174 // If we have a new posedge.
175 if (!info.current) {
176 if (last_hall_) {
Lee Mracek598a2452019-01-07 00:50:44 -0800177 min_low_position_ = max_low_position_ = info.encoder;
Austin Schuh55934032017-03-11 12:45:27 -0800178 } else {
Lee Mracek598a2452019-01-07 00:50:44 -0800179 min_low_position_ = ::std::min(min_low_position_, info.encoder);
180 max_low_position_ = ::std::max(max_low_position_, info.encoder);
Austin Schuh55934032017-03-11 12:45:27 -0800181 }
182 }
183 last_hall_ = info.current;
184}
185
186void HallEffectAndPositionZeroingEstimator::UpdateEstimate(
187 const HallEffectAndPosition &info) {
188 // We want to make sure that we encounter at least one posedge while zeroing.
189 // So we take the posedge count from the first sample after reset and wait for
190 // that count to change and for the hall effect to stay high before we
191 // consider ourselves zeroed.
192 if (!initialized_) {
193 last_used_posedge_count_ = info.posedge_count;
194 initialized_ = true;
195 last_hall_ = info.current;
196 }
197
198 StoreEncoderMaxAndMin(info);
199
200 if (info.current) {
201 cycles_high_++;
202 } else {
203 cycles_high_ = 0;
204 last_used_posedge_count_ = info.posedge_count;
205 }
206
207 high_long_enough_ = cycles_high_ >= constants_.hall_trigger_zeroing_length;
208
209 bool moving_backward = false;
210 if (constants_.zeroing_move_direction) {
Lee Mracek598a2452019-01-07 00:50:44 -0800211 moving_backward = info.encoder > min_low_position_;
Austin Schuh55934032017-03-11 12:45:27 -0800212 } else {
Lee Mracek598a2452019-01-07 00:50:44 -0800213 moving_backward = info.encoder < max_low_position_;
Austin Schuh55934032017-03-11 12:45:27 -0800214 }
215
216 // If there are no posedges to use or we don't have enough samples yet to
217 // have a well-filtered starting position then we use the filtered value as
218 // our best guess.
219 if (last_used_posedge_count_ != info.posedge_count && high_long_enough_ &&
220 moving_backward) {
221 // Note the offset and the current posedge count so that we only run this
222 // logic once per posedge. That should be more resilient to corrupted
223 // intermediate data.
224 offset_ = -info.posedge_value;
225 if (constants_.zeroing_move_direction) {
226 offset_ += constants_.lower_hall_position;
227 } else {
228 offset_ += constants_.upper_hall_position;
229 }
230 last_used_posedge_count_ = info.posedge_count;
231
232 // Save the first starting position.
233 if (!zeroed_) {
234 first_start_pos_ = offset_;
235 LOG(INFO, "latching start position %f\n", first_start_pos_);
236 }
237
238 // Now that we have an accurate starting position we can consider ourselves
239 // zeroed.
240 zeroed_ = true;
241 }
242
Lee Mracek598a2452019-01-07 00:50:44 -0800243 position_ = info.encoder - offset_;
Austin Schuh55934032017-03-11 12:45:27 -0800244}
245
246HallEffectAndPositionZeroingEstimator::State
247HallEffectAndPositionZeroingEstimator::GetEstimatorState() const {
248 State r;
249 r.error = error_;
250 r.zeroed = zeroed_;
251 r.encoder = position_;
252 r.high_long_enough = high_long_enough_;
253 r.offset = offset_;
254 return r;
255}
Brian Silverman4f2e2ce2017-02-19 17:49:47 -0800256
Austin Schuh72db9a12019-01-21 18:02:51 -0800257PotAndAbsoluteEncoderZeroingEstimator::PotAndAbsoluteEncoderZeroingEstimator(
Austin Schuh5f01f152017-02-11 21:34:08 -0800258 const constants::PotAndAbsoluteEncoderZeroingConstants &constants)
259 : constants_(constants) {
260 relative_to_absolute_offset_samples_.reserve(constants_.average_filter_size);
261 offset_samples_.reserve(constants_.average_filter_size);
Austin Schuh409ffe02019-01-21 18:46:41 -0800262 buffered_samples_.reserve(constants_.moving_buffer_size);
Austin Schuh5f01f152017-02-11 21:34:08 -0800263 Reset();
264}
265
Austin Schuh72db9a12019-01-21 18:02:51 -0800266void PotAndAbsoluteEncoderZeroingEstimator::Reset() {
Austin Schuhddd08f82018-03-02 20:05:29 -0800267 first_offset_ = 0.0;
268 pot_relative_encoder_offset_ = 0.0;
269 offset_ = 0.0;
270 samples_idx_ = 0;
271 filtered_position_ = 0.0;
272 position_ = 0.0;
Austin Schuh5f01f152017-02-11 21:34:08 -0800273 zeroed_ = false;
Austin Schuhddd08f82018-03-02 20:05:29 -0800274 nan_samples_ = 0;
Austin Schuh5f01f152017-02-11 21:34:08 -0800275 relative_to_absolute_offset_samples_.clear();
276 offset_samples_.clear();
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800277 buffered_samples_.clear();
Brian Silvermana10d20a2017-02-19 14:28:53 -0800278 error_ = false;
Austin Schuh5f01f152017-02-11 21:34:08 -0800279}
280
281// So, this needs to be a multistep process. We need to first estimate the
282// offset between the absolute encoder and the relative encoder. That process
283// should get us an absolute number which is off by integer multiples of the
284// distance/rev. In parallel, we can estimate the offset between the pot and
285// encoder. When both estimates have converged, we can then compute the offset
286// in a cycle, and which cycle, which gives us the accurate global offset.
287//
288// It's tricky to compute the offset between the absolute and relative encoder.
289// We need to compute this inside 1 revolution. The easiest way to do this
290// would be to wrap the encoder, subtract the two of them, and then average the
291// result. That will struggle when they are off by PI. Instead, we need to
292// wrap the number to +- PI from the current averaged offset.
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800293//
294// To guard against the robot moving while updating estimates, buffer a number
295// of samples and check that the buffered samples are not different than the
296// zeroing threshold. At any point that the samples differ too much, do not
297// update estimates based on those samples.
Austin Schuh72db9a12019-01-21 18:02:51 -0800298void PotAndAbsoluteEncoderZeroingEstimator::UpdateEstimate(
Austin Schuh5f01f152017-02-11 21:34:08 -0800299 const PotAndAbsolutePosition &info) {
Neil Balch16275e32017-02-18 16:38:45 -0800300 // Check for Abs Encoder NaN value that would mess up the rest of the zeroing
301 // code below. NaN values are given when the Absolute Encoder is disconnected.
302 if (::std::isnan(info.absolute_encoder)) {
Austin Schuhddd08f82018-03-02 20:05:29 -0800303 if (zeroed_) {
304 LOG(ERROR, "NAN on absolute encoder\n");
305 error_ = true;
306 } else {
307 ++nan_samples_;
308 LOG(ERROR, "NAN on absolute encoder while zeroing %d\n",
309 static_cast<int>(nan_samples_));
310 if (nan_samples_ >= constants_.average_filter_size) {
311 error_ = true;
312 zeroed_ = true;
313 }
314 }
315 // Throw some dummy values in for now.
316 filtered_absolute_encoder_ = info.absolute_encoder;
317 filtered_position_ = pot_relative_encoder_offset_ + info.encoder;
318 position_ = offset_ + info.encoder;
Neil Balch16275e32017-02-18 16:38:45 -0800319 return;
320 }
321
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800322 bool moving = true;
323 if (buffered_samples_.size() < constants_.moving_buffer_size) {
324 // Not enough samples to start determining if the robot is moving or not,
325 // don't use the samples yet.
326 buffered_samples_.push_back(info);
Austin Schuh5f01f152017-02-11 21:34:08 -0800327 } else {
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800328 // Have enough samples to start determining if the robot is moving or not.
329 buffered_samples_[buffered_samples_idx_] = info;
330 auto max_value =
331 ::std::max_element(buffered_samples_.begin(), buffered_samples_.end(),
Philipp Schrader3f5b6182017-03-25 22:36:37 +0000332 compare_encoder)->encoder;
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800333 auto min_value =
334 ::std::min_element(buffered_samples_.begin(), buffered_samples_.end(),
Philipp Schrader3f5b6182017-03-25 22:36:37 +0000335 compare_encoder)->encoder;
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800336 if (::std::abs(max_value - min_value) < constants_.zeroing_threshold) {
337 // Robot isn't moving, use middle sample to determine offsets.
338 moving = false;
339 }
340 }
341 buffered_samples_idx_ =
342 (buffered_samples_idx_ + 1) % constants_.moving_buffer_size;
343
344 if (!moving) {
345 // The robot is not moving, use the middle sample to determine offsets.
346 const int middle_index =
347 (buffered_samples_idx_ + (constants_.moving_buffer_size - 1) / 2) %
348 constants_.moving_buffer_size;
Austin Schuh409ffe02019-01-21 18:46:41 -0800349 const PotAndAbsolutePosition &sample = buffered_samples_[middle_index];
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800350
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800351 // Compute the average offset between the absolute encoder and relative
352 // encoder. If we have 0 samples, assume it is 0.
353 double average_relative_to_absolute_offset =
354 relative_to_absolute_offset_samples_.size() == 0
355 ? 0.0
Austin Schuha8f88d42019-01-26 12:33:54 -0800356 : ::std::accumulate(relative_to_absolute_offset_samples_.begin(),
357 relative_to_absolute_offset_samples_.end(),
358 0.0) /
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800359 relative_to_absolute_offset_samples_.size();
360
Austin Schuh0e1c2c62017-02-21 02:03:25 -0800361 const double adjusted_incremental_encoder =
Austin Schuh409ffe02019-01-21 18:46:41 -0800362 sample.encoder + average_relative_to_absolute_offset;
Austin Schuh0e1c2c62017-02-21 02:03:25 -0800363
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800364 // Now, compute the nearest absolute encoder value to the offset relative
365 // encoder position.
366 const double adjusted_absolute_encoder =
Austin Schuh0e1c2c62017-02-21 02:03:25 -0800367 Wrap(adjusted_incremental_encoder,
Austin Schuh409ffe02019-01-21 18:46:41 -0800368 sample.absolute_encoder - constants_.measured_absolute_position,
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800369 constants_.one_revolution_distance);
370
Austin Schuh0e1c2c62017-02-21 02:03:25 -0800371 // Reverse the math on the previous line to compute the absolute encoder.
372 // Do this by taking the adjusted encoder, and then subtracting off the
373 // second argument above, and the value that was added by Wrap.
374 filtered_absolute_encoder_ =
Austin Schuh409ffe02019-01-21 18:46:41 -0800375 ((sample.encoder + average_relative_to_absolute_offset) -
Austin Schuh0e1c2c62017-02-21 02:03:25 -0800376 (-constants_.measured_absolute_position +
377 (adjusted_absolute_encoder -
Austin Schuh409ffe02019-01-21 18:46:41 -0800378 (sample.absolute_encoder - constants_.measured_absolute_position))));
Austin Schuh0e1c2c62017-02-21 02:03:25 -0800379
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800380 const double relative_to_absolute_offset =
Austin Schuh409ffe02019-01-21 18:46:41 -0800381 adjusted_absolute_encoder - sample.encoder;
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800382
383 // Add the sample and update the average with the new reading.
384 const size_t relative_to_absolute_offset_samples_size =
385 relative_to_absolute_offset_samples_.size();
386 if (relative_to_absolute_offset_samples_size <
387 constants_.average_filter_size) {
388 average_relative_to_absolute_offset =
389 (average_relative_to_absolute_offset *
390 relative_to_absolute_offset_samples_size +
391 relative_to_absolute_offset) /
392 (relative_to_absolute_offset_samples_size + 1);
393
394 relative_to_absolute_offset_samples_.push_back(
395 relative_to_absolute_offset);
396 } else {
397 average_relative_to_absolute_offset -=
398 relative_to_absolute_offset_samples_[samples_idx_] /
399 relative_to_absolute_offset_samples_size;
400 relative_to_absolute_offset_samples_[samples_idx_] =
401 relative_to_absolute_offset;
402 average_relative_to_absolute_offset +=
403 relative_to_absolute_offset /
404 relative_to_absolute_offset_samples_size;
405 }
406
407 // Now compute the offset between the pot and relative encoder.
408 if (offset_samples_.size() < constants_.average_filter_size) {
Austin Schuh409ffe02019-01-21 18:46:41 -0800409 offset_samples_.push_back(sample.pot - sample.encoder);
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800410 } else {
Austin Schuh409ffe02019-01-21 18:46:41 -0800411 offset_samples_[samples_idx_] = sample.pot - sample.encoder;
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800412 }
413
414 // Drop the oldest sample when we run this function the next time around.
415 samples_idx_ = (samples_idx_ + 1) % constants_.average_filter_size;
416
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800417 pot_relative_encoder_offset_ =
Austin Schuha8f88d42019-01-26 12:33:54 -0800418 ::std::accumulate(offset_samples_.begin(), offset_samples_.end(), 0.0) /
419 offset_samples_.size();
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800420
Austin Schuh409ffe02019-01-21 18:46:41 -0800421 offset_ = Wrap(sample.encoder + pot_relative_encoder_offset_,
422 average_relative_to_absolute_offset + sample.encoder,
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800423 constants_.one_revolution_distance) -
Austin Schuh409ffe02019-01-21 18:46:41 -0800424 sample.encoder;
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800425 if (offset_ready()) {
Brian Silvermana10d20a2017-02-19 14:28:53 -0800426 if (!zeroed_) {
427 first_offset_ = offset_;
428 }
429
430 if (::std::abs(first_offset_ - offset_) >
431 constants_.allowable_encoder_error *
432 constants_.one_revolution_distance) {
433 LOG(ERROR,
434 "Offset moved too far. Initial: %f, current %f, allowable change: "
435 "%f\n",
436 first_offset_, offset_, constants_.allowable_encoder_error *
437 constants_.one_revolution_distance);
438 error_ = true;
439 }
440
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800441 zeroed_ = true;
442 }
Austin Schuh5f01f152017-02-11 21:34:08 -0800443 }
444
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800445 // Update the position.
446 filtered_position_ = pot_relative_encoder_offset_ + info.encoder;
Austin Schuh5f01f152017-02-11 21:34:08 -0800447 position_ = offset_ + info.encoder;
448}
449
Austin Schuh72db9a12019-01-21 18:02:51 -0800450PotAndAbsoluteEncoderZeroingEstimator::State
451PotAndAbsoluteEncoderZeroingEstimator::GetEstimatorState() const {
Brian Silverman4f2e2ce2017-02-19 17:49:47 -0800452 State r;
453 r.error = error_;
454 r.zeroed = zeroed_;
455 r.position = position_;
456 r.pot_position = filtered_position_;
Austin Schuh0e1c2c62017-02-21 02:03:25 -0800457 r.absolute_position = filtered_absolute_encoder_;
Brian Silverman4f2e2ce2017-02-19 17:49:47 -0800458 return r;
459}
460
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000461void PulseIndexZeroingEstimator::Reset() {
462 max_index_position_ = ::std::numeric_limits<double>::lowest();
463 min_index_position_ = ::std::numeric_limits<double>::max();
464 offset_ = 0;
465 last_used_index_pulse_count_ = 0;
466 zeroed_ = false;
467 error_ = false;
468}
469
470void PulseIndexZeroingEstimator::StoreIndexPulseMaxAndMin(
471 const IndexPosition &info) {
472 // If we have a new index pulse.
473 if (last_used_index_pulse_count_ != info.index_pulses) {
474 // If the latest pulses's position is outside the range we've currently
475 // seen, record it appropriately.
476 if (info.latched_encoder > max_index_position_) {
477 max_index_position_ = info.latched_encoder;
478 }
479 if (info.latched_encoder < min_index_position_) {
480 min_index_position_ = info.latched_encoder;
481 }
482 last_used_index_pulse_count_ = info.index_pulses;
483 }
484}
485
Brian Silvermanf37839c2017-02-19 18:07:15 -0800486int PulseIndexZeroingEstimator::IndexPulseCount() const {
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000487 if (min_index_position_ > max_index_position_) {
488 // This condition means we haven't seen a pulse yet.
489 return 0;
490 }
491
492 // Calculate the number of pulses encountered so far.
493 return 1 + static_cast<int>(
494 ::std::round((max_index_position_ - min_index_position_) /
495 constants_.index_difference));
496}
497
498void PulseIndexZeroingEstimator::UpdateEstimate(const IndexPosition &info) {
499 StoreIndexPulseMaxAndMin(info);
500 const int index_pulse_count = IndexPulseCount();
501 if (index_pulse_count > constants_.index_pulse_count) {
Philipp Schrader3f5b6182017-03-25 22:36:37 +0000502 if (!error_) {
503 LOG(ERROR, "Got more index pulses than expected. Got %d expected %d.\n",
504 index_pulse_count, constants_.index_pulse_count);
505 error_ = true;
506 }
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000507 }
508
509 // TODO(austin): Detect if the encoder or index pulse is unplugged.
510 // TODO(austin): Detect missing counts.
511
512 if (index_pulse_count == constants_.index_pulse_count && !zeroed_) {
513 offset_ = constants_.measured_index_position -
514 constants_.known_index_pulse * constants_.index_difference -
515 min_index_position_;
516 zeroed_ = true;
Philipp Schrader3f5b6182017-03-25 22:36:37 +0000517 } else if (zeroed_ && !error_) {
518 // Detect whether the index pulse is somewhere other than where we expect
519 // it to be. First we compute the position of the most recent index pulse.
520 double index_pulse_distance =
521 info.latched_encoder + offset_ - constants_.measured_index_position;
522 // Second we compute the position of the index pulse in terms of
523 // the index difference. I.e. if this index pulse is two pulses away from
524 // the index pulse that we know about then this number should be positive
525 // or negative two.
526 double relative_distance =
527 index_pulse_distance / constants_.index_difference;
528 // Now we compute how far away the measured index pulse is from the
529 // expected index pulse.
530 double error = relative_distance - ::std::round(relative_distance);
531 // This lets us check if the index pulse is within an acceptable error
532 // margin of where we expected it to be.
533 if (::std::abs(error) > constants_.allowable_encoder_error) {
534 LOG(ERROR,
535 "Encoder ticks out of range since last index pulse. known index "
536 "pulse: %f, expected index pulse: %f, actual index pulse: %f, "
537 "allowable error: %f\n",
538 constants_.measured_index_position,
539 round(relative_distance) * constants_.index_difference +
540 constants_.measured_index_position,
541 info.latched_encoder + offset_,
542 constants_.allowable_encoder_error * constants_.index_difference);
543 error_ = true;
544 }
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000545 }
Brian Silvermanf37839c2017-02-19 18:07:15 -0800546
547 position_ = info.encoder + offset_;
548}
549
550PulseIndexZeroingEstimator::State
551PulseIndexZeroingEstimator::GetEstimatorState() const {
552 State r;
553 r.error = error_;
554 r.zeroed = zeroed_;
555 r.position = position_;
556 r.min_index_position = min_index_position_;
557 r.max_index_position = max_index_position_;
558 r.index_pulses_seen = IndexPulseCount();
559 return r;
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000560}
561
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000562} // namespace zeroing
563} // namespace frc971