RealSense D455 相机-IMU 标定:Kalibr 之外的替代方案

Kalibr 确实是视觉–惯性传感器(如 D455 的相机 + IMU)外参标定的 经典、高精度方案,但它对硬件操作、数据采集和计算要求较高。如果你希望 更简单、更快上手、或适配 ROS 2 / RealSense 特性,本文介绍多种替代路线供你选择。


📋 目录

  1. 标定基础知识
  2. 方案 1:使用 RealSense 出厂外参(最简单)
  3. 方案 2:rs-imu-calibration(IMU 内参校准)
  4. 方案 3:imu_utils + 手动外参估计
  5. 方案 4:Basalt 在线标定(进阶替代)
  6. 方案 5:从 TF 树读取外参
  7. 路线选择建议
  8. 附录:Kalibr Docker 快速上手

标定基础知识

什么需要标定?

参数类型 内容 影响
相机内参 fx, fy, cx, cy, 畸变系数 像素坐标 ↔ 相机坐标转换精度
IMU 内参 bias, scale, misalignment IMU 测量值校正
IMU-Camera 外参 旋转 R + 平移 t(T_imu_cam 传感器融合的坐标对齐
时间偏移 IMU 与相机的时间戳差 时空同步精度

标定精度对 VIO 的影响

外参误差            →  轨迹漂移
  ↓
旋转误差 1°        →  100m 处约 1.7m 横向偏移
平移误差 1cm       →  直接传递到位置估计
时间偏移 10ms      →  高速运动时严重错位

💡 好消息:OpenVINS 的 MSCKF 框架对初始外参误差有一定容忍度,只要不太离谱(< 5°旋转、< 5cm 平移)。


方案 1:使用 RealSense 出厂外参(最简单)

适用场景

  • ✅ 快速验证、学习、非高精度任务
  • ✅ 初次接触 VIO/SLAM
  • ✅ D455 出厂校准精度已相当不错(~毫米级)

获取出厂外参

# 安装 RealSense SDK(如未安装)
sudo apt install librealsense2-utils

# 查看设备详细信息
rs-enumerate-devices -v

输出中会包含 Motion Module 到各传感器的变换:

Motion Module:
  ...
  Extrinsic from "Gyro" To "Color":
    Rotation Matrix:
      [  0.999977,   0.006456,  -0.002135]
      [ -0.006459,   0.999979,  -0.001023]
      [  0.002128,   0.001037,   0.999997]
    Translation Vector: [0.014851, 0.000455, 0.000367]

转换为 OpenVINS 格式

# T_imu_cam0: 从相机坐标系到 IMU 坐标系的变换
# 需要将上述 "Gyro to Color" 转换为 4x4 齐次矩阵

T_imu_cam0:
  - [0.999977,  0.006456, -0.002135, 0.014851]
  - [-0.006459, 0.999979, -0.001023, 0.000455]
  - [0.002128,  0.001037,  0.999997, 0.000367]
  - [0.0,       0.0,       0.0,      1.0]

Python 转换脚本

#!/usr/bin/env python3
"""从 RealSense 提取外参并转换为 OpenVINS 格式"""

import pyrealsense2 as rs
import numpy as np

def get_extrinsics():
    ctx = rs.context()
    devices = ctx.query_devices()
    
    if len(devices) == 0:
        print("未检测到 RealSense 设备")
        return
    
    dev = devices[0]
    
    # 获取传感器
    sensors = dev.query_sensors()
    
    for sensor in sensors:
        if sensor.is_motion_sensor():
            # 获取 IMU 配置
            profiles = sensor.get_stream_profiles()
            for profile in profiles:
                if profile.stream_type() == rs.stream.gyro:
                    gyro_profile = profile
                    break
        elif sensor.is_color_sensor():
            profiles = sensor.get_stream_profiles()
            for profile in profiles:
                if profile.stream_type() == rs.stream.color:
                    color_profile = profile
                    break
    
    # 获取外参
    extrinsics = gyro_profile.get_extrinsics_to(color_profile)
    
    # 转换为 4x4 矩阵
    R = np.array(extrinsics.rotation).reshape(3, 3)
    t = np.array(extrinsics.translation)
    
    T = np.eye(4)
    T[:3, :3] = R
    T[:3, 3] = t
    
    print("T_imu_cam0 (OpenVINS 格式):")
    print("T_imu_cam0:")
    for row in T:
        print(f"  - [{row[0]:.6f}, {row[1]:.6f}, {row[2]:.6f}, {row[3]:.6f}]")

if __name__ == "__main__":
    get_extrinsics()

推荐你先用这个,90% 的初学者/实验者完全够用。


方案 2:rs-imu-calibration(IMU 内参校准)

作用

校准 IMU 的 bias、scale、misalignment(即 IMU 内参),不校外参

适用场景

  • 怀疑 IMU 本身不准(静止时加速度计不为 9.8 或陀螺不为 0)
  • 长期使用后 IMU 特性漂移

安装与使用

# 工具已包含在 librealsense2-utils 中
# 或从源码编译:https://github.com/IntelRealSense/librealsense/tree/master/tools/rs-imu-calibration

# 运行校准
rs-imu-calibration

校准流程

  1. 将 D455 放置在稳定平面
  2. 按提示将设备放置在 6 个不同朝向(±X, ±Y, ±Z)
  3. 每个朝向保持静止约 4 秒
  4. 工具自动计算校准参数并写入设备 EEPROM

输出示例

Calibration completed successfully!
Accel Bias:   [0.012, -0.008, 0.045] m/s²
Gyro Bias:    [0.0012, -0.0008, 0.0003] rad/s
Scale Factor: [1.002, 0.998, 1.001]

⚠️ 注意:此工具仅校准 IMU 内参,仍需配合 Kalibr 或其他工具来标 IMU–Camera 外参


方案 3:imu_utils + 手动外参估计

原理

  • IMU 静止时,加速度计方向应指向重力方向(即世界 Z 轴)
  • 结合相机看到的水平面,可粗略反推相对朝向

imu_utils 工具

GitHub: https://github.com/gaowenliang/imu_utils

# ROS 1 环境下安装
cd ~/catkin_ws/src
git clone https://github.com/gaowenliang/imu_utils.git
git clone https://github.com/gaowenliang/code_utils.git
cd ..
catkin_make

使用方法

  1. 采集静态 IMU 数据(约 2 小时)
# 录制 bag
rosbag record /camera/imu -O imu_static.bag
  1. 分析噪声参数
roslaunch imu_utils d455_imu.launch
rosbag play imu_static.bag
  1. 输出 Allan 方差分析结果
# 输出文件:d455_imu_param.yaml
gyr_n: 0.005        # 陀螺仪白噪声
gyr_w: 0.0001       # 陀螺仪随机游走
acc_n: 0.01         # 加速度计白噪声
acc_w: 0.0002       # 加速度计随机游走

局限性

  • ❌ 只能估计 旋转部分(无法得平移)
  • ❌ 精度低
  • ⚠️ 不推荐用于 OpenVINS 的外参,仅作调试参考

方案 4:Basalt 在线标定(进阶替代)

简介

Basalt 是苏黎世联邦理工(ETH Zurich)开发的视觉惯性里程计系统,支持 在线标定 IMU-Camera 外参 + 内参 + 延迟。

GitHub: https://gitlab.com/VladyslavUsenko/basalt

优势

对比项 Kalibr Basalt
标定模式 离线(需录制 bag) 在线(实时优化)
数据采集要求 严格(需匀速运动) 宽松(手持随意走动)
计算时间 数小时 实时
ROS 支持 ROS 1 ROS 1 + 自定义格式

安装(Ubuntu 22.04)

# 安装依赖
sudo apt install cmake git libeigen3-dev libfmt-dev \
    libglew-dev libopencv-dev libtbb-dev libpangolin-dev

# 克隆源码
git clone --recursive https://gitlab.com/VladyslavUsenko/basalt.git
cd basalt

# 编译
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo
make -j$(nproc)

使用 Basalt 进行在线标定

# 运行标定模式
./basalt_calibrate \
    --dataset-path /path/to/euroc_format_data \
    --dataset-type euroc \
    --result-path ./calibration_result \
    --cam-calib /path/to/initial_calib.json

输出格式转换

Basalt 输出 JSON 格式,需转换为 OpenVINS YAML:

import json
import yaml

with open('calibration_result.json') as f:
    basalt_calib = json.load(f)

# 提取 T_imu_cam
T_imu_cam = basalt_calib['T_imu_cam'][0]

openvins_config = {
    'T_imu_cam0': T_imu_cam
}

with open('openvins_extrinsics.yaml', 'w') as f:
    yaml.dump(openvins_config, f)

方案 5:从 TF 树读取外参

适用场景

  • 使用 robot_state_publisher 发布机器人模型
  • URDF/Xacro 中已定义传感器外参

原理

┌─────────────────────────────────────────────────────┐
│                    TF 树                             │
│                                                     │
│    base_link ──▶ imu_link ──▶ camera_link          │
│                     │              │                │
│                     └──────────────┘                │
│                      T_imu_cam                      │
└─────────────────────────────────────────────────────┘

从 TF 获取变换

# 查看 TF 树
ros2 run tf2_tools view_frames

# 获取两个坐标系之间的变换
ros2 run tf2_ros tf2_echo imu_link camera_color_optical_frame

输出:

At time 0.0
- Translation: [0.015, 0.000, 0.000]
- Rotation: in Quaternion [0.000, 0.000, 0.000, 1.000]

转换为 OpenVINS 格式

from scipy.spatial.transform import Rotation
import numpy as np

# TF 输出的四元数和平移
quat = [0.0, 0.0, 0.0, 1.0]  # [x, y, z, w]
trans = [0.015, 0.0, 0.0]

# 转换为旋转矩阵
R = Rotation.from_quat(quat).as_matrix()

# 构建 4x4 变换矩阵
T = np.eye(4)
T[:3, :3] = R
T[:3, 3] = trans

print("T_imu_cam0:")
for row in T:
    print(f"  - [{', '.join(f'{v:.6f}' for v in row)}]")

路线选择建议

你的目标 推荐路线
快速跑通 OpenVINS ✅ 直接用 RealSense 出厂外参(rs-enumerate-devices -v
提升精度(科研/产品) ✅ Kalibr(高精度) 或 ✅ Basalt(在线自动标定)
仅校 IMU 本身不准 rs-imu-calibration
不想装 ROS 1 / 只用 ROS 2 ✅ 出厂外参 + 在线 VIO(如 Basalt ROS2 wrapper)
手持随意走动标定 ✅ Basalt(比 Kalibr 对采集要求低)
已有 URDF 模型 ✅ 从 TF 树读取外参

针对 D455 + Ubuntu 22.04 + ROS 2 Humble + OpenVINS 学习

强烈建议先用出厂外参跑通流程 → 看轨迹是否合理 → 再决定是否标定

┌─────────────────────────────────────────────────────┐
│                   推荐学习路径                       │
│                                                     │
│  1. 出厂外参 ──▶ 跑通 OpenVINS ──▶ 观察轨迹          │
│        │                              │             │
│        │                              ▼             │
│        │                      轨迹合理?            │
│        │                      /      \             │
│        │                    是        否            │
│        │                    │          │            │
│        │                    ▼          ▼            │
│        │               继续使用    考虑 Kalibr       │
│        │                          或 Basalt        │
└─────────────────────────────────────────────────────┘

附录:Kalibr Docker 快速上手

如果你后续确实需要 Kalibr,使用 Docker 可避免 ROS 1 环境冲突。

拉取 Docker 镜像

docker pull stereolabs/kalibr:kinetic

准备 AprilGrid 标定板

下载打印文件:https://github.com/ethz-asl/kalibr/wiki/downloads

推荐配置:

# april_6x6.yaml
target_type: 'aprilgrid'
tagCols: 6
tagRows: 6
tagSize: 0.024          # 每个 tag 的边长(米)
tagSpacing: 0.3         # tag 间距比例

录制标定数据

# ROS 2 环境下录制
ros2 bag record /camera/color/image_raw /camera/imu -o calibration_data

# 转换为 ROS 1 bag(Kalibr 需要)
# 使用 rosbags 工具:pip install rosbags
rosbags-convert calibration_data/ --dst calibration_data.bag

运行 Kalibr

docker run -it --rm \
    -v $(pwd):/data \
    stereolabs/kalibr:kinetic \
    rosrun kalibr kalibr_calibrate_imu_camera \
        --bag /data/calibration_data.bag \
        --cam /data/cam_chain.yaml \
        --imu /data/imu.yaml \
        --target /data/april_6x6.yaml

采集技巧

  1. 充分激励所有轴:绕 X、Y、Z 轴各旋转 ±90°
  2. 平稳运动:避免剧烈晃动
  3. 覆盖整个视野:让标定板出现在画面各处
  4. 持续 60-120 秒

🔗 相关链接

  • [[ROS2 Humble + OpenVINS 源码编译]]
  • [[OpenVINS 参数配置详解]]
  • [[Kalibr 完整标定教程]]
  • [[Basalt VIO 系统介绍]]

🔄 更新日志

  • 2025-12-22: 创建初始版本,涵盖 6 种标定替代方案

最后更新: 2025-12-22
维护者: Jesse