blob: c2a795f648ecd6f010888ad43ae0288f67e197ee [file] [log] [blame]
Brian Silvermana57b7012020-03-11 20:19:23 -07001#include "frc971/zeroing/absolute_encoder.h"
2
3#include <cmath>
4#include <numeric>
5
Austin Schuh99f7c6a2024-06-25 22:07:44 -07006#include "absl/log/check.h"
7#include "absl/log/log.h"
Philipp Schrader790cb542023-07-05 21:06:52 -07008
Ravago Jones726deb02021-05-29 14:36:43 -07009#include "aos/containers/error_list.h"
Brian Silvermana57b7012020-03-11 20:19:23 -070010#include "frc971/zeroing/wrap.h"
11
Stephan Pleinesf63bde82024-01-13 15:59:33 -080012namespace frc971::zeroing {
Brian Silvermana57b7012020-03-11 20:19:23 -070013
14AbsoluteEncoderZeroingEstimator::AbsoluteEncoderZeroingEstimator(
15 const constants::AbsoluteEncoderZeroingConstants &constants)
16 : constants_(constants), move_detector_(constants_.moving_buffer_size) {
17 relative_to_absolute_offset_samples_.reserve(constants_.average_filter_size);
18 Reset();
19}
20
21void AbsoluteEncoderZeroingEstimator::Reset() {
22 zeroed_ = false;
23 error_ = false;
24 first_offset_ = 0.0;
25 offset_ = 0.0;
26 samples_idx_ = 0;
27 position_ = 0.0;
28 nan_samples_ = 0;
29 relative_to_absolute_offset_samples_.clear();
30 move_detector_.Reset();
31}
32
Brian Silvermana57b7012020-03-11 20:19:23 -070033// The math here is a bit backwards, but I think it'll be less error prone that
34// way and more similar to the version with a pot as well.
35//
36// We start by unwrapping the absolute encoder using the relative encoder. This
37// puts us in a non-wrapping space and lets us average a bit easier. From
38// there, we can compute an offset and wrap ourselves back such that we stay
39// close to the middle value.
40//
41// To guard against the robot moving while updating estimates, buffer a number
42// of samples and check that the buffered samples are not different than the
43// zeroing threshold. At any point that the samples differ too much, do not
44// update estimates based on those samples.
45void AbsoluteEncoderZeroingEstimator::UpdateEstimate(
46 const AbsolutePosition &info) {
47 // Check for Abs Encoder NaN value that would mess up the rest of the zeroing
48 // code below. NaN values are given when the Absolute Encoder is disconnected.
49 if (::std::isnan(info.absolute_encoder())) {
50 if (zeroed_) {
51 VLOG(1) << "NAN on absolute encoder.";
Ravago Jones726deb02021-05-29 14:36:43 -070052 errors_.Set(ZeroingError::LOST_ABSOLUTE_ENCODER);
Brian Silvermana57b7012020-03-11 20:19:23 -070053 error_ = true;
54 } else {
55 ++nan_samples_;
56 VLOG(1) << "NAN on absolute encoder while zeroing " << nan_samples_;
57 if (nan_samples_ >= constants_.average_filter_size) {
Ravago Jones726deb02021-05-29 14:36:43 -070058 errors_.Set(ZeroingError::LOST_ABSOLUTE_ENCODER);
Brian Silvermana57b7012020-03-11 20:19:23 -070059 error_ = true;
60 zeroed_ = true;
61 }
62 }
63 // Throw some dummy values in for now.
64 filtered_absolute_encoder_ = info.absolute_encoder();
65 position_ = offset_ + info.encoder();
66 return;
67 }
68
69 const bool moving = move_detector_.Update(info, constants_.moving_buffer_size,
70 constants_.zeroing_threshold);
71
72 if (!moving) {
73 const PositionStruct &sample = move_detector_.GetSample();
74
75 // Compute the average offset between the absolute encoder and relative
76 // encoder. If we have 0 samples, assume it is 0.
77 double average_relative_to_absolute_offset =
78 relative_to_absolute_offset_samples_.size() == 0
79 ? 0.0
80 : ::std::accumulate(relative_to_absolute_offset_samples_.begin(),
81 relative_to_absolute_offset_samples_.end(),
82 0.0) /
83 relative_to_absolute_offset_samples_.size();
84
85 // Now, compute the estimated absolute position using the previously
86 // estimated offset and the incremental encoder.
87 const double adjusted_incremental_encoder =
88 sample.encoder + average_relative_to_absolute_offset;
89
90 // Now, compute the absolute encoder value nearest to the offset relative
91 // encoder position.
92 const double adjusted_absolute_encoder =
93 UnWrap(adjusted_incremental_encoder,
94 sample.absolute_encoder - constants_.measured_absolute_position,
95 constants_.one_revolution_distance);
96
97 // We can now compute the offset now that we have unwrapped the absolute
98 // encoder.
99 const double relative_to_absolute_offset =
100 adjusted_absolute_encoder - sample.encoder;
101
102 // Add the sample and update the average with the new reading.
103 const size_t relative_to_absolute_offset_samples_size =
104 relative_to_absolute_offset_samples_.size();
105 if (relative_to_absolute_offset_samples_size <
106 constants_.average_filter_size) {
107 average_relative_to_absolute_offset =
108 (average_relative_to_absolute_offset *
109 relative_to_absolute_offset_samples_size +
110 relative_to_absolute_offset) /
111 (relative_to_absolute_offset_samples_size + 1);
112
113 relative_to_absolute_offset_samples_.push_back(
114 relative_to_absolute_offset);
115 } else {
116 average_relative_to_absolute_offset -=
117 relative_to_absolute_offset_samples_[samples_idx_] /
118 relative_to_absolute_offset_samples_size;
119 relative_to_absolute_offset_samples_[samples_idx_] =
120 relative_to_absolute_offset;
121 average_relative_to_absolute_offset +=
122 relative_to_absolute_offset /
123 relative_to_absolute_offset_samples_size;
124 }
125
126 // Drop the oldest sample when we run this function the next time around.
127 samples_idx_ = (samples_idx_ + 1) % constants_.average_filter_size;
128
129 // And our offset is the offset that gives us the position within +- ord/2
130 // of the middle position.
131 offset_ = Wrap(constants_.middle_position,
132 average_relative_to_absolute_offset + sample.encoder,
133 constants_.one_revolution_distance) -
134 sample.encoder;
135
136 // Reverse the math for adjusted_absolute_encoder to compute the absolute
137 // encoder. Do this by taking the adjusted encoder, and then subtracting off
138 // the second argument above, and the value that was added by Wrap.
139 filtered_absolute_encoder_ =
140 ((sample.encoder + average_relative_to_absolute_offset) -
141 (-constants_.measured_absolute_position +
142 (adjusted_absolute_encoder -
143 (sample.absolute_encoder - constants_.measured_absolute_position))));
144
145 if (offset_ready()) {
146 if (!zeroed_) {
147 first_offset_ = offset_;
148 }
149
150 if (::std::abs(first_offset_ - offset_) >
151 constants_.allowable_encoder_error *
152 constants_.one_revolution_distance) {
153 VLOG(1) << "Offset moved too far. Initial: " << first_offset_
154 << ", current " << offset_ << ", allowable change: "
155 << constants_.allowable_encoder_error *
156 constants_.one_revolution_distance;
Ravago Jones726deb02021-05-29 14:36:43 -0700157 errors_.Set(ZeroingError::OFFSET_MOVED_TOO_FAR);
Brian Silvermana57b7012020-03-11 20:19:23 -0700158 error_ = true;
159 }
160
161 zeroed_ = true;
162 }
163 }
164
165 // Update the position.
166 position_ = offset_ + info.encoder();
167}
168
169flatbuffers::Offset<AbsoluteEncoderZeroingEstimator::State>
170AbsoluteEncoderZeroingEstimator::GetEstimatorState(
171 flatbuffers::FlatBufferBuilder *fbb) const {
Ravago Jones726deb02021-05-29 14:36:43 -0700172 flatbuffers::Offset<flatbuffers::Vector<ZeroingError>> errors_offset =
173 errors_.ToFlatbuffer(fbb);
174
Brian Silvermana57b7012020-03-11 20:19:23 -0700175 State::Builder builder(*fbb);
176 builder.add_error(error_);
177 builder.add_zeroed(zeroed_);
178 builder.add_position(position_);
179 builder.add_absolute_position(filtered_absolute_encoder_);
Ravago Jones726deb02021-05-29 14:36:43 -0700180 builder.add_errors(errors_offset);
Brian Silvermana57b7012020-03-11 20:19:23 -0700181 return builder.Finish();
182}
183
Stephan Pleinesf63bde82024-01-13 15:59:33 -0800184} // namespace frc971::zeroing