blob: ed9c65f3aa3af5f0921afc6f0c5a2935eb2660ec [file] [log] [blame]
James Kuszmaul9f2f53c2023-02-19 14:08:18 -08001#include "frc971/imu_reader/imu_watcher.h"
2
3#include "frc971/wpilib/imu_batch_generated.h"
4
5namespace frc971::controls {
6namespace {
7// Return the amount of distance that the drivetrain can travel before the
8// encoders will wrap. Necessary because the pico only sends over the encoders
9// in 16-bit counters, which will wrap relatively readily.
10double EncoderWrapDistance(double drivetrain_distance_per_encoder_tick) {
11 return drivetrain_distance_per_encoder_tick * (1 << 16);
12}
13} // namespace
14ImuWatcher::ImuWatcher(
15 aos::EventLoop *event_loop,
16 const control_loops::drivetrain::DrivetrainConfig<double> &dt_config,
17 const double drivetrain_distance_per_encoder_tick,
18 std::function<
19 void(aos::monotonic_clock::time_point, aos::monotonic_clock::time_point,
20 std::optional<Eigen::Vector2d>, Eigen::Vector3d, Eigen::Vector3d)>
21 callback)
22 : dt_config_(dt_config),
23 callback_(std::move(callback)),
24 zeroer_(zeroing::ImuZeroer::FaultBehavior::kTemporary),
25 left_encoder_(
26 -EncoderWrapDistance(drivetrain_distance_per_encoder_tick) / 2.0,
27 EncoderWrapDistance(drivetrain_distance_per_encoder_tick)),
28 right_encoder_(
29 -EncoderWrapDistance(drivetrain_distance_per_encoder_tick) / 2.0,
30 EncoderWrapDistance(drivetrain_distance_per_encoder_tick)) {
James Kuszmaul04a343c2023-02-20 16:38:22 -080031 event_loop->MakeWatcher("/localizer", [this](const IMUValuesBatch &values) {
James Kuszmaul9f2f53c2023-02-19 14:08:18 -080032 CHECK(values.has_readings());
33 for (const IMUValues *value : *values.readings()) {
34 zeroer_.InsertAndProcessMeasurement(*value);
35 if (zeroer_.Faulted()) {
36 if (value->checksum_failed()) {
37 imu_fault_tracker_.pico_to_pi_checksum_mismatch++;
38 } else if (value->previous_reading_diag_stat()->checksum_mismatch()) {
39 imu_fault_tracker_.imu_to_pico_checksum_mismatch++;
40 } else {
41 imu_fault_tracker_.other_zeroing_faults++;
42 }
43 } else {
44 if (!first_valid_data_counter_.has_value()) {
45 first_valid_data_counter_ = value->data_counter();
46 }
47 }
48 if (first_valid_data_counter_.has_value()) {
49 total_imu_messages_received_++;
50 // Only update when we have good checksums, since the data counter
51 // could get corrupted.
52 if (!zeroer_.Faulted()) {
53 if (value->data_counter() < last_data_counter_) {
54 data_counter_offset_ += 1 << 16;
55 }
56 imu_fault_tracker_.missed_messages =
57 (1 + value->data_counter() + data_counter_offset_ -
58 first_valid_data_counter_.value()) -
59 total_imu_messages_received_;
60 last_data_counter_ = value->data_counter();
61 }
62 }
63 // Set encoders to nullopt if we are faulted at all (faults may include
64 // checksum mismatches).
65 const std::optional<Eigen::Vector2d> encoders =
66 zeroer_.Faulted()
67 ? std::nullopt
68 : std::make_optional(Eigen::Vector2d{
69 left_encoder_.Unwrap(value->left_encoder()),
70 right_encoder_.Unwrap(value->right_encoder())});
71 {
72 // If we can't trust the imu reading, just naively increment the
73 // pico timestamp.
74 const aos::monotonic_clock::time_point pico_timestamp =
75 zeroer_.Faulted()
76 ? (last_pico_timestamp_.has_value()
77 ? last_pico_timestamp_.value() + kNominalDt
78 : aos::monotonic_clock::epoch())
79 : aos::monotonic_clock::time_point(
80 std::chrono::microseconds(value->pico_timestamp_us()));
James Kuszmaul04a343c2023-02-20 16:38:22 -080081 const aos::monotonic_clock::time_point pi_read_timestamp =
82 aos::monotonic_clock::time_point(
83 std::chrono::nanoseconds(value->monotonic_timestamp_ns()));
James Kuszmaul9f2f53c2023-02-19 14:08:18 -080084 // TODO(james): If we get large enough drift off of the pico,
85 // actually do something about it.
86 if (!pico_offset_.has_value()) {
James Kuszmaul04a343c2023-02-20 16:38:22 -080087 pico_offset_ = pi_read_timestamp - pico_timestamp;
James Kuszmaul9f2f53c2023-02-19 14:08:18 -080088 last_pico_timestamp_ = pico_timestamp;
89 }
90 if (pico_timestamp < last_pico_timestamp_) {
91 pico_offset_.value() += std::chrono::microseconds(1ULL << 32);
92 }
93 const aos::monotonic_clock::time_point sample_timestamp =
94 pico_offset_.value() + pico_timestamp;
James Kuszmaul04a343c2023-02-20 16:38:22 -080095 pico_offset_error_ = pi_read_timestamp - sample_timestamp;
James Kuszmaul9f2f53c2023-02-19 14:08:18 -080096 const bool zeroed = zeroer_.Zeroed();
97
98 // When not zeroed, we aim to approximate zero acceleration by doing a
99 // zero-order hold on the gyro and setting the accelerometer readings to
100 // gravity.
James Kuszmaul04a343c2023-02-20 16:38:22 -0800101 callback_(sample_timestamp, pi_read_timestamp, encoders,
102 zeroed ? zeroer_.ZeroedGyro().value() : last_gyro_,
James Kuszmaul9f2f53c2023-02-19 14:08:18 -0800103 zeroed ? zeroer_.ZeroedAccel().value()
104 : dt_config_.imu_transform.transpose() *
105 Eigen::Vector3d::UnitZ());
106
107 if (zeroed) {
108 last_gyro_ = zeroer_.ZeroedGyro().value();
109 }
110 last_pico_timestamp_ = pico_timestamp;
111 }
112 }
113 });
114}
115} // namespace frc971::controls