blob: 57068421ab34ecfd05e5646b9306de22ebd46690 [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>
6#include <vector>
Adam Snaiderc4b3c192015-02-01 01:30:39 +00007
Austin Schuh5f01f152017-02-11 21:34:08 -08008#include "frc971/zeroing/wrap.h"
9
Adam Snaiderc4b3c192015-02-01 01:30:39 +000010namespace frc971 {
11namespace zeroing {
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -080012namespace {
13
14bool compare_encoder(const PotAndAbsolutePosition &left,
15 const PotAndAbsolutePosition &right) {
16 return left.encoder < right.encoder;
17}
18
19} // namespace
Adam Snaiderc4b3c192015-02-01 01:30:39 +000020
Tyler Chatowf8f03112017-02-05 14:31:34 -080021PotAndIndexPulseZeroingEstimator::PotAndIndexPulseZeroingEstimator(
Austin Schuh5f01f152017-02-11 21:34:08 -080022 const constants::PotAndIndexPulseZeroingConstants &constants)
23 : constants_(constants) {
24 start_pos_samples_.reserve(constants_.average_filter_size);
Adam Snaiderb4119252015-02-15 01:30:57 +000025 Reset();
Austin Schuh703b8d42015-02-01 14:56:34 -080026}
27
Tyler Chatowf8f03112017-02-05 14:31:34 -080028void PotAndIndexPulseZeroingEstimator::Reset() {
Adam Snaiderc4b3c192015-02-01 01:30:39 +000029 samples_idx_ = 0;
Isaac Wilcove0851ffd2017-02-16 04:13:14 +000030 offset_ = 0;
Adam Snaiderb4119252015-02-15 01:30:57 +000031 start_pos_samples_.clear();
32 zeroed_ = false;
Philipp Schrader41d82912015-02-15 03:44:23 +000033 wait_for_index_pulse_ = true;
Philipp Schradere828be72015-02-15 07:07:37 +000034 last_used_index_pulse_count_ = 0;
Philipp Schrader53f4b6d2015-02-15 22:32:08 +000035 error_ = false;
36}
37
Tyler Chatowf8f03112017-02-05 14:31:34 -080038void PotAndIndexPulseZeroingEstimator::TriggerError() {
Philipp Schrader53f4b6d2015-02-15 22:32:08 +000039 if (!error_) {
40 LOG(ERROR, "Manually triggered zeroing error.\n");
41 error_ = true;
42 }
Philipp Schradere828be72015-02-15 07:07:37 +000043}
44
Tyler Chatowf8f03112017-02-05 14:31:34 -080045double PotAndIndexPulseZeroingEstimator::CalculateStartPosition(
46 double start_average, double latched_encoder) const {
Philipp Schradere828be72015-02-15 07:07:37 +000047 // We calculate an aproximation of the value of the last index position.
48 // Also account for index pulses not lining up with integer multiples of the
49 // index_diff.
Austin Schuh5f01f152017-02-11 21:34:08 -080050 double index_pos =
51 start_average + latched_encoder - constants_.measured_index_position;
Philipp Schradere828be72015-02-15 07:07:37 +000052 // We round index_pos to the closest valid value of the index.
Austin Schuh5f01f152017-02-11 21:34:08 -080053 double accurate_index_pos = (round(index_pos / constants_.index_difference)) *
54 constants_.index_difference;
Philipp Schradere828be72015-02-15 07:07:37 +000055 // Now we reverse the first calculation to get the accurate start position.
Austin Schuh5f01f152017-02-11 21:34:08 -080056 return accurate_index_pos - latched_encoder +
57 constants_.measured_index_position;
Adam Snaiderc4b3c192015-02-01 01:30:39 +000058}
59
Tyler Chatowf8f03112017-02-05 14:31:34 -080060void PotAndIndexPulseZeroingEstimator::UpdateEstimate(
61 const PotAndIndexPosition &info) {
Philipp Schrader41d82912015-02-15 03:44:23 +000062 // We want to make sure that we encounter at least one index pulse while
63 // zeroing. So we take the index pulse count from the first sample after
64 // reset and wait for that count to change before we consider ourselves
65 // zeroed.
66 if (wait_for_index_pulse_) {
Philipp Schradere828be72015-02-15 07:07:37 +000067 last_used_index_pulse_count_ = info.index_pulses;
Philipp Schrader41d82912015-02-15 03:44:23 +000068 wait_for_index_pulse_ = false;
69 }
70
Austin Schuh5f01f152017-02-11 21:34:08 -080071 if (start_pos_samples_.size() < constants_.average_filter_size) {
Adam Snaiderc4b3c192015-02-01 01:30:39 +000072 start_pos_samples_.push_back(info.pot - info.encoder);
73 } else {
74 start_pos_samples_[samples_idx_] = info.pot - info.encoder;
75 }
Adam Snaiderb4119252015-02-15 01:30:57 +000076
77 // Drop the oldest sample when we run this function the next time around.
Austin Schuh5f01f152017-02-11 21:34:08 -080078 samples_idx_ = (samples_idx_ + 1) % constants_.average_filter_size;
Adam Snaiderc4b3c192015-02-01 01:30:39 +000079
Adam Snaiderb4119252015-02-15 01:30:57 +000080 double sample_sum = 0.0;
81
Adam Snaiderc4b3c192015-02-01 01:30:39 +000082 for (size_t i = 0; i < start_pos_samples_.size(); ++i) {
Adam Snaiderb4119252015-02-15 01:30:57 +000083 sample_sum += start_pos_samples_[i];
Adam Snaiderc4b3c192015-02-01 01:30:39 +000084 }
85
86 // Calculates the average of the starting position.
Adam Snaiderb4119252015-02-15 01:30:57 +000087 double start_average = sample_sum / start_pos_samples_.size();
88
89 // If there are no index pulses to use or we don't have enough samples yet to
90 // have a well-filtered starting position then we use the filtered value as
91 // our best guess.
Austin Schuh7485dbb2016-02-08 00:21:58 -080092 if (!zeroed_ &&
93 (info.index_pulses == last_used_index_pulse_count_ || !offset_ready())) {
Isaac Wilcove0851ffd2017-02-16 04:13:14 +000094 offset_ = start_average;
Philipp Schradere828be72015-02-15 07:07:37 +000095 } else if (!zeroed_ || last_used_index_pulse_count_ != info.index_pulses) {
96 // Note the accurate start position and the current index pulse count so
97 // that we only run this logic once per index pulse. That should be more
98 // resilient to corrupted intermediate data.
Isaac Wilcove0851ffd2017-02-16 04:13:14 +000099 offset_ = CalculateStartPosition(start_average, info.latched_encoder);
Philipp Schradere828be72015-02-15 07:07:37 +0000100 last_used_index_pulse_count_ = info.index_pulses;
Austin Schuh7485dbb2016-02-08 00:21:58 -0800101
102 // TODO(austin): Reject encoder positions which have x% error rather than
103 // rounding to the closest index pulse.
104
Adam Snaider3cd11c52015-02-16 02:16:09 +0000105 // Save the first starting position.
106 if (!zeroed_) {
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000107 first_start_pos_ = offset_;
Adam Snaider3cd11c52015-02-16 02:16:09 +0000108 LOG(INFO, "latching start position %f\n", first_start_pos_);
109 }
Adam Snaiderb4119252015-02-15 01:30:57 +0000110
111 // Now that we have an accurate starting position we can consider ourselves
112 // zeroed.
Austin Schuh703b8d42015-02-01 14:56:34 -0800113 zeroed_ = true;
Adam Snaider3cd11c52015-02-16 02:16:09 +0000114 // Throw an error if first_start_pos is bigger/smaller than
Austin Schuh5f01f152017-02-11 21:34:08 -0800115 // constants_.allowable_encoder_error * index_diff + start_pos.
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000116 if (::std::abs(first_start_pos_ - offset_) >
Austin Schuh5f01f152017-02-11 21:34:08 -0800117 constants_.allowable_encoder_error * constants_.index_difference) {
Adam Snaider3cd11c52015-02-16 02:16:09 +0000118 if (!error_) {
119 LOG(ERROR,
120 "Encoder ticks out of range since last index pulse. first start "
Austin Schuh1c85bc82016-04-03 21:36:31 -0700121 "position: %f recent starting position: %f, allowable error: %f\n",
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000122 first_start_pos_, offset_,
Austin Schuh5f01f152017-02-11 21:34:08 -0800123 constants_.allowable_encoder_error * constants_.index_difference);
Adam Snaider3cd11c52015-02-16 02:16:09 +0000124 error_ = true;
125 }
126 }
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000127 }
Adam Snaiderb4119252015-02-15 01:30:57 +0000128
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000129 position_ = offset_ + info.encoder;
Austin Schuhbe133ed2016-03-11 21:23:34 -0800130 filtered_position_ = start_average + info.encoder;
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000131}
132
Brian Silverman4f2e2ce2017-02-19 17:49:47 -0800133PotAndIndexPulseZeroingEstimator::State
134PotAndIndexPulseZeroingEstimator::GetEstimatorState() const {
135 State r;
136 r.error = error_;
137 r.zeroed = zeroed_;
138 r.position = position_;
139 r.pot_position = filtered_position_;
140 return r;
141}
142
Austin Schuh55934032017-03-11 12:45:27 -0800143HallEffectAndPositionZeroingEstimator::HallEffectAndPositionZeroingEstimator(
144 const ZeroingConstants &constants)
145 : constants_(constants) {
146 Reset();
147}
148
149void HallEffectAndPositionZeroingEstimator::Reset() {
150 offset_ = 0.0;
151 min_low_position_ = ::std::numeric_limits<double>::max();
152 max_low_position_ = ::std::numeric_limits<double>::lowest();
153 zeroed_ = false;
154 initialized_ = false;
155 last_used_posedge_count_ = 0;
156 cycles_high_ = 0;
157 high_long_enough_ = false;
158 first_start_pos_ = 0.0;
159 error_ = false;
160 current_ = 0.0;
161 first_start_pos_ = 0.0;
162}
163
164void HallEffectAndPositionZeroingEstimator::TriggerError() {
165 if (!error_) {
166 LOG(ERROR, "Manually triggered zeroing error.\n");
167 error_ = true;
168 }
169}
170
171void HallEffectAndPositionZeroingEstimator::StoreEncoderMaxAndMin(
172 const HallEffectAndPosition &info) {
173 // If we have a new posedge.
174 if (!info.current) {
175 if (last_hall_) {
Lee Mracek598a2452019-01-07 00:50:44 -0800176 min_low_position_ = max_low_position_ = info.encoder;
Austin Schuh55934032017-03-11 12:45:27 -0800177 } else {
Lee Mracek598a2452019-01-07 00:50:44 -0800178 min_low_position_ = ::std::min(min_low_position_, info.encoder);
179 max_low_position_ = ::std::max(max_low_position_, info.encoder);
Austin Schuh55934032017-03-11 12:45:27 -0800180 }
181 }
182 last_hall_ = info.current;
183}
184
185void HallEffectAndPositionZeroingEstimator::UpdateEstimate(
186 const HallEffectAndPosition &info) {
187 // We want to make sure that we encounter at least one posedge while zeroing.
188 // So we take the posedge count from the first sample after reset and wait for
189 // that count to change and for the hall effect to stay high before we
190 // consider ourselves zeroed.
191 if (!initialized_) {
192 last_used_posedge_count_ = info.posedge_count;
193 initialized_ = true;
194 last_hall_ = info.current;
195 }
196
197 StoreEncoderMaxAndMin(info);
198
199 if (info.current) {
200 cycles_high_++;
201 } else {
202 cycles_high_ = 0;
203 last_used_posedge_count_ = info.posedge_count;
204 }
205
206 high_long_enough_ = cycles_high_ >= constants_.hall_trigger_zeroing_length;
207
208 bool moving_backward = false;
209 if (constants_.zeroing_move_direction) {
Lee Mracek598a2452019-01-07 00:50:44 -0800210 moving_backward = info.encoder > min_low_position_;
Austin Schuh55934032017-03-11 12:45:27 -0800211 } else {
Lee Mracek598a2452019-01-07 00:50:44 -0800212 moving_backward = info.encoder < max_low_position_;
Austin Schuh55934032017-03-11 12:45:27 -0800213 }
214
215 // If there are no posedges to use or we don't have enough samples yet to
216 // have a well-filtered starting position then we use the filtered value as
217 // our best guess.
218 if (last_used_posedge_count_ != info.posedge_count && high_long_enough_ &&
219 moving_backward) {
220 // Note the offset and the current posedge count so that we only run this
221 // logic once per posedge. That should be more resilient to corrupted
222 // intermediate data.
223 offset_ = -info.posedge_value;
224 if (constants_.zeroing_move_direction) {
225 offset_ += constants_.lower_hall_position;
226 } else {
227 offset_ += constants_.upper_hall_position;
228 }
229 last_used_posedge_count_ = info.posedge_count;
230
231 // Save the first starting position.
232 if (!zeroed_) {
233 first_start_pos_ = offset_;
234 LOG(INFO, "latching start position %f\n", first_start_pos_);
235 }
236
237 // Now that we have an accurate starting position we can consider ourselves
238 // zeroed.
239 zeroed_ = true;
240 }
241
Lee Mracek598a2452019-01-07 00:50:44 -0800242 position_ = info.encoder - offset_;
Austin Schuh55934032017-03-11 12:45:27 -0800243}
244
245HallEffectAndPositionZeroingEstimator::State
246HallEffectAndPositionZeroingEstimator::GetEstimatorState() const {
247 State r;
248 r.error = error_;
249 r.zeroed = zeroed_;
250 r.encoder = position_;
251 r.high_long_enough = high_long_enough_;
252 r.offset = offset_;
253 return r;
254}
Brian Silverman4f2e2ce2017-02-19 17:49:47 -0800255
Austin Schuh72db9a12019-01-21 18:02:51 -0800256PotAndAbsoluteEncoderZeroingEstimator::PotAndAbsoluteEncoderZeroingEstimator(
Austin Schuh5f01f152017-02-11 21:34:08 -0800257 const constants::PotAndAbsoluteEncoderZeroingConstants &constants)
258 : constants_(constants) {
259 relative_to_absolute_offset_samples_.reserve(constants_.average_filter_size);
260 offset_samples_.reserve(constants_.average_filter_size);
Austin Schuh409ffe02019-01-21 18:46:41 -0800261 buffered_samples_.reserve(constants_.moving_buffer_size);
Austin Schuh5f01f152017-02-11 21:34:08 -0800262 Reset();
263}
264
Austin Schuh72db9a12019-01-21 18:02:51 -0800265void PotAndAbsoluteEncoderZeroingEstimator::Reset() {
Austin Schuhddd08f82018-03-02 20:05:29 -0800266 first_offset_ = 0.0;
267 pot_relative_encoder_offset_ = 0.0;
268 offset_ = 0.0;
269 samples_idx_ = 0;
270 filtered_position_ = 0.0;
271 position_ = 0.0;
Austin Schuh5f01f152017-02-11 21:34:08 -0800272 zeroed_ = false;
Austin Schuhddd08f82018-03-02 20:05:29 -0800273 nan_samples_ = 0;
Austin Schuh5f01f152017-02-11 21:34:08 -0800274 relative_to_absolute_offset_samples_.clear();
275 offset_samples_.clear();
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800276 buffered_samples_.clear();
Brian Silvermana10d20a2017-02-19 14:28:53 -0800277 error_ = false;
Austin Schuh5f01f152017-02-11 21:34:08 -0800278}
279
280// So, this needs to be a multistep process. We need to first estimate the
281// offset between the absolute encoder and the relative encoder. That process
282// should get us an absolute number which is off by integer multiples of the
283// distance/rev. In parallel, we can estimate the offset between the pot and
284// encoder. When both estimates have converged, we can then compute the offset
285// in a cycle, and which cycle, which gives us the accurate global offset.
286//
287// It's tricky to compute the offset between the absolute and relative encoder.
288// We need to compute this inside 1 revolution. The easiest way to do this
289// would be to wrap the encoder, subtract the two of them, and then average the
290// result. That will struggle when they are off by PI. Instead, we need to
291// wrap the number to +- PI from the current averaged offset.
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800292//
293// To guard against the robot moving while updating estimates, buffer a number
294// of samples and check that the buffered samples are not different than the
295// zeroing threshold. At any point that the samples differ too much, do not
296// update estimates based on those samples.
Austin Schuh72db9a12019-01-21 18:02:51 -0800297void PotAndAbsoluteEncoderZeroingEstimator::UpdateEstimate(
Austin Schuh5f01f152017-02-11 21:34:08 -0800298 const PotAndAbsolutePosition &info) {
Neil Balch16275e32017-02-18 16:38:45 -0800299 // Check for Abs Encoder NaN value that would mess up the rest of the zeroing
300 // code below. NaN values are given when the Absolute Encoder is disconnected.
301 if (::std::isnan(info.absolute_encoder)) {
Austin Schuhddd08f82018-03-02 20:05:29 -0800302 if (zeroed_) {
303 LOG(ERROR, "NAN on absolute encoder\n");
304 error_ = true;
305 } else {
306 ++nan_samples_;
307 LOG(ERROR, "NAN on absolute encoder while zeroing %d\n",
308 static_cast<int>(nan_samples_));
309 if (nan_samples_ >= constants_.average_filter_size) {
310 error_ = true;
311 zeroed_ = true;
312 }
313 }
314 // Throw some dummy values in for now.
315 filtered_absolute_encoder_ = info.absolute_encoder;
316 filtered_position_ = pot_relative_encoder_offset_ + info.encoder;
317 position_ = offset_ + info.encoder;
Neil Balch16275e32017-02-18 16:38:45 -0800318 return;
319 }
320
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800321 bool moving = true;
322 if (buffered_samples_.size() < constants_.moving_buffer_size) {
323 // Not enough samples to start determining if the robot is moving or not,
324 // don't use the samples yet.
325 buffered_samples_.push_back(info);
Austin Schuh5f01f152017-02-11 21:34:08 -0800326 } else {
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800327 // Have enough samples to start determining if the robot is moving or not.
328 buffered_samples_[buffered_samples_idx_] = info;
329 auto max_value =
330 ::std::max_element(buffered_samples_.begin(), buffered_samples_.end(),
Philipp Schrader3f5b6182017-03-25 22:36:37 +0000331 compare_encoder)->encoder;
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800332 auto min_value =
333 ::std::min_element(buffered_samples_.begin(), buffered_samples_.end(),
Philipp Schrader3f5b6182017-03-25 22:36:37 +0000334 compare_encoder)->encoder;
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800335 if (::std::abs(max_value - min_value) < constants_.zeroing_threshold) {
336 // Robot isn't moving, use middle sample to determine offsets.
337 moving = false;
338 }
339 }
340 buffered_samples_idx_ =
341 (buffered_samples_idx_ + 1) % constants_.moving_buffer_size;
342
343 if (!moving) {
344 // The robot is not moving, use the middle sample to determine offsets.
345 const int middle_index =
346 (buffered_samples_idx_ + (constants_.moving_buffer_size - 1) / 2) %
347 constants_.moving_buffer_size;
Austin Schuh409ffe02019-01-21 18:46:41 -0800348 const PotAndAbsolutePosition &sample = buffered_samples_[middle_index];
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800349
350 // Compute the sum of all the offset samples.
351 double relative_to_absolute_offset_sum = 0.0;
352 for (size_t i = 0; i < relative_to_absolute_offset_samples_.size(); ++i) {
353 relative_to_absolute_offset_sum +=
354 relative_to_absolute_offset_samples_[i];
355 }
356
357 // Compute the average offset between the absolute encoder and relative
358 // encoder. If we have 0 samples, assume it is 0.
359 double average_relative_to_absolute_offset =
360 relative_to_absolute_offset_samples_.size() == 0
361 ? 0.0
362 : relative_to_absolute_offset_sum /
363 relative_to_absolute_offset_samples_.size();
364
Austin Schuh0e1c2c62017-02-21 02:03:25 -0800365 const double adjusted_incremental_encoder =
Austin Schuh409ffe02019-01-21 18:46:41 -0800366 sample.encoder + average_relative_to_absolute_offset;
Austin Schuh0e1c2c62017-02-21 02:03:25 -0800367
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800368 // Now, compute the nearest absolute encoder value to the offset relative
369 // encoder position.
370 const double adjusted_absolute_encoder =
Austin Schuh0e1c2c62017-02-21 02:03:25 -0800371 Wrap(adjusted_incremental_encoder,
Austin Schuh409ffe02019-01-21 18:46:41 -0800372 sample.absolute_encoder - constants_.measured_absolute_position,
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800373 constants_.one_revolution_distance);
374
Austin Schuh0e1c2c62017-02-21 02:03:25 -0800375 // Reverse the math on the previous line to compute the absolute encoder.
376 // Do this by taking the adjusted encoder, and then subtracting off the
377 // second argument above, and the value that was added by Wrap.
378 filtered_absolute_encoder_ =
Austin Schuh409ffe02019-01-21 18:46:41 -0800379 ((sample.encoder + average_relative_to_absolute_offset) -
Austin Schuh0e1c2c62017-02-21 02:03:25 -0800380 (-constants_.measured_absolute_position +
381 (adjusted_absolute_encoder -
Austin Schuh409ffe02019-01-21 18:46:41 -0800382 (sample.absolute_encoder - constants_.measured_absolute_position))));
Austin Schuh0e1c2c62017-02-21 02:03:25 -0800383
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800384 const double relative_to_absolute_offset =
Austin Schuh409ffe02019-01-21 18:46:41 -0800385 adjusted_absolute_encoder - sample.encoder;
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800386
387 // Add the sample and update the average with the new reading.
388 const size_t relative_to_absolute_offset_samples_size =
389 relative_to_absolute_offset_samples_.size();
390 if (relative_to_absolute_offset_samples_size <
391 constants_.average_filter_size) {
392 average_relative_to_absolute_offset =
393 (average_relative_to_absolute_offset *
394 relative_to_absolute_offset_samples_size +
395 relative_to_absolute_offset) /
396 (relative_to_absolute_offset_samples_size + 1);
397
398 relative_to_absolute_offset_samples_.push_back(
399 relative_to_absolute_offset);
400 } else {
401 average_relative_to_absolute_offset -=
402 relative_to_absolute_offset_samples_[samples_idx_] /
403 relative_to_absolute_offset_samples_size;
404 relative_to_absolute_offset_samples_[samples_idx_] =
405 relative_to_absolute_offset;
406 average_relative_to_absolute_offset +=
407 relative_to_absolute_offset /
408 relative_to_absolute_offset_samples_size;
409 }
410
411 // Now compute the offset between the pot and relative encoder.
412 if (offset_samples_.size() < constants_.average_filter_size) {
Austin Schuh409ffe02019-01-21 18:46:41 -0800413 offset_samples_.push_back(sample.pot - sample.encoder);
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800414 } else {
Austin Schuh409ffe02019-01-21 18:46:41 -0800415 offset_samples_[samples_idx_] = sample.pot - sample.encoder;
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800416 }
417
418 // Drop the oldest sample when we run this function the next time around.
419 samples_idx_ = (samples_idx_ + 1) % constants_.average_filter_size;
420
421 double pot_relative_encoder_offset_sum = 0.0;
422 for (size_t i = 0; i < offset_samples_.size(); ++i) {
423 pot_relative_encoder_offset_sum += offset_samples_[i];
424 }
425 pot_relative_encoder_offset_ =
426 pot_relative_encoder_offset_sum / offset_samples_.size();
427
Austin Schuh409ffe02019-01-21 18:46:41 -0800428 offset_ = Wrap(sample.encoder + pot_relative_encoder_offset_,
429 average_relative_to_absolute_offset + sample.encoder,
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800430 constants_.one_revolution_distance) -
Austin Schuh409ffe02019-01-21 18:46:41 -0800431 sample.encoder;
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800432 if (offset_ready()) {
Brian Silvermana10d20a2017-02-19 14:28:53 -0800433 if (!zeroed_) {
434 first_offset_ = offset_;
435 }
436
437 if (::std::abs(first_offset_ - offset_) >
438 constants_.allowable_encoder_error *
439 constants_.one_revolution_distance) {
440 LOG(ERROR,
441 "Offset moved too far. Initial: %f, current %f, allowable change: "
442 "%f\n",
443 first_offset_, offset_, constants_.allowable_encoder_error *
444 constants_.one_revolution_distance);
445 error_ = true;
446 }
447
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800448 zeroed_ = true;
449 }
Austin Schuh5f01f152017-02-11 21:34:08 -0800450 }
451
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800452 // Update the position.
453 filtered_position_ = pot_relative_encoder_offset_ + info.encoder;
Austin Schuh5f01f152017-02-11 21:34:08 -0800454 position_ = offset_ + info.encoder;
455}
456
Austin Schuh72db9a12019-01-21 18:02:51 -0800457PotAndAbsoluteEncoderZeroingEstimator::State
458PotAndAbsoluteEncoderZeroingEstimator::GetEstimatorState() const {
Brian Silverman4f2e2ce2017-02-19 17:49:47 -0800459 State r;
460 r.error = error_;
461 r.zeroed = zeroed_;
462 r.position = position_;
463 r.pot_position = filtered_position_;
Austin Schuh0e1c2c62017-02-21 02:03:25 -0800464 r.absolute_position = filtered_absolute_encoder_;
Brian Silverman4f2e2ce2017-02-19 17:49:47 -0800465 return r;
466}
467
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000468void PulseIndexZeroingEstimator::Reset() {
469 max_index_position_ = ::std::numeric_limits<double>::lowest();
470 min_index_position_ = ::std::numeric_limits<double>::max();
471 offset_ = 0;
472 last_used_index_pulse_count_ = 0;
473 zeroed_ = false;
474 error_ = false;
475}
476
477void PulseIndexZeroingEstimator::StoreIndexPulseMaxAndMin(
478 const IndexPosition &info) {
479 // If we have a new index pulse.
480 if (last_used_index_pulse_count_ != info.index_pulses) {
481 // If the latest pulses's position is outside the range we've currently
482 // seen, record it appropriately.
483 if (info.latched_encoder > max_index_position_) {
484 max_index_position_ = info.latched_encoder;
485 }
486 if (info.latched_encoder < min_index_position_) {
487 min_index_position_ = info.latched_encoder;
488 }
489 last_used_index_pulse_count_ = info.index_pulses;
490 }
491}
492
Brian Silvermanf37839c2017-02-19 18:07:15 -0800493int PulseIndexZeroingEstimator::IndexPulseCount() const {
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000494 if (min_index_position_ > max_index_position_) {
495 // This condition means we haven't seen a pulse yet.
496 return 0;
497 }
498
499 // Calculate the number of pulses encountered so far.
500 return 1 + static_cast<int>(
501 ::std::round((max_index_position_ - min_index_position_) /
502 constants_.index_difference));
503}
504
505void PulseIndexZeroingEstimator::UpdateEstimate(const IndexPosition &info) {
506 StoreIndexPulseMaxAndMin(info);
507 const int index_pulse_count = IndexPulseCount();
508 if (index_pulse_count > constants_.index_pulse_count) {
Philipp Schrader3f5b6182017-03-25 22:36:37 +0000509 if (!error_) {
510 LOG(ERROR, "Got more index pulses than expected. Got %d expected %d.\n",
511 index_pulse_count, constants_.index_pulse_count);
512 error_ = true;
513 }
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000514 }
515
516 // TODO(austin): Detect if the encoder or index pulse is unplugged.
517 // TODO(austin): Detect missing counts.
518
519 if (index_pulse_count == constants_.index_pulse_count && !zeroed_) {
520 offset_ = constants_.measured_index_position -
521 constants_.known_index_pulse * constants_.index_difference -
522 min_index_position_;
523 zeroed_ = true;
Philipp Schrader3f5b6182017-03-25 22:36:37 +0000524 } else if (zeroed_ && !error_) {
525 // Detect whether the index pulse is somewhere other than where we expect
526 // it to be. First we compute the position of the most recent index pulse.
527 double index_pulse_distance =
528 info.latched_encoder + offset_ - constants_.measured_index_position;
529 // Second we compute the position of the index pulse in terms of
530 // the index difference. I.e. if this index pulse is two pulses away from
531 // the index pulse that we know about then this number should be positive
532 // or negative two.
533 double relative_distance =
534 index_pulse_distance / constants_.index_difference;
535 // Now we compute how far away the measured index pulse is from the
536 // expected index pulse.
537 double error = relative_distance - ::std::round(relative_distance);
538 // This lets us check if the index pulse is within an acceptable error
539 // margin of where we expected it to be.
540 if (::std::abs(error) > constants_.allowable_encoder_error) {
541 LOG(ERROR,
542 "Encoder ticks out of range since last index pulse. known index "
543 "pulse: %f, expected index pulse: %f, actual index pulse: %f, "
544 "allowable error: %f\n",
545 constants_.measured_index_position,
546 round(relative_distance) * constants_.index_difference +
547 constants_.measured_index_position,
548 info.latched_encoder + offset_,
549 constants_.allowable_encoder_error * constants_.index_difference);
550 error_ = true;
551 }
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000552 }
Brian Silvermanf37839c2017-02-19 18:07:15 -0800553
554 position_ = info.encoder + offset_;
555}
556
557PulseIndexZeroingEstimator::State
558PulseIndexZeroingEstimator::GetEstimatorState() const {
559 State r;
560 r.error = error_;
561 r.zeroed = zeroed_;
562 r.position = position_;
563 r.min_index_position = min_index_position_;
564 r.max_index_position = max_index_position_;
565 r.index_pulses_seen = IndexPulseCount();
566 return r;
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000567}
568
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000569} // namespace zeroing
570} // namespace frc971