# IMU

IMU（[惯性测量单元](https://en.wikipedia.org/wiki/Inertial_measurement_unit)）节点可用于接收设备上IMU芯片的数据。

Luxonis设备根据平台代次使用不同的IMU堆栈：

 * RVC2设备使用[BNO08X](https://docs.luxonis.com/hardware/platform/sensors/imu/bno08x.md)或[BMI270](https://docs.luxonis.com/hardware/platform/sensors/imu/bmi270.md)
 * RVC4设备使用[LSM6DSV](https://docs.luxonis.com/hardware/platform/sensors/imu/lsm6dsv.md)以及[AK09919](https://docs.luxonis.com/hardware/platform/sensors/imu/ak09919.md)磁力计。

IMU芯片通过SPI连接到[RVC](https://docs.luxonis.com/hardware/platform/rvc/rvc2.md#rvc2)。请参阅[OAK硬件文档](https://docs.luxonis.com/hardware.md)以检查您的OAK相机是否集成了IMU。

> **寻找概念文档？**
> 关于传感器系列、输出语义、参考坐标系、工厂校准和噪声特性，请参阅
> [IMU硬件参考](https://docs.luxonis.com/hardware/platform/sensors/imu.md)
> 。关于实际用法，请参见下面的IMU示例。

## 如何放置

#### Python

```python
pipeline = dai.Pipeline()
imu = pipeline.create(dai.node.IMU)
```

#### C++

```cpp
dai::Pipeline pipeline;
auto imu = pipeline.create<dai::node::IMU>();
```

## 输入和输出

## 管道流程

通常的IMU节点流程是：

 1. 创建 dai::node::IMU
 2. 以请求的速率启用一个或多个 IMUSensor 报告
 3. 使用 setBatchReportThreshold() 和 setMaxBatchReports() 配置批处理
 4. 从主机输出队列消费 IMUData 数据包

## 限制

 * 在搭载BNO08X的RVC2设备上，由于传感器硬件限制，陀螺仪频率超过400 Hz时可能偶尔出现抖动。

## 请求的报告速率

imu.enableIMUSensor(..., reportRate) 接受以Hz为单位的请求报告速率。

实际运行时行为取决于IMU和平台：

 * 在搭载BNO08X的RVC2设备上，请求的速率会向上舍入到下一个支持的速率。
 * 在搭载BMI270的RVC2设备上，请求的速率会向下舍入到下一个支持的速率。超过400 Hz的请求目前最高约为250 Hz。
 * 在搭载LSM6DSV + AK09919的RVC4设备上，将已验证的运行时速率视为平台特定的，而不是假设硅片最大ODR直接映射到主机流速率。

有关每个传感器的详细运行时行为和硬件特性，请参阅[IMU硬件参考](https://docs.luxonis.com/hardware/platform/sensors/imu.md)。

### 示例中使用的高速率原始流

当前的 depthai-core IMU示例和测试使用以下原始流速率进行高速率捕获：

 * ACCELEROMETER_RAW 以 480 Hz
 * GYROSCOPE_RAW 以 400 Hz

## 批处理

IMU节点在将数据包发送到主机之前对其进行批处理。

 * setBatchReportThreshold(N) 设置允许设备发送批处理之前应准备好的最小IMU数据包数量
 * setMaxBatchReports(M) 设置每个批处理中包含的最大数据包数量

更高的批处理可以减少主机开销，并在USB带宽或主机调度成为瓶颈时有所帮助。

## 数据包字段和报告语义

每个 IMUData 消息包含一个或多个 IMUPacket 条目。每个数据包仅携带在节点配置中启用的报告的字段。

典型字段包括：

 * acceleroMeter
 * gyroscope
 * magneticField
 * rotationVector

使用如下报告系列：

 * *_RAW：传感器原生坐标系中的直接传感器输出
 * *_UNCALIBRATED：使用 imuExtrinsics 旋转到Luxonis RDF坐标系，未应用IMU校准参数
 * *_CALIBRATED：旋转到Luxonis RDF坐标系并使用IMU校准参数校正
 * 融合输出，如 ROTATION_VECTOR 和 GAME_ROTATION_VECTOR：从传感器内部处理路径转发

## 用法

#### Python

```python
pipeline = dai.Pipeline()
imu = pipeline.create(dai.node.IMU)

# 启用 ACCELEROMETER_RAW 和 GYROSCOPE_RAW，速率为 100 Hz
imu.enableIMUSensor([dai.IMUSensor.ACCELEROMETER_RAW, dai.IMUSensor.GYROSCOPE_RAW], 100)
# 超过此阈值时，数据包将以 X 个为一组批量发送，前提是主机未阻塞且 USB 带宽可用
imu.setBatchReportThreshold(1)
# 每批的最大 IMU 数据包数，达到此数量时设备将阻塞发送，直到主机能够接收
# 如果小于或等于 batchReportThreshold，则发送始终在设备端阻塞
# 有助于减少设备 CPU 负载和丢失的数据包数，如果设备端因多个节点而导致 CPU 负载较高
imu.setMaxBatchReports(10)
```

#### C++

```cpp
dai::Pipeline pipeline;
auto imu = pipeline.create<dai::node::IMU>();

// 启用 ACCELEROMETER_RAW 和 GYROSCOPE_RAW，速率为 100 Hz
imu->enableIMUSensor({dai::IMUSensor::ACCELEROMETER_RAW, dai::IMUSensor::GYROSCOPE_RAW}, 100);
// 超过此阈值时，数据包将以 X 个为一组批量发送，前提是主机未阻塞且 USB 带宽可用
imu->setBatchReportThreshold(1);
// 每批的最大 IMU 数据包数，达到此数量时设备将阻塞发送，直到主机能够接收
// 如果小于或等于 batchReportThreshold，则发送始终在设备端阻塞
// 有助于减少设备 CPU 负载和丢失的数据包数，如果设备端因多个节点而导致 CPU 负载较高
imu->setMaxBatchReports(10);
```

## 按硬件报告可用性

> **Current public API scope**
> 当前的
> `depthai-core`
> IMU 枚举和文档字符串仍以 BNO08X 为导向。在绑定中，该节点仍被记录为
> `BNO08X 的 IMU 节点`
> ，因此融合和处理的报告覆盖率应结合硬件文档阅读，并在传感器路径不是 BNO08X 时在目标设备上进行验证。

| 报告系列 | `IMUSensor` 值 | BNO08X | BMI270 | LSM6DSV | AK09919 | 备注 |
| --- | --- | --- | --- | --- | --- | --- |
| 原始加速度 | `ACCELEROMETER_RAW` | 是 | 是 | 是 | 否 | 原始加速度计数据流 |
| Luxonis 坐标系中的加速度计（无 IMU 校准参数） | `ACCELEROMETER_UNCALIBRATED` | 是 | 是 | 是 | 否 | `RAW -> UNCALIBRATED` 应用 `imuExtrinsics`。这是统一的
IMU 坐标系加速度计路径。 |
| Luxonis 坐标系中的加速度计（含 IMU 校准参数） | `ACCELEROMETER_CALIBRATED` | 是 | 是 | 是 | 否 | 需要运行时校准负载中存在 IMU 校准参数。 |
| 原始角速度 | `GYROSCOPE_RAW` | 是 | 是 | 是 | 否 | 原始陀螺仪数据流 |
| Luxonis 坐标系中的陀螺仪（无 IMU 校准参数） | `GYROSCOPE_UNCALIBRATED` | 是 | 是 | 是 | 否 | `RAW -> UNCALIBRATED` 应用
`imuExtrinsics`。在较旧设备上，预计开箱即用。 |
| Luxonis 坐标系中的陀螺仪（含 IMU 校准参数） | `GYROSCOPE_CALIBRATED` | 是 | 是 | 是 | 否 | 需要运行时校准负载中存在 IMU 校准参数。 |
| 原始磁场 | `MAGNETOMETER_RAW` | 是 | 否 | 否 | 是 | 在 RVC4 上，原始磁力计数据来自 AK09919 配套传感器。 |
| 处理后的磁场 | `MAGNETOMETER_CALIBRATED`, `MAGNETOMETER_UNCALIBRATED` | 是 | 否 | 否 | 在目标硬件上验证 | 处理后的磁力计输出的公共枚举/文档字符串仍为 BNO08X 样式。 |
| 导出的加速度输出 | `LINEAR_ACCELERATION`, `GRAVITY` | 是 | 否 | 在目标硬件上验证 | 否 | BNO08X 直接暴露这些。对于非 BNO 路径，在目标设备上验证可用性并进行分支处理。 |
| 融合姿态输出 | `ROTATION_VECTOR`, `GAME_ROTATION_VECTOR`, `GEOMAGNETIC_ROTATION_VECTOR`, `ARVR_STABILIZED_ROTATION_VECTOR`,
`ARVR_STABILIZED_GAME_ROTATION_VECTOR` | 是 | 否 | 在目标硬件上验证 | 无独立支持 | 这些是传感器融合输出，并非由 IMU 节点在主机端完成的融合。 |

## 校准与外部参数

使用 device.readCalibration() 从设备读取出厂校准负载。使用 device.getCalibration() 和 device.setCalibration() 处理运行时校准覆盖。

对于处理后的惯性输出，请使用以下模型：

 * RAW -> UNCALIBRATED：使用 imuExtrinsics 将传感器原生坐标系对齐到 Luxonis RDF 坐标系
 * UNCALIBRATED -> CALIBRATED：应用 IMU 校准参数中设置的加速度计或陀螺仪校准
 * 因此 ACCELEROMETER_UNCALIBRATED 和 GYROSCOPE_UNCALIBRATED 共享统一的 IMU 坐标系路径
 * ACCELEROMETER_CALIBRATED 和 GYROSCOPE_CALIBRATED 在此基础上添加运行时校准校正

### 处理后的输出变换

#### RAW -> UNCALIBRATED

```python
import depthai as dai
import numpy as np

with dai.Pipeline() as pipeline:
    imu = pipeline.create(dai.node.IMU)
    imu.enableIMUSensor(dai.IMUSensor.ACCELEROMETER_RAW, 100)
    imu_q = imu.out.createOutputQueue(maxSize=10, blocking=False)

    device = pipeline.getDefaultDevice()
    calib = device.readCalibration()
    imu_to_cam = np.array(calib.getImuToCameraExtrinsics(dai.CameraBoardSocket.CAM_A, False))
    R_imu_to_cam = imu_to_cam[:3, :3]

    pipeline.start()
    pkt = imu_q.get().packets[0]
    raw = np.array([pkt.acceleroMeter.x, pkt.acceleroMeter.y, pkt.acceleroMeter.z])
    uncalibrated = R_imu_to_cam @ raw
```

#### UNCALIBRATED -> CALIBRATED

```python
import depthai as dai
import numpy as np

imu_calibration = [
    [1.0, 0.0, 0.0, 0.125],
    [0.0, 1.0, 0.0, 0.000],
    [0.0, 0.0, 1.0, 0.000],
]

with dai.Pipeline() as pipeline:
    imu = pipeline.create(dai.node.IMU)
    imu.enableIMUSensor(dai.IMUSensor.ACCELEROMETER_UNCALIBRATED, 100)
    imu_q = imu.out.createOutputQueue(maxSize=10, blocking=False)

    pipeline.start()
    pkt = imu_q.get().packets[0]
    uncalibrated = np.array([pkt.acceleroMeter.x, pkt.acceleroMeter.y, pkt.acceleroMeter.z])

    calibration = np.array(imu_calibration)
    calibrated = calibration[:, :3] @ uncalibrated + calibration[:, 3]
```

## 读取多个IMU数据流

如果目标硬件支持所选的报告，您可以启用多个IMU报告家族，并仅读取已请求的字段。

#### Python

```python
import depthai as dai

with dai.Pipeline() as pipeline:
    imu = pipeline.create(dai.node.IMU)

    imu.enableIMUSensor(dai.IMUSensor.ACCELEROMETER_RAW, 100)
    imu.enableIMUSensor(dai.IMUSensor.GYROSCOPE_RAW, 100)
    imu.enableIMUSensor(dai.IMUSensor.MAGNETOMETER_RAW, 100)

    imu.setBatchReportThreshold(1)
    imu.setMaxBatchReports(10)

    imu_q = imu.out.createOutputQueue(maxSize=50, blocking=False)

    pipeline.start()
    while pipeline.isRunning():
        imu_data = imu_q.get()
        for pkt in imu_data.packets:
            a = pkt.acceleroMeter
            g = pkt.gyroscope
            m = pkt.magneticField
```

#### C++

```cpp
#include "depthai/depthai.hpp"

int main() {
    dai::Pipeline pipeline;
    auto imu = pipeline.create<dai::node::IMU>();

    imu->enableIMUSensor(dai::IMUSensor::ACCELEROMETER_RAW, 100);
    imu->enableIMUSensor(dai::IMUSensor::GYROSCOPE_RAW, 100);
    imu->enableIMUSensor(dai::IMUSensor::MAGNETOMETER_RAW, 100);

    imu->setBatchReportThreshold(1);
    imu->setMaxBatchReports(10);

    auto imuQ = imu->out.createOutputQueue(50, false);

    pipeline.start();
    while(pipeline.isRunning()) {
        auto data = imuQ->get<dai::IMUData>();
        for(const auto& pkt : data->packets) {
            const auto& a = pkt.acceleroMeter;
            const auto& g = pkt.gyroscope;
            const auto& m = pkt.magneticField;
        }
    }
}
```

## 功能示例

 * [IMU加速度计与陀螺仪](https://docs.luxonis.com/software-v3/depthai/examples/imu/imu_accelerometer_gyroscope.md)
 * [IMU旋转矢量](https://docs.luxonis.com/software-v3/depthai/examples/imu_rotation_vector.md)
 * [IMU与视频同步](https://docs.luxonis.com/software-v3/depthai/examples/imu_video_sync.md)

## 参考

### dai::node::IMU

Kind: class

IMU node for BNO08X.

#### Output out

Kind: variable

Outputs IMUData message that carries IMU packets.

#### Input mockIn

Kind: variable

Mock IMU data for replaying recorded data

#### void enableIMUSensor(IMUSensorConfig sensorConfig)

Kind: function

Enable a new IMU sensor with explicit configuration

#### void enableIMUSensor(const std::vector< IMUSensorConfig > & sensorConfigs)

Kind: function

Enable a list of IMU sensors with explicit configuration

#### void enableIMUSensor(IMUSensor sensor, uint32_t reportRate)

Kind: function

Enable a new IMU sensor with default configuration

#### void enableIMUSensor(const std::vector< IMUSensor > & sensors, uint32_t reportRate)

Kind: function

Enable a list of IMU sensors with default configuration

#### void setBatchReportThreshold(std::int32_t batchReportThreshold)

Kind: function

Above this packet threshold data will be sent to host, if queue is not blocked

#### std::int32_t getBatchReportThreshold()

Kind: function

Above this packet threshold data will be sent to host, if queue is not blocked

#### void setMaxBatchReports(std::int32_t maxBatchReports)

Kind: function

Maximum number of IMU packets in a batch report

#### std::int32_t getMaxBatchReports()

Kind: function

Maximum number of IMU packets in a batch report

#### void enableFirmwareUpdate(bool enable)

Kind: function

Whether to perform firmware update or not. Default value: false.

#### DeviceNodeCRTP()

Kind: function

#### DeviceNodeCRTP(const std::shared_ptr< Device > & device)

Kind: function

#### DeviceNodeCRTP(std::unique_ptr< Properties > props)

Kind: function

#### DeviceNodeCRTP(std::unique_ptr< Properties > props, bool confMode)

Kind: function

#### DeviceNodeCRTP(const std::shared_ptr< Device > & device, std::unique_ptr< Properties > props, bool confMode)

Kind: function

### 需要帮助？

请前往 [OAKChina 官网](https://www.oakchina.cn/) 获取技术支持或解答您的任何疑问。
