WPILibC++ 2024.1.1-beta-4
ADIS16448_IMU.h
Go to the documentation of this file.
1// Copyright (c) FIRST and other WPILib contributors.
2// Open Source Software; you can modify and/or share it under the terms of
3// the WPILib BSD license file in the root directory of this project.
4
5/*----------------------------------------------------------------------------*/
6/* Copyright (c) 2016-2020 Analog Devices Inc. All Rights Reserved. */
7/* Open Source Software - may be modified and shared by FRC teams. The code */
8/* must be accompanied by the FIRST BSD license file in the root directory of */
9/* the project. */
10/* */
11/* Modified by Juan Chong - frcsupport@analog.com */
12/*----------------------------------------------------------------------------*/
13
14#pragma once
15
16#include <stdint.h>
17
18#include <atomic>
19#include <memory>
20#include <thread>
21
22#include <hal/SimDevice.h>
23#include <units/acceleration.h>
24#include <units/angle.h>
27#include <units/pressure.h>
28#include <units/temperature.h>
30#include <wpi/mutex.h>
33
34#include "frc/DigitalInput.h"
35#include "frc/DigitalOutput.h"
36#include "frc/DigitalSource.h"
37#include "frc/SPI.h"
38
39namespace frc {
40/**
41 * Use DMA SPI to read rate, acceleration, and magnetometer data from the
42 * ADIS16448 IMU and return the robots heading relative to a starting position,
43 * AHRS, and instant measurements
44 *
45 * The ADIS16448 gyro angle outputs track the robot's heading based on the
46 * starting position. As the robot rotates the new heading is computed by
47 * integrating the rate of rotation returned by the IMU. When the class is
48 * instantiated, a short calibration routine is performed where the IMU samples
49 * the gyros while at rest to determine the initial offset. This is subtracted
50 * from each sample to determine the heading.
51 *
52 * This class is for the ADIS16448 IMU connected via the SPI port available on
53 * the RoboRIO MXP port.
54 */
55
57 public wpi::SendableHelper<ADIS16448_IMU> {
58 public:
59 /* ADIS16448 Calibration Time Enum Class */
60 enum class CalibrationTime {
61 _32ms = 0,
62 _64ms = 1,
63 _128ms = 2,
64 _256ms = 3,
65 _512ms = 4,
66 _1s = 5,
67 _2s = 6,
68 _4s = 7,
69 _8s = 8,
70 _16s = 9,
71 _32s = 10,
72 _64s = 11
73 };
74
75 enum IMUAxis { kX, kY, kZ };
76
77 /**
78 * IMU constructor on onboard MXP CS0, Z-up orientation, and complementary
79 * AHRS computation.
80 */
82
83 /**
84 * IMU constructor on the specified MXP port and orientation.
85 *
86 * @param yaw_axis The axis where gravity is present. Valid options are kX,
87 * kY, and kZ
88 * @param port The SPI port where the IMU is connected.
89 * @param cal_time The calibration time that should be used on start-up.
90 */
91 explicit ADIS16448_IMU(IMUAxis yaw_axis, SPI::Port port,
92 CalibrationTime cal_time);
93
94 ~ADIS16448_IMU() override;
95
98
99 /**
100 * Initialize the IMU.
101 *
102 * Perform gyro offset calibration by collecting data for a number of seconds
103 * and computing the center value. The center value is subtracted from
104 * subsequent measurements.
105 *
106 * It's important to make sure that the robot is not moving while the
107 * centering calculations are in progress, this is typically done when the
108 * robot is first turned on while it's sitting at rest before the match
109 * starts.
110 *
111 * The calibration routine can be triggered by the user during runtime.
112 */
113 void Calibrate();
114
115 /**
116 * Configures the calibration time used for the next calibrate.
117 *
118 * @param new_cal_time The calibration time that should be used
119 */
121
122 /**
123 * Reset the gyro.
124 *
125 * Resets the gyro accumulations to a heading of zero. This can be used if
126 * there is significant drift in the gyro and it needs to be recalibrated
127 * after running.
128 */
129 void Reset();
130
131 /**
132 * Returns the yaw axis angle in degrees (CCW positive).
133 */
134 units::degree_t GetAngle() const;
135
136 /**
137 * Returns the yaw axis angular rate in degrees per second (CCW positive).
138 */
139 units::degrees_per_second_t GetRate() const;
140
141 /**
142 * Returns the accumulated gyro angle in the X axis.
143 */
144 units::degree_t GetGyroAngleX() const;
145
146 /**
147 * Returns the accumulated gyro angle in the Y axis.
148 */
149 units::degree_t GetGyroAngleY() const;
150
151 /**
152 * Returns the accumulated gyro angle in the Z axis.
153 */
154 units::degree_t GetGyroAngleZ() const;
155
156 /**
157 * Returns the angular rate in the X axis.
158 */
159 units::degrees_per_second_t GetGyroRateX() const;
160
161 /**
162 * Returns the angular rate in the Y axis.
163 */
164 units::degrees_per_second_t GetGyroRateY() const;
165
166 /**
167 * Returns the angular rate in the Z axis.
168 */
169 units::degrees_per_second_t GetGyroRateZ() const;
170
171 /**
172 * Returns the acceleration in the X axis.
173 */
174 units::meters_per_second_squared_t GetAccelX() const;
175
176 /**
177 * Returns the acceleration in the Y axis.
178 */
179 units::meters_per_second_squared_t GetAccelY() const;
180
181 /**
182 * Returns the acceleration in the Z axis.
183 */
184 units::meters_per_second_squared_t GetAccelZ() const;
185
186 units::degree_t GetXComplementaryAngle() const;
187
188 units::degree_t GetYComplementaryAngle() const;
189
190 units::degree_t GetXFilteredAccelAngle() const;
191
192 units::degree_t GetYFilteredAccelAngle() const;
193
194 units::tesla_t GetMagneticFieldX() const;
195
196 units::tesla_t GetMagneticFieldY() const;
197
198 units::tesla_t GetMagneticFieldZ() const;
199
200 units::pounds_per_square_inch_t GetBarometricPressure() const;
201
202 units::celsius_t GetTemperature() const;
203
205
206 int SetYawAxis(IMUAxis yaw_axis);
207
208 bool IsConnected() const;
209
210 int ConfigDecRate(uint16_t DecimationRate);
211
212 /**
213 * Get the SPI port number.
214 *
215 * @return The SPI port number.
216 */
217 int GetPort() const;
218
219 void InitSendable(wpi::SendableBuilder& builder) override;
220
221 private:
222 /** @brief ADIS16448 Register Map Declaration */
223 static constexpr uint8_t FLASH_CNT = 0x00; // Flash memory write count
224 static constexpr uint8_t XGYRO_OUT = 0x04; // X-axis gyroscope output
225 static constexpr uint8_t YGYRO_OUT = 0x06; // Y-axis gyroscope output
226 static constexpr uint8_t ZGYRO_OUT = 0x08; // Z-axis gyroscope output
227 static constexpr uint8_t XACCL_OUT = 0x0A; // X-axis accelerometer output
228 static constexpr uint8_t YACCL_OUT = 0x0C; // Y-axis accelerometer output
229 static constexpr uint8_t ZACCL_OUT = 0x0E; // Z-axis accelerometer output
230 static constexpr uint8_t XMAGN_OUT = 0x10; // X-axis magnetometer output
231 static constexpr uint8_t YMAGN_OUT = 0x12; // Y-axis magnetometer output
232 static constexpr uint8_t ZMAGN_OUT = 0x14; // Z-axis magnetometer output
233 static constexpr uint8_t BARO_OUT =
234 0x16; // Barometer pressure measurement, high word
235 static constexpr uint8_t TEMP_OUT = 0x18; // Temperature output
236 static constexpr uint8_t XGYRO_OFF =
237 0x1A; // X-axis gyroscope bias offset factor
238 static constexpr uint8_t YGYRO_OFF =
239 0x1C; // Y-axis gyroscope bias offset factor
240 static constexpr uint8_t ZGYRO_OFF =
241 0x1E; // Z-axis gyroscope bias offset factor
242 static constexpr uint8_t XACCL_OFF =
243 0x20; // X-axis acceleration bias offset factor
244 static constexpr uint8_t YACCL_OFF =
245 0x22; // Y-axis acceleration bias offset factor
246 static constexpr uint8_t ZACCL_OFF =
247 0x24; // Z-axis acceleration bias offset factor
248 static constexpr uint8_t XMAGN_HIC =
249 0x26; // X-axis magnetometer, hard iron factor
250 static constexpr uint8_t YMAGN_HIC =
251 0x28; // Y-axis magnetometer, hard iron factor
252 static constexpr uint8_t ZMAGN_HIC =
253 0x2A; // Z-axis magnetometer, hard iron factor
254 static constexpr uint8_t XMAGN_SIC =
255 0x2C; // X-axis magnetometer, soft iron factor
256 static constexpr uint8_t YMAGN_SIC =
257 0x2E; // Y-axis magnetometer, soft iron factor
258 static constexpr uint8_t ZMAGN_SIC =
259 0x30; // Z-axis magnetometer, soft iron factor
260 static constexpr uint8_t GPIO_CTRL = 0x32; // GPIO control
261 static constexpr uint8_t MSC_CTRL = 0x34; // MISC control
262 static constexpr uint8_t SMPL_PRD =
263 0x36; // Sample clock/Decimation filter control
264 static constexpr uint8_t SENS_AVG = 0x38; // Digital filter control
265 static constexpr uint8_t SEQ_CNT = 0x3A; // MAGN_OUT and BARO_OUT counter
266 static constexpr uint8_t DIAG_STAT = 0x3C; // System status
267 static constexpr uint8_t GLOB_CMD = 0x3E; // System command
268 static constexpr uint8_t ALM_MAG1 = 0x40; // Alarm 1 amplitude threshold
269 static constexpr uint8_t ALM_MAG2 = 0x42; // Alarm 2 amplitude threshold
270 static constexpr uint8_t ALM_SMPL1 = 0x44; // Alarm 1 sample size
271 static constexpr uint8_t ALM_SMPL2 = 0x46; // Alarm 2 sample size
272 static constexpr uint8_t ALM_CTRL = 0x48; // Alarm control
273 static constexpr uint8_t LOT_ID1 = 0x52; // Lot identification number
274 static constexpr uint8_t LOT_ID2 = 0x54; // Lot identification number
275 static constexpr uint8_t PROD_ID = 0x56; // Product identifier
276 static constexpr uint8_t SERIAL_NUM = 0x58; // Lot-specific serial number
277
278 /** @brief ADIS16448 Static Constants */
279 static constexpr double rad_to_deg = 57.2957795;
280 static constexpr double deg_to_rad = 0.0174532;
281 static constexpr double grav = 9.81;
282
283 /** @brief struct to store offset data */
284 struct OffsetData {
285 double gyro_rate_x = 0.0;
286 double gyro_rate_y = 0.0;
287 double gyro_rate_z = 0.0;
288 };
289
290 /** @brief Internal Resources **/
291 DigitalInput* m_reset_in;
292 DigitalOutput* m_status_led;
293
294 bool SwitchToStandardSPI();
295
296 bool SwitchToAutoSPI();
297
298 uint16_t ReadRegister(uint8_t reg);
299
300 void WriteRegister(uint8_t reg, uint16_t val);
301
302 void Acquire();
303
304 void Close();
305
306 // User-specified yaw axis
307 IMUAxis m_yaw_axis;
308
309 // Last read values (post-scaling)
310 double m_gyro_rate_x = 0.0;
311 double m_gyro_rate_y = 0.0;
312 double m_gyro_rate_z = 0.0;
313 double m_accel_x = 0.0;
314 double m_accel_y = 0.0;
315 double m_accel_z = 0.0;
316 double m_mag_x = 0.0;
317 double m_mag_y = 0.0;
318 double m_mag_z = 0.0;
319 double m_baro = 0.0;
320 double m_temp = 0.0;
321
322 // Complementary filter variables
323 double m_tau = 0.5;
324 double m_dt, m_alpha = 0.0;
325 double m_compAngleX, m_compAngleY, m_accelAngleX, m_accelAngleY = 0.0;
326
327 // vector for storing most recent imu values
328 OffsetData* m_offset_buffer = nullptr;
329
330 double m_gyro_rate_offset_x = 0.0;
331 double m_gyro_rate_offset_y = 0.0;
332 double m_gyro_rate_offset_z = 0.0;
333
334 // function to re-init offset buffer
335 void InitOffsetBuffer(int size);
336
337 // Accumulated gyro values (for offset calculation)
338 int m_avg_size = 0;
339 int m_accum_count = 0;
340
341 // Integrated gyro values
342 double m_integ_gyro_angle_x = 0.0;
343 double m_integ_gyro_angle_y = 0.0;
344 double m_integ_gyro_angle_z = 0.0;
345
346 // Complementary filter functions
347 double FormatFastConverge(double compAngle, double accAngle);
348
349 double FormatRange0to2PI(double compAngle);
350
351 double FormatAccelRange(double accelAngle, double accelZ);
352
353 double CompFilterProcess(double compAngle, double accelAngle, double omega);
354
355 // State and resource variables
356 volatile bool m_thread_active = false;
357 volatile bool m_first_run = true;
358 volatile bool m_thread_idle = false;
359 volatile bool m_start_up_mode = true;
360
361 bool m_auto_configured = false;
362 SPI::Port m_spi_port;
363 CalibrationTime m_calibration_time{0};
364 SPI* m_spi = nullptr;
365 DigitalInput* m_auto_interrupt = nullptr;
366 bool m_connected{false};
367
368 std::thread m_acquire_task;
369
370 hal::SimDevice m_simDevice;
371 hal::SimBoolean m_simConnected;
372 hal::SimDouble m_simGyroAngleX;
373 hal::SimDouble m_simGyroAngleY;
374 hal::SimDouble m_simGyroAngleZ;
375 hal::SimDouble m_simGyroRateX;
376 hal::SimDouble m_simGyroRateY;
377 hal::SimDouble m_simGyroRateZ;
378 hal::SimDouble m_simAccelX;
379 hal::SimDouble m_simAccelY;
380 hal::SimDouble m_simAccelZ;
381
382 struct NonMovableMutexWrapper {
383 wpi::mutex mutex;
384 NonMovableMutexWrapper() = default;
385
386 NonMovableMutexWrapper(const NonMovableMutexWrapper&) = delete;
387 NonMovableMutexWrapper& operator=(const NonMovableMutexWrapper&) = delete;
388
389 NonMovableMutexWrapper(NonMovableMutexWrapper&&) {}
390 NonMovableMutexWrapper& operator=(NonMovableMutexWrapper&&) {
391 return *this;
392 }
393
394 void lock() { mutex.lock(); }
395
396 void unlock() { mutex.unlock(); }
397
398 bool try_lock() noexcept { return mutex.try_lock(); }
399 };
400
401 mutable NonMovableMutexWrapper m_mutex;
402
403 // CRC-16 Look-Up Table
404 static constexpr uint16_t adiscrc[256] = {
405 0x0000, 0x17CE, 0x0FDF, 0x1811, 0x1FBE, 0x0870, 0x1061, 0x07AF, 0x1F3F,
406 0x08F1, 0x10E0, 0x072E, 0x0081, 0x174F, 0x0F5E, 0x1890, 0x1E3D, 0x09F3,
407 0x11E2, 0x062C, 0x0183, 0x164D, 0x0E5C, 0x1992, 0x0102, 0x16CC, 0x0EDD,
408 0x1913, 0x1EBC, 0x0972, 0x1163, 0x06AD, 0x1C39, 0x0BF7, 0x13E6, 0x0428,
409 0x0387, 0x1449, 0x0C58, 0x1B96, 0x0306, 0x14C8, 0x0CD9, 0x1B17, 0x1CB8,
410 0x0B76, 0x1367, 0x04A9, 0x0204, 0x15CA, 0x0DDB, 0x1A15, 0x1DBA, 0x0A74,
411 0x1265, 0x05AB, 0x1D3B, 0x0AF5, 0x12E4, 0x052A, 0x0285, 0x154B, 0x0D5A,
412 0x1A94, 0x1831, 0x0FFF, 0x17EE, 0x0020, 0x078F, 0x1041, 0x0850, 0x1F9E,
413 0x070E, 0x10C0, 0x08D1, 0x1F1F, 0x18B0, 0x0F7E, 0x176F, 0x00A1, 0x060C,
414 0x11C2, 0x09D3, 0x1E1D, 0x19B2, 0x0E7C, 0x166D, 0x01A3, 0x1933, 0x0EFD,
415 0x16EC, 0x0122, 0x068D, 0x1143, 0x0952, 0x1E9C, 0x0408, 0x13C6, 0x0BD7,
416 0x1C19, 0x1BB6, 0x0C78, 0x1469, 0x03A7, 0x1B37, 0x0CF9, 0x14E8, 0x0326,
417 0x0489, 0x1347, 0x0B56, 0x1C98, 0x1A35, 0x0DFB, 0x15EA, 0x0224, 0x058B,
418 0x1245, 0x0A54, 0x1D9A, 0x050A, 0x12C4, 0x0AD5, 0x1D1B, 0x1AB4, 0x0D7A,
419 0x156B, 0x02A5, 0x1021, 0x07EF, 0x1FFE, 0x0830, 0x0F9F, 0x1851, 0x0040,
420 0x178E, 0x0F1E, 0x18D0, 0x00C1, 0x170F, 0x10A0, 0x076E, 0x1F7F, 0x08B1,
421 0x0E1C, 0x19D2, 0x01C3, 0x160D, 0x11A2, 0x066C, 0x1E7D, 0x09B3, 0x1123,
422 0x06ED, 0x1EFC, 0x0932, 0x0E9D, 0x1953, 0x0142, 0x168C, 0x0C18, 0x1BD6,
423 0x03C7, 0x1409, 0x13A6, 0x0468, 0x1C79, 0x0BB7, 0x1327, 0x04E9, 0x1CF8,
424 0x0B36, 0x0C99, 0x1B57, 0x0346, 0x1488, 0x1225, 0x05EB, 0x1DFA, 0x0A34,
425 0x0D9B, 0x1A55, 0x0244, 0x158A, 0x0D1A, 0x1AD4, 0x02C5, 0x150B, 0x12A4,
426 0x056A, 0x1D7B, 0x0AB5, 0x0810, 0x1FDE, 0x07CF, 0x1001, 0x17AE, 0x0060,
427 0x1871, 0x0FBF, 0x172F, 0x00E1, 0x18F0, 0x0F3E, 0x0891, 0x1F5F, 0x074E,
428 0x1080, 0x162D, 0x01E3, 0x19F2, 0x0E3C, 0x0993, 0x1E5D, 0x064C, 0x1182,
429 0x0912, 0x1EDC, 0x06CD, 0x1103, 0x16AC, 0x0162, 0x1973, 0x0EBD, 0x1429,
430 0x03E7, 0x1BF6, 0x0C38, 0x0B97, 0x1C59, 0x0448, 0x1386, 0x0B16, 0x1CD8,
431 0x04C9, 0x1307, 0x14A8, 0x0366, 0x1B77, 0x0CB9, 0x0A14, 0x1DDA, 0x05CB,
432 0x1205, 0x15AA, 0x0264, 0x1A75, 0x0DBB, 0x152B, 0x02E5, 0x1AF4, 0x0D3A,
433 0x0A95, 0x1D5B, 0x054A, 0x1284};
434};
435
436} // namespace frc
Use DMA SPI to read rate, acceleration, and magnetometer data from the ADIS16448 IMU and return the r...
Definition: ADIS16448_IMU.h:57
units::tesla_t GetMagneticFieldZ() const
units::meters_per_second_squared_t GetAccelZ() const
Returns the acceleration in the Z axis.
units::degree_t GetYFilteredAccelAngle() const
units::degree_t GetGyroAngleX() const
Returns the accumulated gyro angle in the X axis.
units::degree_t GetXFilteredAccelAngle() const
units::degree_t GetYComplementaryAngle() const
units::celsius_t GetTemperature() const
units::degree_t GetAngle() const
Returns the yaw axis angle in degrees (CCW positive).
units::tesla_t GetMagneticFieldY() const
units::degree_t GetXComplementaryAngle() const
int SetYawAxis(IMUAxis yaw_axis)
int ConfigCalTime(CalibrationTime new_cal_time)
Configures the calibration time used for the next calibrate.
ADIS16448_IMU(IMUAxis yaw_axis, SPI::Port port, CalibrationTime cal_time)
IMU constructor on the specified MXP port and orientation.
IMUAxis
Definition: ADIS16448_IMU.h:75
@ kY
Definition: ADIS16448_IMU.h:75
@ kX
Definition: ADIS16448_IMU.h:75
@ kZ
Definition: ADIS16448_IMU.h:75
void Reset()
Reset the gyro.
int ConfigDecRate(uint16_t DecimationRate)
CalibrationTime
Definition: ADIS16448_IMU.h:60
units::pounds_per_square_inch_t GetBarometricPressure() const
void Calibrate()
Initialize the IMU.
units::degrees_per_second_t GetGyroRateX() const
Returns the angular rate in the X axis.
~ADIS16448_IMU() override
units::degrees_per_second_t GetGyroRateY() const
Returns the angular rate in the Y axis.
units::degrees_per_second_t GetRate() const
Returns the yaw axis angular rate in degrees per second (CCW positive).
units::meters_per_second_squared_t GetAccelY() const
Returns the acceleration in the Y axis.
int GetPort() const
Get the SPI port number.
void InitSendable(wpi::SendableBuilder &builder) override
Initializes this Sendable object.
units::degree_t GetGyroAngleY() const
Returns the accumulated gyro angle in the Y axis.
units::degree_t GetGyroAngleZ() const
Returns the accumulated gyro angle in the Z axis.
units::meters_per_second_squared_t GetAccelX() const
Returns the acceleration in the X axis.
IMUAxis GetYawAxis() const
ADIS16448_IMU & operator=(ADIS16448_IMU &&)=default
ADIS16448_IMU(ADIS16448_IMU &&)=default
bool IsConnected() const
ADIS16448_IMU()
IMU constructor on onboard MXP CS0, Z-up orientation, and complementary AHRS computation.
units::tesla_t GetMagneticFieldX() const
units::degrees_per_second_t GetGyroRateZ() const
Returns the angular rate in the Z axis.
Class to read a digital input.
Definition: DigitalInput.h:27
Class to write to digital outputs.
Definition: DigitalOutput.h:25
Port
Definition: SPI.h:28
C++ wrapper around a HAL simulator boolean value handle.
Definition: SimDevice.h:610
A move-only C++ wrapper around a HAL simulator device handle.
Definition: SimDevice.h:644
C++ wrapper around a HAL simulator double value handle.
Definition: SimDevice.h:535
Definition: SendableBuilder.h:18
A helper class for use with objects that add themselves to SendableRegistry.
Definition: SendableHelper.h:19
Interface for Sendable objects.
Definition: Sendable.h:16
Definition: AprilTagPoseEstimator.h:15
::std::mutex mutex
Definition: mutex.h:17