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