Skip to main content

<alp/ahrs.h> — Attitude & Heading Reference

A caller-owned Madgwick orientation filter that fuses a 3-axis gyroscope with a 3-axis accelerometer into a drift-corrected orientation quaternion. The accelerometer pins roll/pitch against gravity, so the estimate does not drift the way a raw gyro integrator does.

Only the IMU variant (gyro + accel) ships today. A magnetometer-aided MARG variant may be added later — without a magnetometer, yaw is not absolute and will drift about the gravity axis.

#include <alp/ahrs.h>

board.yaml

The filter is opt-in — it compiles only when the madgwick_ahrs library knob is selected, which emits CONFIG_ALP_SDK_AHRS:

libraries:
- madgwick_ahrs

The implementation is pure C (libm only) and builds on every OS target. Faster hardware paths (ALP_MADGWICK_FPU, ALP_MADGWICK_TMU for the GD32 TMU CORDIC) bind transparently on SoMs that expose them; ALP_MADGWICK_LIBM is the always-available software floor.

Run the filter

Like <alp/pid.h>, this needs no pool and no handle — allocate an alp_ahrs_t (static or on the stack), initialise it once, then drive it every sample:

static alp_ahrs_t ahrs;

alp_ahrs_init(&ahrs, &(alp_ahrs_config_t){ .beta = 0.1f });

for (;;) {
// gyro in rad/s; accel in any consistent unit (the vector is normalised)
alp_ahrs_update_imu(&ahrs, gx, gy, gz, ax, ay, az, dt_s);

float roll, pitch, yaw;
alp_ahrs_euler(&ahrs, &roll, &pitch, &yaw); // degrees
}

Config struct

FieldTypeNotes
betafloatMadgwick filter gain, rad/s. Pass <= 0 to use ALP_AHRS_DEFAULT_BETA (0.1f). Raise for faster accelerometer correction (more noise); lower for smoother output (slower drift correction).

Passing a NULL config to alp_ahrs_init is legal — it uses ALP_AHRS_DEFAULT_BETA.

Functions

CallReturns
alp_ahrs_init(alp_ahrs_t *ahrs, const alp_ahrs_config_t *cfg)ALP_OK / ALP_ERR_INVAL (NULL ahrs). Sets the quaternion to identity (1,0,0,0) — level, facing +x — and copies the gain. Safe to call again to re-seed.
alp_ahrs_update_imu(ahrs, gx, gy, gz, ax, ay, az, dt_s)void. One Madgwick IMU update: integrates the gyro, applies a gradient-descent correction toward the measured gravity direction, renormalises the quaternion.
alp_ahrs_euler(const alp_ahrs_t *ahrs, float *roll_deg, float *pitch_deg, float *yaw_deg)void. Converts the quaternion to intrinsic Z-Y-X (yaw-pitch-roll) Euler angles in degrees. Any output pointer may be NULL to skip it; a NULL ahrs leaves the outputs untouched.
alp_ahrs_reset(alp_ahrs_t *ahrs)void. Resets the orientation to identity, keeping the gain. NULL is a no-op.

Units and edge cases

  • Gyro is rad/s. Accelerometer may be in any consistent unit — the vector is normalised internally, so raw counts are fine.
  • dt_s is the time since the previous update, in seconds, and must be > 0. A non-positive dt_s is ignored (the sample is dropped).
  • If the accelerometer vector is zero (free-fall, or a sensor dropout) the update falls back to gyro-only integration for that sample.
  • pitch_deg is bounded to [-90, 90]; roll and yaw are unbounded.

State struct

alp_ahrs_t is caller-owned and transparent (so it is stack-allocatable), holding the orientation as a unit quaternion (q0, q1, q2, q3) mapping the sensor frame to the earth frame, plus the filter gain. Treat it as opaque — do not read or write the fields directly; the layout is [ABI-EXPERIMENTAL] and may gain fields before v1.0. Read the orientation through alp_ahrs_euler.

ABI status

[ABI-EXPERIMENTAL] — new in v0.10. The struct layout may change before v1.0; pin your SDK to a specific commit if you depend on it.

See also

Questions about this page? Discuss in Community Forum