blob: 87debdd78bcfb53b8362330b11d8f71b91c3b5cd [file] [log] [blame]
Adam Snaiderc4b3c192015-02-01 01:30:39 +00001#include <unistd.h>
2
3#include <memory>
4
5#include <random>
6
7#include "gtest/gtest.h"
Adam Snaiderc4b3c192015-02-01 01:30:39 +00008#include "frc971/zeroing/zeroing.h"
Adam Snaiderb4119252015-02-15 01:30:57 +00009#include "frc971/control_loops/control_loops.q.h"
Brian Silvermanf5f8d8e2015-12-06 18:39:12 -050010#include "aos/testing/test_shm.h"
Adam Snaiderc4b3c192015-02-01 01:30:39 +000011#include "aos/common/util/thread.h"
12#include "aos/common/die.h"
Adam Snaiderb4119252015-02-15 01:30:57 +000013#include "frc971/control_loops/position_sensor_sim.h"
Adam Snaiderc4b3c192015-02-01 01:30:39 +000014
15namespace frc971 {
16namespace zeroing {
17
Adam Snaiderb4119252015-02-15 01:30:57 +000018using control_loops::PositionSensorSimulator;
Tyler Chatowf8f03112017-02-05 14:31:34 -080019using constants::PotAndIndexPulseZeroingConstants;
Austin Schuh5f01f152017-02-11 21:34:08 -080020using constants::PotAndAbsoluteEncoderZeroingConstants;
Adam Snaiderc4b3c192015-02-01 01:30:39 +000021
Adam Snaiderb4119252015-02-15 01:30:57 +000022static const size_t kSampleSize = 30;
23static const double kAcceptableUnzeroedError = 0.2;
Adam Snaider3cd11c52015-02-16 02:16:09 +000024static const double kIndexErrorFraction = 0.3;
Adam Snaiderc4b3c192015-02-01 01:30:39 +000025
Adam Snaiderb4119252015-02-15 01:30:57 +000026class ZeroingTest : public ::testing::Test {
Adam Snaiderc4b3c192015-02-01 01:30:39 +000027 protected:
28 void SetUp() override { aos::SetDieTestMode(true); }
29
Tyler Chatowf8f03112017-02-05 14:31:34 -080030 void MoveTo(PositionSensorSimulator *simulator,
31 PotAndIndexPulseZeroingEstimator *estimator,
Adam Snaiderb4119252015-02-15 01:30:57 +000032 double new_position) {
Brian Silvermandc4eb102017-02-05 17:34:41 -080033 PotAndIndexPosition sensor_values;
Adam Snaiderb4119252015-02-15 01:30:57 +000034 simulator->MoveTo(new_position);
Brian Silvermandc4eb102017-02-05 17:34:41 -080035 simulator->GetSensorValues(&sensor_values);
36 estimator->UpdateEstimate(sensor_values);
Adam Snaiderb4119252015-02-15 01:30:57 +000037 }
Adam Snaiderc4b3c192015-02-01 01:30:39 +000038
Austin Schuh5f01f152017-02-11 21:34:08 -080039 void MoveTo(PositionSensorSimulator *simulator,
40 PotAndAbsEncoderZeroingEstimator *estimator, double new_position) {
41 PotAndAbsolutePosition sensor_values_;
42 simulator->MoveTo(new_position);
43 simulator->GetSensorValues(&sensor_values_);
44 estimator->UpdateEstimate(sensor_values_);
45 }
46 // TODO(phil): Add new MoveTo overloads for different zeroing estimators.
47
Brian Silvermanf5f8d8e2015-12-06 18:39:12 -050048 ::aos::testing::TestSharedMemory my_shm_;
Adam Snaiderc4b3c192015-02-01 01:30:39 +000049};
50
Adam Snaiderb4119252015-02-15 01:30:57 +000051TEST_F(ZeroingTest, TestMovingAverageFilter) {
52 const double index_diff = 1.0;
53 PositionSensorSimulator sim(index_diff);
54 sim.Initialize(3.6 * index_diff, index_diff / 3.0);
Tyler Chatowf8f03112017-02-05 14:31:34 -080055 PotAndIndexPulseZeroingEstimator estimator(PotAndIndexPulseZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +000056 kSampleSize, index_diff, 0.0, kIndexErrorFraction});
Adam Snaiderc4b3c192015-02-01 01:30:39 +000057
58 // The zeroing code is supposed to perform some filtering on the difference
59 // between the potentiometer value and the encoder value. We assume that 300
60 // samples are sufficient to have updated the filter.
61 for (int i = 0; i < 300; i++) {
Adam Snaiderb4119252015-02-15 01:30:57 +000062 MoveTo(&sim, &estimator, 3.3 * index_diff);
Adam Snaiderc4b3c192015-02-01 01:30:39 +000063 }
Adam Snaiderb4119252015-02-15 01:30:57 +000064 ASSERT_NEAR(3.3 * index_diff, estimator.position(),
65 kAcceptableUnzeroedError * index_diff);
Adam Snaiderc4b3c192015-02-01 01:30:39 +000066
67 for (int i = 0; i < 300; i++) {
Adam Snaiderb4119252015-02-15 01:30:57 +000068 MoveTo(&sim, &estimator, 3.9 * index_diff);
Adam Snaiderc4b3c192015-02-01 01:30:39 +000069 }
Adam Snaiderb4119252015-02-15 01:30:57 +000070 ASSERT_NEAR(3.9 * index_diff, estimator.position(),
71 kAcceptableUnzeroedError * index_diff);
Adam Snaiderc4b3c192015-02-01 01:30:39 +000072}
73
Adam Snaiderb4119252015-02-15 01:30:57 +000074TEST_F(ZeroingTest, NotZeroedBeforeEnoughSamplesCollected) {
75 double index_diff = 0.5;
76 double position = 3.6 * index_diff;
77 PositionSensorSimulator sim(index_diff);
78 sim.Initialize(position, index_diff / 3.0);
Tyler Chatowf8f03112017-02-05 14:31:34 -080079 PotAndIndexPulseZeroingEstimator estimator(PotAndIndexPulseZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +000080 kSampleSize, index_diff, 0.0, kIndexErrorFraction});
Adam Snaiderb4119252015-02-15 01:30:57 +000081
82 // Make sure that the zeroing code does not consider itself zeroed until we
83 // collect a good amount of samples. In this case we're waiting until the
84 // moving average filter is full.
85 for (unsigned int i = 0; i < kSampleSize - 1; i++) {
86 MoveTo(&sim, &estimator, position += index_diff);
87 ASSERT_FALSE(estimator.zeroed());
88 }
89
90 MoveTo(&sim, &estimator, position);
91 ASSERT_TRUE(estimator.zeroed());
92}
93
94TEST_F(ZeroingTest, TestLotsOfMovement) {
95 double index_diff = 1.0;
96 PositionSensorSimulator sim(index_diff);
97 sim.Initialize(3.6, index_diff / 3.0);
Tyler Chatowf8f03112017-02-05 14:31:34 -080098 PotAndIndexPulseZeroingEstimator estimator(PotAndIndexPulseZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +000099 kSampleSize, index_diff, 0.0, kIndexErrorFraction});
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000100
101 // The zeroing code is supposed to perform some filtering on the difference
102 // between the potentiometer value and the encoder value. We assume that 300
103 // samples are sufficient to have updated the filter.
104 for (int i = 0; i < 300; i++) {
Adam Snaiderb4119252015-02-15 01:30:57 +0000105 MoveTo(&sim, &estimator, 3.6);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000106 }
Adam Snaiderb4119252015-02-15 01:30:57 +0000107 ASSERT_NEAR(3.6, estimator.position(), kAcceptableUnzeroedError * index_diff);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000108
109 // With a single index pulse the zeroing estimator should be able to lock
110 // onto the true value of the position.
Adam Snaiderb4119252015-02-15 01:30:57 +0000111 MoveTo(&sim, &estimator, 4.01);
112 ASSERT_NEAR(4.01, estimator.position(), 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000113
Adam Snaiderb4119252015-02-15 01:30:57 +0000114 MoveTo(&sim, &estimator, 4.99);
115 ASSERT_NEAR(4.99, estimator.position(), 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000116
Adam Snaiderb4119252015-02-15 01:30:57 +0000117 MoveTo(&sim, &estimator, 3.99);
118 ASSERT_NEAR(3.99, estimator.position(), 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000119
Adam Snaiderb4119252015-02-15 01:30:57 +0000120 MoveTo(&sim, &estimator, 3.01);
121 ASSERT_NEAR(3.01, estimator.position(), 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000122
Adam Snaiderb4119252015-02-15 01:30:57 +0000123 MoveTo(&sim, &estimator, 13.55);
124 ASSERT_NEAR(13.55, estimator.position(), 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000125}
126
Adam Snaiderb4119252015-02-15 01:30:57 +0000127TEST_F(ZeroingTest, TestDifferentIndexDiffs) {
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000128 double index_diff = 0.89;
Adam Snaiderb4119252015-02-15 01:30:57 +0000129 PositionSensorSimulator sim(index_diff);
130 sim.Initialize(3.5 * index_diff, index_diff / 3.0);
Tyler Chatowf8f03112017-02-05 14:31:34 -0800131 PotAndIndexPulseZeroingEstimator estimator(PotAndIndexPulseZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +0000132 kSampleSize, index_diff, 0.0, kIndexErrorFraction});
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000133
134 // The zeroing code is supposed to perform some filtering on the difference
135 // between the potentiometer value and the encoder value. We assume that 300
136 // samples are sufficient to have updated the filter.
137 for (int i = 0; i < 300; i++) {
Adam Snaiderb4119252015-02-15 01:30:57 +0000138 MoveTo(&sim, &estimator, 3.5 * index_diff);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000139 }
Adam Snaiderb4119252015-02-15 01:30:57 +0000140 ASSERT_NEAR(3.5 * index_diff, estimator.position(),
141 kAcceptableUnzeroedError * index_diff);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000142
143 // With a single index pulse the zeroing estimator should be able to lock
144 // onto the true value of the position.
Adam Snaiderb4119252015-02-15 01:30:57 +0000145 MoveTo(&sim, &estimator, 4.01);
146 ASSERT_NEAR(4.01, estimator.position(), 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000147
Adam Snaiderb4119252015-02-15 01:30:57 +0000148 MoveTo(&sim, &estimator, 4.99);
149 ASSERT_NEAR(4.99, estimator.position(), 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000150
Adam Snaiderb4119252015-02-15 01:30:57 +0000151 MoveTo(&sim, &estimator, 3.99);
152 ASSERT_NEAR(3.99, estimator.position(), 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000153
Adam Snaiderb4119252015-02-15 01:30:57 +0000154 MoveTo(&sim, &estimator, 3.01);
155 ASSERT_NEAR(3.01, estimator.position(), 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000156
Adam Snaiderb4119252015-02-15 01:30:57 +0000157 MoveTo(&sim, &estimator, 13.55);
158 ASSERT_NEAR(13.55, estimator.position(), 0.001);
159}
160
161TEST_F(ZeroingTest, TestPercentage) {
162 double index_diff = 0.89;
163 PositionSensorSimulator sim(index_diff);
164 sim.Initialize(3.5 * index_diff, index_diff / 3.0);
Tyler Chatowf8f03112017-02-05 14:31:34 -0800165 PotAndIndexPulseZeroingEstimator estimator(PotAndIndexPulseZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +0000166 kSampleSize, index_diff, 0.0, kIndexErrorFraction});
Adam Snaiderb4119252015-02-15 01:30:57 +0000167
168 for (unsigned int i = 0; i < kSampleSize / 2; i++) {
169 MoveTo(&sim, &estimator, 3.5 * index_diff);
170 }
171 ASSERT_NEAR(0.5, estimator.offset_ratio_ready(), 0.001);
Austin Schuh7485dbb2016-02-08 00:21:58 -0800172 ASSERT_FALSE(estimator.offset_ready());
173
174 for (unsigned int i = 0; i < kSampleSize / 2; i++) {
175 MoveTo(&sim, &estimator, 3.5 * index_diff);
176 }
177 ASSERT_NEAR(1.0, estimator.offset_ratio_ready(), 0.001);
178 ASSERT_TRUE(estimator.offset_ready());
Adam Snaiderb4119252015-02-15 01:30:57 +0000179}
180
181TEST_F(ZeroingTest, TestOffset) {
182 double index_diff = 0.89;
183 PositionSensorSimulator sim(index_diff);
184 sim.Initialize(3.1 * index_diff, index_diff / 3.0);
Tyler Chatowf8f03112017-02-05 14:31:34 -0800185 PotAndIndexPulseZeroingEstimator estimator(PotAndIndexPulseZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +0000186 kSampleSize, index_diff, 0.0, kIndexErrorFraction});
Adam Snaiderb4119252015-02-15 01:30:57 +0000187
Philipp Schrader41d82912015-02-15 03:44:23 +0000188 MoveTo(&sim, &estimator, 3.1 * index_diff);
189
Adam Snaiderb4119252015-02-15 01:30:57 +0000190 for (unsigned int i = 0; i < kSampleSize; i++) {
191 MoveTo(&sim, &estimator, 5.0 * index_diff);
192 }
Philipp Schrader41d82912015-02-15 03:44:23 +0000193
Adam Snaiderb4119252015-02-15 01:30:57 +0000194 ASSERT_NEAR(3.1 * index_diff, estimator.offset(), 0.001);
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000195}
196
Philipp Schrader41d82912015-02-15 03:44:23 +0000197TEST_F(ZeroingTest, WaitForIndexPulseAfterReset) {
198 double index_diff = 0.6;
199 PositionSensorSimulator sim(index_diff);
200 sim.Initialize(3.1 * index_diff, index_diff / 3.0);
Tyler Chatowf8f03112017-02-05 14:31:34 -0800201 PotAndIndexPulseZeroingEstimator estimator(PotAndIndexPulseZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +0000202 kSampleSize, index_diff, 0.0, kIndexErrorFraction});
Philipp Schrader41d82912015-02-15 03:44:23 +0000203
204 // Make sure to fill up the averaging filter with samples.
205 for (unsigned int i = 0; i < kSampleSize; i++) {
206 MoveTo(&sim, &estimator, 3.1 * index_diff);
207 }
208
209 // Make sure we're not zeroed until we hit an index pulse.
210 ASSERT_FALSE(estimator.zeroed());
211
212 // Trigger an index pulse; we should now be zeroed.
213 MoveTo(&sim, &estimator, 4.5 * index_diff);
214 ASSERT_TRUE(estimator.zeroed());
215
216 // Reset the zeroing logic and supply a bunch of samples within the current
217 // index segment.
218 estimator.Reset();
219 for (unsigned int i = 0; i < kSampleSize; i++) {
220 MoveTo(&sim, &estimator, 4.2 * index_diff);
221 }
222
223 // Make sure we're not zeroed until we hit an index pulse.
224 ASSERT_FALSE(estimator.zeroed());
225
226 // Trigger another index pulse; we should be zeroed again.
227 MoveTo(&sim, &estimator, 3.1 * index_diff);
228 ASSERT_TRUE(estimator.zeroed());
229}
230
Philipp Schrader030ad182015-02-15 05:40:58 +0000231TEST_F(ZeroingTest, TestNonZeroIndexPulseOffsets) {
232 const double index_diff = 0.9;
233 const double known_index_pos = 3.5 * index_diff;
234 PositionSensorSimulator sim(index_diff);
235 sim.Initialize(3.3 * index_diff, index_diff / 3.0, known_index_pos);
Tyler Chatowf8f03112017-02-05 14:31:34 -0800236 PotAndIndexPulseZeroingEstimator estimator(PotAndIndexPulseZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +0000237 kSampleSize, index_diff, known_index_pos, kIndexErrorFraction});
Philipp Schrader030ad182015-02-15 05:40:58 +0000238
239 // Make sure to fill up the averaging filter with samples.
240 for (unsigned int i = 0; i < kSampleSize; i++) {
241 MoveTo(&sim, &estimator, 3.3 * index_diff);
242 }
243
244 // Make sure we're not zeroed until we hit an index pulse.
245 ASSERT_FALSE(estimator.zeroed());
246
247 // Trigger an index pulse; we should now be zeroed.
248 MoveTo(&sim, &estimator, 3.7 * index_diff);
249 ASSERT_TRUE(estimator.zeroed());
250 ASSERT_DOUBLE_EQ(3.3 * index_diff, estimator.offset());
251 ASSERT_DOUBLE_EQ(3.7 * index_diff, estimator.position());
252
253 // Trigger one more index pulse and check the offset.
254 MoveTo(&sim, &estimator, 4.7 * index_diff);
255 ASSERT_DOUBLE_EQ(3.3 * index_diff, estimator.offset());
256 ASSERT_DOUBLE_EQ(4.7 * index_diff, estimator.position());
257}
258
Philipp Schrader53f4b6d2015-02-15 22:32:08 +0000259TEST_F(ZeroingTest, BasicErrorAPITest) {
260 const double index_diff = 1.0;
Tyler Chatowf8f03112017-02-05 14:31:34 -0800261 PotAndIndexPulseZeroingEstimator estimator(PotAndIndexPulseZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +0000262 kSampleSize, index_diff, 0.0, kIndexErrorFraction});
Philipp Schrader53f4b6d2015-02-15 22:32:08 +0000263 PositionSensorSimulator sim(index_diff);
264 sim.Initialize(1.5 * index_diff, index_diff / 3.0, 0.0);
265
266 // Perform a simple move and make sure that no error occured.
267 MoveTo(&sim, &estimator, 3.5 * index_diff);
268 ASSERT_FALSE(estimator.error());
269
270 // Trigger an error and make sure it's reported.
271 estimator.TriggerError();
272 ASSERT_TRUE(estimator.error());
273
274 // Make sure that it can recover after a reset.
275 estimator.Reset();
276 ASSERT_FALSE(estimator.error());
277 MoveTo(&sim, &estimator, 4.5 * index_diff);
278 MoveTo(&sim, &estimator, 5.5 * index_diff);
279 ASSERT_FALSE(estimator.error());
280}
281
Adam Snaider3cd11c52015-02-16 02:16:09 +0000282// I want to test that the the zeroing class can
283// detect an error when the starting position
284// changes too much. I do so by creating the
285// simulator at an 'X' positon, making sure
286// that the estimator is zeroed, and then
287// initializing the simulator at another
288// position. After making sure it's zeroed,
289// if the error() function returns true,
290// then, it works.
291TEST_F(ZeroingTest, TestOffsetError) {
292 const double index_diff = 0.8;
293 const double known_index_pos = 2 * index_diff;
Austin Schuh5f01f152017-02-11 21:34:08 -0800294 const size_t sample_size = 30;
Adam Snaider3cd11c52015-02-16 02:16:09 +0000295 PositionSensorSimulator sim(index_diff);
296 sim.Initialize(10 * index_diff, index_diff / 3.0, known_index_pos);
Tyler Chatowf8f03112017-02-05 14:31:34 -0800297 PotAndIndexPulseZeroingEstimator estimator(PotAndIndexPulseZeroingConstants{
Adam Snaider3cd11c52015-02-16 02:16:09 +0000298 sample_size, index_diff, known_index_pos, kIndexErrorFraction});
299
Austin Schuh5f01f152017-02-11 21:34:08 -0800300 for (size_t i = 0; i < sample_size; i++) {
Adam Snaider3cd11c52015-02-16 02:16:09 +0000301 MoveTo(&sim, &estimator, 13 * index_diff);
302 }
303 MoveTo(&sim, &estimator, 8 * index_diff);
304
305 ASSERT_TRUE(estimator.zeroed());
306 ASSERT_FALSE(estimator.error());
307 sim.Initialize(9.0 * index_diff + 0.31 * index_diff, index_diff / 3.0,
308 known_index_pos);
309 MoveTo(&sim, &estimator, 9 * index_diff);
310 ASSERT_TRUE(estimator.zeroed());
311 ASSERT_TRUE(estimator.error());
312}
313
Austin Schuh5f01f152017-02-11 21:34:08 -0800314// Makes sure that using an absolute encoder lets us zero without moving.
315TEST_F(ZeroingTest, TestAbsoluteEncoderZeroingWithoutMovement) {
316 const double index_diff = 1.0;
317 PositionSensorSimulator sim(index_diff);
318
319 const double start_pos = 2.1;
320 double measured_absolute_position = 0.3 * index_diff;
321
322 PotAndAbsoluteEncoderZeroingConstants constants{
323 kSampleSize, index_diff, measured_absolute_position, 0.1};
324
325 sim.Initialize(start_pos, index_diff / 3.0, 0.0,
326 constants.measured_absolute_position);
327
328 PotAndAbsEncoderZeroingEstimator estimator(constants);
329
330 for (size_t i = 0; i < kSampleSize - 1; ++i) {
331 MoveTo(&sim, &estimator, start_pos);
332 ASSERT_FALSE(estimator.zeroed());
333 }
334
335 MoveTo(&sim, &estimator, start_pos);
336 ASSERT_TRUE(estimator.zeroed());
337 EXPECT_DOUBLE_EQ(start_pos, estimator.offset());
338}
339
340// Makes sure that using an absolute encoder doesn't let us zero while moving.
341TEST_F(ZeroingTest, TestAbsoluteEncoderZeroingWithMovement) {
342 const double index_diff = 1.0;
343 PositionSensorSimulator sim(index_diff);
344
345 const double start_pos = 10 * index_diff;
346 double measured_absolute_position = 0.3 * index_diff;
347
348 PotAndAbsoluteEncoderZeroingConstants constants{
349 kSampleSize, index_diff, measured_absolute_position, 0.1};
350
351 sim.Initialize(start_pos, index_diff / 3.0, 0.0,
352 constants.measured_absolute_position);
353
354 PotAndAbsEncoderZeroingEstimator estimator(constants);
355
356 for (size_t i = 0; i < kSampleSize - 1; ++i) {
357 MoveTo(&sim, &estimator, start_pos + i * index_diff);
358 ASSERT_FALSE(estimator.zeroed());
359 }
360 MoveTo(&sim, &estimator, start_pos + 10 * index_diff);
361
362 MoveTo(&sim, &estimator, start_pos);
363 ASSERT_FALSE(estimator.zeroed());
364}
Adam Snaiderc4b3c192015-02-01 01:30:39 +0000365} // namespace zeroing
366} // namespace frc971