blob: e97fbe28c7cd0516c0ecf5945a8253d7039ca5f9 [file] [log] [blame]
#include "aos/flatbuffers.h"
#include "gtest/gtest.h"
#include "frc971/zeroing/imu_zeroer.h"
namespace frc971::zeroing {
static constexpr int kMinSamplesToZero =
2 * ImuZeroer::kSamplesToAverage * ImuZeroer::kRequiredZeroPoints;
aos::FlatbufferDetachedBuffer<IMUValues> MakeMeasurement(
const Eigen::Vector3d &gyro, const Eigen::Vector3d &accel) {
flatbuffers::FlatBufferBuilder fbb;
fbb.ForceDefaults(true);
IMUValuesBuilder builder(fbb);
builder.add_gyro_x(gyro.x());
builder.add_gyro_y(gyro.y());
builder.add_gyro_z(gyro.z());
builder.add_accelerometer_x(accel.x());
builder.add_accelerometer_y(accel.y());
builder.add_accelerometer_z(accel.z());
fbb.Finish(builder.Finish());
return fbb.Release();
}
// Tests that when we initialize everything is in a sane state.
TEST(ImuZeroerTest, InitializeUnzeroed) {
ImuZeroer zeroer;
ASSERT_FALSE(zeroer.Zeroed());
ASSERT_FALSE(zeroer.Faulted());
ASSERT_EQ(0.0, zeroer.GyroOffset().norm());
ASSERT_EQ(0.0, zeroer.ZeroedGyro().norm());
ASSERT_EQ(0.0, zeroer.ZeroedAccel().norm());
// A measurement before we are zeroed should just result in the measurement
// being passed through without modification.
zeroer.InsertAndProcessMeasurement(
MakeMeasurement({0.01, 0.02, 0.03}, {4, 5, 6}).message());
ASSERT_FALSE(zeroer.Zeroed());
ASSERT_FALSE(zeroer.Faulted());
ASSERT_EQ(0.0, zeroer.GyroOffset().norm());
ASSERT_FLOAT_EQ(0.01, zeroer.ZeroedGyro().x());
ASSERT_FLOAT_EQ(0.02, zeroer.ZeroedGyro().y());
ASSERT_FLOAT_EQ(0.03, zeroer.ZeroedGyro().z());
ASSERT_EQ(4.0, zeroer.ZeroedAccel().x());
ASSERT_EQ(5.0, zeroer.ZeroedAccel().y());
ASSERT_EQ(6.0, zeroer.ZeroedAccel().z());
}
// Tests that we zero if we receive a bunch of identical measurements.
TEST(ImuZeroerTest, ZeroOnConstantData) {
ImuZeroer zeroer;
ASSERT_FALSE(zeroer.Zeroed());
for (size_t ii = 0; ii < kMinSamplesToZero; ++ii) {
zeroer.InsertAndProcessMeasurement(
MakeMeasurement({0.01, 0.02, 0.03}, {4, 5, 6}).message());
}
ASSERT_TRUE(zeroer.Zeroed());
ASSERT_FALSE(zeroer.Faulted());
// Gyro should be zeroed to {1, 2, 3}.
ASSERT_FLOAT_EQ(0.01, zeroer.GyroOffset().x());
ASSERT_FLOAT_EQ(0.02, zeroer.GyroOffset().y());
ASSERT_FLOAT_EQ(0.03, zeroer.GyroOffset().z());
ASSERT_EQ(0.0, zeroer.ZeroedGyro().x());
ASSERT_EQ(0.0, zeroer.ZeroedGyro().y());
ASSERT_EQ(0.0, zeroer.ZeroedGyro().z());
// Accelerometer readings should not be affected.
ASSERT_EQ(4.0, zeroer.ZeroedAccel().x());
ASSERT_EQ(5.0, zeroer.ZeroedAccel().y());
ASSERT_EQ(6.0, zeroer.ZeroedAccel().z());
// If we get another measurement offset by {1, 1, 1} we should read the result
// as {1, 1, 1}.
zeroer.InsertAndProcessMeasurement(
MakeMeasurement({0.02, 0.03, 0.04}, {0, 0, 0}).message());
ASSERT_FALSE(zeroer.Faulted());
ASSERT_FLOAT_EQ(0.01, zeroer.ZeroedGyro().x());
ASSERT_FLOAT_EQ(0.01, zeroer.ZeroedGyro().y());
ASSERT_FLOAT_EQ(0.01, zeroer.ZeroedGyro().z());
}
// Tests that we do not zero if the gyro is producing particularly high
// magnitude results.
TEST(ImuZeroerTest, NoZeroOnHighMagnitudeGyro) {
ImuZeroer zeroer;
ASSERT_FALSE(zeroer.Zeroed());
for (size_t ii = 0; ii < kMinSamplesToZero; ++ii) {
zeroer.InsertAndProcessMeasurement(
MakeMeasurement({0.1, 0.2, 0.3}, {4, 5, 6}).message());
ASSERT_FALSE(zeroer.Zeroed());
}
ASSERT_FALSE(zeroer.Faulted());
}
// Tests that we tolerate small amounts of noise in the incoming data and can
// still zero.
TEST(ImuZeroerTest, ZeroOnLowNoiseData) {
ImuZeroer zeroer;
ASSERT_FALSE(zeroer.Zeroed());
for (size_t ii = 0; ii < kMinSamplesToZero; ++ii) {
const double offset =
(static_cast<double>(ii) / (kMinSamplesToZero - 1) - 0.5) * 0.001;
zeroer.InsertAndProcessMeasurement(
MakeMeasurement({0.01 + offset, 0.02 + offset, 0.03 + offset},
{4 + offset, 5 + offset, 6 + offset}).message());
}
ASSERT_TRUE(zeroer.Zeroed());
ASSERT_FALSE(zeroer.Faulted());
ASSERT_NEAR(0.01, zeroer.GyroOffset().x(), 1e-3);
ASSERT_NEAR(0.02, zeroer.GyroOffset().y(), 1e-3);
ASSERT_NEAR(0.03, zeroer.GyroOffset().z(), 1e-3);
// If we get another measurement offset by {0.01, 0.01, 0.01} we should read
// the result as {0.01, 0.01, 0.01}.
zeroer.InsertAndProcessMeasurement(
MakeMeasurement({0.02, 0.03, 0.04}, {0, 0, 0}).message());
ASSERT_FALSE(zeroer.Faulted());
ASSERT_NEAR(0.01, zeroer.ZeroedGyro().x(), 1e-3);
ASSERT_NEAR(0.01, zeroer.ZeroedGyro().y(), 1e-3);
ASSERT_NEAR(0.01, zeroer.ZeroedGyro().z(), 1e-3);
ASSERT_EQ(0.0, zeroer.ZeroedAccel().x());
ASSERT_EQ(0.0, zeroer.ZeroedAccel().y());
ASSERT_EQ(0.0, zeroer.ZeroedAccel().z());
}
// Tests that we do not zero if there is too much noise in the input data.
TEST(ImuZeroerTest, NoZeroOnHighNoiseData) {
ImuZeroer zeroer;
ASSERT_FALSE(zeroer.Zeroed());
for (size_t ii = 0; ii < kMinSamplesToZero; ++ii) {
ASSERT_FALSE(zeroer.Zeroed());
const double offset =
(static_cast<double>(ii) / (kMinSamplesToZero - 1) - 0.5) * 1.0;
zeroer.InsertAndProcessMeasurement(
MakeMeasurement({0.01 + offset, 0.02 + offset, 0.03 + offset},
{4 + offset, 5 + offset, 6 + offset}).message());
}
ASSERT_FALSE(zeroer.Zeroed());
ASSERT_FALSE(zeroer.Faulted());
}
// Tests that we fault if we successfully rezero and get a significantly offset
// zero.
TEST(ImuZeroerTest, FaultOnNewZero) {
ImuZeroer zeroer;
ASSERT_FALSE(zeroer.Zeroed());
for (size_t ii = 0; ii < kMinSamplesToZero; ++ii) {
zeroer.InsertAndProcessMeasurement(
MakeMeasurement({0.01, 0.02, 0.03}, {4, 5, 6}).message());
}
ASSERT_TRUE(zeroer.Zeroed());
ASSERT_FALSE(zeroer.Faulted())
<< "We should not fault until we complete a second cycle of zeroing.";
for (size_t ii = 0; ii < kMinSamplesToZero; ++ii) {
zeroer.InsertAndProcessMeasurement(
MakeMeasurement({0.01, 0.05, 0.03}, {4, 5, 6}).message());
}
ASSERT_TRUE(zeroer.Faulted());
}
// Tests that we do not fault if the zero only changes by a small amount.
TEST(ImuZeroerTest, NoFaultOnSimilarZero) {
ImuZeroer zeroer;
ASSERT_FALSE(zeroer.Zeroed());
for (size_t ii = 0; ii < kMinSamplesToZero; ++ii) {
zeroer.InsertAndProcessMeasurement(
MakeMeasurement({0.01, 0.02, 0.03}, {4, 5, 6}).message());
}
ASSERT_TRUE(zeroer.Zeroed());
for (size_t ii = 0; ii < kMinSamplesToZero; ++ii) {
zeroer.InsertAndProcessMeasurement(
MakeMeasurement({0.01, 0.020001, 0.03}, {4, 5, 6}).message());
}
ASSERT_FALSE(zeroer.Faulted());
}
} // namespace frc971::zeroing