blob: 70fa866c206a7c420828b26803659ac9535526e9 [file] [log] [blame]
Adam Snaiderc4b3c192015-02-01 01:30:39 +00001#include "frc971/zeroing/zeroing.h"
Adam Snaiderb4119252015-02-15 01:30:57 +00002
Brian Silvermanb691f5e2015-08-02 11:37:55 -07003#include <cmath>
Adam Snaiderc4b3c192015-02-01 01:30:39 +00004#include <vector>
5
Austin Schuh5f01f152017-02-11 21:34:08 -08006#include "frc971/zeroing/wrap.h"
7
Adam Snaiderc4b3c192015-02-01 01:30:39 +00008namespace frc971 {
9namespace zeroing {
10
Tyler Chatowf8f03112017-02-05 14:31:34 -080011void PopulateEstimatorState(
12 const zeroing::PotAndIndexPulseZeroingEstimator &estimator,
13 EstimatorState *state) {
Daniel Pettiab274232015-02-16 19:15:34 -080014 state->error = estimator.error();
15 state->zeroed = estimator.zeroed();
16 state->position = estimator.position();
Austin Schuhbe133ed2016-03-11 21:23:34 -080017 state->pot_position = estimator.filtered_position();
Daniel Pettiab274232015-02-16 19:15:34 -080018}
19
Austin Schuh5f01f152017-02-11 21:34:08 -080020void PopulateEstimatorState(
21 const zeroing::PotAndAbsEncoderZeroingEstimator &estimator,
22 AbsoluteEstimatorState *state) {
23 state->error = estimator.error();
24 state->zeroed = estimator.zeroed();
25
26 state->position = estimator.position();
27 state->pot_position = estimator.filtered_position();
28}
29
Tyler Chatowf8f03112017-02-05 14:31:34 -080030PotAndIndexPulseZeroingEstimator::PotAndIndexPulseZeroingEstimator(
Austin Schuh5f01f152017-02-11 21:34:08 -080031 const constants::PotAndIndexPulseZeroingConstants &constants)
32 : constants_(constants) {
33 start_pos_samples_.reserve(constants_.average_filter_size);
Adam Snaiderb4119252015-02-15 01:30:57 +000034 Reset();
Austin Schuh703b8d42015-02-01 14:56:34 -080035}
36
Tyler Chatowf8f03112017-02-05 14:31:34 -080037void PotAndIndexPulseZeroingEstimator::Reset() {
Adam Snaiderc4b3c192015-02-01 01:30:39 +000038 samples_idx_ = 0;
Adam Snaiderb4119252015-02-15 01:30:57 +000039 start_pos_ = 0;
40 start_pos_samples_.clear();
41 zeroed_ = false;
Philipp Schrader41d82912015-02-15 03:44:23 +000042 wait_for_index_pulse_ = true;
Philipp Schradere828be72015-02-15 07:07:37 +000043 last_used_index_pulse_count_ = 0;
Adam Snaider3cd11c52015-02-16 02:16:09 +000044 first_start_pos_ = 0.0;
Philipp Schrader53f4b6d2015-02-15 22:32:08 +000045 error_ = false;
46}
47
Tyler Chatowf8f03112017-02-05 14:31:34 -080048void PotAndIndexPulseZeroingEstimator::TriggerError() {
Philipp Schrader53f4b6d2015-02-15 22:32:08 +000049 if (!error_) {
50 LOG(ERROR, "Manually triggered zeroing error.\n");
51 error_ = true;
52 }
Philipp Schradere828be72015-02-15 07:07:37 +000053}
54
Tyler Chatowf8f03112017-02-05 14:31:34 -080055double PotAndIndexPulseZeroingEstimator::CalculateStartPosition(
56 double start_average, double latched_encoder) const {
Philipp Schradere828be72015-02-15 07:07:37 +000057 // We calculate an aproximation of the value of the last index position.
58 // Also account for index pulses not lining up with integer multiples of the
59 // index_diff.
Austin Schuh5f01f152017-02-11 21:34:08 -080060 double index_pos =
61 start_average + latched_encoder - constants_.measured_index_position;
Philipp Schradere828be72015-02-15 07:07:37 +000062 // We round index_pos to the closest valid value of the index.
Austin Schuh5f01f152017-02-11 21:34:08 -080063 double accurate_index_pos = (round(index_pos / constants_.index_difference)) *
64 constants_.index_difference;
Philipp Schradere828be72015-02-15 07:07:37 +000065 // Now we reverse the first calculation to get the accurate start position.
Austin Schuh5f01f152017-02-11 21:34:08 -080066 return accurate_index_pos - latched_encoder +
67 constants_.measured_index_position;
Adam Snaiderc4b3c192015-02-01 01:30:39 +000068}
69
Tyler Chatowf8f03112017-02-05 14:31:34 -080070void PotAndIndexPulseZeroingEstimator::UpdateEstimate(
71 const PotAndIndexPosition &info) {
Philipp Schrader41d82912015-02-15 03:44:23 +000072 // We want to make sure that we encounter at least one index pulse while
73 // zeroing. So we take the index pulse count from the first sample after
74 // reset and wait for that count to change before we consider ourselves
75 // zeroed.
76 if (wait_for_index_pulse_) {
Philipp Schradere828be72015-02-15 07:07:37 +000077 last_used_index_pulse_count_ = info.index_pulses;
Philipp Schrader41d82912015-02-15 03:44:23 +000078 wait_for_index_pulse_ = false;
79 }
80
Austin Schuh5f01f152017-02-11 21:34:08 -080081 if (start_pos_samples_.size() < constants_.average_filter_size) {
Adam Snaiderc4b3c192015-02-01 01:30:39 +000082 start_pos_samples_.push_back(info.pot - info.encoder);
83 } else {
84 start_pos_samples_[samples_idx_] = info.pot - info.encoder;
85 }
Adam Snaiderb4119252015-02-15 01:30:57 +000086
87 // Drop the oldest sample when we run this function the next time around.
Austin Schuh5f01f152017-02-11 21:34:08 -080088 samples_idx_ = (samples_idx_ + 1) % constants_.average_filter_size;
Adam Snaiderc4b3c192015-02-01 01:30:39 +000089
Adam Snaiderb4119252015-02-15 01:30:57 +000090 double sample_sum = 0.0;
91
Adam Snaiderc4b3c192015-02-01 01:30:39 +000092 for (size_t i = 0; i < start_pos_samples_.size(); ++i) {
Adam Snaiderb4119252015-02-15 01:30:57 +000093 sample_sum += start_pos_samples_[i];
Adam Snaiderc4b3c192015-02-01 01:30:39 +000094 }
95
96 // Calculates the average of the starting position.
Adam Snaiderb4119252015-02-15 01:30:57 +000097 double start_average = sample_sum / start_pos_samples_.size();
98
99 // If there are no index pulses to use or we don't have enough samples yet to
100 // have a well-filtered starting position then we use the filtered value as
101 // our best guess.
Austin Schuh7485dbb2016-02-08 00:21:58 -0800102 if (!zeroed_ &&
103 (info.index_pulses == last_used_index_pulse_count_ || !offset_ready())) {
Adam Snaiderb4119252015-02-15 01:30:57 +0000104 start_pos_ = start_average;
Philipp Schradere828be72015-02-15 07:07:37 +0000105 } else if (!zeroed_ || last_used_index_pulse_count_ != info.index_pulses) {
106 // Note the accurate start position and the current index pulse count so
107 // that we only run this logic once per index pulse. That should be more
108 // resilient to corrupted intermediate data.
109 start_pos_ = CalculateStartPosition(start_average, info.latched_encoder);
110 last_used_index_pulse_count_ = info.index_pulses;
Austin Schuh7485dbb2016-02-08 00:21:58 -0800111
112 // TODO(austin): Reject encoder positions which have x% error rather than
113 // rounding to the closest index pulse.
114
Adam Snaider3cd11c52015-02-16 02:16:09 +0000115 // Save the first starting position.
116 if (!zeroed_) {
117 first_start_pos_ = start_pos_;
118 LOG(INFO, "latching start position %f\n", first_start_pos_);
119 }
Adam Snaiderb4119252015-02-15 01:30:57 +0000120
121 // Now that we have an accurate starting position we can consider ourselves
122 // zeroed.
Austin Schuh703b8d42015-02-01 14:56:34 -0800123 zeroed_ = true;
Adam Snaider3cd11c52015-02-16 02:16:09 +0000124 // Throw an error if first_start_pos is bigger/smaller than
Austin Schuh5f01f152017-02-11 21:34:08 -0800125 // constants_.allowable_encoder_error * index_diff + start_pos.
Adam Snaider3cd11c52015-02-16 02:16:09 +0000126 if (::std::abs(first_start_pos_ - start_pos_) >
Austin Schuh5f01f152017-02-11 21:34:08 -0800127 constants_.allowable_encoder_error * constants_.index_difference) {
Adam Snaider3cd11c52015-02-16 02:16:09 +0000128 if (!error_) {
129 LOG(ERROR,
130 "Encoder ticks out of range since last index pulse. first start "
Austin Schuh1c85bc82016-04-03 21:36:31 -0700131 "position: %f recent starting position: %f, allowable error: %f\n",
132 first_start_pos_, start_pos_,
Austin Schuh5f01f152017-02-11 21:34:08 -0800133 constants_.allowable_encoder_error * constants_.index_difference);
Adam Snaider3cd11c52015-02-16 02:16:09 +0000134 error_ = true;
135 }
136 }
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000137 }
Adam Snaiderb4119252015-02-15 01:30:57 +0000138
Austin Schuh5f01f152017-02-11 21:34:08 -0800139 position_ = start_pos_ + info.encoder;
Austin Schuhbe133ed2016-03-11 21:23:34 -0800140 filtered_position_ = start_average + info.encoder;
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000141}
142
Austin Schuh5f01f152017-02-11 21:34:08 -0800143PotAndAbsEncoderZeroingEstimator::PotAndAbsEncoderZeroingEstimator(
144 const constants::PotAndAbsoluteEncoderZeroingConstants &constants)
145 : constants_(constants) {
146 relative_to_absolute_offset_samples_.reserve(constants_.average_filter_size);
147 offset_samples_.reserve(constants_.average_filter_size);
148 Reset();
149}
150
151void PotAndAbsEncoderZeroingEstimator::Reset() {
152 zeroed_ = false;
153 relative_to_absolute_offset_samples_.clear();
154 offset_samples_.clear();
155}
156
157// So, this needs to be a multistep process. We need to first estimate the
158// offset between the absolute encoder and the relative encoder. That process
159// should get us an absolute number which is off by integer multiples of the
160// distance/rev. In parallel, we can estimate the offset between the pot and
161// encoder. When both estimates have converged, we can then compute the offset
162// in a cycle, and which cycle, which gives us the accurate global offset.
163//
164// It's tricky to compute the offset between the absolute and relative encoder.
165// We need to compute this inside 1 revolution. The easiest way to do this
166// would be to wrap the encoder, subtract the two of them, and then average the
167// result. That will struggle when they are off by PI. Instead, we need to
168// wrap the number to +- PI from the current averaged offset.
169void PotAndAbsEncoderZeroingEstimator::UpdateEstimate(
170 const PotAndAbsolutePosition &info) {
171 // TODO(austin): Only add this sample if the robot is stopped.
172
173 // Compute the sum of all the offset samples.
174 double relative_to_absolute_offset_sum = 0.0;
175 for (size_t i = 0; i < relative_to_absolute_offset_samples_.size(); ++i) {
176 relative_to_absolute_offset_sum += relative_to_absolute_offset_samples_[i];
177 }
178
179 // Compute the average offset between the absolute encoder and relative
180 // encoder. If we have 0 samples, assume it is 0.
181 double average_relative_to_absolute_offset =
182 relative_to_absolute_offset_samples_.size() == 0
183 ? 0.0
184 : relative_to_absolute_offset_sum /
185 relative_to_absolute_offset_samples_.size();
186
187 // Now, compute the nearest absolute encoder value to the offset relative
188 // encoder position.
189 const double adjusted_absolute_encoder =
190 Wrap(info.encoder + average_relative_to_absolute_offset,
191 info.absolute_encoder - constants_.measured_absolute_position,
192 constants_.one_revolution_distance);
193
194 const double relative_to_absolute_offset =
195 adjusted_absolute_encoder - info.encoder;
196
197 // Add the sample and update the average with the new reading.
198 const size_t relative_to_absolute_offset_samples_size =
199 relative_to_absolute_offset_samples_.size();
200 if (relative_to_absolute_offset_samples_size <
201 constants_.average_filter_size) {
202 average_relative_to_absolute_offset =
203 (average_relative_to_absolute_offset *
204 relative_to_absolute_offset_samples_size +
205 relative_to_absolute_offset) /
206 (relative_to_absolute_offset_samples_size + 1);
207
208 relative_to_absolute_offset_samples_.push_back(relative_to_absolute_offset);
209 } else {
210 average_relative_to_absolute_offset -=
211 relative_to_absolute_offset_samples_[samples_idx_] /
212 relative_to_absolute_offset_samples_size;
213 relative_to_absolute_offset_samples_[samples_idx_] =
214 relative_to_absolute_offset;
215 average_relative_to_absolute_offset +=
216 relative_to_absolute_offset / relative_to_absolute_offset_samples_size;
217 }
218
219 // Now compute the offset between the pot and relative encoder.
220 if (offset_samples_.size() < constants_.average_filter_size) {
221 offset_samples_.push_back(info.pot - info.encoder);
222 } else {
223 offset_samples_[samples_idx_] = info.pot - info.encoder;
224 }
225
226 // Drop the oldest sample when we run this function the next time around.
227 samples_idx_ = (samples_idx_ + 1) % constants_.average_filter_size;
228
229 double pot_relative_encoder_offset_sum = 0.0;
230 for (size_t i = 0; i < offset_samples_.size(); ++i) {
231 pot_relative_encoder_offset_sum += offset_samples_[i];
232 }
233 const double pot_relative_encoder_offset =
234 pot_relative_encoder_offset_sum / offset_samples_.size();
235
236 offset_ = Wrap(info.encoder + pot_relative_encoder_offset,
237 average_relative_to_absolute_offset + info.encoder,
238 constants_.one_revolution_distance) -
239 info.encoder;
240 if (offset_ready()) {
241 zeroed_ = true;
242 }
243
244 filtered_position_ = pot_relative_encoder_offset + info.encoder;
245 position_ = offset_ + info.encoder;
246}
247
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000248} // namespace zeroing
249} // namespace frc971