blob: 54a5e78e1634d00352a3063276c37d0683d43bfe [file] [log] [blame]
Ravago Jonesea6464c2020-10-10 15:40:46 -07001#include "frc971/zeroing/absolute_and_absolute_encoder.h"
2
3#include <cmath>
4#include <numeric>
5
6#include "glog/logging.h"
7
8#include "frc971/zeroing/wrap.h"
9
10namespace frc971 {
11namespace zeroing {
12
13AbsoluteAndAbsoluteEncoderZeroingEstimator::
14 AbsoluteAndAbsoluteEncoderZeroingEstimator(
15 const constants::AbsoluteAndAbsoluteEncoderZeroingConstants &constants)
16 : constants_(constants), move_detector_(constants_.moving_buffer_size) {
17 relative_to_absolute_offset_samples_.reserve(constants_.average_filter_size);
18 offset_samples_.reserve(constants_.average_filter_size);
19 Reset();
20}
21
22void AbsoluteAndAbsoluteEncoderZeroingEstimator::Reset() {
23 first_offset_ = 0.0;
24 single_turn_to_relative_encoder_offset_ = 0.0;
25 offset_ = 0.0;
26 samples_idx_ = 0;
27 filtered_position_ = 0.0;
28 position_ = 0.0;
29 zeroed_ = false;
30 nan_samples_ = 0;
31 relative_to_absolute_offset_samples_.clear();
32 offset_samples_.clear();
33 move_detector_.Reset();
34 error_ = false;
35}
36
37// So, this needs to be a multistep process. We need to first estimate the
38// offset between the absolute encoder and the relative encoder. That process
39// should get us an absolute number which is off by integer multiples of the
40// distance/rev. In parallel, we can estimate the offset between the single
41// turn encoder and encoder. When both estimates have converged, we can then
42// compute the offset in a cycle, and which cycle, which gives us the accurate
43// global offset.
44//
45// It's tricky to compute the offset between the absolute and relative encoder.
46// We need to compute this inside 1 revolution. The easiest way to do this
47// would be to wrap the encoder, subtract the two of them, and then average the
48// result. That will struggle when they are off by PI. Instead, we need to
49// wrap the number to +- PI from the current averaged offset.
50//
51// To guard against the robot moving while updating estimates, buffer a number
52// of samples and check that the buffered samples are not different than the
53// zeroing threshold. At any point that the samples differ too much, do not
54// update estimates based on those samples.
55void AbsoluteAndAbsoluteEncoderZeroingEstimator::UpdateEstimate(
56 const AbsoluteAndAbsolutePosition &info) {
57 // Check for Abs Encoder NaN value that would mess up the rest of the zeroing
58 // code below. NaN values are given when the Absolute Encoder is disconnected.
59 if (::std::isnan(info.absolute_encoder())) {
60 if (zeroed_) {
61 VLOG(1) << "NAN on absolute encoder.";
62 error_ = true;
63 } else {
64 ++nan_samples_;
65 VLOG(1) << "NAN on absolute encoder while zeroing" << nan_samples_;
66 if (nan_samples_ >= constants_.average_filter_size) {
67 error_ = true;
68 zeroed_ = true;
69 }
70 }
71 // Throw some dummy values in for now.
72 filtered_absolute_encoder_ = info.absolute_encoder();
73 filtered_position_ =
74 single_turn_to_relative_encoder_offset_ + info.encoder();
75 position_ = offset_ + info.encoder();
76 return;
77 }
78
79 const bool moving = move_detector_.Update(info, constants_.moving_buffer_size,
80 constants_.zeroing_threshold);
81
82 if (!moving) {
83 const PositionStruct &sample = move_detector_.GetSample();
84
85 // Compute the average offset between the absolute encoder and relative
86 // encoder. If we have 0 samples, assume it is 0.
87 double average_relative_to_absolute_offset =
88 relative_to_absolute_offset_samples_.size() == 0
89 ? 0.0
90 : ::std::accumulate(relative_to_absolute_offset_samples_.begin(),
91 relative_to_absolute_offset_samples_.end(),
92 0.0) /
93 relative_to_absolute_offset_samples_.size();
94
95 const double adjusted_incremental_encoder =
96 sample.encoder + average_relative_to_absolute_offset;
97
98 // Now, compute the nearest absolute encoder value to the offset relative
99 // encoder position.
100 const double adjusted_absolute_encoder =
101 UnWrap(adjusted_incremental_encoder,
102 sample.absolute_encoder - constants_.measured_absolute_position,
103 constants_.one_revolution_distance);
104
105 // We can now compute the offset now that we have unwrapped the absolute
106 // encoder.
107 const double relative_to_absolute_offset =
108 adjusted_absolute_encoder - sample.encoder;
109
110 // Add the sample and update the average with the new reading.
111 const size_t relative_to_absolute_offset_samples_size =
112 relative_to_absolute_offset_samples_.size();
113 if (relative_to_absolute_offset_samples_size <
114 constants_.average_filter_size) {
115 average_relative_to_absolute_offset =
116 (average_relative_to_absolute_offset *
117 relative_to_absolute_offset_samples_size +
118 relative_to_absolute_offset) /
119 (relative_to_absolute_offset_samples_size + 1);
120
121 relative_to_absolute_offset_samples_.push_back(
122 relative_to_absolute_offset);
123 } else {
124 average_relative_to_absolute_offset -=
125 relative_to_absolute_offset_samples_[samples_idx_] /
126 relative_to_absolute_offset_samples_size;
127 relative_to_absolute_offset_samples_[samples_idx_] =
128 relative_to_absolute_offset;
129 average_relative_to_absolute_offset +=
130 relative_to_absolute_offset /
131 relative_to_absolute_offset_samples_size;
132 }
133
134 const double adjusted_single_turn_absolute_encoder =
135 UnWrap(constants_.single_turn_middle_position,
136 sample.single_turn_absolute_encoder -
137 constants_.single_turn_measured_absolute_position,
138 constants_.single_turn_one_revolution_distance);
139
140 // Now compute the offset between the pot and relative encoder.
141 if (offset_samples_.size() < constants_.average_filter_size) {
142 offset_samples_.push_back(adjusted_single_turn_absolute_encoder -
143 sample.encoder);
144 } else {
145 offset_samples_[samples_idx_] =
146 adjusted_single_turn_absolute_encoder - sample.encoder;
147 }
148
149 // Drop the oldest sample when we run this function the next time around.
150 samples_idx_ = (samples_idx_ + 1) % constants_.average_filter_size;
151
152 single_turn_to_relative_encoder_offset_ =
153 ::std::accumulate(offset_samples_.begin(), offset_samples_.end(), 0.0) /
154 offset_samples_.size();
155
156 offset_ = UnWrap(sample.encoder + single_turn_to_relative_encoder_offset_,
157 average_relative_to_absolute_offset + sample.encoder,
158 constants_.one_revolution_distance) -
159 sample.encoder;
160
161 // Reverse the math for adjusted_absolute_encoder to compute the absolute
162 // encoder. Do this by taking the adjusted encoder, and then subtracting off
163 // the second argument above, and the value that was added by Wrap.
164 filtered_absolute_encoder_ =
165 ((sample.encoder + average_relative_to_absolute_offset) -
166 (-constants_.measured_absolute_position +
167 (adjusted_absolute_encoder -
168 (sample.absolute_encoder - constants_.measured_absolute_position))));
169
170 // TODO(Ravago): this is impossible to read.
171 filtered_single_turn_absolute_encoder_ =
172 ((sample.encoder + single_turn_to_relative_encoder_offset_) -
173 (-constants_.single_turn_measured_absolute_position +
174 (adjusted_single_turn_absolute_encoder -
175 (sample.single_turn_absolute_encoder -
176 constants_.single_turn_measured_absolute_position))));
177
178 if (offset_ready()) {
179 if (!zeroed_) {
180 first_offset_ = offset_;
181 }
182
183 if (::std::abs(first_offset_ - offset_) >
184 constants_.allowable_encoder_error *
185 constants_.one_revolution_distance) {
186 VLOG(1) << "Offset moved too far. Initial: " << first_offset_
187 << ", current " << offset_ << ", allowable change: "
188 << constants_.allowable_encoder_error *
189 constants_.one_revolution_distance;
190 error_ = true;
191 }
192
193 zeroed_ = true;
194 }
195 }
196
197 // Update the position.
198 filtered_position_ = single_turn_to_relative_encoder_offset_ + info.encoder();
199 position_ = offset_ + info.encoder();
200}
201
202flatbuffers::Offset<AbsoluteAndAbsoluteEncoderZeroingEstimator::State>
203AbsoluteAndAbsoluteEncoderZeroingEstimator::GetEstimatorState(
204 flatbuffers::FlatBufferBuilder *fbb) const {
205 State::Builder builder(*fbb);
206 builder.add_error(error_);
207 builder.add_zeroed(zeroed_);
208 builder.add_position(position_);
209 builder.add_absolute_position(filtered_absolute_encoder_);
210 builder.add_single_turn_absolute_position(
211 filtered_single_turn_absolute_encoder_);
212 return builder.Finish();
213}
214
215} // namespace zeroing
216} // namespace frc971