blob: e4e100a95abd09f32f453b5ce9c51ec58093857a [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)>
James Kuszmaul54fe9d02023-03-23 20:32:40 -070021 callback,
22 TimestampSource timestamp_source)
James Kuszmaul9f2f53c2023-02-19 14:08:18 -080023 : dt_config_(dt_config),
24 callback_(std::move(callback)),
25 zeroer_(zeroing::ImuZeroer::FaultBehavior::kTemporary),
26 left_encoder_(
27 -EncoderWrapDistance(drivetrain_distance_per_encoder_tick) / 2.0,
28 EncoderWrapDistance(drivetrain_distance_per_encoder_tick)),
29 right_encoder_(
30 -EncoderWrapDistance(drivetrain_distance_per_encoder_tick) / 2.0,
31 EncoderWrapDistance(drivetrain_distance_per_encoder_tick)) {
James Kuszmaul54fe9d02023-03-23 20:32:40 -070032 event_loop->MakeWatcher("/localizer", [this, timestamp_source](
33 const IMUValuesBatch &values) {
James Kuszmaul9f2f53c2023-02-19 14:08:18 -080034 CHECK(values.has_readings());
35 for (const IMUValues *value : *values.readings()) {
36 zeroer_.InsertAndProcessMeasurement(*value);
37 if (zeroer_.Faulted()) {
38 if (value->checksum_failed()) {
39 imu_fault_tracker_.pico_to_pi_checksum_mismatch++;
40 } else if (value->previous_reading_diag_stat()->checksum_mismatch()) {
41 imu_fault_tracker_.imu_to_pico_checksum_mismatch++;
42 } else {
43 imu_fault_tracker_.other_zeroing_faults++;
44 }
45 } else {
46 if (!first_valid_data_counter_.has_value()) {
47 first_valid_data_counter_ = value->data_counter();
48 }
49 }
50 if (first_valid_data_counter_.has_value()) {
51 total_imu_messages_received_++;
52 // Only update when we have good checksums, since the data counter
53 // could get corrupted.
54 if (!zeroer_.Faulted()) {
55 if (value->data_counter() < last_data_counter_) {
56 data_counter_offset_ += 1 << 16;
57 }
58 imu_fault_tracker_.missed_messages =
59 (1 + value->data_counter() + data_counter_offset_ -
60 first_valid_data_counter_.value()) -
61 total_imu_messages_received_;
62 last_data_counter_ = value->data_counter();
63 }
64 }
65 // Set encoders to nullopt if we are faulted at all (faults may include
66 // checksum mismatches).
67 const std::optional<Eigen::Vector2d> encoders =
68 zeroer_.Faulted()
69 ? std::nullopt
70 : std::make_optional(Eigen::Vector2d{
71 left_encoder_.Unwrap(value->left_encoder()),
72 right_encoder_.Unwrap(value->right_encoder())});
73 {
James Kuszmaul04a343c2023-02-20 16:38:22 -080074 const aos::monotonic_clock::time_point pi_read_timestamp =
75 aos::monotonic_clock::time_point(
76 std::chrono::nanoseconds(value->monotonic_timestamp_ns()));
James Kuszmaul54fe9d02023-03-23 20:32:40 -070077 // If we can't trust the imu reading, just naively increment the
78 // pico timestamp.
79 const aos::monotonic_clock::time_point pico_timestamp =
80 timestamp_source == TimestampSource::kPi
81 ? pi_read_timestamp
82 : (zeroer_.Faulted()
83 ? (last_pico_timestamp_.has_value()
84 ? last_pico_timestamp_.value() + kNominalDt
85 : aos::monotonic_clock::epoch())
86 : aos::monotonic_clock::time_point(
87 std::chrono::microseconds(
88 value->pico_timestamp_us())));
James Kuszmaul9f2f53c2023-02-19 14:08:18 -080089 // TODO(james): If we get large enough drift off of the pico,
90 // actually do something about it.
91 if (!pico_offset_.has_value()) {
James Kuszmaul04a343c2023-02-20 16:38:22 -080092 pico_offset_ = pi_read_timestamp - pico_timestamp;
James Kuszmaul9f2f53c2023-02-19 14:08:18 -080093 last_pico_timestamp_ = pico_timestamp;
94 }
95 if (pico_timestamp < last_pico_timestamp_) {
96 pico_offset_.value() += std::chrono::microseconds(1ULL << 32);
97 }
98 const aos::monotonic_clock::time_point sample_timestamp =
99 pico_offset_.value() + pico_timestamp;
James Kuszmaul04a343c2023-02-20 16:38:22 -0800100 pico_offset_error_ = pi_read_timestamp - sample_timestamp;
James Kuszmaul9f2f53c2023-02-19 14:08:18 -0800101 const bool zeroed = zeroer_.Zeroed();
102
103 // When not zeroed, we aim to approximate zero acceleration by doing a
104 // zero-order hold on the gyro and setting the accelerometer readings to
105 // gravity.
James Kuszmaul04a343c2023-02-20 16:38:22 -0800106 callback_(sample_timestamp, pi_read_timestamp, encoders,
107 zeroed ? zeroer_.ZeroedGyro().value() : last_gyro_,
James Kuszmaul9f2f53c2023-02-19 14:08:18 -0800108 zeroed ? zeroer_.ZeroedAccel().value()
109 : dt_config_.imu_transform.transpose() *
110 Eigen::Vector3d::UnitZ());
111
112 if (zeroed) {
113 last_gyro_ = zeroer_.ZeroedGyro().value();
114 }
115 last_pico_timestamp_ = pico_timestamp;
116 }
117 }
118 });
119}
120} // namespace frc971::controls