blob: 1399efddad7199b6057deaaaad116fd8ad0aeb30 [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_) {
Austin Schuhf257f3c2019-10-27 21:00:43 -070033 AOS_LOG(ERROR, "Manually triggered zeroing error.\n");
Philipp Schrader53f4b6d2015-02-15 22:32:08 +000034 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_;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700101 AOS_LOG(INFO, "latching start position %f\n", first_start_pos_);
Adam Snaider3cd11c52015-02-16 02:16:09 +0000102 }
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_) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700112 AOS_LOG(
113 ERROR,
Adam Snaider3cd11c52015-02-16 02:16:09 +0000114 "Encoder ticks out of range since last index pulse. first start "
Austin Schuh1c85bc82016-04-03 21:36:31 -0700115 "position: %f recent starting position: %f, allowable error: %f\n",
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000116 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 error_ = true;
119 }
120 }
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000121 }
Adam Snaiderb4119252015-02-15 01:30:57 +0000122
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000123 position_ = offset_ + info.encoder;
Austin Schuhbe133ed2016-03-11 21:23:34 -0800124 filtered_position_ = start_average + info.encoder;
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000125}
126
Brian Silverman4f2e2ce2017-02-19 17:49:47 -0800127PotAndIndexPulseZeroingEstimator::State
128PotAndIndexPulseZeroingEstimator::GetEstimatorState() const {
129 State r;
130 r.error = error_;
131 r.zeroed = zeroed_;
132 r.position = position_;
133 r.pot_position = filtered_position_;
134 return r;
135}
136
Austin Schuh55934032017-03-11 12:45:27 -0800137HallEffectAndPositionZeroingEstimator::HallEffectAndPositionZeroingEstimator(
138 const ZeroingConstants &constants)
139 : constants_(constants) {
140 Reset();
141}
142
143void HallEffectAndPositionZeroingEstimator::Reset() {
144 offset_ = 0.0;
145 min_low_position_ = ::std::numeric_limits<double>::max();
146 max_low_position_ = ::std::numeric_limits<double>::lowest();
147 zeroed_ = false;
148 initialized_ = false;
149 last_used_posedge_count_ = 0;
150 cycles_high_ = 0;
151 high_long_enough_ = false;
152 first_start_pos_ = 0.0;
153 error_ = false;
154 current_ = 0.0;
155 first_start_pos_ = 0.0;
156}
157
158void HallEffectAndPositionZeroingEstimator::TriggerError() {
159 if (!error_) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700160 AOS_LOG(ERROR, "Manually triggered zeroing error.\n");
Austin Schuh55934032017-03-11 12:45:27 -0800161 error_ = true;
162 }
163}
164
165void HallEffectAndPositionZeroingEstimator::StoreEncoderMaxAndMin(
166 const HallEffectAndPosition &info) {
167 // If we have a new posedge.
168 if (!info.current) {
169 if (last_hall_) {
Lee Mracek598a2452019-01-07 00:50:44 -0800170 min_low_position_ = max_low_position_ = info.encoder;
Austin Schuh55934032017-03-11 12:45:27 -0800171 } else {
Lee Mracek598a2452019-01-07 00:50:44 -0800172 min_low_position_ = ::std::min(min_low_position_, info.encoder);
173 max_low_position_ = ::std::max(max_low_position_, info.encoder);
Austin Schuh55934032017-03-11 12:45:27 -0800174 }
175 }
176 last_hall_ = info.current;
177}
178
179void HallEffectAndPositionZeroingEstimator::UpdateEstimate(
180 const HallEffectAndPosition &info) {
181 // We want to make sure that we encounter at least one posedge while zeroing.
182 // So we take the posedge count from the first sample after reset and wait for
183 // that count to change and for the hall effect to stay high before we
184 // consider ourselves zeroed.
185 if (!initialized_) {
186 last_used_posedge_count_ = info.posedge_count;
187 initialized_ = true;
188 last_hall_ = info.current;
189 }
190
191 StoreEncoderMaxAndMin(info);
192
193 if (info.current) {
194 cycles_high_++;
195 } else {
196 cycles_high_ = 0;
197 last_used_posedge_count_ = info.posedge_count;
198 }
199
200 high_long_enough_ = cycles_high_ >= constants_.hall_trigger_zeroing_length;
201
202 bool moving_backward = false;
203 if (constants_.zeroing_move_direction) {
Lee Mracek598a2452019-01-07 00:50:44 -0800204 moving_backward = info.encoder > min_low_position_;
Austin Schuh55934032017-03-11 12:45:27 -0800205 } else {
Lee Mracek598a2452019-01-07 00:50:44 -0800206 moving_backward = info.encoder < max_low_position_;
Austin Schuh55934032017-03-11 12:45:27 -0800207 }
208
209 // If there are no posedges to use or we don't have enough samples yet to
210 // have a well-filtered starting position then we use the filtered value as
211 // our best guess.
212 if (last_used_posedge_count_ != info.posedge_count && high_long_enough_ &&
213 moving_backward) {
214 // Note the offset and the current posedge count so that we only run this
215 // logic once per posedge. That should be more resilient to corrupted
216 // intermediate data.
217 offset_ = -info.posedge_value;
218 if (constants_.zeroing_move_direction) {
219 offset_ += constants_.lower_hall_position;
220 } else {
221 offset_ += constants_.upper_hall_position;
222 }
223 last_used_posedge_count_ = info.posedge_count;
224
225 // Save the first starting position.
226 if (!zeroed_) {
227 first_start_pos_ = offset_;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700228 AOS_LOG(INFO, "latching start position %f\n", first_start_pos_);
Austin Schuh55934032017-03-11 12:45:27 -0800229 }
230
231 // Now that we have an accurate starting position we can consider ourselves
232 // zeroed.
233 zeroed_ = true;
234 }
235
Lee Mracek598a2452019-01-07 00:50:44 -0800236 position_ = info.encoder - offset_;
Austin Schuh55934032017-03-11 12:45:27 -0800237}
238
239HallEffectAndPositionZeroingEstimator::State
240HallEffectAndPositionZeroingEstimator::GetEstimatorState() const {
241 State r;
242 r.error = error_;
243 r.zeroed = zeroed_;
244 r.encoder = position_;
245 r.high_long_enough = high_long_enough_;
246 r.offset = offset_;
247 return r;
248}
Brian Silverman4f2e2ce2017-02-19 17:49:47 -0800249
Austin Schuh72db9a12019-01-21 18:02:51 -0800250PotAndAbsoluteEncoderZeroingEstimator::PotAndAbsoluteEncoderZeroingEstimator(
Austin Schuh5f01f152017-02-11 21:34:08 -0800251 const constants::PotAndAbsoluteEncoderZeroingConstants &constants)
Austin Schuh66c59ba2019-01-26 20:34:35 -0800252 : constants_(constants), move_detector_(constants_.moving_buffer_size) {
Austin Schuh5f01f152017-02-11 21:34:08 -0800253 relative_to_absolute_offset_samples_.reserve(constants_.average_filter_size);
254 offset_samples_.reserve(constants_.average_filter_size);
255 Reset();
256}
257
Austin Schuh72db9a12019-01-21 18:02:51 -0800258void PotAndAbsoluteEncoderZeroingEstimator::Reset() {
Austin Schuhddd08f82018-03-02 20:05:29 -0800259 first_offset_ = 0.0;
260 pot_relative_encoder_offset_ = 0.0;
261 offset_ = 0.0;
262 samples_idx_ = 0;
263 filtered_position_ = 0.0;
264 position_ = 0.0;
Austin Schuh5f01f152017-02-11 21:34:08 -0800265 zeroed_ = false;
Austin Schuhddd08f82018-03-02 20:05:29 -0800266 nan_samples_ = 0;
Austin Schuh5f01f152017-02-11 21:34:08 -0800267 relative_to_absolute_offset_samples_.clear();
268 offset_samples_.clear();
Austin Schuh66c59ba2019-01-26 20:34:35 -0800269 move_detector_.Reset();
Brian Silvermana10d20a2017-02-19 14:28:53 -0800270 error_ = false;
Austin Schuh5f01f152017-02-11 21:34:08 -0800271}
272
273// So, this needs to be a multistep process. We need to first estimate the
274// offset between the absolute encoder and the relative encoder. That process
275// should get us an absolute number which is off by integer multiples of the
276// distance/rev. In parallel, we can estimate the offset between the pot and
277// encoder. When both estimates have converged, we can then compute the offset
278// in a cycle, and which cycle, which gives us the accurate global offset.
279//
280// It's tricky to compute the offset between the absolute and relative encoder.
281// We need to compute this inside 1 revolution. The easiest way to do this
282// would be to wrap the encoder, subtract the two of them, and then average the
283// result. That will struggle when they are off by PI. Instead, we need to
284// wrap the number to +- PI from the current averaged offset.
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800285//
286// To guard against the robot moving while updating estimates, buffer a number
287// of samples and check that the buffered samples are not different than the
288// zeroing threshold. At any point that the samples differ too much, do not
289// update estimates based on those samples.
Austin Schuh72db9a12019-01-21 18:02:51 -0800290void PotAndAbsoluteEncoderZeroingEstimator::UpdateEstimate(
Austin Schuh5f01f152017-02-11 21:34:08 -0800291 const PotAndAbsolutePosition &info) {
Neil Balch16275e32017-02-18 16:38:45 -0800292 // Check for Abs Encoder NaN value that would mess up the rest of the zeroing
293 // code below. NaN values are given when the Absolute Encoder is disconnected.
294 if (::std::isnan(info.absolute_encoder)) {
Austin Schuhddd08f82018-03-02 20:05:29 -0800295 if (zeroed_) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700296 AOS_LOG(ERROR, "NAN on absolute encoder\n");
Austin Schuhddd08f82018-03-02 20:05:29 -0800297 error_ = true;
298 } else {
299 ++nan_samples_;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700300 AOS_LOG(ERROR, "NAN on absolute encoder while zeroing %d\n",
301 static_cast<int>(nan_samples_));
Austin Schuhddd08f82018-03-02 20:05:29 -0800302 if (nan_samples_ >= constants_.average_filter_size) {
303 error_ = true;
304 zeroed_ = true;
305 }
306 }
307 // Throw some dummy values in for now.
308 filtered_absolute_encoder_ = info.absolute_encoder;
309 filtered_position_ = pot_relative_encoder_offset_ + info.encoder;
310 position_ = offset_ + info.encoder;
Neil Balch16275e32017-02-18 16:38:45 -0800311 return;
312 }
313
Austin Schuh66c59ba2019-01-26 20:34:35 -0800314 const bool moving = move_detector_.Update(info, constants_.moving_buffer_size,
315 constants_.zeroing_threshold);
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800316
317 if (!moving) {
Austin Schuh66c59ba2019-01-26 20:34:35 -0800318 const PotAndAbsolutePosition &sample = move_detector_.GetSample();
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800319
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800320 // Compute the average offset between the absolute encoder and relative
321 // encoder. If we have 0 samples, assume it is 0.
322 double average_relative_to_absolute_offset =
323 relative_to_absolute_offset_samples_.size() == 0
324 ? 0.0
Austin Schuha8f88d42019-01-26 12:33:54 -0800325 : ::std::accumulate(relative_to_absolute_offset_samples_.begin(),
326 relative_to_absolute_offset_samples_.end(),
327 0.0) /
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800328 relative_to_absolute_offset_samples_.size();
329
Austin Schuh0e1c2c62017-02-21 02:03:25 -0800330 const double adjusted_incremental_encoder =
Austin Schuh409ffe02019-01-21 18:46:41 -0800331 sample.encoder + average_relative_to_absolute_offset;
Austin Schuh0e1c2c62017-02-21 02:03:25 -0800332
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800333 // Now, compute the nearest absolute encoder value to the offset relative
334 // encoder position.
335 const double adjusted_absolute_encoder =
Austin Schuhd82068e2019-01-26 20:05:42 -0800336 UnWrap(adjusted_incremental_encoder,
337 sample.absolute_encoder - constants_.measured_absolute_position,
338 constants_.one_revolution_distance);
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800339
Austin Schuhd82068e2019-01-26 20:05:42 -0800340 // We can now compute the offset now that we have unwrapped the absolute
341 // encoder.
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800342 const double relative_to_absolute_offset =
Austin Schuh409ffe02019-01-21 18:46:41 -0800343 adjusted_absolute_encoder - sample.encoder;
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800344
345 // Add the sample and update the average with the new reading.
346 const size_t relative_to_absolute_offset_samples_size =
347 relative_to_absolute_offset_samples_.size();
348 if (relative_to_absolute_offset_samples_size <
349 constants_.average_filter_size) {
350 average_relative_to_absolute_offset =
351 (average_relative_to_absolute_offset *
352 relative_to_absolute_offset_samples_size +
353 relative_to_absolute_offset) /
354 (relative_to_absolute_offset_samples_size + 1);
355
356 relative_to_absolute_offset_samples_.push_back(
357 relative_to_absolute_offset);
358 } else {
359 average_relative_to_absolute_offset -=
360 relative_to_absolute_offset_samples_[samples_idx_] /
361 relative_to_absolute_offset_samples_size;
362 relative_to_absolute_offset_samples_[samples_idx_] =
363 relative_to_absolute_offset;
364 average_relative_to_absolute_offset +=
365 relative_to_absolute_offset /
366 relative_to_absolute_offset_samples_size;
367 }
368
369 // Now compute the offset between the pot and relative encoder.
370 if (offset_samples_.size() < constants_.average_filter_size) {
Austin Schuh409ffe02019-01-21 18:46:41 -0800371 offset_samples_.push_back(sample.pot - sample.encoder);
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800372 } else {
Austin Schuh409ffe02019-01-21 18:46:41 -0800373 offset_samples_[samples_idx_] = sample.pot - sample.encoder;
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800374 }
375
376 // Drop the oldest sample when we run this function the next time around.
377 samples_idx_ = (samples_idx_ + 1) % constants_.average_filter_size;
378
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800379 pot_relative_encoder_offset_ =
Austin Schuha8f88d42019-01-26 12:33:54 -0800380 ::std::accumulate(offset_samples_.begin(), offset_samples_.end(), 0.0) /
381 offset_samples_.size();
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800382
Austin Schuhd82068e2019-01-26 20:05:42 -0800383 offset_ = UnWrap(sample.encoder + pot_relative_encoder_offset_,
384 average_relative_to_absolute_offset + sample.encoder,
385 constants_.one_revolution_distance) -
Austin Schuh409ffe02019-01-21 18:46:41 -0800386 sample.encoder;
Austin Schuhd82068e2019-01-26 20:05:42 -0800387
388 // Reverse the math for adjusted_absolute_encoder to compute the absolute
389 // encoder. Do this by taking the adjusted encoder, and then subtracting off
390 // the second argument above, and the value that was added by Wrap.
391 filtered_absolute_encoder_ =
392 ((sample.encoder + average_relative_to_absolute_offset) -
393 (-constants_.measured_absolute_position +
394 (adjusted_absolute_encoder -
395 (sample.absolute_encoder - constants_.measured_absolute_position))));
396
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800397 if (offset_ready()) {
Brian Silvermana10d20a2017-02-19 14:28:53 -0800398 if (!zeroed_) {
399 first_offset_ = offset_;
400 }
401
402 if (::std::abs(first_offset_ - offset_) >
403 constants_.allowable_encoder_error *
404 constants_.one_revolution_distance) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700405 AOS_LOG(
406 ERROR,
Brian Silvermana10d20a2017-02-19 14:28:53 -0800407 "Offset moved too far. Initial: %f, current %f, allowable change: "
408 "%f\n",
Austin Schuhf257f3c2019-10-27 21:00:43 -0700409 first_offset_, offset_,
410 constants_.allowable_encoder_error *
411 constants_.one_revolution_distance);
Brian Silvermana10d20a2017-02-19 14:28:53 -0800412 error_ = true;
413 }
414
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800415 zeroed_ = true;
416 }
Austin Schuh5f01f152017-02-11 21:34:08 -0800417 }
418
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800419 // Update the position.
420 filtered_position_ = pot_relative_encoder_offset_ + info.encoder;
Austin Schuh5f01f152017-02-11 21:34:08 -0800421 position_ = offset_ + info.encoder;
422}
423
Austin Schuh72db9a12019-01-21 18:02:51 -0800424PotAndAbsoluteEncoderZeroingEstimator::State
425PotAndAbsoluteEncoderZeroingEstimator::GetEstimatorState() const {
Brian Silverman4f2e2ce2017-02-19 17:49:47 -0800426 State r;
427 r.error = error_;
428 r.zeroed = zeroed_;
429 r.position = position_;
430 r.pot_position = filtered_position_;
Austin Schuh0e1c2c62017-02-21 02:03:25 -0800431 r.absolute_position = filtered_absolute_encoder_;
Brian Silverman4f2e2ce2017-02-19 17:49:47 -0800432 return r;
433}
434
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000435void PulseIndexZeroingEstimator::Reset() {
436 max_index_position_ = ::std::numeric_limits<double>::lowest();
437 min_index_position_ = ::std::numeric_limits<double>::max();
438 offset_ = 0;
439 last_used_index_pulse_count_ = 0;
440 zeroed_ = false;
441 error_ = false;
442}
443
444void PulseIndexZeroingEstimator::StoreIndexPulseMaxAndMin(
445 const IndexPosition &info) {
446 // If we have a new index pulse.
447 if (last_used_index_pulse_count_ != info.index_pulses) {
448 // If the latest pulses's position is outside the range we've currently
449 // seen, record it appropriately.
450 if (info.latched_encoder > max_index_position_) {
451 max_index_position_ = info.latched_encoder;
452 }
453 if (info.latched_encoder < min_index_position_) {
454 min_index_position_ = info.latched_encoder;
455 }
456 last_used_index_pulse_count_ = info.index_pulses;
457 }
458}
459
Brian Silvermanf37839c2017-02-19 18:07:15 -0800460int PulseIndexZeroingEstimator::IndexPulseCount() const {
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000461 if (min_index_position_ > max_index_position_) {
462 // This condition means we haven't seen a pulse yet.
463 return 0;
464 }
465
466 // Calculate the number of pulses encountered so far.
467 return 1 + static_cast<int>(
468 ::std::round((max_index_position_ - min_index_position_) /
469 constants_.index_difference));
470}
471
472void PulseIndexZeroingEstimator::UpdateEstimate(const IndexPosition &info) {
473 StoreIndexPulseMaxAndMin(info);
474 const int index_pulse_count = IndexPulseCount();
475 if (index_pulse_count > constants_.index_pulse_count) {
Philipp Schrader3f5b6182017-03-25 22:36:37 +0000476 if (!error_) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700477 AOS_LOG(ERROR,
478 "Got more index pulses than expected. Got %d expected %d.\n",
479 index_pulse_count, constants_.index_pulse_count);
Philipp Schrader3f5b6182017-03-25 22:36:37 +0000480 error_ = true;
481 }
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000482 }
483
484 // TODO(austin): Detect if the encoder or index pulse is unplugged.
485 // TODO(austin): Detect missing counts.
486
487 if (index_pulse_count == constants_.index_pulse_count && !zeroed_) {
488 offset_ = constants_.measured_index_position -
489 constants_.known_index_pulse * constants_.index_difference -
490 min_index_position_;
491 zeroed_ = true;
Philipp Schrader3f5b6182017-03-25 22:36:37 +0000492 } else if (zeroed_ && !error_) {
493 // Detect whether the index pulse is somewhere other than where we expect
494 // it to be. First we compute the position of the most recent index pulse.
495 double index_pulse_distance =
496 info.latched_encoder + offset_ - constants_.measured_index_position;
497 // Second we compute the position of the index pulse in terms of
498 // the index difference. I.e. if this index pulse is two pulses away from
499 // the index pulse that we know about then this number should be positive
500 // or negative two.
501 double relative_distance =
502 index_pulse_distance / constants_.index_difference;
503 // Now we compute how far away the measured index pulse is from the
504 // expected index pulse.
505 double error = relative_distance - ::std::round(relative_distance);
506 // This lets us check if the index pulse is within an acceptable error
507 // margin of where we expected it to be.
508 if (::std::abs(error) > constants_.allowable_encoder_error) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700509 AOS_LOG(ERROR,
510 "Encoder ticks out of range since last index pulse. known index "
511 "pulse: %f, expected index pulse: %f, actual index pulse: %f, "
512 "allowable error: %f\n",
Philipp Schrader3f5b6182017-03-25 22:36:37 +0000513 constants_.measured_index_position,
Austin Schuhf257f3c2019-10-27 21:00:43 -0700514 round(relative_distance) * constants_.index_difference +
515 constants_.measured_index_position,
516 info.latched_encoder + offset_,
517 constants_.allowable_encoder_error * constants_.index_difference);
Philipp Schrader3f5b6182017-03-25 22:36:37 +0000518 error_ = true;
519 }
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000520 }
Brian Silvermanf37839c2017-02-19 18:07:15 -0800521
522 position_ = info.encoder + offset_;
523}
524
525PulseIndexZeroingEstimator::State
526PulseIndexZeroingEstimator::GetEstimatorState() const {
527 State r;
528 r.error = error_;
529 r.zeroed = zeroed_;
530 r.position = position_;
531 r.min_index_position = min_index_position_;
532 r.max_index_position = max_index_position_;
533 r.index_pulses_seen = IndexPulseCount();
534 return r;
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000535}
536
Austin Schuhd82068e2019-01-26 20:05:42 -0800537AbsoluteEncoderZeroingEstimator::AbsoluteEncoderZeroingEstimator(
538 const constants::AbsoluteEncoderZeroingConstants &constants)
539 : constants_(constants), move_detector_(constants_.moving_buffer_size) {
540 relative_to_absolute_offset_samples_.reserve(constants_.average_filter_size);
541 Reset();
542}
543
544void AbsoluteEncoderZeroingEstimator::Reset() {
545 zeroed_ = false;
546 error_ = false;
547 first_offset_ = 0.0;
548 offset_ = 0.0;
549 samples_idx_ = 0;
550 position_ = 0.0;
551 nan_samples_ = 0;
552 relative_to_absolute_offset_samples_.clear();
553 move_detector_.Reset();
554}
555
556
557// The math here is a bit backwards, but I think it'll be less error prone that
558// way and more similar to the version with a pot as well.
559//
560// We start by unwrapping the absolute encoder using the relative encoder. This
561// puts us in a non-wrapping space and lets us average a bit easier. From
562// there, we can compute an offset and wrap ourselves back such that we stay
563// close to the middle value.
564//
565// To guard against the robot moving while updating estimates, buffer a number
566// of samples and check that the buffered samples are not different than the
567// zeroing threshold. At any point that the samples differ too much, do not
568// update estimates based on those samples.
569void AbsoluteEncoderZeroingEstimator::UpdateEstimate(
570 const AbsolutePosition &info) {
571 // Check for Abs Encoder NaN value that would mess up the rest of the zeroing
572 // code below. NaN values are given when the Absolute Encoder is disconnected.
573 if (::std::isnan(info.absolute_encoder)) {
574 if (zeroed_) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700575 AOS_LOG(ERROR, "NAN on absolute encoder\n");
Austin Schuhd82068e2019-01-26 20:05:42 -0800576 error_ = true;
577 } else {
578 ++nan_samples_;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700579 AOS_LOG(ERROR, "NAN on absolute encoder while zeroing %d\n",
580 static_cast<int>(nan_samples_));
Austin Schuhd82068e2019-01-26 20:05:42 -0800581 if (nan_samples_ >= constants_.average_filter_size) {
582 error_ = true;
583 zeroed_ = true;
584 }
585 }
586 // Throw some dummy values in for now.
587 filtered_absolute_encoder_ = info.absolute_encoder;
588 position_ = offset_ + info.encoder;
589 return;
590 }
591
592 const bool moving = move_detector_.Update(info, constants_.moving_buffer_size,
593 constants_.zeroing_threshold);
594
595 if (!moving) {
596 const AbsolutePosition &sample = move_detector_.GetSample();
597
598 // Compute the average offset between the absolute encoder and relative
599 // encoder. If we have 0 samples, assume it is 0.
600 double average_relative_to_absolute_offset =
601 relative_to_absolute_offset_samples_.size() == 0
602 ? 0.0
603 : ::std::accumulate(relative_to_absolute_offset_samples_.begin(),
604 relative_to_absolute_offset_samples_.end(),
605 0.0) /
606 relative_to_absolute_offset_samples_.size();
607
608 // Now, compute the estimated absolute position using the previously
609 // estimated offset and the incremental encoder.
610 const double adjusted_incremental_encoder =
611 sample.encoder + average_relative_to_absolute_offset;
612
613 // Now, compute the absolute encoder value nearest to the offset relative
614 // encoder position.
615 const double adjusted_absolute_encoder =
616 UnWrap(adjusted_incremental_encoder,
617 sample.absolute_encoder - constants_.measured_absolute_position,
618 constants_.one_revolution_distance);
619
620 // We can now compute the offset now that we have unwrapped the absolute
621 // encoder.
622 const double relative_to_absolute_offset =
623 adjusted_absolute_encoder - sample.encoder;
624
625 // Add the sample and update the average with the new reading.
626 const size_t relative_to_absolute_offset_samples_size =
627 relative_to_absolute_offset_samples_.size();
628 if (relative_to_absolute_offset_samples_size <
629 constants_.average_filter_size) {
630 average_relative_to_absolute_offset =
631 (average_relative_to_absolute_offset *
632 relative_to_absolute_offset_samples_size +
633 relative_to_absolute_offset) /
634 (relative_to_absolute_offset_samples_size + 1);
635
636 relative_to_absolute_offset_samples_.push_back(
637 relative_to_absolute_offset);
638 } else {
639 average_relative_to_absolute_offset -=
640 relative_to_absolute_offset_samples_[samples_idx_] /
641 relative_to_absolute_offset_samples_size;
642 relative_to_absolute_offset_samples_[samples_idx_] =
643 relative_to_absolute_offset;
644 average_relative_to_absolute_offset +=
645 relative_to_absolute_offset /
646 relative_to_absolute_offset_samples_size;
647 }
648
649 // Drop the oldest sample when we run this function the next time around.
650 samples_idx_ = (samples_idx_ + 1) % constants_.average_filter_size;
651
652 // And our offset is the offset that gives us the position within +- ord/2
653 // of the middle position.
654 offset_ = Wrap(constants_.middle_position,
655 average_relative_to_absolute_offset + sample.encoder,
656 constants_.one_revolution_distance) -
657 sample.encoder;
658
659 // Reverse the math for adjusted_absolute_encoder to compute the absolute
660 // encoder. Do this by taking the adjusted encoder, and then subtracting off
661 // the second argument above, and the value that was added by Wrap.
662 filtered_absolute_encoder_ =
663 ((sample.encoder + average_relative_to_absolute_offset) -
664 (-constants_.measured_absolute_position +
665 (adjusted_absolute_encoder -
666 (sample.absolute_encoder - constants_.measured_absolute_position))));
667
668 if (offset_ready()) {
669 if (!zeroed_) {
670 first_offset_ = offset_;
671 }
672
673 if (::std::abs(first_offset_ - offset_) >
674 constants_.allowable_encoder_error *
675 constants_.one_revolution_distance) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700676 AOS_LOG(
677 ERROR,
Austin Schuhd82068e2019-01-26 20:05:42 -0800678 "Offset moved too far. Initial: %f, current %f, allowable change: "
679 "%f\n",
Austin Schuhf257f3c2019-10-27 21:00:43 -0700680 first_offset_, offset_,
681 constants_.allowable_encoder_error *
682 constants_.one_revolution_distance);
Austin Schuhd82068e2019-01-26 20:05:42 -0800683 error_ = true;
684 }
685
686 zeroed_ = true;
687 }
688 }
689
690 // Update the position.
691 position_ = offset_ + info.encoder;
692}
693
694AbsoluteEncoderZeroingEstimator::State
695 AbsoluteEncoderZeroingEstimator::GetEstimatorState() const {
696 State r;
697 r.error = error_;
698 r.zeroed = zeroed_;
699 r.position = position_;
700 r.absolute_position = filtered_absolute_encoder_;
701 return r;
702}
703
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000704} // namespace zeroing
705} // namespace frc971