ON THIS PAGE

  • How to place it
  • Inputs and Outputs
  • Pipeline flow
  • Limitations
  • Requested report rate
  • High-rate raw streaming used in examples
  • Batching
  • Packet fields and report semantics
  • Usage
  • Report availability by hardware
  • Calibration and extrinsics
  • Processed output transforms
  • Reading multiple IMU streams
  • Examples of functionality
  • Reference

IMU

Supported on:RVC2RVC4
IMU (inertial measurement unit) node can be used to receive data from the IMU chip on the device.Luxonis devices use different IMU stacks depending on platform generation:The IMU chip is connected to the RVC over SPI. See OAK Hardware documentation to check whether your OAK camera has IMU integrated.

How to place it

Python

Python
1pipeline = dai.Pipeline()
2imu = pipeline.create(dai.node.IMU)

C++

C++
1dai::Pipeline pipeline;
2auto imu = pipeline.create<dai::node::IMU>();

Inputs and Outputs

Pipeline flow

The usual IMU node flow is:
  1. Create dai::node::IMU
  2. Enable one or more IMUSensor reports at a requested rate
  3. Configure batching with setBatchReportThreshold() and setMaxBatchReports()
  4. Consume IMUData packets from the host output queue

Limitations

  • On RVC2 devices with BNO08X, gyroscope frequencies above 400 Hz can produce occasional jitter due to sensor hardware limitations.

Requested report rate

imu.enableIMUSensor(..., reportRate) accepts a requested report rate in Hz.Actual runtime behavior depends on the IMU and platform:
  • On RVC2 devices using BNO08X, requested rates round up to the next supported rate.
  • On RVC2 devices using BMI270, requested rates round down to the next supported rate. Requests above 400 Hz currently top out around 250 Hz.
  • On RVC4 devices using LSM6DSV + AK09919, treat validated runtime rates as platform-specific rather than assuming the silicon max ODR maps directly to the host stream rate.
For detailed per-sensor runtime behavior and hardware characteristics, see the IMU hardware reference.

High-rate raw streaming used in examples

The current depthai-core IMU examples and tests use the following raw-stream rates for high-rate capture:
  • ACCELEROMETER_RAW at 480 Hz
  • GYROSCOPE_RAW at 400 Hz

Batching

The IMU node batches packets before sending them to the host.
  • setBatchReportThreshold(N) sets the minimum number of IMU packets that should be ready before the device is allowed to send a batch
  • setMaxBatchReports(M) sets the maximum number of packets included in one batch
Higher batching can reduce host overhead and help when USB bandwidth or host scheduling becomes the bottleneck.

Packet fields and report semantics

Each IMUData message contains one or more IMUPacket entries. Each packet only carries the fields for the reports that were enabled in the node configuration.Typical fields include:
  • acceleroMeter
  • gyroscope
  • magneticField
  • rotationVector
Use the report families like this:
  • *_RAW: direct sensor output in the sensor-native frame
  • *_UNCALIBRATED: rotated into the Luxonis RDF frame using imuExtrinsics, without IMU calibration parameters applied
  • *_CALIBRATED: rotated into the Luxonis RDF frame and corrected using IMU calibration parameters
  • fused outputs such as ROTATION_VECTOR and GAME_ROTATION_VECTOR: forwarded from the sensor's internal processing path

Usage

Python

Python
1pipeline = dai.Pipeline()
2imu = pipeline.create(dai.node.IMU)
3
4# enable ACCELEROMETER_RAW and GYROSCOPE_RAW at 100 hz rate
5imu.enableIMUSensor([dai.IMUSensor.ACCELEROMETER_RAW, dai.IMUSensor.GYROSCOPE_RAW], 100)
6# above this threshold packets will be sent in batch of X, if the host is not blocked and USB bandwidth is available
7imu.setBatchReportThreshold(1)
8# maximum number of IMU packets in a batch, if it's reached device will block sending until host can receive it
9# if lower or equal to batchReportThreshold then the sending is always blocking on device
10# useful to reduce device's CPU load  and number of lost packets, if CPU load is high on device side due to multiple nodes
11imu.setMaxBatchReports(10)

C++

C++
1dai::Pipeline pipeline;
2auto imu = pipeline.create<dai::node::IMU>();
3
4// enable ACCELEROMETER_RAW and GYROSCOPE_RAW at 100 hz rate
5imu->enableIMUSensor({dai::IMUSensor::ACCELEROMETER_RAW, dai::IMUSensor::GYROSCOPE_RAW}, 100);
6// above this threshold packets will be sent in batch of X, if the host is not blocked and USB bandwidth is available
7imu->setBatchReportThreshold(1);
8// maximum number of IMU packets in a batch, if it's reached device will block sending until host can receive it
9// if lower or equal to batchReportThreshold then the sending is always blocking on device
10// useful to reduce device's CPU load  and number of lost packets, if CPU load is high on device side due to multiple nodes
11imu->setMaxBatchReports(10);

Report availability by hardware

Report familyIMUSensor valuesBNO08XBMI270LSM6DSVAK09919Notes
Raw accelerationACCELEROMETER_RAWYesYesYesNoRaw accelerometer stream
Accelerometer in Luxonis frame, without IMU calibration parametersACCELEROMETER_UNCALIBRATEDYesYesYesNoRAW -> UNCALIBRATED applies imuExtrinsics. This is the unified IMU-frame accelerometer path.
Accelerometer in Luxonis frame, with IMU calibration parametersACCELEROMETER_CALIBRATEDYesYesYesNoRequires IMU calibration parameters to be present in the runtime calibration payload.
Raw angular velocityGYROSCOPE_RAWYesYesYesNoRaw gyroscope stream
Gyroscope in Luxonis frame, without IMU calibration parametersGYROSCOPE_UNCALIBRATEDYesYesYesNoRAW -> UNCALIBRATED applies imuExtrinsics. On older devices this is expected to work out of the box.
Gyroscope in Luxonis frame, with IMU calibration parametersGYROSCOPE_CALIBRATEDYesYesYesNoRequires IMU calibration parameters to be present in the runtime calibration payload.
Raw magnetic fieldMAGNETOMETER_RAWYesNoNoYesOn RVC4, raw magnetometer data comes from the AK09919 companion sensor.
Processed magnetic fieldMAGNETOMETER_CALIBRATED, MAGNETOMETER_UNCALIBRATEDYesNoNoValidate on target hardwarePublic enum/docstrings are still BNO08X-shaped for processed magnetometer outputs.
Derived acceleration outputsLINEAR_ACCELERATION, GRAVITYYesNoValidate on target hardwareNoBNO08X exposes these directly. For non-BNO paths, validate availability on the target device and branch.
Fused orientation outputsROTATION_VECTOR, GAME_ROTATION_VECTOR, GEOMAGNETIC_ROTATION_VECTOR, ARVR_STABILIZED_ROTATION_VECTOR, ARVR_STABILIZED_GAME_ROTATION_VECTORYesNoValidate on target hardwareNo standaloneThese are sensor-fusion outputs, not host-side fusion done by the IMU node.

Calibration and extrinsics

Use device.readCalibration() to read the factory calibration payload from the device. Use device.getCalibration() and device.setCalibration() when working with runtime calibration overrides.For the processed inertial outputs, use this model:
  • RAW -> UNCALIBRATED: align the sensor-native frame into the Luxonis RDF frame using imuExtrinsics
  • UNCALIBRATED -> CALIBRATED: apply the accelerometer or gyroscope calibration set in the IMU calibration parameters
  • ACCELEROMETER_UNCALIBRATED and GYROSCOPE_UNCALIBRATED therefore share the unified IMU-frame path
  • ACCELEROMETER_CALIBRATED and GYROSCOPE_CALIBRATED add the runtime calibration correction on top of that

Processed output transforms

RAW -> UNCALIBRATED

Python
1import depthai as dai
2import numpy as np
3
4with dai.Pipeline() as pipeline:
5    imu = pipeline.create(dai.node.IMU)
6    imu.enableIMUSensor(dai.IMUSensor.ACCELEROMETER_RAW, 100)
7    imu_q = imu.out.createOutputQueue(maxSize=10, blocking=False)
8
9    device = pipeline.getDefaultDevice()
10    calib = device.readCalibration()
11    imu_to_cam = np.array(calib.getImuToCameraExtrinsics(dai.CameraBoardSocket.CAM_A, False))
12    R_imu_to_cam = imu_to_cam[:3, :3]
13
14    pipeline.start()
15    pkt = imu_q.get().packets[0]
16    raw = np.array([pkt.acceleroMeter.x, pkt.acceleroMeter.y, pkt.acceleroMeter.z])
17    uncalibrated = R_imu_to_cam @ raw

UNCALIBRATED -> CALIBRATED

Python
1import depthai as dai
2import numpy as np
3
4imu_calibration = [
5    [1.0, 0.0, 0.0, 0.125],
6    [0.0, 1.0, 0.0, 0.000],
7    [0.0, 0.0, 1.0, 0.000],
8]
9
10with dai.Pipeline() as pipeline:
11    imu = pipeline.create(dai.node.IMU)
12    imu.enableIMUSensor(dai.IMUSensor.ACCELEROMETER_UNCALIBRATED, 100)
13    imu_q = imu.out.createOutputQueue(maxSize=10, blocking=False)
14
15    pipeline.start()
16    pkt = imu_q.get().packets[0]
17    uncalibrated = np.array([pkt.acceleroMeter.x, pkt.acceleroMeter.y, pkt.acceleroMeter.z])
18
19    calibration = np.array(imu_calibration)
20    calibrated = calibration[:, :3] @ uncalibrated + calibration[:, 3]

Reading multiple IMU streams

If the target hardware exposes the selected reports, you can enable multiple IMU report families and read only the fields that were requested.

Python

Python
1import depthai as dai
2
3with dai.Pipeline() as pipeline:
4    imu = pipeline.create(dai.node.IMU)
5
6    imu.enableIMUSensor(dai.IMUSensor.ACCELEROMETER_RAW, 100)
7    imu.enableIMUSensor(dai.IMUSensor.GYROSCOPE_RAW, 100)
8    imu.enableIMUSensor(dai.IMUSensor.MAGNETOMETER_RAW, 100)
9
10    imu.setBatchReportThreshold(1)
11    imu.setMaxBatchReports(10)
12
13    imu_q = imu.out.createOutputQueue(maxSize=50, blocking=False)
14
15    pipeline.start()
16    while pipeline.isRunning():
17        imu_data = imu_q.get()
18        for pkt in imu_data.packets:
19            a = pkt.acceleroMeter
20            g = pkt.gyroscope
21            m = pkt.magneticField

C++

C++
1#include "depthai/depthai.hpp"
2
3int main() {
4    dai::Pipeline pipeline;
5    auto imu = pipeline.create<dai::node::IMU>();
6
7    imu->enableIMUSensor(dai::IMUSensor::ACCELEROMETER_RAW, 100);
8    imu->enableIMUSensor(dai::IMUSensor::GYROSCOPE_RAW, 100);
9    imu->enableIMUSensor(dai::IMUSensor::MAGNETOMETER_RAW, 100);
10
11    imu->setBatchReportThreshold(1);
12    imu->setMaxBatchReports(10);
13
14    auto imuQ = imu->out.createOutputQueue(50, false);
15
16    pipeline.start();
17    while(pipeline.isRunning()) {
18        auto data = imuQ->get<dai::IMUData>();
19        for(const auto& pkt : data->packets) {
20            const auto& a = pkt.acceleroMeter;
21            const auto& g = pkt.gyroscope;
22            const auto& m = pkt.magneticField;
23        }
24    }
25}

Examples of functionality

Reference

class

dai::node::IMU

#include IMU.hpp
variable
Output out
Outputs IMUData message that carries IMU packets.
variable
Input mockIn
Mock IMU data for replaying recorded data
function
void enableIMUSensor(IMUSensorConfig sensorConfig)
Enable a new IMU sensor with explicit configuration
function
void enableIMUSensor(const std::vector< IMUSensorConfig > & sensorConfigs)
Enable a list of IMU sensors with explicit configuration
function
void enableIMUSensor(IMUSensor sensor, uint32_t reportRate)
Enable a new IMU sensor with default configuration
function
void enableIMUSensor(const std::vector< IMUSensor > & sensors, uint32_t reportRate)
Enable a list of IMU sensors with default configuration
function
void setBatchReportThreshold(std::int32_t batchReportThreshold)
Above this packet threshold data will be sent to host, if queue is not blocked
function
std::int32_t getBatchReportThreshold()
Above this packet threshold data will be sent to host, if queue is not blocked
function
void setMaxBatchReports(std::int32_t maxBatchReports)
Maximum number of IMU packets in a batch report
function
std::int32_t getMaxBatchReports()
Maximum number of IMU packets in a batch report
function
void enableFirmwareUpdate(bool enable)
Whether to perform firmware update or not. Default value: false.
inline function
DeviceNodeCRTP()
inline function
DeviceNodeCRTP(const std::shared_ptr< Device > & device)
inline function
DeviceNodeCRTP(std::unique_ptr< Properties > props)
inline function
DeviceNodeCRTP(std::unique_ptr< Properties > props, bool confMode)
inline function
DeviceNodeCRTP(const std::shared_ptr< Device > & device, std::unique_ptr< Properties > props, bool confMode)

Need assistance?

Head over to Discussion Forum for technical support or any other questions you might have.