# IMU

The Inertial Measurement Unit (IMU) provides three-axis acceleration, angular velocity, and magnetic field measurements for attitude determination and motion analysis.

### Hardware

<table id="bkmrk-imu-hw-table"><colgroup><col></col><col></col></colgroup><tbody><tr><th>Parameter

</th><th>Value

</th></tr><tr><td>Device

</td><td>Xsens Avior (Xbus protocol, MTi-1-series compatible pipe interface)

</td></tr><tr><td>Interface

</td><td>I2C1, pins PB8 (IMU\_I2C\_Clock) / PB9 (IMU\_I2C\_Data), internal pull-ups enabled

</td></tr><tr><td>I2C address

</td><td>0x6B (7-bit, MTi-1 series default, override with -D XSENS\_I2C\_ADDR\_7BIT)

</td></tr><tr><td>Output rate

</td><td>100 Hz requested for accel/gyro/mag (XSENS\_OUTPUT\_RATE\_HZ)

</td></tr><tr><td>I2C timeout

</td><td>100 ms (XSENS\_I2C\_TIMEOUT\_MS)

</td></tr></tbody></table>

**Important:**<span style="white-space: pre-wrap;"> the pipe opcodes, default I2C address, and Xbus data identifiers used by the driver are the documented Xsens MTi-1-series values. The Avior is Xbus-compatible, but confirm these against the Avior datasheet (mtidocs.xsens.com) and override with -D build flags if your unit differs. The driver was never validated against physical hardware.</span>

### Communication Protocol (Xbus over I2C)

Xbus messages move through "pipe" opcodes used as an 8-bit register address:

- 0x03 ControlPipe: write Xbus command messages
- 0x04 PipeStatus: read 4 bytes, notification size (LE16) and measurement size (LE16)
- 0x06 MeasurementPipe: read a pending MTData2 measurement message

```
Xbus frame: [0xFA][0xFF][MID][LEN][DATA...][CHK]
CHK makes (BID + MID + LEN + DATA + CHK) & 0xFF == 0
```

On the first poll the driver configures the device once: GoToConfig, SetOutputConfiguration (acceleration + rate of turn + magnetic field at 100 Hz, float32), GoToMeasure. If configuration fails, poll returns RESULT\_ERR\_COMMS (device not responding on I2C).

## Data Structure

```c
typedef struct {
    float accel[3];            /* [X, Y, Z] acceleration, m/s² */
    float gyro[3];             /* [X, Y, Z] angular velocity, °/s
                                  (converted from rad/s by the driver) */
    float mag[3];              /* [X, Y, Z] magnetic field, Xsens arbitrary
                                  units (~1.0 = local Earth field), NOT µT */
    uint32_t timestamp;        /* Current reading timestamp (HAL_GetTick ms) */
    uint32_t last_timestamp;   /* Previous reading timestamp */
} imu_data_t;
```

**Unit notes:**<span style="white-space: pre-wrap;"> the gyroscope values are converted from rad/s to °/s inside the driver. The Xsens magnetic field output is in arbitrary units where roughly 1.0 equals the local Earth field; it is stored as-is and must be scaled externally if µT are needed.</span>

## Initialization &amp; Usage

### Initialize IMU

```c
imu_data_t imu_data;
imu_sensor_init(&imu_data);   /* zeroes the structure */
```

### Poll IMU Data

```c
result_t imu_result = poll_imu_sensor(&imu_data);

if (imu_result == RESULT_OK) {
    /* Acceleration (m/s²) */
    float accel_x = imu_data.accel[0];
    float accel_y = imu_data.accel[1];
    float accel_z = imu_data.accel[2];

    /* Angular velocity (°/s) */
    float gyro_x = imu_data.gyro[0];
    float gyro_y = imu_data.gyro[1];
    float gyro_z = imu_data.gyro[2];

    /* Magnetic field (Xsens arbitrary units) */
    float mag_x = imu_data.mag[0];
    float mag_y = imu_data.mag[1];
    float mag_z = imu_data.mag[2];
}
/* RESULT_ERR_COMMS: device not responding, or no fresh sample ready yet */
```

## Advanced Functions

### Update with Raw Values

```c
result_t imu_sensor_update(
    imu_data_t *imu,
    float ax, float ay, float az,  /* Accelerometer values */
    float gx, float gy, float gz,  /* Gyroscope values */
    float mx, float my, float mz,  /* Magnetometer values */
    uint32_t timestamp
);
```

### Calculate Acceleration Magnitude

```c
float acceleration_magnitude = imu_get_acceleration_magnitude(&imu_data);
/* |a| = sqrt(ax² + ay² + az²), useful for impact and free-fall detection */
```

### Orientation Helpers (from the gravity vector)

```c
float pitch_deg = imu_get_pitch(&imu_data); /* atan2(ay, sqrt(ax²+az²)) in degrees */
float roll_deg  = imu_get_roll(&imu_data);  /* atan2(ax, sqrt(ay²+az²)) in degrees */
/* Assumes the device is relatively stationary */
```

### Gyroscope Drift Check

```c
/* true when all gyro axes are below the threshold (device at rest) */
bool stable = imu_check_gyroscope_drift(&imu_data, 1.0f);
```

### Copying Data for Another Context

```c
imu_data_t imu_copy;
imu_sensor_read(&imu_data, &imu_copy);
/* Plain struct copy, no locking: safe only if the source is not
 * being updated concurrently */
```

## Sensor Ranges Used by the Driver Validators

<table id="bkmrk-imu-ranges-table"><colgroup><col></col><col></col><col></col></colgroup><tbody><tr><th>Measurement

</th><th>Accepted Range

</th><th>Units

</th></tr><tr><td>Acceleration

</td><td>±16 g (±156.9 m/s²)

</td><td>m/s²

</td></tr><tr><td>Angular Velocity

</td><td>±2000

</td><td>°/s

</td></tr><tr><td>Magnetic Field

</td><td>±4900

</td><td>driver limit (see unit note above)

</td></tr></tbody></table>

### Conversion Reference

<table id="bkmrk-imu-conv-table"><colgroup><col></col><col></col><col></col></colgroup><tbody><tr><th>From

</th><th>To

</th><th>Factor

</th></tr><tr><td>g

</td><td>m/s²

</td><td>× 9.80665

</td></tr><tr><td>rad/s

</td><td>°/s

</td><td>× 180/π (applied inside the driver)

</td></tr><tr><td>Gauss

</td><td>µT

</td><td>× 100

</td></tr></tbody></table>

## Validation Functions

```c
/* Driver-level validators (used by the main loop) */
bool imu_validate_accelerometer_range(imu_data_t *imu);  /* ±16 g in m/s² */
bool imu_validate_gyroscope_range(imu_data_t *imu);      /* ±2000 °/s */
bool imu_validate_magnetometer_range(imu_data_t *imu);   /* ±4900 */

/* Utility-library validators (sensor_basics.h) */
result_t validate_accelerometer_value(float accel_value); /* ±160 m/s² */
result_t validate_imu_data(float accel_x, float accel_y, float accel_z);
```

## Protobuf Message Format

```protobuf
message SensorBoardIMUInfo {
    float accel_x;
    float accel_y;
    float accel_z;
    float gyro_x;
    float gyro_y;
    float gyro_z;
    float mag_x;
    float mag_y;
    float mag_z;
    SensorState state;
    IMUErrorCode error_code;
}

enum IMUErrorCode {
    IMU_NO_ERROR = 0;
    IMU_COMMUNICATION_FAILURE = 1;
    IMU_ACCELEROMETER_ERROR = 2;
    IMU_GYROSCOPE_ERROR = 3;
    IMU_MAGNETOMETER_ERROR = 4;
}
```

## Common Applications

### Impact Detection

```c
float mag = imu_get_acceleration_magnitude(&imu_data);
if (mag > IMPACT_THRESHOLD) {
    /* High acceleration detected */
}
```

### Tilt Detection

```c
float pitch = imu_get_pitch(&imu_data);
float roll  = imu_get_roll(&imu_data);
```

### Motion Classification

```c
/* Static vs dynamic based on gyro magnitude */
float gyro_mag = sqrtf(gyro_x*gyro_x + gyro_y*gyro_y + gyro_z*gyro_z);
```

## Integration Notes

- Single IMU instance in the main application (dual IMU planned)
- All nine axes (accel, gyro, mag) transmitted as independent fields at the main loop interval
- First poll performs one-time device configuration; a failing device degrades to IDLE / DISCONNECTED without blocking the loop
- On successful poll the main loop runs the three range validators and sets IMU\_ACCELEROMETER\_ERROR, IMU\_GYROSCOPE\_ERROR, or IMU\_MAGNETOMETER\_ERROR accordingly
- Timestamp tracking (HAL\_GetTick) enables dead reckoning applications
- Filter algorithms can be applied to the raw data for smoothing
- Unit tested on host: init defaults, update/read round-trip, magnitude, pitch/roll, range validators (test/sensor\_board/test\_imu\_sensor)