RealSense D455 相机-IMU 标定:Kalibr 之外的替代方案
Kalibr 确实是视觉–惯性传感器(如 D455 的相机 + IMU)外参标定的 经典、高精度方案,但它对硬件操作、数据采集和计算要求较高。如果你希望 更简单、更快上手、或适配 ROS 2 / RealSense 特性,本文介绍多种替代路线供你选择。
📋 目录
- 标定基础知识
- 方案 1:使用 RealSense 出厂外参(最简单)
- 方案 2:rs-imu-calibration(IMU 内参校准)
- 方案 3:imu_utils + 手动外参估计
- 方案 4:Basalt 在线标定(进阶替代)
- 方案 5:从 TF 树读取外参
- 路线选择建议
- 附录: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
校准流程
- 将 D455 放置在稳定平面上
- 按提示将设备放置在 6 个不同朝向(±X, ±Y, ±Z)
- 每个朝向保持静止约 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
使用方法
- 采集静态 IMU 数据(约 2 小时)
# 录制 bag
rosbag record /camera/imu -O imu_static.bag
- 分析噪声参数
roslaunch imu_utils d455_imu.launch
rosbag play imu_static.bag
- 输出 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
采集技巧
- 充分激励所有轴:绕 X、Y、Z 轴各旋转 ±90°
- 平稳运动:避免剧烈晃动
- 覆盖整个视野:让标定板出现在画面各处
- 持续 60-120 秒
🔗 相关链接
- [[ROS2 Humble + OpenVINS 源码编译]]
- [[OpenVINS 参数配置详解]]
- [[Kalibr 完整标定教程]]
- [[Basalt VIO 系统介绍]]
🔄 更新日志
- 2025-12-22: 创建初始版本,涵盖 6 种标定替代方案
最后更新: 2025-12-22
维护者: Jesse