blob: 0e0d65620ed2d6ca93f07ab7b1163f963addb269 [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)) {
31 event_loop->MakeWatcher("/localizer", [this, event_loop](
32 const IMUValuesBatch &values) {
33 CHECK(values.has_readings());
34 for (const IMUValues *value : *values.readings()) {
35 zeroer_.InsertAndProcessMeasurement(*value);
36 if (zeroer_.Faulted()) {
37 if (value->checksum_failed()) {
38 imu_fault_tracker_.pico_to_pi_checksum_mismatch++;
39 } else if (value->previous_reading_diag_stat()->checksum_mismatch()) {
40 imu_fault_tracker_.imu_to_pico_checksum_mismatch++;
41 } else {
42 imu_fault_tracker_.other_zeroing_faults++;
43 }
44 } else {
45 if (!first_valid_data_counter_.has_value()) {
46 first_valid_data_counter_ = value->data_counter();
47 }
48 }
49 if (first_valid_data_counter_.has_value()) {
50 total_imu_messages_received_++;
51 // Only update when we have good checksums, since the data counter
52 // could get corrupted.
53 if (!zeroer_.Faulted()) {
54 if (value->data_counter() < last_data_counter_) {
55 data_counter_offset_ += 1 << 16;
56 }
57 imu_fault_tracker_.missed_messages =
58 (1 + value->data_counter() + data_counter_offset_ -
59 first_valid_data_counter_.value()) -
60 total_imu_messages_received_;
61 last_data_counter_ = value->data_counter();
62 }
63 }
64 // Set encoders to nullopt if we are faulted at all (faults may include
65 // checksum mismatches).
66 const std::optional<Eigen::Vector2d> encoders =
67 zeroer_.Faulted()
68 ? std::nullopt
69 : std::make_optional(Eigen::Vector2d{
70 left_encoder_.Unwrap(value->left_encoder()),
71 right_encoder_.Unwrap(value->right_encoder())});
72 {
73 // If we can't trust the imu reading, just naively increment the
74 // pico timestamp.
75 const aos::monotonic_clock::time_point pico_timestamp =
76 zeroer_.Faulted()
77 ? (last_pico_timestamp_.has_value()
78 ? last_pico_timestamp_.value() + kNominalDt
79 : aos::monotonic_clock::epoch())
80 : aos::monotonic_clock::time_point(
81 std::chrono::microseconds(value->pico_timestamp_us()));
82 // TODO(james): If we get large enough drift off of the pico,
83 // actually do something about it.
84 if (!pico_offset_.has_value()) {
85 pico_offset_ =
86 event_loop->context().monotonic_event_time - pico_timestamp;
87 last_pico_timestamp_ = pico_timestamp;
88 }
89 if (pico_timestamp < last_pico_timestamp_) {
90 pico_offset_.value() += std::chrono::microseconds(1ULL << 32);
91 }
92 const aos::monotonic_clock::time_point sample_timestamp =
93 pico_offset_.value() + pico_timestamp;
94 pico_offset_error_ =
95 event_loop->context().monotonic_event_time - sample_timestamp;
96 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.
101 callback_(sample_timestamp,
102 aos::monotonic_clock::time_point(std::chrono::nanoseconds(
103 value->monotonic_timestamp_ns())),
104 encoders, zeroed ? zeroer_.ZeroedGyro().value() : last_gyro_,
105 zeroed ? zeroer_.ZeroedAccel().value()
106 : dt_config_.imu_transform.transpose() *
107 Eigen::Vector3d::UnitZ());
108
109 if (zeroed) {
110 last_gyro_ = zeroer_.ZeroedGyro().value();
111 }
112 last_pico_timestamp_ = pico_timestamp;
113 }
114 }
115 });
116}
117} // namespace frc971::controls