7.3 标定数据
每台设备出厂时都单独标定——相机内参、双目外参与相机–IMU 对齐。你这边不需要做标定,也没有标定板要拍:录制开箱即可用于度量。
每段录制都在 MCAP 内部携带该标定,以约 1 Hz 在五个 topic 上重复发布。这些值在一段录制内恒定——读每个 topic 的第一条消息即可,忽略后面的重复(它们是为了让中途加入的实时订阅者也能收到)。
本页明确说明这些值是什么、其中哪些是我们的标定真正测量的、哪些是根本没有标定的固定默认值,以及 SLAM / VIO 管线应如何使用它们。
标定 topic
| Topic | Schema | 内容 |
|---|---|---|
/ego/camera/0/intrinsics | foxglove.CameraCalibration | 相机 0 内参 + 畸变 |
/ego/camera/1/intrinsics | foxglove.CameraCalibration | 相机 1 内参 + 畸变 |
/ego/camera/1/extrinsics | foxglove.FrameTransform | cam1 在 cam0 坐标系下的位姿 |
/ego/imu/0/extrinsics | foxglove.FrameTransform | imu0 在 cam0 坐标系下的位姿 |
/ego/imu/0/info | visio_schema.v1.calibration.ImuCalibration | 相机–IMU 时间偏移 + IMU 名义默认值 |
所有空间标定都以 cam0(右目)为锚点:每个 FrameTransform 给出该传感器坐标系在 cam0 坐标系下的位姿(平移单位为米,旋转四元数按 x, y, z, w 顺序)。
我们的标定测量了什么(逐台)
EGO 标定是相机与相机–IMU 标定(kalibr,AprilGrid 标定板),逐台产出以下值:
- 相机内参——
kannala_brandt(鱼眼)模型:K中的焦距fx, fy与主点cx, cy,D中的四个畸变系数k1..k4。R为单位阵,P为[K|0]——图像未做去畸变,请自行套用鱼眼模型。 - 双目外参(
/ego/camera/1/extrinsics)——cam1在cam0下的位姿。基线约 63 mm,几乎纯 X 方向,相对旋转在一度以内。 - 相机–IMU 外参(
/ego/imu/0/extrinsics)——imu0在cam0下的位姿。IMU 相对cam0大约绕 Y 轴旋转了 180° 安装;不要假设两者坐标轴对齐。 - 相机–IMU 时间偏移(
/ego/imu/0/info中的time_offset_to_cam0_s)——按t_imu = t_cam0 + offset理解。通常为几十毫秒;把 IMU 样本与视频帧关联时要套用。
两款是两台不同的相机
kannala_brandt 模型对两款都适用——它对两种镜头都是正确的模型,读取内参的方式没有逐款差异。变的是数值,而且差很多:EGO1 覆盖 120° × 70°,EGO1GS
覆盖 157° × 84°。视场角宽这么多,意味着明显更短的焦距和不同的畸变曲线。
请从每段录制里读出 fx, fy, cx, cy 与 k1..k4。 混合两款的数据集绝不要写死一组——EGO1 的内参对 EGO1GS 的帧重投影会离谱地错。IMU 也不同:/ego/imu/0/info
与外参是逐台的,但采样率是机型的属性(EGO1 200 Hz,EGO1GS 400 Hz),写死 IMU 周期的管线对其中一款是错的。见 EGO1/EGO1GS。
什么没有标定
IMU 内参——尺度、零偏、失准——以及 IMU 噪声参数不属于 EGO 标定。 没有对它们做逐台测量。ImuCalibration schema 有这些字段,但在每段录制里它们都是固定默认值,/ego/imu/0/info
上唯一标定过的量是 time_offset_to_cam0_s:
accel_scale_* = 1.0、gyro_scale_* = 1.0(单位)、零偏字段 = 0、失准字段为空——常量,每台相同。不要理解为「这台设备被测过且完美」;它们的意思是从未测量过。原始 IMU 流依赖传感器的出厂微调(尺度 / 失准误差在百分之一以下),且没有零偏修正——下文说明为什么这是有意的设计。- 噪声密度 / 随机游走(
accel_noise_density、gyro_noise_density……)——该传感器系列的数据手册级数值,每台相同。可作为估计器噪声模型的初值,但不是逐台的 Allan 方差测量。
schema 的语义是 corrected = scale * (raw - bias);用出厂的单位值这是个空操作,所以 /ego/imu/0/raw 就是传感器的直接输出(仅做了到机体坐标系的轴映射)。
这对你的管线意味着什么
在线估计 IMU 零偏——不要指望文件里有。 陀螺与加速度计零偏每次上电都不同,还随温度漂移,任何存储的常量在运行时都不可能正确。所有标准 VIO / SLAM 估计器(OpenVINS、VINS-Mono/Fusion、ORB-SLAM3……)都把零偏放在状态向量中,从原始测量持续估计——把
/ego/imu/0/raw 喂给它们即可。相比之下尺度与失准在设备寿命内稳定,且由传感器出厂微调覆盖;假设为单位值(如出厂默认)是标准做法。
SLAM 用原始 IMU 流,不要用四元数。 /ego/imu/0/quat 是机内融合的姿态——适合可视化、监控和初始化,但它只有姿态(没有平移或尺度信息),且其误差是滤波过的、不是白噪声,这违反了紧耦合估计器假设的噪声模型。SLAM
应当消费的是 /ego/imu/0/raw。
原始 IMU 的正常表现(不是缺陷)
因为原始流是真正的原始数据,下列现象正常,不应作为标定或漂移故障上报:
- 静止时的陀螺零偏。 每轴最多约 ±1 °/s 的恒定偏移,随每次上电与温度变化。不处理零偏直接积分原始陀螺会以同样的速率漂移(例如 0.3 °/s 的零偏在 10 s 内累积约 3°)。这是开环航位推算的属性,不是设备的——机内融合以及任何正确的 SLAM 估计器都会在线去除零偏。
- 加速度计的重力读数。 静止的加速度计读到约 9.8 m/s² 的支撑力,按设备倾角分布到三个轴上——单轴读到
9.8 · cos(tilt),例如某轴 9.57 m/s² 只是说明该轴偏离竖直约 12°。健康检查看静止段的向量模sqrt(x² + y² + z²):应为当地重力(按纬度 9.78–9.83 m/s²),未标定的逐轴零偏在 ±0.3 m/s² 以内。
用 Python 读取标定
from mcap.reader import make_reader
from mcap_protobuf.decoder import DecoderFactory
TOPICS = [
"/ego/camera/0/intrinsics", "/ego/camera/1/intrinsics",
"/ego/camera/1/extrinsics", "/ego/imu/0/extrinsics", "/ego/imu/0/info",
]
calibration = {}
with open("recording.mcap", "rb") as f:
reader = make_reader(f, decoder_factories=[DecoderFactory()])
for _, channel, _, message in reader.iter_decoded_messages(topics=TOPICS):
calibration.setdefault(channel.topic, message) # 值会重复;第一条就够
if len(calibration) == len(TOPICS):
break
info = calibration["/ego/imu/0/info"]
print("cam-imu time offset:", info.time_offset_to_cam0_s, "s")
下一步
- 7.2 时间戳与 Unix 时间——关联各数据流时的时钟语义。
- 7.4 频道与网络速查——完整的 topic 表。