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