blob: f0aab233364c2e239a6d89986aceafeabaa8a033 [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
Alex Perrycb7da4b2019-08-28 19:35:56 -070011#include "flatbuffers/flatbuffers.h"
12#include "glog/logging.h"
13
Adam Snaiderc4b3c192015-02-01 01:30:39 +000014namespace frc971 {
15namespace zeroing {
16
Tyler Chatowf8f03112017-02-05 14:31:34 -080017PotAndIndexPulseZeroingEstimator::PotAndIndexPulseZeroingEstimator(
Austin Schuh5f01f152017-02-11 21:34:08 -080018 const constants::PotAndIndexPulseZeroingConstants &constants)
19 : constants_(constants) {
20 start_pos_samples_.reserve(constants_.average_filter_size);
Adam Snaiderb4119252015-02-15 01:30:57 +000021 Reset();
Austin Schuh703b8d42015-02-01 14:56:34 -080022}
23
Tyler Chatowf8f03112017-02-05 14:31:34 -080024void PotAndIndexPulseZeroingEstimator::Reset() {
Adam Snaiderc4b3c192015-02-01 01:30:39 +000025 samples_idx_ = 0;
Isaac Wilcove0851ffd2017-02-16 04:13:14 +000026 offset_ = 0;
Adam Snaiderb4119252015-02-15 01:30:57 +000027 start_pos_samples_.clear();
28 zeroed_ = false;
Philipp Schrader41d82912015-02-15 03:44:23 +000029 wait_for_index_pulse_ = true;
Philipp Schradere828be72015-02-15 07:07:37 +000030 last_used_index_pulse_count_ = 0;
Philipp Schrader53f4b6d2015-02-15 22:32:08 +000031 error_ = false;
32}
33
Tyler Chatowf8f03112017-02-05 14:31:34 -080034void PotAndIndexPulseZeroingEstimator::TriggerError() {
Philipp Schrader53f4b6d2015-02-15 22:32:08 +000035 if (!error_) {
Alex Perrycb7da4b2019-08-28 19:35:56 -070036 VLOG(1) << "Manually triggered zeroing error.";
Philipp Schrader53f4b6d2015-02-15 22:32:08 +000037 error_ = true;
38 }
Philipp Schradere828be72015-02-15 07:07:37 +000039}
40
Tyler Chatowf8f03112017-02-05 14:31:34 -080041double PotAndIndexPulseZeroingEstimator::CalculateStartPosition(
42 double start_average, double latched_encoder) const {
Philipp Schradere828be72015-02-15 07:07:37 +000043 // We calculate an aproximation of the value of the last index position.
44 // Also account for index pulses not lining up with integer multiples of the
45 // index_diff.
Austin Schuh5f01f152017-02-11 21:34:08 -080046 double index_pos =
47 start_average + latched_encoder - constants_.measured_index_position;
Philipp Schradere828be72015-02-15 07:07:37 +000048 // We round index_pos to the closest valid value of the index.
Austin Schuh5f01f152017-02-11 21:34:08 -080049 double accurate_index_pos = (round(index_pos / constants_.index_difference)) *
50 constants_.index_difference;
Philipp Schradere828be72015-02-15 07:07:37 +000051 // Now we reverse the first calculation to get the accurate start position.
Austin Schuh5f01f152017-02-11 21:34:08 -080052 return accurate_index_pos - latched_encoder +
53 constants_.measured_index_position;
Adam Snaiderc4b3c192015-02-01 01:30:39 +000054}
55
Tyler Chatowf8f03112017-02-05 14:31:34 -080056void PotAndIndexPulseZeroingEstimator::UpdateEstimate(
57 const PotAndIndexPosition &info) {
Philipp Schrader41d82912015-02-15 03:44:23 +000058 // We want to make sure that we encounter at least one index pulse while
59 // zeroing. So we take the index pulse count from the first sample after
60 // reset and wait for that count to change before we consider ourselves
61 // zeroed.
62 if (wait_for_index_pulse_) {
Alex Perrycb7da4b2019-08-28 19:35:56 -070063 last_used_index_pulse_count_ = info.index_pulses();
Philipp Schrader41d82912015-02-15 03:44:23 +000064 wait_for_index_pulse_ = false;
65 }
66
Austin Schuh5f01f152017-02-11 21:34:08 -080067 if (start_pos_samples_.size() < constants_.average_filter_size) {
Alex Perrycb7da4b2019-08-28 19:35:56 -070068 start_pos_samples_.push_back(info.pot() - info.encoder());
Adam Snaiderc4b3c192015-02-01 01:30:39 +000069 } else {
Alex Perrycb7da4b2019-08-28 19:35:56 -070070 start_pos_samples_[samples_idx_] = info.pot() - info.encoder();
Adam Snaiderc4b3c192015-02-01 01:30:39 +000071 }
Adam Snaiderb4119252015-02-15 01:30:57 +000072
73 // Drop the oldest sample when we run this function the next time around.
Austin Schuh5f01f152017-02-11 21:34:08 -080074 samples_idx_ = (samples_idx_ + 1) % constants_.average_filter_size;
Adam Snaiderc4b3c192015-02-01 01:30:39 +000075
Adam Snaiderb4119252015-02-15 01:30:57 +000076 double sample_sum = 0.0;
77
Adam Snaiderc4b3c192015-02-01 01:30:39 +000078 for (size_t i = 0; i < start_pos_samples_.size(); ++i) {
Adam Snaiderb4119252015-02-15 01:30:57 +000079 sample_sum += start_pos_samples_[i];
Adam Snaiderc4b3c192015-02-01 01:30:39 +000080 }
81
82 // Calculates the average of the starting position.
Adam Snaiderb4119252015-02-15 01:30:57 +000083 double start_average = sample_sum / start_pos_samples_.size();
84
85 // If there are no index pulses to use or we don't have enough samples yet to
86 // have a well-filtered starting position then we use the filtered value as
87 // our best guess.
Austin Schuh7485dbb2016-02-08 00:21:58 -080088 if (!zeroed_ &&
Alex Perrycb7da4b2019-08-28 19:35:56 -070089 (info.index_pulses() == last_used_index_pulse_count_ || !offset_ready())) {
Isaac Wilcove0851ffd2017-02-16 04:13:14 +000090 offset_ = start_average;
Alex Perrycb7da4b2019-08-28 19:35:56 -070091 } else if (!zeroed_ || last_used_index_pulse_count_ != info.index_pulses()) {
Philipp Schradere828be72015-02-15 07:07:37 +000092 // Note the accurate start position and the current index pulse count so
93 // that we only run this logic once per index pulse. That should be more
94 // resilient to corrupted intermediate data.
Alex Perrycb7da4b2019-08-28 19:35:56 -070095 offset_ = CalculateStartPosition(start_average, info.latched_encoder());
96 last_used_index_pulse_count_ = info.index_pulses();
Austin Schuh7485dbb2016-02-08 00:21:58 -080097
98 // TODO(austin): Reject encoder positions which have x% error rather than
99 // rounding to the closest index pulse.
100
Adam Snaider3cd11c52015-02-16 02:16:09 +0000101 // Save the first starting position.
102 if (!zeroed_) {
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000103 first_start_pos_ = offset_;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700104 VLOG(2) << "latching start position" << first_start_pos_;
Adam Snaider3cd11c52015-02-16 02:16:09 +0000105 }
Adam Snaiderb4119252015-02-15 01:30:57 +0000106
107 // Now that we have an accurate starting position we can consider ourselves
108 // zeroed.
Austin Schuh703b8d42015-02-01 14:56:34 -0800109 zeroed_ = true;
Adam Snaider3cd11c52015-02-16 02:16:09 +0000110 // Throw an error if first_start_pos is bigger/smaller than
Austin Schuh5f01f152017-02-11 21:34:08 -0800111 // constants_.allowable_encoder_error * index_diff + start_pos.
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000112 if (::std::abs(first_start_pos_ - offset_) >
Austin Schuh5f01f152017-02-11 21:34:08 -0800113 constants_.allowable_encoder_error * constants_.index_difference) {
Adam Snaider3cd11c52015-02-16 02:16:09 +0000114 if (!error_) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700115 VLOG(1)
116 << "Encoder ticks out of range since last index pulse. first start "
117 "position: "
118 << first_start_pos_ << " recent starting position: " << offset_
119 << ", allowable error: "
120 << constants_.allowable_encoder_error * constants_.index_difference;
Adam Snaider3cd11c52015-02-16 02:16:09 +0000121 error_ = true;
122 }
123 }
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000124 }
Adam Snaiderb4119252015-02-15 01:30:57 +0000125
Alex Perrycb7da4b2019-08-28 19:35:56 -0700126 position_ = offset_ + info.encoder();
127 filtered_position_ = start_average + info.encoder();
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000128}
129
Alex Perrycb7da4b2019-08-28 19:35:56 -0700130flatbuffers::Offset<PotAndIndexPulseZeroingEstimator::State>
131PotAndIndexPulseZeroingEstimator::GetEstimatorState(
132 flatbuffers::FlatBufferBuilder *fbb) const {
133 State::Builder builder(*fbb);
134 builder.add_error(error_);
135 builder.add_zeroed(zeroed_);
136 builder.add_position(position_);
137 builder.add_pot_position(filtered_position_);
138 return builder.Finish();
Brian Silverman4f2e2ce2017-02-19 17:49:47 -0800139}
140
Austin Schuh55934032017-03-11 12:45:27 -0800141HallEffectAndPositionZeroingEstimator::HallEffectAndPositionZeroingEstimator(
142 const ZeroingConstants &constants)
143 : constants_(constants) {
144 Reset();
145}
146
147void HallEffectAndPositionZeroingEstimator::Reset() {
148 offset_ = 0.0;
149 min_low_position_ = ::std::numeric_limits<double>::max();
150 max_low_position_ = ::std::numeric_limits<double>::lowest();
151 zeroed_ = false;
152 initialized_ = false;
153 last_used_posedge_count_ = 0;
154 cycles_high_ = 0;
155 high_long_enough_ = false;
156 first_start_pos_ = 0.0;
157 error_ = false;
158 current_ = 0.0;
159 first_start_pos_ = 0.0;
160}
161
162void HallEffectAndPositionZeroingEstimator::TriggerError() {
163 if (!error_) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700164 VLOG(1) << "Manually triggered zeroing error.\n";
Austin Schuh55934032017-03-11 12:45:27 -0800165 error_ = true;
166 }
167}
168
169void HallEffectAndPositionZeroingEstimator::StoreEncoderMaxAndMin(
170 const HallEffectAndPosition &info) {
171 // If we have a new posedge.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700172 if (!info.current()) {
Austin Schuh55934032017-03-11 12:45:27 -0800173 if (last_hall_) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700174 min_low_position_ = max_low_position_ = info.encoder();
Austin Schuh55934032017-03-11 12:45:27 -0800175 } else {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700176 min_low_position_ = ::std::min(min_low_position_, info.encoder());
177 max_low_position_ = ::std::max(max_low_position_, info.encoder());
Austin Schuh55934032017-03-11 12:45:27 -0800178 }
179 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700180 last_hall_ = info.current();
Austin Schuh55934032017-03-11 12:45:27 -0800181}
182
183void HallEffectAndPositionZeroingEstimator::UpdateEstimate(
184 const HallEffectAndPosition &info) {
185 // We want to make sure that we encounter at least one posedge while zeroing.
186 // So we take the posedge count from the first sample after reset and wait for
187 // that count to change and for the hall effect to stay high before we
188 // consider ourselves zeroed.
189 if (!initialized_) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700190 last_used_posedge_count_ = info.posedge_count();
Austin Schuh55934032017-03-11 12:45:27 -0800191 initialized_ = true;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700192 last_hall_ = info.current();
Austin Schuh55934032017-03-11 12:45:27 -0800193 }
194
195 StoreEncoderMaxAndMin(info);
196
Alex Perrycb7da4b2019-08-28 19:35:56 -0700197 if (info.current()) {
Austin Schuh55934032017-03-11 12:45:27 -0800198 cycles_high_++;
199 } else {
200 cycles_high_ = 0;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700201 last_used_posedge_count_ = info.posedge_count();
Austin Schuh55934032017-03-11 12:45:27 -0800202 }
203
204 high_long_enough_ = cycles_high_ >= constants_.hall_trigger_zeroing_length;
205
206 bool moving_backward = false;
207 if (constants_.zeroing_move_direction) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700208 moving_backward = info.encoder() > min_low_position_;
Austin Schuh55934032017-03-11 12:45:27 -0800209 } else {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700210 moving_backward = info.encoder() < max_low_position_;
Austin Schuh55934032017-03-11 12:45:27 -0800211 }
212
213 // If there are no posedges to use or we don't have enough samples yet to
214 // have a well-filtered starting position then we use the filtered value as
215 // our best guess.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700216 if (last_used_posedge_count_ != info.posedge_count() && high_long_enough_ &&
Austin Schuh55934032017-03-11 12:45:27 -0800217 moving_backward) {
218 // Note the offset and the current posedge count so that we only run this
219 // logic once per posedge. That should be more resilient to corrupted
220 // intermediate data.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700221 offset_ = -info.posedge_value();
Austin Schuh55934032017-03-11 12:45:27 -0800222 if (constants_.zeroing_move_direction) {
223 offset_ += constants_.lower_hall_position;
224 } else {
225 offset_ += constants_.upper_hall_position;
226 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700227 last_used_posedge_count_ = info.posedge_count();
Austin Schuh55934032017-03-11 12:45:27 -0800228
229 // Save the first starting position.
230 if (!zeroed_) {
231 first_start_pos_ = offset_;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700232 VLOG(2) << "latching start position" << first_start_pos_;
Austin Schuh55934032017-03-11 12:45:27 -0800233 }
234
235 // Now that we have an accurate starting position we can consider ourselves
236 // zeroed.
237 zeroed_ = true;
238 }
239
Alex Perrycb7da4b2019-08-28 19:35:56 -0700240 position_ = info.encoder() - offset_;
Austin Schuh55934032017-03-11 12:45:27 -0800241}
242
Alex Perrycb7da4b2019-08-28 19:35:56 -0700243flatbuffers::Offset<HallEffectAndPositionZeroingEstimator::State>
244HallEffectAndPositionZeroingEstimator::GetEstimatorState(
245 flatbuffers::FlatBufferBuilder *fbb) const {
246 State::Builder builder(*fbb);
247 builder.add_error(error_);
248 builder.add_zeroed(zeroed_);
249 builder.add_encoder(position_);
250 builder.add_high_long_enough(high_long_enough_);
251 builder.add_offset(offset_);
252 return builder.Finish();
Austin Schuh55934032017-03-11 12:45:27 -0800253}
Brian Silverman4f2e2ce2017-02-19 17:49:47 -0800254
Austin Schuh72db9a12019-01-21 18:02:51 -0800255PotAndAbsoluteEncoderZeroingEstimator::PotAndAbsoluteEncoderZeroingEstimator(
Austin Schuh5f01f152017-02-11 21:34:08 -0800256 const constants::PotAndAbsoluteEncoderZeroingConstants &constants)
Austin Schuh66c59ba2019-01-26 20:34:35 -0800257 : constants_(constants), move_detector_(constants_.moving_buffer_size) {
Austin Schuh5f01f152017-02-11 21:34:08 -0800258 relative_to_absolute_offset_samples_.reserve(constants_.average_filter_size);
259 offset_samples_.reserve(constants_.average_filter_size);
260 Reset();
261}
262
Austin Schuh72db9a12019-01-21 18:02:51 -0800263void PotAndAbsoluteEncoderZeroingEstimator::Reset() {
Austin Schuhddd08f82018-03-02 20:05:29 -0800264 first_offset_ = 0.0;
265 pot_relative_encoder_offset_ = 0.0;
266 offset_ = 0.0;
267 samples_idx_ = 0;
268 filtered_position_ = 0.0;
269 position_ = 0.0;
Austin Schuh5f01f152017-02-11 21:34:08 -0800270 zeroed_ = false;
Austin Schuhddd08f82018-03-02 20:05:29 -0800271 nan_samples_ = 0;
Austin Schuh5f01f152017-02-11 21:34:08 -0800272 relative_to_absolute_offset_samples_.clear();
273 offset_samples_.clear();
Austin Schuh66c59ba2019-01-26 20:34:35 -0800274 move_detector_.Reset();
Brian Silvermana10d20a2017-02-19 14:28:53 -0800275 error_ = false;
Austin Schuh5f01f152017-02-11 21:34:08 -0800276}
277
278// So, this needs to be a multistep process. We need to first estimate the
279// offset between the absolute encoder and the relative encoder. That process
280// should get us an absolute number which is off by integer multiples of the
281// distance/rev. In parallel, we can estimate the offset between the pot and
282// encoder. When both estimates have converged, we can then compute the offset
283// in a cycle, and which cycle, which gives us the accurate global offset.
284//
285// It's tricky to compute the offset between the absolute and relative encoder.
286// We need to compute this inside 1 revolution. The easiest way to do this
287// would be to wrap the encoder, subtract the two of them, and then average the
288// result. That will struggle when they are off by PI. Instead, we need to
289// wrap the number to +- PI from the current averaged offset.
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800290//
291// To guard against the robot moving while updating estimates, buffer a number
292// of samples and check that the buffered samples are not different than the
293// zeroing threshold. At any point that the samples differ too much, do not
294// update estimates based on those samples.
Austin Schuh72db9a12019-01-21 18:02:51 -0800295void PotAndAbsoluteEncoderZeroingEstimator::UpdateEstimate(
Austin Schuh5f01f152017-02-11 21:34:08 -0800296 const PotAndAbsolutePosition &info) {
Neil Balch16275e32017-02-18 16:38:45 -0800297 // Check for Abs Encoder NaN value that would mess up the rest of the zeroing
298 // code below. NaN values are given when the Absolute Encoder is disconnected.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700299 if (::std::isnan(info.absolute_encoder())) {
Austin Schuhddd08f82018-03-02 20:05:29 -0800300 if (zeroed_) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700301 VLOG(1) << "NAN on absolute encoder.";
Austin Schuhddd08f82018-03-02 20:05:29 -0800302 error_ = true;
303 } else {
304 ++nan_samples_;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700305 VLOG(1) << "NAN on absolute encoder while zeroing" << nan_samples_;
Austin Schuhddd08f82018-03-02 20:05:29 -0800306 if (nan_samples_ >= constants_.average_filter_size) {
307 error_ = true;
308 zeroed_ = true;
309 }
310 }
311 // Throw some dummy values in for now.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700312 filtered_absolute_encoder_ = info.absolute_encoder();
313 filtered_position_ = pot_relative_encoder_offset_ + info.encoder();
314 position_ = offset_ + info.encoder();
Neil Balch16275e32017-02-18 16:38:45 -0800315 return;
316 }
317
Austin Schuh66c59ba2019-01-26 20:34:35 -0800318 const bool moving = move_detector_.Update(info, constants_.moving_buffer_size,
319 constants_.zeroing_threshold);
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800320
321 if (!moving) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700322 const PositionStruct &sample = move_detector_.GetSample();
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800323
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800324 // Compute the average offset between the absolute encoder and relative
325 // encoder. If we have 0 samples, assume it is 0.
326 double average_relative_to_absolute_offset =
327 relative_to_absolute_offset_samples_.size() == 0
328 ? 0.0
Austin Schuha8f88d42019-01-26 12:33:54 -0800329 : ::std::accumulate(relative_to_absolute_offset_samples_.begin(),
330 relative_to_absolute_offset_samples_.end(),
331 0.0) /
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800332 relative_to_absolute_offset_samples_.size();
333
Austin Schuh0e1c2c62017-02-21 02:03:25 -0800334 const double adjusted_incremental_encoder =
Austin Schuh409ffe02019-01-21 18:46:41 -0800335 sample.encoder + average_relative_to_absolute_offset;
Austin Schuh0e1c2c62017-02-21 02:03:25 -0800336
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800337 // Now, compute the nearest absolute encoder value to the offset relative
338 // encoder position.
339 const double adjusted_absolute_encoder =
Austin Schuhd82068e2019-01-26 20:05:42 -0800340 UnWrap(adjusted_incremental_encoder,
341 sample.absolute_encoder - constants_.measured_absolute_position,
342 constants_.one_revolution_distance);
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800343
Austin Schuhd82068e2019-01-26 20:05:42 -0800344 // We can now compute the offset now that we have unwrapped the absolute
345 // encoder.
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800346 const double relative_to_absolute_offset =
Austin Schuh409ffe02019-01-21 18:46:41 -0800347 adjusted_absolute_encoder - sample.encoder;
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800348
349 // Add the sample and update the average with the new reading.
350 const size_t relative_to_absolute_offset_samples_size =
351 relative_to_absolute_offset_samples_.size();
352 if (relative_to_absolute_offset_samples_size <
353 constants_.average_filter_size) {
354 average_relative_to_absolute_offset =
355 (average_relative_to_absolute_offset *
356 relative_to_absolute_offset_samples_size +
357 relative_to_absolute_offset) /
358 (relative_to_absolute_offset_samples_size + 1);
359
360 relative_to_absolute_offset_samples_.push_back(
361 relative_to_absolute_offset);
362 } else {
363 average_relative_to_absolute_offset -=
364 relative_to_absolute_offset_samples_[samples_idx_] /
365 relative_to_absolute_offset_samples_size;
366 relative_to_absolute_offset_samples_[samples_idx_] =
367 relative_to_absolute_offset;
368 average_relative_to_absolute_offset +=
369 relative_to_absolute_offset /
370 relative_to_absolute_offset_samples_size;
371 }
372
373 // Now compute the offset between the pot and relative encoder.
374 if (offset_samples_.size() < constants_.average_filter_size) {
Austin Schuh409ffe02019-01-21 18:46:41 -0800375 offset_samples_.push_back(sample.pot - sample.encoder);
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800376 } else {
Austin Schuh409ffe02019-01-21 18:46:41 -0800377 offset_samples_[samples_idx_] = sample.pot - sample.encoder;
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800378 }
379
380 // Drop the oldest sample when we run this function the next time around.
381 samples_idx_ = (samples_idx_ + 1) % constants_.average_filter_size;
382
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800383 pot_relative_encoder_offset_ =
Austin Schuha8f88d42019-01-26 12:33:54 -0800384 ::std::accumulate(offset_samples_.begin(), offset_samples_.end(), 0.0) /
385 offset_samples_.size();
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800386
Austin Schuhd82068e2019-01-26 20:05:42 -0800387 offset_ = UnWrap(sample.encoder + pot_relative_encoder_offset_,
388 average_relative_to_absolute_offset + sample.encoder,
389 constants_.one_revolution_distance) -
Austin Schuh409ffe02019-01-21 18:46:41 -0800390 sample.encoder;
Austin Schuhd82068e2019-01-26 20:05:42 -0800391
392 // Reverse the math for adjusted_absolute_encoder to compute the absolute
393 // encoder. Do this by taking the adjusted encoder, and then subtracting off
394 // the second argument above, and the value that was added by Wrap.
395 filtered_absolute_encoder_ =
396 ((sample.encoder + average_relative_to_absolute_offset) -
397 (-constants_.measured_absolute_position +
398 (adjusted_absolute_encoder -
399 (sample.absolute_encoder - constants_.measured_absolute_position))));
400
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800401 if (offset_ready()) {
Brian Silvermana10d20a2017-02-19 14:28:53 -0800402 if (!zeroed_) {
403 first_offset_ = offset_;
404 }
405
406 if (::std::abs(first_offset_ - offset_) >
407 constants_.allowable_encoder_error *
408 constants_.one_revolution_distance) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700409 VLOG(1) << "Offset moved too far. Initial: " << first_offset_
410 << ", current " << offset_ << ", allowable change: "
411 << constants_.allowable_encoder_error *
412 constants_.one_revolution_distance;
Brian Silvermana10d20a2017-02-19 14:28:53 -0800413 error_ = true;
414 }
415
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800416 zeroed_ = true;
417 }
Austin Schuh5f01f152017-02-11 21:34:08 -0800418 }
419
Diana Vandenberg8fea6ea2017-02-18 17:24:45 -0800420 // Update the position.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700421 filtered_position_ = pot_relative_encoder_offset_ + info.encoder();
422 position_ = offset_ + info.encoder();
Austin Schuh5f01f152017-02-11 21:34:08 -0800423}
424
Alex Perrycb7da4b2019-08-28 19:35:56 -0700425flatbuffers::Offset<PotAndAbsoluteEncoderZeroingEstimator::State>
426PotAndAbsoluteEncoderZeroingEstimator::GetEstimatorState(
427 flatbuffers::FlatBufferBuilder *fbb) const {
428 State::Builder builder(*fbb);
429 builder.add_error(error_);
430 builder.add_zeroed(zeroed_);
431 builder.add_position(position_);
432 builder.add_pot_position(filtered_position_);
433 builder.add_absolute_position(filtered_absolute_encoder_);
434 return builder.Finish();
Brian Silverman4f2e2ce2017-02-19 17:49:47 -0800435}
436
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000437void PulseIndexZeroingEstimator::Reset() {
438 max_index_position_ = ::std::numeric_limits<double>::lowest();
439 min_index_position_ = ::std::numeric_limits<double>::max();
440 offset_ = 0;
441 last_used_index_pulse_count_ = 0;
442 zeroed_ = false;
443 error_ = false;
444}
445
446void PulseIndexZeroingEstimator::StoreIndexPulseMaxAndMin(
447 const IndexPosition &info) {
448 // If we have a new index pulse.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700449 if (last_used_index_pulse_count_ != info.index_pulses()) {
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000450 // If the latest pulses's position is outside the range we've currently
451 // seen, record it appropriately.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700452 if (info.latched_encoder() > max_index_position_) {
453 max_index_position_ = info.latched_encoder();
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000454 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700455 if (info.latched_encoder() < min_index_position_) {
456 min_index_position_ = info.latched_encoder();
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000457 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700458 last_used_index_pulse_count_ = info.index_pulses();
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000459 }
460}
461
Brian Silvermanf37839c2017-02-19 18:07:15 -0800462int PulseIndexZeroingEstimator::IndexPulseCount() const {
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000463 if (min_index_position_ > max_index_position_) {
464 // This condition means we haven't seen a pulse yet.
465 return 0;
466 }
467
468 // Calculate the number of pulses encountered so far.
469 return 1 + static_cast<int>(
470 ::std::round((max_index_position_ - min_index_position_) /
471 constants_.index_difference));
472}
473
474void PulseIndexZeroingEstimator::UpdateEstimate(const IndexPosition &info) {
475 StoreIndexPulseMaxAndMin(info);
476 const int index_pulse_count = IndexPulseCount();
477 if (index_pulse_count > constants_.index_pulse_count) {
Philipp Schrader3f5b6182017-03-25 22:36:37 +0000478 if (!error_) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700479 VLOG(1) << "Got more index pulses than expected. Got "
480 << index_pulse_count << " expected "
481 << constants_.index_pulse_count;
Philipp Schrader3f5b6182017-03-25 22:36:37 +0000482 error_ = true;
483 }
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000484 }
485
486 // TODO(austin): Detect if the encoder or index pulse is unplugged.
487 // TODO(austin): Detect missing counts.
488
489 if (index_pulse_count == constants_.index_pulse_count && !zeroed_) {
490 offset_ = constants_.measured_index_position -
491 constants_.known_index_pulse * constants_.index_difference -
492 min_index_position_;
493 zeroed_ = true;
Philipp Schrader3f5b6182017-03-25 22:36:37 +0000494 } else if (zeroed_ && !error_) {
495 // Detect whether the index pulse is somewhere other than where we expect
496 // it to be. First we compute the position of the most recent index pulse.
497 double index_pulse_distance =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700498 info.latched_encoder() + offset_ - constants_.measured_index_position;
Philipp Schrader3f5b6182017-03-25 22:36:37 +0000499 // Second we compute the position of the index pulse in terms of
500 // the index difference. I.e. if this index pulse is two pulses away from
501 // the index pulse that we know about then this number should be positive
502 // or negative two.
503 double relative_distance =
504 index_pulse_distance / constants_.index_difference;
505 // Now we compute how far away the measured index pulse is from the
506 // expected index pulse.
507 double error = relative_distance - ::std::round(relative_distance);
508 // This lets us check if the index pulse is within an acceptable error
509 // margin of where we expected it to be.
510 if (::std::abs(error) > constants_.allowable_encoder_error) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700511 VLOG(1)
512 << "Encoder ticks out of range since last index pulse. known index "
513 "pulse: "
514 << constants_.measured_index_position << ", expected index pulse: "
515 << round(relative_distance) * constants_.index_difference +
516 constants_.measured_index_position
517 << ", actual index pulse: " << info.latched_encoder() + offset_
518 << ", "
519 "allowable error: "
520 << constants_.allowable_encoder_error * constants_.index_difference;
Philipp Schrader3f5b6182017-03-25 22:36:37 +0000521 error_ = true;
522 }
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000523 }
Brian Silvermanf37839c2017-02-19 18:07:15 -0800524
Alex Perrycb7da4b2019-08-28 19:35:56 -0700525 position_ = info.encoder() + offset_;
Brian Silvermanf37839c2017-02-19 18:07:15 -0800526}
527
Alex Perrycb7da4b2019-08-28 19:35:56 -0700528flatbuffers::Offset<PulseIndexZeroingEstimator::State>
529PulseIndexZeroingEstimator::GetEstimatorState(
530 flatbuffers::FlatBufferBuilder *fbb) const {
531 State::Builder builder(*fbb);
532 builder.add_error(error_);
533 builder.add_zeroed(zeroed_);
534 builder.add_position(position_);
535 builder.add_min_index_position(min_index_position_);
536 builder.add_max_index_position(max_index_position_);
537 builder.add_index_pulses_seen(IndexPulseCount());
538 return builder.Finish();
Isaac Wilcove0851ffd2017-02-16 04:13:14 +0000539}
540
Austin Schuhd82068e2019-01-26 20:05:42 -0800541AbsoluteEncoderZeroingEstimator::AbsoluteEncoderZeroingEstimator(
542 const constants::AbsoluteEncoderZeroingConstants &constants)
543 : constants_(constants), move_detector_(constants_.moving_buffer_size) {
544 relative_to_absolute_offset_samples_.reserve(constants_.average_filter_size);
545 Reset();
546}
547
548void AbsoluteEncoderZeroingEstimator::Reset() {
549 zeroed_ = false;
550 error_ = false;
551 first_offset_ = 0.0;
552 offset_ = 0.0;
553 samples_idx_ = 0;
554 position_ = 0.0;
555 nan_samples_ = 0;
556 relative_to_absolute_offset_samples_.clear();
557 move_detector_.Reset();
558}
559
560
561// The math here is a bit backwards, but I think it'll be less error prone that
562// way and more similar to the version with a pot as well.
563//
564// We start by unwrapping the absolute encoder using the relative encoder. This
565// puts us in a non-wrapping space and lets us average a bit easier. From
566// there, we can compute an offset and wrap ourselves back such that we stay
567// close to the middle value.
568//
569// To guard against the robot moving while updating estimates, buffer a number
570// of samples and check that the buffered samples are not different than the
571// zeroing threshold. At any point that the samples differ too much, do not
572// update estimates based on those samples.
573void AbsoluteEncoderZeroingEstimator::UpdateEstimate(
574 const AbsolutePosition &info) {
575 // Check for Abs Encoder NaN value that would mess up the rest of the zeroing
576 // code below. NaN values are given when the Absolute Encoder is disconnected.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700577 if (::std::isnan(info.absolute_encoder())) {
Austin Schuhd82068e2019-01-26 20:05:42 -0800578 if (zeroed_) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700579 VLOG(1) << "NAN on absolute encoder.";
Austin Schuhd82068e2019-01-26 20:05:42 -0800580 error_ = true;
581 } else {
582 ++nan_samples_;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700583 VLOG(1) << "NAN on absolute encoder while zeroing " << nan_samples_;
Austin Schuhd82068e2019-01-26 20:05:42 -0800584 if (nan_samples_ >= constants_.average_filter_size) {
585 error_ = true;
586 zeroed_ = true;
587 }
588 }
589 // Throw some dummy values in for now.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700590 filtered_absolute_encoder_ = info.absolute_encoder();
591 position_ = offset_ + info.encoder();
Austin Schuhd82068e2019-01-26 20:05:42 -0800592 return;
593 }
594
595 const bool moving = move_detector_.Update(info, constants_.moving_buffer_size,
596 constants_.zeroing_threshold);
597
598 if (!moving) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700599 const PositionStruct &sample = move_detector_.GetSample();
Austin Schuhd82068e2019-01-26 20:05:42 -0800600
601 // Compute the average offset between the absolute encoder and relative
602 // encoder. If we have 0 samples, assume it is 0.
603 double average_relative_to_absolute_offset =
604 relative_to_absolute_offset_samples_.size() == 0
605 ? 0.0
606 : ::std::accumulate(relative_to_absolute_offset_samples_.begin(),
607 relative_to_absolute_offset_samples_.end(),
608 0.0) /
609 relative_to_absolute_offset_samples_.size();
610
611 // Now, compute the estimated absolute position using the previously
612 // estimated offset and the incremental encoder.
613 const double adjusted_incremental_encoder =
614 sample.encoder + average_relative_to_absolute_offset;
615
616 // Now, compute the absolute encoder value nearest to the offset relative
617 // encoder position.
618 const double adjusted_absolute_encoder =
619 UnWrap(adjusted_incremental_encoder,
620 sample.absolute_encoder - constants_.measured_absolute_position,
621 constants_.one_revolution_distance);
622
623 // We can now compute the offset now that we have unwrapped the absolute
624 // encoder.
625 const double relative_to_absolute_offset =
626 adjusted_absolute_encoder - sample.encoder;
627
628 // Add the sample and update the average with the new reading.
629 const size_t relative_to_absolute_offset_samples_size =
630 relative_to_absolute_offset_samples_.size();
631 if (relative_to_absolute_offset_samples_size <
632 constants_.average_filter_size) {
633 average_relative_to_absolute_offset =
634 (average_relative_to_absolute_offset *
635 relative_to_absolute_offset_samples_size +
636 relative_to_absolute_offset) /
637 (relative_to_absolute_offset_samples_size + 1);
638
639 relative_to_absolute_offset_samples_.push_back(
640 relative_to_absolute_offset);
641 } else {
642 average_relative_to_absolute_offset -=
643 relative_to_absolute_offset_samples_[samples_idx_] /
644 relative_to_absolute_offset_samples_size;
645 relative_to_absolute_offset_samples_[samples_idx_] =
646 relative_to_absolute_offset;
647 average_relative_to_absolute_offset +=
648 relative_to_absolute_offset /
649 relative_to_absolute_offset_samples_size;
650 }
651
652 // Drop the oldest sample when we run this function the next time around.
653 samples_idx_ = (samples_idx_ + 1) % constants_.average_filter_size;
654
655 // And our offset is the offset that gives us the position within +- ord/2
656 // of the middle position.
657 offset_ = Wrap(constants_.middle_position,
658 average_relative_to_absolute_offset + sample.encoder,
659 constants_.one_revolution_distance) -
660 sample.encoder;
661
662 // Reverse the math for adjusted_absolute_encoder to compute the absolute
663 // encoder. Do this by taking the adjusted encoder, and then subtracting off
664 // the second argument above, and the value that was added by Wrap.
665 filtered_absolute_encoder_ =
666 ((sample.encoder + average_relative_to_absolute_offset) -
667 (-constants_.measured_absolute_position +
668 (adjusted_absolute_encoder -
669 (sample.absolute_encoder - constants_.measured_absolute_position))));
670
671 if (offset_ready()) {
672 if (!zeroed_) {
673 first_offset_ = offset_;
674 }
675
676 if (::std::abs(first_offset_ - offset_) >
677 constants_.allowable_encoder_error *
678 constants_.one_revolution_distance) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700679 VLOG(1) << "Offset moved too far. Initial: " << first_offset_
680 << ", current " << offset_ << ", allowable change: "
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.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700691 position_ = offset_ + info.encoder();
Austin Schuhd82068e2019-01-26 20:05:42 -0800692}
693
Alex Perrycb7da4b2019-08-28 19:35:56 -0700694flatbuffers::Offset<AbsoluteEncoderZeroingEstimator::State>
695AbsoluteEncoderZeroingEstimator::GetEstimatorState(
696 flatbuffers::FlatBufferBuilder *fbb) const {
697 State::Builder builder(*fbb);
698 builder.add_error(error_);
699 builder.add_zeroed(zeroed_);
700 builder.add_position(position_);
701 builder.add_absolute_position(filtered_absolute_encoder_);
702 return builder.Finish();
Austin Schuhd82068e2019-01-26 20:05:42 -0800703}
704
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000705} // namespace zeroing
706} // namespace frc971