blob: 1ed98a369d014b3ed308234191d52372a42b1bb6 [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 {
13
Tyler Chatowf8f03112017-02-05 14:31:34 -080014PotAndIndexPulseZeroingEstimator::PotAndIndexPulseZeroingEstimator(
Austin Schuh5f01f152017-02-11 21:34:08 -080015 const constants::PotAndIndexPulseZeroingConstants &constants)
16 : constants_(constants) {
17 start_pos_samples_.reserve(constants_.average_filter_size);
Adam Snaiderb4119252015-02-15 01:30:57 +000018 Reset();
Austin Schuh703b8d42015-02-01 14:56:34 -080019}
20
Tyler Chatowf8f03112017-02-05 14:31:34 -080021void PotAndIndexPulseZeroingEstimator::Reset() {
Adam Snaiderc4b3c192015-02-01 01:30:39 +000022 samples_idx_ = 0;
Isaac Wilcove0851ffd2017-02-16 04:13:14 +000023 offset_ = 0;
Adam Snaiderb4119252015-02-15 01:30:57 +000024 start_pos_samples_.clear();
25 zeroed_ = false;
Philipp Schrader41d82912015-02-15 03:44:23 +000026 wait_for_index_pulse_ = true;
Philipp Schradere828be72015-02-15 07:07:37 +000027 last_used_index_pulse_count_ = 0;
Philipp Schrader53f4b6d2015-02-15 22:32:08 +000028 error_ = false;
29}
30
Tyler Chatowf8f03112017-02-05 14:31:34 -080031void PotAndIndexPulseZeroingEstimator::TriggerError() {
Philipp Schrader53f4b6d2015-02-15 22:32:08 +000032 if (!error_) {
33 LOG(ERROR, "Manually triggered zeroing error.\n");
34 error_ = true;
35 }
Philipp Schradere828be72015-02-15 07:07:37 +000036}
37
Tyler Chatowf8f03112017-02-05 14:31:34 -080038double PotAndIndexPulseZeroingEstimator::CalculateStartPosition(
39 double start_average, double latched_encoder) const {
Philipp Schradere828be72015-02-15 07:07:37 +000040 // We calculate an aproximation of the value of the last index position.
41 // Also account for index pulses not lining up with integer multiples of the
42 // index_diff.
Austin Schuh5f01f152017-02-11 21:34:08 -080043 double index_pos =
44 start_average + latched_encoder - constants_.measured_index_position;
Philipp Schradere828be72015-02-15 07:07:37 +000045 // We round index_pos to the closest valid value of the index.
Austin Schuh5f01f152017-02-11 21:34:08 -080046 double accurate_index_pos = (round(index_pos / constants_.index_difference)) *
47 constants_.index_difference;
Philipp Schradere828be72015-02-15 07:07:37 +000048 // Now we reverse the first calculation to get the accurate start position.
Austin Schuh5f01f152017-02-11 21:34:08 -080049 return accurate_index_pos - latched_encoder +
50 constants_.measured_index_position;
Adam Snaiderc4b3c192015-02-01 01:30:39 +000051}
52
Tyler Chatowf8f03112017-02-05 14:31:34 -080053void PotAndIndexPulseZeroingEstimator::UpdateEstimate(
54 const PotAndIndexPosition &info) {
Philipp Schrader41d82912015-02-15 03:44:23 +000055 // We want to make sure that we encounter at least one index pulse while
56 // zeroing. So we take the index pulse count from the first sample after
57 // reset and wait for that count to change before we consider ourselves
58 // zeroed.
59 if (wait_for_index_pulse_) {
Philipp Schradere828be72015-02-15 07:07:37 +000060 last_used_index_pulse_count_ = info.index_pulses;
Philipp Schrader41d82912015-02-15 03:44:23 +000061 wait_for_index_pulse_ = false;
62 }
63
Austin Schuh5f01f152017-02-11 21:34:08 -080064 if (start_pos_samples_.size() < constants_.average_filter_size) {
Adam Snaiderc4b3c192015-02-01 01:30:39 +000065 start_pos_samples_.push_back(info.pot - info.encoder);
66 } else {
67 start_pos_samples_[samples_idx_] = info.pot - info.encoder;
68 }
Adam Snaiderb4119252015-02-15 01:30:57 +000069
70 // Drop the oldest sample when we run this function the next time around.
Austin Schuh5f01f152017-02-11 21:34:08 -080071 samples_idx_ = (samples_idx_ + 1) % constants_.average_filter_size;
Adam Snaiderc4b3c192015-02-01 01:30:39 +000072
Adam Snaiderb4119252015-02-15 01:30:57 +000073 double sample_sum = 0.0;
74
Adam Snaiderc4b3c192015-02-01 01:30:39 +000075 for (size_t i = 0; i < start_pos_samples_.size(); ++i) {
Adam Snaiderb4119252015-02-15 01:30:57 +000076 sample_sum += start_pos_samples_[i];
Adam Snaiderc4b3c192015-02-01 01:30:39 +000077 }
78
79 // Calculates the average of the starting position.
Adam Snaiderb4119252015-02-15 01:30:57 +000080 double start_average = sample_sum / start_pos_samples_.size();
81
82 // If there are no index pulses to use or we don't have enough samples yet to
83 // have a well-filtered starting position then we use the filtered value as
84 // our best guess.
Austin Schuh7485dbb2016-02-08 00:21:58 -080085 if (!zeroed_ &&
86 (info.index_pulses == last_used_index_pulse_count_ || !offset_ready())) {
Isaac Wilcove0851ffd2017-02-16 04:13:14 +000087 offset_ = start_average;
Philipp Schradere828be72015-02-15 07:07:37 +000088 } else if (!zeroed_ || last_used_index_pulse_count_ != info.index_pulses) {
89 // Note the accurate start position and the current index pulse count so
90 // that we only run this logic once per index pulse. That should be more
91 // resilient to corrupted intermediate data.
Isaac Wilcove0851ffd2017-02-16 04:13:14 +000092 offset_ = CalculateStartPosition(start_average, info.latched_encoder);
Philipp Schradere828be72015-02-15 07:07:37 +000093 last_used_index_pulse_count_ = info.index_pulses;
Austin Schuh7485dbb2016-02-08 00:21:58 -080094
95 // TODO(austin): Reject encoder positions which have x% error rather than
96 // rounding to the closest index pulse.
97
Adam Snaider3cd11c52015-02-16 02:16:09 +000098 // Save the first starting position.
99 if (!zeroed_) {
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000100 first_start_pos_ = offset_;
Adam Snaider3cd11c52015-02-16 02:16:09 +0000101 LOG(INFO, "latching start position %f\n", first_start_pos_);
102 }
Adam Snaiderb4119252015-02-15 01:30:57 +0000103
104 // Now that we have an accurate starting position we can consider ourselves
105 // zeroed.
Austin Schuh703b8d42015-02-01 14:56:34 -0800106 zeroed_ = true;
Adam Snaider3cd11c52015-02-16 02:16:09 +0000107 // Throw an error if first_start_pos is bigger/smaller than
Austin Schuh5f01f152017-02-11 21:34:08 -0800108 // constants_.allowable_encoder_error * index_diff + start_pos.
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000109 if (::std::abs(first_start_pos_ - offset_) >
Austin Schuh5f01f152017-02-11 21:34:08 -0800110 constants_.allowable_encoder_error * constants_.index_difference) {
Adam Snaider3cd11c52015-02-16 02:16:09 +0000111 if (!error_) {
112 LOG(ERROR,
113 "Encoder ticks out of range since last index pulse. first start "
Austin Schuh1c85bc82016-04-03 21:36:31 -0700114 "position: %f recent starting position: %f, allowable error: %f\n",
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000115 first_start_pos_, offset_,
Austin Schuh5f01f152017-02-11 21:34:08 -0800116 constants_.allowable_encoder_error * constants_.index_difference);
Adam Snaider3cd11c52015-02-16 02:16:09 +0000117 error_ = true;
118 }
119 }
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000120 }
Adam Snaiderb4119252015-02-15 01:30:57 +0000121
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000122 position_ = offset_ + info.encoder;
Austin Schuhbe133ed2016-03-11 21:23:34 -0800123 filtered_position_ = start_average + info.encoder;
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000124}
125
Brian Silverman4f2e2ce2017-02-19 17:49:47 -0800126PotAndIndexPulseZeroingEstimator::State
127PotAndIndexPulseZeroingEstimator::GetEstimatorState() const {
128 State r;
129 r.error = error_;
130 r.zeroed = zeroed_;
131 r.position = position_;
132 r.pot_position = filtered_position_;
133 return r;
134}
135
Austin Schuh55934032017-03-11 12:45:27 -0800136HallEffectAndPositionZeroingEstimator::HallEffectAndPositionZeroingEstimator(
137 const ZeroingConstants &constants)
138 : constants_(constants) {
139 Reset();
140}
141
142void HallEffectAndPositionZeroingEstimator::Reset() {
143 offset_ = 0.0;
144 min_low_position_ = ::std::numeric_limits<double>::max();
145 max_low_position_ = ::std::numeric_limits<double>::lowest();
146 zeroed_ = false;
147 initialized_ = false;
148 last_used_posedge_count_ = 0;
149 cycles_high_ = 0;
150 high_long_enough_ = false;
151 first_start_pos_ = 0.0;
152 error_ = false;
153 current_ = 0.0;
154 first_start_pos_ = 0.0;
155}
156
157void HallEffectAndPositionZeroingEstimator::TriggerError() {
158 if (!error_) {
159 LOG(ERROR, "Manually triggered zeroing error.\n");
160 error_ = true;
161 }
162}
163
164void HallEffectAndPositionZeroingEstimator::StoreEncoderMaxAndMin(
165 const HallEffectAndPosition &info) {
166 // If we have a new posedge.
167 if (!info.current) {
168 if (last_hall_) {
Lee Mracek598a2452019-01-07 00:50:44 -0800169 min_low_position_ = max_low_position_ = info.encoder;
Austin Schuh55934032017-03-11 12:45:27 -0800170 } else {
Lee Mracek598a2452019-01-07 00:50:44 -0800171 min_low_position_ = ::std::min(min_low_position_, info.encoder);
172 max_low_position_ = ::std::max(max_low_position_, info.encoder);
Austin Schuh55934032017-03-11 12:45:27 -0800173 }
174 }
175 last_hall_ = info.current;
176}
177
178void HallEffectAndPositionZeroingEstimator::UpdateEstimate(
179 const HallEffectAndPosition &info) {
180 // We want to make sure that we encounter at least one posedge while zeroing.
181 // So we take the posedge count from the first sample after reset and wait for
182 // that count to change and for the hall effect to stay high before we
183 // consider ourselves zeroed.
184 if (!initialized_) {
185 last_used_posedge_count_ = info.posedge_count;
186 initialized_ = true;
187 last_hall_ = info.current;
188 }
189
190 StoreEncoderMaxAndMin(info);
191
192 if (info.current) {
193 cycles_high_++;
194 } else {
195 cycles_high_ = 0;
196 last_used_posedge_count_ = info.posedge_count;
197 }
198
199 high_long_enough_ = cycles_high_ >= constants_.hall_trigger_zeroing_length;
200
201 bool moving_backward = false;
202 if (constants_.zeroing_move_direction) {
Lee Mracek598a2452019-01-07 00:50:44 -0800203 moving_backward = info.encoder > min_low_position_;
Austin Schuh55934032017-03-11 12:45:27 -0800204 } else {
Lee Mracek598a2452019-01-07 00:50:44 -0800205 moving_backward = info.encoder < max_low_position_;
Austin Schuh55934032017-03-11 12:45:27 -0800206 }
207
208 // If there are no posedges to use or we don't have enough samples yet to
209 // have a well-filtered starting position then we use the filtered value as
210 // our best guess.
211 if (last_used_posedge_count_ != info.posedge_count && high_long_enough_ &&
212 moving_backward) {
213 // Note the offset and the current posedge count so that we only run this
214 // logic once per posedge. That should be more resilient to corrupted
215 // intermediate data.
216 offset_ = -info.posedge_value;
217 if (constants_.zeroing_move_direction) {
218 offset_ += constants_.lower_hall_position;
219 } else {
220 offset_ += constants_.upper_hall_position;
221 }
222 last_used_posedge_count_ = info.posedge_count;
223
224 // Save the first starting position.
225 if (!zeroed_) {
226 first_start_pos_ = offset_;
227 LOG(INFO, "latching start position %f\n", first_start_pos_);
228 }
229
230 // Now that we have an accurate starting position we can consider ourselves
231 // zeroed.
232 zeroed_ = true;
233 }
234
Lee Mracek598a2452019-01-07 00:50:44 -0800235 position_ = info.encoder - offset_;
Austin Schuh55934032017-03-11 12:45:27 -0800236}
237
238HallEffectAndPositionZeroingEstimator::State
239HallEffectAndPositionZeroingEstimator::GetEstimatorState() const {
240 State r;
241 r.error = error_;
242 r.zeroed = zeroed_;
243 r.encoder = position_;
244 r.high_long_enough = high_long_enough_;
245 r.offset = offset_;
246 return r;
247}
Brian Silverman4f2e2ce2017-02-19 17:49:47 -0800248
Austin Schuh72db9a12019-01-21 18:02:51 -0800249PotAndAbsoluteEncoderZeroingEstimator::PotAndAbsoluteEncoderZeroingEstimator(
Austin Schuh5f01f152017-02-11 21:34:08 -0800250 const constants::PotAndAbsoluteEncoderZeroingConstants &constants)
Austin Schuh66c59ba2019-01-26 20:34:35 -0800251 : constants_(constants), move_detector_(constants_.moving_buffer_size) {
Austin Schuh5f01f152017-02-11 21:34:08 -0800252 relative_to_absolute_offset_samples_.reserve(constants_.average_filter_size);
253 offset_samples_.reserve(constants_.average_filter_size);
254 Reset();
255}
256
Austin Schuh72db9a12019-01-21 18:02:51 -0800257void PotAndAbsoluteEncoderZeroingEstimator::Reset() {
Austin Schuhddd08f82018-03-02 20:05:29 -0800258 first_offset_ = 0.0;
259 pot_relative_encoder_offset_ = 0.0;
260 offset_ = 0.0;
261 samples_idx_ = 0;
262 filtered_position_ = 0.0;
263 position_ = 0.0;
Austin Schuh5f01f152017-02-11 21:34:08 -0800264 zeroed_ = false;
Austin Schuhddd08f82018-03-02 20:05:29 -0800265 nan_samples_ = 0;
Austin Schuh5f01f152017-02-11 21:34:08 -0800266 relative_to_absolute_offset_samples_.clear();
267 offset_samples_.clear();
Austin Schuh66c59ba2019-01-26 20:34:35 -0800268 move_detector_.Reset();
Brian Silvermana10d20a2017-02-19 14:28:53 -0800269 error_ = false;
Austin Schuh5f01f152017-02-11 21:34:08 -0800270}
271
272// So, this needs to be a multistep process. We need to first estimate the
273// offset between the absolute encoder and the relative encoder. That process
274// should get us an absolute number which is off by integer multiples of the
275// distance/rev. In parallel, we can estimate the offset between the pot and
276// encoder. When both estimates have converged, we can then compute the offset
277// in a cycle, and which cycle, which gives us the accurate global offset.
278//
279// It's tricky to compute the offset between the absolute and relative encoder.
280// We need to compute this inside 1 revolution. The easiest way to do this
281// would be to wrap the encoder, subtract the two of them, and then average the
282// result. That will struggle when they are off by PI. Instead, we need to
283// wrap the number to +- PI from the current averaged offset.
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800284//
285// To guard against the robot moving while updating estimates, buffer a number
286// of samples and check that the buffered samples are not different than the
287// zeroing threshold. At any point that the samples differ too much, do not
288// update estimates based on those samples.
Austin Schuh72db9a12019-01-21 18:02:51 -0800289void PotAndAbsoluteEncoderZeroingEstimator::UpdateEstimate(
Austin Schuh5f01f152017-02-11 21:34:08 -0800290 const PotAndAbsolutePosition &info) {
Neil Balch16275e32017-02-18 16:38:45 -0800291 // Check for Abs Encoder NaN value that would mess up the rest of the zeroing
292 // code below. NaN values are given when the Absolute Encoder is disconnected.
293 if (::std::isnan(info.absolute_encoder)) {
Austin Schuhddd08f82018-03-02 20:05:29 -0800294 if (zeroed_) {
295 LOG(ERROR, "NAN on absolute encoder\n");
296 error_ = true;
297 } else {
298 ++nan_samples_;
299 LOG(ERROR, "NAN on absolute encoder while zeroing %d\n",
300 static_cast<int>(nan_samples_));
301 if (nan_samples_ >= constants_.average_filter_size) {
302 error_ = true;
303 zeroed_ = true;
304 }
305 }
306 // Throw some dummy values in for now.
307 filtered_absolute_encoder_ = info.absolute_encoder;
308 filtered_position_ = pot_relative_encoder_offset_ + info.encoder;
309 position_ = offset_ + info.encoder;
Neil Balch16275e32017-02-18 16:38:45 -0800310 return;
311 }
312
Austin Schuh66c59ba2019-01-26 20:34:35 -0800313 const bool moving = move_detector_.Update(info, constants_.moving_buffer_size,
314 constants_.zeroing_threshold);
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800315
316 if (!moving) {
Austin Schuh66c59ba2019-01-26 20:34:35 -0800317 const PotAndAbsolutePosition &sample = move_detector_.GetSample();
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800318
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800319 // Compute the average offset between the absolute encoder and relative
320 // encoder. If we have 0 samples, assume it is 0.
321 double average_relative_to_absolute_offset =
322 relative_to_absolute_offset_samples_.size() == 0
323 ? 0.0
Austin Schuha8f88d42019-01-26 12:33:54 -0800324 : ::std::accumulate(relative_to_absolute_offset_samples_.begin(),
325 relative_to_absolute_offset_samples_.end(),
326 0.0) /
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800327 relative_to_absolute_offset_samples_.size();
328
Austin Schuh0e1c2c62017-02-21 02:03:25 -0800329 const double adjusted_incremental_encoder =
Austin Schuh409ffe02019-01-21 18:46:41 -0800330 sample.encoder + average_relative_to_absolute_offset;
Austin Schuh0e1c2c62017-02-21 02:03:25 -0800331
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800332 // Now, compute the nearest absolute encoder value to the offset relative
333 // encoder position.
334 const double adjusted_absolute_encoder =
Austin Schuhd82068e2019-01-26 20:05:42 -0800335 UnWrap(adjusted_incremental_encoder,
336 sample.absolute_encoder - constants_.measured_absolute_position,
337 constants_.one_revolution_distance);
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800338
Austin Schuhd82068e2019-01-26 20:05:42 -0800339 // We can now compute the offset now that we have unwrapped the absolute
340 // encoder.
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800341 const double relative_to_absolute_offset =
Austin Schuh409ffe02019-01-21 18:46:41 -0800342 adjusted_absolute_encoder - sample.encoder;
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800343
344 // Add the sample and update the average with the new reading.
345 const size_t relative_to_absolute_offset_samples_size =
346 relative_to_absolute_offset_samples_.size();
347 if (relative_to_absolute_offset_samples_size <
348 constants_.average_filter_size) {
349 average_relative_to_absolute_offset =
350 (average_relative_to_absolute_offset *
351 relative_to_absolute_offset_samples_size +
352 relative_to_absolute_offset) /
353 (relative_to_absolute_offset_samples_size + 1);
354
355 relative_to_absolute_offset_samples_.push_back(
356 relative_to_absolute_offset);
357 } else {
358 average_relative_to_absolute_offset -=
359 relative_to_absolute_offset_samples_[samples_idx_] /
360 relative_to_absolute_offset_samples_size;
361 relative_to_absolute_offset_samples_[samples_idx_] =
362 relative_to_absolute_offset;
363 average_relative_to_absolute_offset +=
364 relative_to_absolute_offset /
365 relative_to_absolute_offset_samples_size;
366 }
367
368 // Now compute the offset between the pot and relative encoder.
369 if (offset_samples_.size() < constants_.average_filter_size) {
Austin Schuh409ffe02019-01-21 18:46:41 -0800370 offset_samples_.push_back(sample.pot - sample.encoder);
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800371 } else {
Austin Schuh409ffe02019-01-21 18:46:41 -0800372 offset_samples_[samples_idx_] = sample.pot - sample.encoder;
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800373 }
374
375 // Drop the oldest sample when we run this function the next time around.
376 samples_idx_ = (samples_idx_ + 1) % constants_.average_filter_size;
377
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800378 pot_relative_encoder_offset_ =
Austin Schuha8f88d42019-01-26 12:33:54 -0800379 ::std::accumulate(offset_samples_.begin(), offset_samples_.end(), 0.0) /
380 offset_samples_.size();
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800381
Austin Schuhd82068e2019-01-26 20:05:42 -0800382 offset_ = UnWrap(sample.encoder + pot_relative_encoder_offset_,
383 average_relative_to_absolute_offset + sample.encoder,
384 constants_.one_revolution_distance) -
Austin Schuh409ffe02019-01-21 18:46:41 -0800385 sample.encoder;
Austin Schuhd82068e2019-01-26 20:05:42 -0800386
387 // Reverse the math for adjusted_absolute_encoder to compute the absolute
388 // encoder. Do this by taking the adjusted encoder, and then subtracting off
389 // the second argument above, and the value that was added by Wrap.
390 filtered_absolute_encoder_ =
391 ((sample.encoder + average_relative_to_absolute_offset) -
392 (-constants_.measured_absolute_position +
393 (adjusted_absolute_encoder -
394 (sample.absolute_encoder - constants_.measured_absolute_position))));
395
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800396 if (offset_ready()) {
Brian Silvermana10d20a2017-02-19 14:28:53 -0800397 if (!zeroed_) {
398 first_offset_ = offset_;
399 }
400
401 if (::std::abs(first_offset_ - offset_) >
402 constants_.allowable_encoder_error *
403 constants_.one_revolution_distance) {
404 LOG(ERROR,
405 "Offset moved too far. Initial: %f, current %f, allowable change: "
406 "%f\n",
407 first_offset_, offset_, constants_.allowable_encoder_error *
408 constants_.one_revolution_distance);
409 error_ = true;
410 }
411
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800412 zeroed_ = true;
413 }
Austin Schuh5f01f152017-02-11 21:34:08 -0800414 }
415
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800416 // Update the position.
417 filtered_position_ = pot_relative_encoder_offset_ + info.encoder;
Austin Schuh5f01f152017-02-11 21:34:08 -0800418 position_ = offset_ + info.encoder;
419}
420
Austin Schuh72db9a12019-01-21 18:02:51 -0800421PotAndAbsoluteEncoderZeroingEstimator::State
422PotAndAbsoluteEncoderZeroingEstimator::GetEstimatorState() const {
Brian Silverman4f2e2ce2017-02-19 17:49:47 -0800423 State r;
424 r.error = error_;
425 r.zeroed = zeroed_;
426 r.position = position_;
427 r.pot_position = filtered_position_;
Austin Schuh0e1c2c62017-02-21 02:03:25 -0800428 r.absolute_position = filtered_absolute_encoder_;
Brian Silverman4f2e2ce2017-02-19 17:49:47 -0800429 return r;
430}
431
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000432void PulseIndexZeroingEstimator::Reset() {
433 max_index_position_ = ::std::numeric_limits<double>::lowest();
434 min_index_position_ = ::std::numeric_limits<double>::max();
435 offset_ = 0;
436 last_used_index_pulse_count_ = 0;
437 zeroed_ = false;
438 error_ = false;
439}
440
441void PulseIndexZeroingEstimator::StoreIndexPulseMaxAndMin(
442 const IndexPosition &info) {
443 // If we have a new index pulse.
444 if (last_used_index_pulse_count_ != info.index_pulses) {
445 // If the latest pulses's position is outside the range we've currently
446 // seen, record it appropriately.
447 if (info.latched_encoder > max_index_position_) {
448 max_index_position_ = info.latched_encoder;
449 }
450 if (info.latched_encoder < min_index_position_) {
451 min_index_position_ = info.latched_encoder;
452 }
453 last_used_index_pulse_count_ = info.index_pulses;
454 }
455}
456
Brian Silvermanf37839c2017-02-19 18:07:15 -0800457int PulseIndexZeroingEstimator::IndexPulseCount() const {
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000458 if (min_index_position_ > max_index_position_) {
459 // This condition means we haven't seen a pulse yet.
460 return 0;
461 }
462
463 // Calculate the number of pulses encountered so far.
464 return 1 + static_cast<int>(
465 ::std::round((max_index_position_ - min_index_position_) /
466 constants_.index_difference));
467}
468
469void PulseIndexZeroingEstimator::UpdateEstimate(const IndexPosition &info) {
470 StoreIndexPulseMaxAndMin(info);
471 const int index_pulse_count = IndexPulseCount();
472 if (index_pulse_count > constants_.index_pulse_count) {
Philipp Schrader3f5b6182017-03-25 22:36:37 +0000473 if (!error_) {
474 LOG(ERROR, "Got more index pulses than expected. Got %d expected %d.\n",
475 index_pulse_count, constants_.index_pulse_count);
476 error_ = true;
477 }
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000478 }
479
480 // TODO(austin): Detect if the encoder or index pulse is unplugged.
481 // TODO(austin): Detect missing counts.
482
483 if (index_pulse_count == constants_.index_pulse_count && !zeroed_) {
484 offset_ = constants_.measured_index_position -
485 constants_.known_index_pulse * constants_.index_difference -
486 min_index_position_;
487 zeroed_ = true;
Philipp Schrader3f5b6182017-03-25 22:36:37 +0000488 } else if (zeroed_ && !error_) {
489 // Detect whether the index pulse is somewhere other than where we expect
490 // it to be. First we compute the position of the most recent index pulse.
491 double index_pulse_distance =
492 info.latched_encoder + offset_ - constants_.measured_index_position;
493 // Second we compute the position of the index pulse in terms of
494 // the index difference. I.e. if this index pulse is two pulses away from
495 // the index pulse that we know about then this number should be positive
496 // or negative two.
497 double relative_distance =
498 index_pulse_distance / constants_.index_difference;
499 // Now we compute how far away the measured index pulse is from the
500 // expected index pulse.
501 double error = relative_distance - ::std::round(relative_distance);
502 // This lets us check if the index pulse is within an acceptable error
503 // margin of where we expected it to be.
504 if (::std::abs(error) > constants_.allowable_encoder_error) {
505 LOG(ERROR,
506 "Encoder ticks out of range since last index pulse. known index "
507 "pulse: %f, expected index pulse: %f, actual index pulse: %f, "
508 "allowable error: %f\n",
509 constants_.measured_index_position,
510 round(relative_distance) * constants_.index_difference +
511 constants_.measured_index_position,
512 info.latched_encoder + offset_,
513 constants_.allowable_encoder_error * constants_.index_difference);
514 error_ = true;
515 }
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000516 }
Brian Silvermanf37839c2017-02-19 18:07:15 -0800517
518 position_ = info.encoder + offset_;
519}
520
521PulseIndexZeroingEstimator::State
522PulseIndexZeroingEstimator::GetEstimatorState() const {
523 State r;
524 r.error = error_;
525 r.zeroed = zeroed_;
526 r.position = position_;
527 r.min_index_position = min_index_position_;
528 r.max_index_position = max_index_position_;
529 r.index_pulses_seen = IndexPulseCount();
530 return r;
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000531}
532
Austin Schuhd82068e2019-01-26 20:05:42 -0800533AbsoluteEncoderZeroingEstimator::AbsoluteEncoderZeroingEstimator(
534 const constants::AbsoluteEncoderZeroingConstants &constants)
535 : constants_(constants), move_detector_(constants_.moving_buffer_size) {
536 relative_to_absolute_offset_samples_.reserve(constants_.average_filter_size);
537 Reset();
538}
539
540void AbsoluteEncoderZeroingEstimator::Reset() {
541 zeroed_ = false;
542 error_ = false;
543 first_offset_ = 0.0;
544 offset_ = 0.0;
545 samples_idx_ = 0;
546 position_ = 0.0;
547 nan_samples_ = 0;
548 relative_to_absolute_offset_samples_.clear();
549 move_detector_.Reset();
550}
551
552
553// The math here is a bit backwards, but I think it'll be less error prone that
554// way and more similar to the version with a pot as well.
555//
556// We start by unwrapping the absolute encoder using the relative encoder. This
557// puts us in a non-wrapping space and lets us average a bit easier. From
558// there, we can compute an offset and wrap ourselves back such that we stay
559// close to the middle value.
560//
561// To guard against the robot moving while updating estimates, buffer a number
562// of samples and check that the buffered samples are not different than the
563// zeroing threshold. At any point that the samples differ too much, do not
564// update estimates based on those samples.
565void AbsoluteEncoderZeroingEstimator::UpdateEstimate(
566 const AbsolutePosition &info) {
567 // Check for Abs Encoder NaN value that would mess up the rest of the zeroing
568 // code below. NaN values are given when the Absolute Encoder is disconnected.
569 if (::std::isnan(info.absolute_encoder)) {
570 if (zeroed_) {
571 LOG(ERROR, "NAN on absolute encoder\n");
572 error_ = true;
573 } else {
574 ++nan_samples_;
575 LOG(ERROR, "NAN on absolute encoder while zeroing %d\n",
576 static_cast<int>(nan_samples_));
577 if (nan_samples_ >= constants_.average_filter_size) {
578 error_ = true;
579 zeroed_ = true;
580 }
581 }
582 // Throw some dummy values in for now.
583 filtered_absolute_encoder_ = info.absolute_encoder;
584 position_ = offset_ + info.encoder;
585 return;
586 }
587
588 const bool moving = move_detector_.Update(info, constants_.moving_buffer_size,
589 constants_.zeroing_threshold);
590
591 if (!moving) {
592 const AbsolutePosition &sample = move_detector_.GetSample();
593
594 // Compute the average offset between the absolute encoder and relative
595 // encoder. If we have 0 samples, assume it is 0.
596 double average_relative_to_absolute_offset =
597 relative_to_absolute_offset_samples_.size() == 0
598 ? 0.0
599 : ::std::accumulate(relative_to_absolute_offset_samples_.begin(),
600 relative_to_absolute_offset_samples_.end(),
601 0.0) /
602 relative_to_absolute_offset_samples_.size();
603
604 // Now, compute the estimated absolute position using the previously
605 // estimated offset and the incremental encoder.
606 const double adjusted_incremental_encoder =
607 sample.encoder + average_relative_to_absolute_offset;
608
609 // Now, compute the absolute encoder value nearest to the offset relative
610 // encoder position.
611 const double adjusted_absolute_encoder =
612 UnWrap(adjusted_incremental_encoder,
613 sample.absolute_encoder - constants_.measured_absolute_position,
614 constants_.one_revolution_distance);
615
616 // We can now compute the offset now that we have unwrapped the absolute
617 // encoder.
618 const double relative_to_absolute_offset =
619 adjusted_absolute_encoder - sample.encoder;
620
621 // Add the sample and update the average with the new reading.
622 const size_t relative_to_absolute_offset_samples_size =
623 relative_to_absolute_offset_samples_.size();
624 if (relative_to_absolute_offset_samples_size <
625 constants_.average_filter_size) {
626 average_relative_to_absolute_offset =
627 (average_relative_to_absolute_offset *
628 relative_to_absolute_offset_samples_size +
629 relative_to_absolute_offset) /
630 (relative_to_absolute_offset_samples_size + 1);
631
632 relative_to_absolute_offset_samples_.push_back(
633 relative_to_absolute_offset);
634 } else {
635 average_relative_to_absolute_offset -=
636 relative_to_absolute_offset_samples_[samples_idx_] /
637 relative_to_absolute_offset_samples_size;
638 relative_to_absolute_offset_samples_[samples_idx_] =
639 relative_to_absolute_offset;
640 average_relative_to_absolute_offset +=
641 relative_to_absolute_offset /
642 relative_to_absolute_offset_samples_size;
643 }
644
645 // Drop the oldest sample when we run this function the next time around.
646 samples_idx_ = (samples_idx_ + 1) % constants_.average_filter_size;
647
648 // And our offset is the offset that gives us the position within +- ord/2
649 // of the middle position.
650 offset_ = Wrap(constants_.middle_position,
651 average_relative_to_absolute_offset + sample.encoder,
652 constants_.one_revolution_distance) -
653 sample.encoder;
654
655 // Reverse the math for adjusted_absolute_encoder to compute the absolute
656 // encoder. Do this by taking the adjusted encoder, and then subtracting off
657 // the second argument above, and the value that was added by Wrap.
658 filtered_absolute_encoder_ =
659 ((sample.encoder + average_relative_to_absolute_offset) -
660 (-constants_.measured_absolute_position +
661 (adjusted_absolute_encoder -
662 (sample.absolute_encoder - constants_.measured_absolute_position))));
663
664 if (offset_ready()) {
665 if (!zeroed_) {
666 first_offset_ = offset_;
667 }
668
669 if (::std::abs(first_offset_ - offset_) >
670 constants_.allowable_encoder_error *
671 constants_.one_revolution_distance) {
672 LOG(ERROR,
673 "Offset moved too far. Initial: %f, current %f, allowable change: "
674 "%f\n",
675 first_offset_, offset_, constants_.allowable_encoder_error *
676 constants_.one_revolution_distance);
677 error_ = true;
678 }
679
680 zeroed_ = true;
681 }
682 }
683
684 // Update the position.
685 position_ = offset_ + info.encoder;
686}
687
688AbsoluteEncoderZeroingEstimator::State
689 AbsoluteEncoderZeroingEstimator::GetEstimatorState() const {
690 State r;
691 r.error = error_;
692 r.zeroed = zeroed_;
693 r.position = position_;
694 r.absolute_position = filtered_absolute_encoder_;
695 return r;
696}
697
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000698} // namespace zeroing
699} // namespace frc971