blob: 3164bed1544f4783c3f52f1833586deb263a3f2d [file] [log] [blame]
#include "frc971/zeroing/imu_zeroer.h"
namespace frc971::zeroing {
namespace {
bool DiagStatHasFaults(const ADIS16470DiagStat &diag) {
return diag.clock_error() || diag.memory_failure() || diag.sensor_failure() ||
diag.standby_mode() || diag.spi_communication_error() ||
diag.flash_memory_update_error() || diag.data_path_overrun() ||
diag.checksum_mismatch();
}
bool ReadingHasFaults(const IMUValues &values) {
if (values.has_previous_reading_diag_stat() &&
DiagStatHasFaults(*values.previous_reading_diag_stat())) {
return true;
}
if (values.has_self_test_diag_stat() &&
DiagStatHasFaults(*values.self_test_diag_stat())) {
return true;
}
if (values.checksum_failed()) {
return true;
}
return false;
}
} // namespace
ImuZeroer::ImuZeroer(FaultBehavior fault_behavior, double gyro_max_variation)
: fault_behavior_(fault_behavior), gyro_max_variation_(gyro_max_variation) {
gyro_average_.setZero();
last_gyro_sample_.setZero();
last_accel_sample_.setZero();
}
bool ImuZeroer::Zeroed() const {
return num_zeroes_ > kRequiredZeroPoints && !Faulted();
}
bool ImuZeroer::Faulted() const { return reading_faulted_ || zeroing_faulted_; }
std::optional<Eigen::Vector3d> ImuZeroer::ZeroedGyro() const {
return Faulted() ? std::nullopt
: std::make_optional<Eigen::Vector3d>(last_gyro_sample_ -
gyro_average_);
}
std::optional<Eigen::Vector3d> ImuZeroer::ZeroedAccel() const {
return Faulted() ? std::nullopt
: std::make_optional<Eigen::Vector3d>(last_accel_sample_);
}
Eigen::Vector3d ImuZeroer::GyroOffset() const { return gyro_average_; }
bool ImuZeroer::GyroZeroReady() const {
return gyro_averager_.full() &&
gyro_averager_.GetRange() < gyro_max_variation_ &&
(last_gyro_sample_.lpNorm<Eigen::Infinity>() <
kGyroMaxZeroingMagnitude);
}
bool ImuZeroer::AccelZeroReady() const {
return accel_averager_.full() &&
accel_averager_.GetRange() < kAccelMaxVariation;
}
void ImuZeroer::InsertAndProcessMeasurement(const IMUValues &values) {
if (InsertMeasurement(values)) {
ProcessMeasurements();
}
}
bool ImuZeroer::InsertMeasurement(const IMUValues &values) {
if (ReadingHasFaults(values)) {
reading_faulted_ = true;
return false;
}
if (fault_behavior_ == FaultBehavior::kTemporary) {
reading_faulted_ = false;
}
last_gyro_sample_ << values.gyro_x(), values.gyro_y(), values.gyro_z();
gyro_averager_.AddData(last_gyro_sample_);
last_accel_sample_ << values.accelerometer_x(), values.accelerometer_y(),
values.accelerometer_z();
accel_averager_.AddData(last_accel_sample_);
return true;
}
void ImuZeroer::ProcessMeasurements() {
if (GyroZeroReady() && AccelZeroReady()) {
++good_iters_;
if (good_iters_ > kSamplesToAverage / 40) {
const Eigen::Vector3d current_gyro_average = gyro_averager_.GetAverage();
constexpr double kAverageUpdateWeight = 0.05;
if (num_zeroes_ > 0) {
gyro_average_ +=
(current_gyro_average - gyro_average_) * kAverageUpdateWeight;
} else {
gyro_average_ = current_gyro_average;
}
if (num_zeroes_ > 0) {
// If we got a new zero and it is substantially different from the
// original zero, fault.
if ((current_gyro_average - gyro_average_).norm() >
kGyroFaultVariation) {
zeroing_faulted_ = true;
} else if (fault_behavior_ == FaultBehavior::kTemporary) {
zeroing_faulted_ = false;
}
}
++num_zeroes_;
gyro_averager_.Reset();
}
} else {
good_iters_ = 0;
}
}
flatbuffers::Offset<control_loops::drivetrain::ImuZeroerState>
ImuZeroer::PopulateStatus(flatbuffers::FlatBufferBuilder *fbb) const {
control_loops::drivetrain::ImuZeroerState::Builder builder(*fbb);
builder.add_zeroed(Zeroed());
builder.add_faulted(Faulted());
builder.add_number_of_zeroes(num_zeroes_);
builder.add_gyro_x_average(GyroOffset().x());
builder.add_gyro_y_average(GyroOffset().y());
builder.add_gyro_z_average(GyroOffset().z());
builder.add_accel_x_average(accel_averager_.GetAverage()[0]);
builder.add_accel_y_average(accel_averager_.GetAverage()[1]);
builder.add_accel_z_average(accel_averager_.GetAverage()[2]);
return builder.Finish();
}
} // namespace frc971::zeroing