|
M5Unit-IMU 0.0.2 git rev:e5b00aa
|
Merges up to 4 IMUDataSource instances by capability and fuses their samples into an attitude estimate via a pluggable AhrsFilterBase. More...
#include <ahrs_controller.hpp>
Classes | |
| struct | calibration_data_t |
| Exportable/importable snapshot of every capability's auto-calibration offset. More... | |
Public Member Functions | |
| AhrsController () | |
| Construct with no sources and the default filter (Complementary) | |
| ~AhrsController () | |
| Destructor. | |
| AhrsController (const AhrsController &)=delete | |
| Copy construction is disabled (holds pointers to registered sources) | |
| AhrsController & | operator= (const AhrsController &)=delete |
| Copy assignment is disabled (holds pointers to registered sources) | |
| bool | add (IMUDataSource &src) |
| Register an IMU data source. | |
| 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. | |
| bool | readAxisOrder (const IMUDataSource &src, AxisOrder &out) const |
| Read back the Accel/Gyro axis remap currently configured for one source. | |
| bool | writeMagAxisOrder (const IMUDataSource &src, const AxisOrder &order) |
| Set the chip-frame-to-body-frame axis remap applied to one source's Mag samples. | |
| bool | readMagAxisOrder (const IMUDataSource &src, AxisOrder &out) const |
| Read back the Mag-only axis remap currently configured for one source. | |
| void | writeUserAxisOrder (const AxisOrder &order) |
| Set a global user axis remap composed on top of every source's fixed order. | |
| void | readUserAxisOrder (AxisOrder &out) const |
| Read back the current global user axis remap. | |
| void | writeAutoCalibration (const uint8_t accel, const uint8_t gyro, const uint8_t mag) |
| Enable/disable continuous background auto-calibration per capability. | |
| void | readAutoCalibration (uint8_t &accel, uint8_t &gyro, uint8_t &mag) const |
| Read back the current auto-calibration strengths. | |
| void | writeMagCalibrationRadius (const float radius_ut) |
| Pin the field strength the magnetometer auto-calibration fits its samples to. | |
| float | readMagCalibrationRadius () const |
| Field strength the magnetometer auto-calibration is currently fitting to. | |
| bool | calibrateGyro (const uint32_t duration_ms) |
| Collect gyro samples for the given duration (real clock) and set the gyro offset to their mean. | |
| bool | calibrateGyro (const uint32_t update_count, const float dt) |
| Deterministic sibling of calibrateGyro(const uint32_t): explicit dt instead of the real clock. | |
| bool | readCalibration (calibration_data_t &out) const |
| Snapshot every capability's current auto-calibration offset. | |
| bool | writeCalibration (const calibration_data_t &data) |
| Overwrite every capability's auto-calibration offset from a snapshot. | |
| bool | begin () |
| Prepare this controller for update() | |
| Capability | update () |
| Drain every source and fuse newly-arrived samples, measuring dt from the real clock (m5::utility::micros()) | |
| Capability | update (const float dt) |
| Drain every source and fuse newly-arrived samples using an explicit dt. | |
| bool | sleep () |
| Put every added source into its lowest reversible power state. | |
| bool | wakeup () |
| Bring every added source back from sleep() | |
| bool | selectFilter (const FilterType type) |
| Select and internally construct one of the built-in filters. | |
| void | setFilter (AhrsFilterBase &filter) |
| Inject an externally-owned filter. | |
| AhrsFilterBase * | filter () const |
| Currently active filter, or nullptr if none has been selected yet. | |
| void | quaternion (float &w, float &x, float &y, float &z) const |
| Current fused attitude. | |
| void | euler (float &roll_deg, float &pitch_deg, float &yaw_deg) const |
| Current fused attitude as aerospace ZYX Euler angles. | |
| void | tareYaw () |
| Zero the publicly reported yaw at the current heading. | |
| void | clearYawTare () |
| Remove a previously applied tareYaw() offset. | |
| const imu_physical_data_t & | latest () const |
| Most recently fused physical-unit sample. | |
| bool | hasAccel () const |
| Whether an accelerometer source has been registered (see add()) | |
| bool | hasGyro () const |
| Whether a gyroscope source has been registered (see add()) | |
| bool | hasTemp () const |
| Whether a temperature source has been registered (see add()) | |
| bool | hasValidMag () const |
| Whether at least one valid magnetometer sample has been seen. | |
| unsigned long | lastMicros () const |
| Microsecond timestamp of the most recent self-clocked update() | |
Merges up to 4 IMUDataSource instances by capability and fuses their samples into an attitude estimate via a pluggable AhrsFilterBase.
AhrsFilterBase::update() call, with the elapsed time divided evenly across the batch (dt / <samples drained this call>). The time of a call that drains no gyro sample is carried over to the next call that does, so calling update() faster than the gyro ODR does not shrink the integrated rotation. The carried-over time is capped at 0.5 s, so a stalled gyro source does not hand the filter one huge dt when it resumes. Accel/mag samples from a different source than the gyro are drained once per update() call and the resulting "latest" value is reused for every filter update() call in that batch (no per-sample interpolation across sources); a source shared with the gyro (e.g. a single 6/9-axis chip) is synchronized sample-for-sample instead.mag == nullptr (6-axis operation).| bool m5::imu::AhrsController::add | ( | IMUDataSource & | src | ) |
Register an IMU data source.
| src | Source to add (must outlive this controller) |
Never fails due to a capability already being claimed by another source – duplicate capabilities are simply ignored (the first source to report them wins).
| bool m5::imu::AhrsController::begin | ( | ) |
Prepare this controller for update()
Selects FilterType::Madgwick if no filter has been chosen yet (see selectFilter()/setFilter()).
| bool m5::imu::AhrsController::calibrateGyro | ( | const uint32_t | duration_ms | ) |
Collect gyro samples for the given duration (real clock) and set the gyro offset to their mean.
| duration_ms | Collection duration, milliseconds |
Intended for hardware use: repeatedly calls update() (real clock, via m5::utility::millis()) for approximately duration_ms while the unit is held still, then overwrites the gyro offset with the mean of every gyro sample seen. See calibrateGyro(const uint32_t, const float) for the deterministic, explicit-dt sibling (drives the same sample collection through update(const float dt) instead of the real clock).
| bool m5::imu::AhrsController::calibrateGyro | ( | const uint32_t | update_count, |
| const float | dt ) |
Deterministic sibling of calibrateGyro(const uint32_t): explicit dt instead of the real clock.
| update_count | Number of update(dt) calls to run while collecting |
| dt | Time step passed to each update(dt) call, seconds |
| void m5::imu::AhrsController::clearYawTare | ( | ) |
Remove a previously applied tareYaw() offset.
Resets the internal yaw tare to identity, so quaternion()/euler() once again report the filter's raw heading as-is (e.g. relative to magnetic north on a 9-axis setup).
| void m5::imu::AhrsController::euler | ( | float & | roll_deg, |
| float & | pitch_deg, | ||
| float & | yaw_deg ) const |
Current fused attitude as aerospace ZYX Euler angles.
| [out] | roll_deg | Roll (X axis), degrees |
| [out] | pitch_deg | Pitch (Y axis), degrees |
| [out] | yaw_deg | Yaw (Z axis), degrees |
|
inline |
Whether at least one valid magnetometer sample has been seen.
False on a 6-axis setup, or before the first non-overflow mag sample arrives (see the "Magnetometer overflow" note on this class). latest().mag stays at its last valid value (or zero) while this reads false.
|
inline |
Microsecond timestamp of the most recent self-clocked update()
Stamped only by the update() overload that measures dt itself; update(const float dt) leaves it untouched, so this stays 0 for callers driving the controller entirely from their own timebase.
| void m5::imu::AhrsController::quaternion | ( | float & | w, |
| float & | x, | ||
| float & | y, | ||
| float & | z ) const |
Current fused attitude.
| [out] | w | Quaternion scalar part |
| [out] | x | Quaternion vector part X |
| [out] | y | Quaternion vector part Y |
| [out] | z | Quaternion vector part Z |
Yields the identity quaternion if no filter has been selected/injected yet.
| void m5::imu::AhrsController::readAutoCalibration | ( | uint8_t & | accel, |
| uint8_t & | gyro, | ||
| uint8_t & | mag ) const |
Read back the current auto-calibration strengths.
| [out] | accel | Accelerometer strength |
| [out] | gyro | Gyroscope strength |
| [out] | mag | Magnetometer strength |
| bool m5::imu::AhrsController::readAxisOrder | ( | const IMUDataSource & | src, |
| AxisOrder & | out ) const |
| bool m5::imu::AhrsController::readCalibration | ( | calibration_data_t & | out | ) | const |
Snapshot every capability's current auto-calibration offset.
| [out] | out | Destination for the snapshot |
| bool m5::imu::AhrsController::readMagAxisOrder | ( | const IMUDataSource & | src, |
| AxisOrder & | out ) const |
| float m5::imu::AhrsController::readMagCalibrationRadius | ( | ) | const |
Field strength the magnetometer auto-calibration is currently fitting to.
| void m5::imu::AhrsController::readUserAxisOrder | ( | AxisOrder & | out | ) | const |
Read back the current global user axis remap.
| [out] | out | Destination for the current user AxisOrder (identity by default) |
| bool m5::imu::AhrsController::selectFilter | ( | const FilterType | type | ) |
Select and internally construct one of the built-in filters.
| type | Filter to construct |
Replaces (and resets) whatever filter is currently active, deleting a previously internally-constructed filter first. Does not delete a filter injected via setFilter().
| void m5::imu::AhrsController::setFilter | ( | AhrsFilterBase & | filter | ) |
Inject an externally-owned filter.
| filter | Filter instance to use from now on (must outlive this controller) |
Deletes a previously internally-constructed filter (selectFilter()), if any. Ownership of filter itself is not taken.
| bool m5::imu::AhrsController::sleep | ( | ) |
Put every added source into its lowest reversible power state.
Calls IMUDataSource::sleep() on all sources, including those whose capability was not claimed. Sources that do not implement sleeping report false and are skipped, so a false return only means nothing could be suspended. Sleeping stops each source's periodic measurement; wakeup() powers the hardware back up but does not restart measurement – restart it explicitly on the units you want sampling again.
| void m5::imu::AhrsController::tareYaw | ( | ) |
Zero the publicly reported yaw at the current heading.
Samples the active filter's current fused yaw (the same value quaternion()/euler() would derive from it) and stores a world-frame Z-axis rotation of the opposite angle as an internal tare offset. From then on every quaternion()/euler() call composes this offset on top of the filter's raw attitude, so the reported yaw reads ~0 right after this call while roll and pitch are unaffected. The filter's own internal attitude estimate – and, on a 9-axis setup, its alignment to magnetic north – is left untouched; only the publicly reported attitude is shifted. Calling this again re-zeros to whatever heading the filter has reached by then. See clearYawTare() to remove the offset. No-op (tare stays whatever it already was) if no filter has been selected yet.
| Capability m5::imu::AhrsController::update | ( | ) |
Drain every source and fuse newly-arrived samples, measuring dt from the real clock (m5::utility::micros())
The very first call after construction only seeds the internal clock (no previous timestamp to compute an elapsed time from) and returns Capability::None without draining anything; every following call computes dt from the elapsed time since the previous call.
| Capability m5::imu::AhrsController::update | ( | const float | dt | ) |
Drain every source and fuse newly-arrived samples using an explicit dt.
| dt | Time elapsed since the previous update call, in seconds; divided evenly across however many gyro samples are drained this call (if none is drained, it is carried over to the next call that drains one; the carried-over part is capped at 0.5 s) |
| bool m5::imu::AhrsController::wakeup | ( | ) |
Bring every added source back from sleep()
Calls IMUDataSource::wakeup() on all sources and then discards the internal clock state, so the first update() after waking re-seeds its timebase instead of handing the filter one enormous dt spanning the whole sleep. Measurement is not restarted: call startPeriodicMeasurement() on the units that should resume sampling.
| void m5::imu::AhrsController::writeAutoCalibration | ( | const uint8_t | accel, |
| const uint8_t | gyro, | ||
| const uint8_t | mag ) |
Enable/disable continuous background auto-calibration per capability.
| accel | Accelerometer strength, 0 = off, 1-255 = adjustment speed (higher = faster) |
| gyro | Gyroscope strength, 0 = off, 1-255 = adjustment speed |
| mag | Magnetometer strength, 0 = off, 1-255 = adjustment speed |
A stillness/sphere-fit auto-offset algorithm operating on physical-unit samples. Each capability tracks how "still" its recent samples have been; once that stillness score exceeds a threshold, the offset is nudged toward fitting the sample to the expected shape at rest (accel/mag: a sphere of a fixed radius around the offset; gyro: the origin). The resulting offset is subtracted from every subsequent sample of that capability regardless of strength – setting strength to 0 only stops further adjustment, it does not clear a previously-converged offset (see writeCalibration()/clearing via calibration_data_t{}).
| bool m5::imu::AhrsController::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.
| src | Source to configure (must already have been add()ed) |
| order | Permutation and per-axis sign flip to apply |
Applied every update() to whichever of Accel/Gyro/Temp that source provides, right after raw-to-physical conversion and before auto-calibration offsets are subtracted (see writeAutoCalibration()). Does not affect Mag – accel and gyro always share a single die and are therefore always co-oriented, but an auxiliary magnetometer die (e.g. a BMM150 mounted as a BMI270 AUX chip) can be mounted at a different orientation on the very same source; use writeMagAxisOrder() for that. Defaults to the identity transform (AxisPermutation::XYZ, no inversion) for every added source.
| bool m5::imu::AhrsController::writeCalibration | ( | const calibration_data_t & | data | ) |
Overwrite every capability's auto-calibration offset from a snapshot.
| data | Snapshot to restore (see readCalibration()) |
| bool m5::imu::AhrsController::writeMagAxisOrder | ( | const IMUDataSource & | src, |
| const AxisOrder & | order ) |
Set the chip-frame-to-body-frame axis remap applied to one source's Mag samples.
| src | Source to configure (must already have been add()ed) |
| order | Permutation and per-axis sign flip to apply to Mag only |
Applied every update() to the Mag capability of whichever source provides it, right after raw-to-physical conversion and before auto-calibration offsets are subtracted (see writeAutoCalibration()) – independent of writeAxisOrder(), which only covers Accel/Gyro/Temp. Defaults to the identity transform (AxisPermutation::XYZ, no inversion) for every added source.
| void m5::imu::AhrsController::writeMagCalibrationRadius | ( | const float | radius_ut | ) |
Pin the field strength the magnetometer auto-calibration fits its samples to.
| radius_ut | Local geomagnetic field strength in uT; pass 0 (or less) to go back to estimating it from the data, which is the default |
The hard-iron fit works by pushing the offset until every sample sits on a sphere of this radius, so the radius has to match the field the device is actually in. Unlike the accelerometer's 1G sphere, that is not a universal constant: the geomagnetic field runs from roughly 25uT to 65uT depending on location, and a mismatched radius mis-scales the calibration rather than correcting it. By default the radius is therefore learned from the samples themselves; set it here when the local value is known (national geomagnetic surveys publish it) to skip the learning transient.
| void m5::imu::AhrsController::writeUserAxisOrder | ( | const AxisOrder & | order | ) |
Set a global user axis remap composed on top of every source's fixed order.
| order | Permutation and per-axis sign flip the caller wants applied to the reported frame |
Mirrors M5Unified's IMU_Class::setAxisOrder user layer. writeAxisOrder()/ writeMagAxisOrder() carry the fixed, per-board correction (chip frame to a unified body frame); this order is applied after that, reorienting the whole device output frame, and so it affects Accel, Gyro, Temp and Mag of every source alike. The two are folded together internally (see compose() in axis_order.hpp), so no extra per-sample transform is paid. Defaults to the identity transform. Composition is recomputed here and on every writeAxisOrder()/writeMagAxisOrder()/add().