blob: ffdf9dacaa1d00a1a61e44a4f47f037b3d0b1275 [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
6#include "glog/logging.h"
7
8#include "frc971/zeroing/wrap.h"
9
10namespace frc971 {
11namespace zeroing {
12
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
32
33// 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.";
52 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) {
57 error_ = true;
58 zeroed_ = true;
59 }
60 }
61 // Throw some dummy values in for now.
62 filtered_absolute_encoder_ = info.absolute_encoder();
63 position_ = offset_ + info.encoder();
64 return;
65 }
66
67 const bool moving = move_detector_.Update(info, constants_.moving_buffer_size,
68 constants_.zeroing_threshold);
69
70 if (!moving) {
71 const PositionStruct &sample = move_detector_.GetSample();
72
73 // Compute the average offset between the absolute encoder and relative
74 // encoder. If we have 0 samples, assume it is 0.
75 double average_relative_to_absolute_offset =
76 relative_to_absolute_offset_samples_.size() == 0
77 ? 0.0
78 : ::std::accumulate(relative_to_absolute_offset_samples_.begin(),
79 relative_to_absolute_offset_samples_.end(),
80 0.0) /
81 relative_to_absolute_offset_samples_.size();
82
83 // Now, compute the estimated absolute position using the previously
84 // estimated offset and the incremental encoder.
85 const double adjusted_incremental_encoder =
86 sample.encoder + average_relative_to_absolute_offset;
87
88 // Now, compute the absolute encoder value nearest to the offset relative
89 // encoder position.
90 const double adjusted_absolute_encoder =
91 UnWrap(adjusted_incremental_encoder,
92 sample.absolute_encoder - constants_.measured_absolute_position,
93 constants_.one_revolution_distance);
94
95 // We can now compute the offset now that we have unwrapped the absolute
96 // encoder.
97 const double relative_to_absolute_offset =
98 adjusted_absolute_encoder - sample.encoder;
99
100 // Add the sample and update the average with the new reading.
101 const size_t relative_to_absolute_offset_samples_size =
102 relative_to_absolute_offset_samples_.size();
103 if (relative_to_absolute_offset_samples_size <
104 constants_.average_filter_size) {
105 average_relative_to_absolute_offset =
106 (average_relative_to_absolute_offset *
107 relative_to_absolute_offset_samples_size +
108 relative_to_absolute_offset) /
109 (relative_to_absolute_offset_samples_size + 1);
110
111 relative_to_absolute_offset_samples_.push_back(
112 relative_to_absolute_offset);
113 } else {
114 average_relative_to_absolute_offset -=
115 relative_to_absolute_offset_samples_[samples_idx_] /
116 relative_to_absolute_offset_samples_size;
117 relative_to_absolute_offset_samples_[samples_idx_] =
118 relative_to_absolute_offset;
119 average_relative_to_absolute_offset +=
120 relative_to_absolute_offset /
121 relative_to_absolute_offset_samples_size;
122 }
123
124 // Drop the oldest sample when we run this function the next time around.
125 samples_idx_ = (samples_idx_ + 1) % constants_.average_filter_size;
126
127 // And our offset is the offset that gives us the position within +- ord/2
128 // of the middle position.
129 offset_ = Wrap(constants_.middle_position,
130 average_relative_to_absolute_offset + sample.encoder,
131 constants_.one_revolution_distance) -
132 sample.encoder;
133
134 // Reverse the math for adjusted_absolute_encoder to compute the absolute
135 // encoder. Do this by taking the adjusted encoder, and then subtracting off
136 // the second argument above, and the value that was added by Wrap.
137 filtered_absolute_encoder_ =
138 ((sample.encoder + average_relative_to_absolute_offset) -
139 (-constants_.measured_absolute_position +
140 (adjusted_absolute_encoder -
141 (sample.absolute_encoder - constants_.measured_absolute_position))));
142
143 if (offset_ready()) {
144 if (!zeroed_) {
145 first_offset_ = offset_;
146 }
147
148 if (::std::abs(first_offset_ - offset_) >
149 constants_.allowable_encoder_error *
150 constants_.one_revolution_distance) {
151 VLOG(1) << "Offset moved too far. Initial: " << first_offset_
152 << ", current " << offset_ << ", allowable change: "
153 << constants_.allowable_encoder_error *
154 constants_.one_revolution_distance;
155 error_ = true;
156 }
157
158 zeroed_ = true;
159 }
160 }
161
162 // Update the position.
163 position_ = offset_ + info.encoder();
164}
165
166flatbuffers::Offset<AbsoluteEncoderZeroingEstimator::State>
167AbsoluteEncoderZeroingEstimator::GetEstimatorState(
168 flatbuffers::FlatBufferBuilder *fbb) const {
169 State::Builder builder(*fbb);
170 builder.add_error(error_);
171 builder.add_zeroed(zeroed_);
172 builder.add_position(position_);
173 builder.add_absolute_position(filtered_absolute_encoder_);
174 return builder.Finish();
175}
176
177} // namespace zeroing
178} // namespace frc971