M5Unit-IMU 0.0.2 git rev:e5b00aa
Loading...
Searching...
No Matches
ahrs_controller.hpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD
3 *
4 * SPDX-License-Identifier: MIT
5 */
10#ifndef M5_UNIT_IMU_IMU_AHRS_CONTROLLER_HPP
11#define M5_UNIT_IMU_IMU_AHRS_CONTROLLER_HPP
12
13#include <cstddef>
14#include <cstdint>
15
16#include <math/quaternion.hpp>
17
18#include "axis_order.hpp"
20#include "imu_data_source.hpp"
21#include "imu_types.hpp"
22
23namespace m5 {
24namespace imu {
25
59public:
67 uint32_t version{1};
68 float accel_offset[3]{};
69 float gyro_offset[3]{};
70 float mag_offset[3]{};
71 };
72
77
82
90 bool add(IMUDataSource& src);
91
107 bool writeAxisOrder(const IMUDataSource& src, const AxisOrder& order);
108
115 bool readAxisOrder(const IMUDataSource& src, AxisOrder& out) const;
116
128 bool writeMagAxisOrder(const IMUDataSource& src, const AxisOrder& order);
129
136 bool readMagAxisOrder(const IMUDataSource& src, AxisOrder& out) const;
137
149 void writeUserAxisOrder(const AxisOrder& order);
150
155 void readUserAxisOrder(AxisOrder& out) const;
156
177 void writeAutoCalibration(const uint8_t accel, const uint8_t gyro, const uint8_t mag);
178
185 void readAutoCalibration(uint8_t& accel, uint8_t& gyro, uint8_t& mag) const;
186
201 void writeMagCalibrationRadius(const float radius_ut);
202
207 float readMagCalibrationRadius() const;
208
221 bool calibrateGyro(const uint32_t duration_ms);
222
230 bool calibrateGyro(const uint32_t update_count, const float dt);
231
237 bool readCalibration(calibration_data_t& out) const;
238
244 bool writeCalibration(const calibration_data_t& data);
245
252 bool begin();
253
264
272 Capability update(const float dt);
273
283 bool sleep();
284
293 bool wakeup();
294
303 bool selectFilter(const FilterType type);
304
312
314 inline AhrsFilterBase* filter() const
315 {
316 return _filter;
317 }
318
327 void quaternion(float& w, float& x, float& y, float& z) const;
328
335 void euler(float& roll_deg, float& pitch_deg, float& yaw_deg) const;
336
350 void tareYaw();
351
358 void clearYawTare();
359
361 inline const imu_physical_data_t& latest() const
362 {
363 return _latest;
364 }
365
367 inline bool hasAccel() const
368 {
369 return _accel_src != nullptr;
370 }
371
373 inline bool hasGyro() const
374 {
375 return _gyro_src != nullptr;
376 }
377
379 inline bool hasTemp() const
380 {
381 return _temp_src != nullptr;
382 }
383
390 inline bool hasValidMag() const
391 {
392 return _has_valid_mag;
393 }
394
401 inline unsigned long lastMicros() const
402 {
403 return _last_micros;
404 }
405
406private:
407 // Per-capability auto-calibration state (M5Unified IMU_Class::offset_point_t, ported to
408 // physical-unit floats -- see ahrs_controller.cpp for update_stillness()/calibrate()).
409 struct axis_offset_state_t {
410 float offset[3]{}; // Current offset estimate, subtracted from every sample
411 float prev[3]{}; // Previous physical sample, for stillness diffing
412 float avg[3]{}; // Exponential moving average of recent samples
413 float radius{}; // Expected magnitude of (sample - offset) at rest
414 float tolerance{}; // Tolerance band around radius before nudging the offset
415 float noise{}; // Per-sample movement below which stillness accumulates
416 uint8_t average_shifter{1}; // avg update rate: avg += diff / (1 << average_shifter)
417 uint8_t stillness{}; // 0 = moving, saturates toward 255 while stationary
418 uint8_t strength{}; // 0 = auto-calibration off, 1-255 = adjustment speed
419 // Radius adaptation: 0 keeps `radius` fixed (correct for accel, whose sphere is 1G
420 // everywhere, and for gyro, whose sphere is the origin). Non-zero makes `radius` track the
421 // observed magnitude at rate 1/(1 << radius_shift) per calibrate() call -- needed for the
422 // magnetometer, whose sphere is the local field strength and therefore varies by more than
423 // a factor of two across the planet (~25-65uT). Must stay much slower than the offset
424 // correction itself, so that it settles on the average over orientations rather than
425 // chasing each individual sample (which would cancel the correction entirely).
426 uint8_t radius_shift{};
427 bool radius_seeded{}; // First observed magnitude has been taken as the initial radius
428
429 uint8_t update_stillness(const float sample[3]);
430 void calibrate();
431 };
432
433 static constexpr size_t MAX_SOURCES{4};
434
435 static bool is_mag_overflow(const int32_t mag[3]);
436 static void apply_offset_and_calibrate(axis_offset_state_t& cal, float v[3]);
437
438 bool find_source_index(const IMUDataSource& src, size_t& out_index) const;
439 Capability drain_and_fuse(const float dt);
440 void begin_gyro_collect();
441 bool end_gyro_collect();
442 void recompute_composed(const size_t index); // Fold _user_axis_order onto one source's fixed order
443 void recompute_all_composed(); // ...for every added source
444
445 IMUDataSource* _sources[MAX_SOURCES]{};
446 AxisOrder _axis_orders[MAX_SOURCES]{}; // Fixed board correction for Accel/Gyro/Temp
447 AxisOrder _mag_axis_orders[MAX_SOURCES]{}; // Fixed board correction for Mag (writeMagAxisOrder())
448 AxisOrder _user_axis_order{}; // Global user remap (writeUserAxisOrder())
449
450 // Fixed order folded with the user order; these are what update() actually applies.
451 AxisOrder _composed_axis_orders[MAX_SOURCES]{};
452 AxisOrder _composed_mag_axis_orders[MAX_SOURCES]{};
453 size_t _source_count{};
454
455 IMUDataSource* _accel_src{};
456 IMUDataSource* _gyro_src{};
457 IMUDataSource* _mag_src{};
458 IMUDataSource* _temp_src{};
459
460 AhrsFilterBase* _owned_filter{}; // Non-null only when constructed by selectFilter()
461 AhrsFilterBase* _filter{}; // Active filter (owned or injected); nullptr until chosen
462
463 // World-frame Z rotation composed onto the filter's raw quaternion by quaternion()/euler()
464 // (see tareYaw()/clearYawTare()); identity (no-op) by default.
465 m5::math::Quaternion _yaw_tare{};
466
467 imu_physical_data_t _latest{};
468 bool _has_valid_mag{false};
469
470 unsigned long _last_micros{};
471 bool _has_last_micros{false};
472 float _dt_ema{};
473 bool _has_dt_ema{false};
474 // Time from update() calls that drained no gyro sample, not yet handed to the filter
475 float _pending_dt{};
476
477 axis_offset_state_t _accel_cal{};
478 axis_offset_state_t _gyro_cal{};
479 axis_offset_state_t _mag_cal{};
480
481 bool _gyro_collecting{false};
482 float _gyro_collect_sum[3]{};
483 uint32_t _gyro_collect_count{};
484};
485
486} // namespace imu
487} // namespace m5
488#endif
Per-source axis permutation and inversion applied to raw-to-physical IMU samples.
Merges up to 4 IMUDataSource instances by capability and fuses their samples into an attitude estimat...
Definition ahrs_controller.hpp:58
void readUserAxisOrder(AxisOrder &out) const
Read back the current global user axis remap.
Definition ahrs_controller.cpp:229
bool wakeup()
Bring every added source back from sleep()
Definition ahrs_controller.cpp:257
bool selectFilter(const FilterType type)
Select and internally construct one of the built-in filters.
Definition ahrs_controller.cpp:389
~AhrsController()
Destructor.
Definition ahrs_controller.cpp:122
bool hasAccel() const
Whether an accelerometer source has been registered (see add())
Definition ahrs_controller.hpp:367
unsigned long lastMicros() const
Microsecond timestamp of the most recent self-clocked update()
Definition ahrs_controller.hpp:401
AhrsController(const AhrsController &)=delete
Copy construction is disabled (holds pointers to registered sources)
bool writeAxisOrder(const IMUDataSource &src, const AxisOrder &order)
Set the chip-frame-to-body-frame axis remap applied to one source's Accel/Gyro (and Temp) samples.
Definition ahrs_controller.cpp:179
void writeAutoCalibration(const uint8_t accel, const uint8_t gyro, const uint8_t mag)
Enable/disable continuous background auto-calibration per capability.
Definition ahrs_controller.cpp:271
void writeMagCalibrationRadius(const float radius_ut)
Pin the field strength the magnetometer auto-calibration fits its samples to.
Definition ahrs_controller.cpp:285
bool begin()
Prepare this controller for update()
Definition ahrs_controller.cpp:378
bool readCalibration(calibration_data_t &out) const
Snapshot every capability's current auto-calibration offset.
Definition ahrs_controller.cpp:352
AhrsFilterBase * filter() const
Currently active filter, or nullptr if none has been selected yet.
Definition ahrs_controller.hpp:314
void quaternion(float &w, float &x, float &y, float &z) const
Current fused attitude.
Definition ahrs_controller.cpp:431
bool readMagAxisOrder(const IMUDataSource &src, AxisOrder &out) const
Read back the Mag-only axis remap currently configured for one source.
Definition ahrs_controller.cpp:213
const imu_physical_data_t & latest() const
Most recently fused physical-unit sample.
Definition ahrs_controller.hpp:361
bool add(IMUDataSource &src)
Register an IMU data source.
Definition ahrs_controller.cpp:127
bool readAxisOrder(const IMUDataSource &src, AxisOrder &out) const
Read back the Accel/Gyro axis remap currently configured for one source.
Definition ahrs_controller.cpp:191
void setFilter(AhrsFilterBase &filter)
Inject an externally-owned filter.
Definition ahrs_controller.cpp:422
AhrsController & operator=(const AhrsController &)=delete
Copy assignment is disabled (holds pointers to registered sources)
bool sleep()
Put every added source into its lowest reversible power state.
Definition ahrs_controller.cpp:247
bool hasGyro() const
Whether a gyroscope source has been registered (see add())
Definition ahrs_controller.hpp:373
bool calibrateGyro(const uint32_t duration_ms)
Collect gyro samples for the given duration (real clock) and set the gyro offset to their mean.
Definition ahrs_controller.cpp:325
bool writeCalibration(const calibration_data_t &data)
Overwrite every capability's auto-calibration offset from a snapshot.
Definition ahrs_controller.cpp:363
void clearYawTare()
Remove a previously applied tareYaw() offset.
Definition ahrs_controller.cpp:469
Capability update()
Drain every source and fuse newly-arrived samples, measuring dt from the real clock (m5::utility::mic...
Definition ahrs_controller.cpp:742
AhrsController()
Construct with no sources and the default filter (Complementary)
Definition ahrs_controller.cpp:103
void euler(float &roll_deg, float &pitch_deg, float &yaw_deg) const
Current fused attitude as aerospace ZYX Euler angles.
Definition ahrs_controller.cpp:446
void readAutoCalibration(uint8_t &accel, uint8_t &gyro, uint8_t &mag) const
Read back the current auto-calibration strengths.
Definition ahrs_controller.cpp:278
bool hasTemp() const
Whether a temperature source has been registered (see add())
Definition ahrs_controller.hpp:379
void writeUserAxisOrder(const AxisOrder &order)
Set a global user axis remap composed on top of every source's fixed order.
Definition ahrs_controller.cpp:223
bool hasValidMag() const
Whether at least one valid magnetometer sample has been seen.
Definition ahrs_controller.hpp:390
float readMagCalibrationRadius() const
Field strength the magnetometer auto-calibration is currently fitting to.
Definition ahrs_controller.cpp:298
bool writeMagAxisOrder(const IMUDataSource &src, const AxisOrder &order)
Set the chip-frame-to-body-frame axis remap applied to one source's Mag samples.
Definition ahrs_controller.cpp:201
void tareYaw()
Zero the publicly reported yaw at the current heading.
Definition ahrs_controller.cpp:453
Framework-independent contract shared by every AHRS attitude filter.
Definition filter_base.hpp:81
Thin contract between measurement units and consumers (e.g. AHRS)
Definition imu_data_source.hpp:27
Common contract for AHRS attitude filters.
FilterType
Built-in AHRS filter implementations selectable via AhrsController::selectFilter.
Definition filter_base.hpp:65
Public interface for IMU data providers.
Common types for M5Unit-IMU.
Capability
Capability of the IMU data source (bitwise combinable)
Definition imu_types.hpp:22
Top level namespace of M5Stack.
Definition ahrs_controller.cpp:26
Minimal quaternion arithmetic for attitude representation (framework independent)
Exportable/importable snapshot of every capability's auto-calibration offset.
Definition ahrs_controller.hpp:66
uint32_t version
Struct layout version.
Definition ahrs_controller.hpp:67
float gyro_offset[3]
Gyroscope offset (dps), X/Y/Z.
Definition ahrs_controller.hpp:69
float mag_offset[3]
Magnetometer offset (uT), X/Y/Z.
Definition ahrs_controller.hpp:70
float accel_offset[3]
Accelerometer offset (G), X/Y/Z.
Definition ahrs_controller.hpp:68
Chip-frame-to-body-frame axis remap: a permutation plus a per-output-axis sign flip.
Definition axis_order.hpp:43
Merged, converted physical-unit IMU sample (AhrsController::latest())
Definition imu_types.hpp:81
Plain Hamilton quaternion (float) used to represent attitude.
Definition quaternion.hpp:31