blob: 2b32371e3783eb9587eaaacd4c756b943d082628 [file] [log] [blame]
James Kuszmaul9f2f53c2023-02-19 14:08:18 -08001#ifndef FRC971_IMU_READER_IMU_WATCHER_H_
2#define FRC971_IMU_READER_IMU_WATCHER_H_
3
4#include "aos/events/event_loop.h"
5#include "frc971/control_loops/drivetrain/drivetrain_config.h"
6#include "frc971/imu_reader/imu_failures_generated.h"
7#include "frc971/zeroing/imu_zeroer.h"
8#include "frc971/zeroing/wrap.h"
9
10namespace frc971::controls {
11// This class handles listening to an IMUValuesBatch channel sourced off of our
12// ADIS16505 pico board and calling the user-specified callback with the
13// relevant data. This intermediary is used to unwrap encoder readings, check
14// for checksum mismatches, zero the gyro/accelerometer, and translate
15// timestamps between devices.
16// TODO(james): Get unit tests for this class specifically written (we already
17// have tests for the code that exercises this).
18class ImuWatcher {
19 public:
20 // Expected frequency of messages from the pico-based IMU.
21 static constexpr std::chrono::microseconds kNominalDt{500};
22
James Kuszmaul54fe9d02023-03-23 20:32:40 -070023 enum class TimestampSource {
24 // Use the pico's timestamp to provide timestamps to the callbacks.
25 kPico,
26 // Use pi-based timestamps--this can result in a clock that has marginally
27 // more jitter relative to the sample times than the pico's clock, but
28 // is less likely to encounter major issues when there is some sort of issue
29 // with the pico <-> pi interface.
30 kPi,
31 };
32
James Kuszmaul9f2f53c2023-02-19 14:08:18 -080033 // The callback specified by the user will take:
34 // sample_time_pico: The pico-based timestamp corresponding to the measurement
35 // time. This will be offset by roughly pico_offset_error from the pi's
36 // monotonic clock.
37 // sample_time_pi: Timestamp from the kernel for when the pi observed the
38 // relevant measurement.
39 // encoders: Current encoder values, [left, right]. nullopt if we have faults.
40 // gyro: Current gyro readings, in the raw IMU axes (i.e., these must be
41 // rotated by dt_config.imu_transform before being used). Suitable
42 // for input to the down estimator.
43 // accel: Current accelerometer readings, in the raw IMU axes (i.e., these
44 // must be rotated by dt_config.imu_transform before being used). Suitable
45 // for input to the down estimator.
46 ImuWatcher(
47 aos::EventLoop *event_loop,
48 const control_loops::drivetrain::DrivetrainConfig<double> &dt_config,
49 double drivetrain_distance_per_encoder_tick,
50 std::function<void(
51 aos::monotonic_clock::time_point, aos::monotonic_clock::time_point,
52 std::optional<Eigen::Vector2d>, Eigen::Vector3d, Eigen::Vector3d)>
James Kuszmaul54fe9d02023-03-23 20:32:40 -070053 callback,
54 TimestampSource timestamp_source = TimestampSource::kPico);
James Kuszmaul9f2f53c2023-02-19 14:08:18 -080055
56 const zeroing::ImuZeroer &zeroer() const { return zeroer_; }
57
58 flatbuffers::Offset<ImuFailures> PopulateImuFailures(
59 flatbuffers::FlatBufferBuilder *fbb) const {
60 return ImuFailures::Pack(*fbb, &imu_fault_tracker_);
61 }
62
63 // t = pico_offset + pico_timestamp.
64 // Note that this can drift over sufficiently long time periods!
65 std::optional<std::chrono::nanoseconds> pico_offset() const {
66 return pico_offset_;
67 }
68 // pico_offset_error = actual_time - (pico_offset + pico_timestamp)
69 // If the pico clock and pi clock are exactly in sync, this will always be
70 // zero.
71 aos::monotonic_clock::duration pico_offset_error() const {
72 return pico_offset_error_;
73 }
74
75 private:
76 const control_loops::drivetrain::DrivetrainConfig<double> dt_config_;
77 std::function<void(
78 aos::monotonic_clock::time_point, aos::monotonic_clock::time_point,
79 std::optional<Eigen::Vector2d>, Eigen::Vector3d, Eigen::Vector3d)>
80 callback_;
81
82 // Last observed pico measurement. Used to track IMU staleness.
83 std::optional<aos::monotonic_clock::time_point> last_pico_timestamp_;
James Kuszmaul723e66e2023-04-05 21:22:57 -070084 // Time at which we received the last imu message on the pi's clock.
85 std::optional<aos::monotonic_clock::time_point> last_imu_message_;
James Kuszmaul9f2f53c2023-02-19 14:08:18 -080086 // Estimate of the drift between the pi and pico clocks. See
87 // pico_offset_error() for definition.
88 aos::monotonic_clock::duration pico_offset_error_;
89 // Raw offset between the pico and pi clocks. Gets updated to compensate for
90 // wrapping in the pico timestamp.
91 std::optional<std::chrono::nanoseconds> pico_offset_;
92
93 zeroing::ImuZeroer zeroer_;
94
95 ImuFailuresT imu_fault_tracker_;
96 // The first observed data counter. This is used to help us track dropped
97 // messages.
98 std::optional<size_t> first_valid_data_counter_;
99 size_t total_imu_messages_received_ = 0;
100 // added to the current read data counter to allow the data counter to
101 // increase monotonically. Will be a multiple of 2 ** 16.
102 size_t data_counter_offset_ = 0;
103 // PRevious data counter value (data_counter_offset_ not included).
104 int last_data_counter_ = 0;
105
106 // Unwrappers for the left and right encoders (necessary because the pico only
107 // sends out 16-bit encoder counts).
108 zeroing::UnwrapSensor left_encoder_;
109 zeroing::UnwrapSensor right_encoder_;
110
111 // When we lose IMU readings (e.g., due to checksum mismatches), we perform a
112 // zero-order hold on gyro readings; in order to do this, store the most
113 // recent gyro readings.
114 Eigen::Vector3d last_gyro_ = Eigen::Vector3d::Zero();
115};
116} // namespace frc971::controls
117#endif // FRC971_IMU_READER_IMU_WATCHER_H_