跳到主要内容

7.3 标定数据

每台设备出厂时都单独标定——相机内参、双目外参与相机–IMU 对齐。你这边不需要做标定,也没有标定板要拍:录制开箱即可用于度量。

每段录制都在 MCAP 内部携带该标定,以约 1 Hz 在五个 topic 上重复发布。这些值在一段录制内恒定——读每个 topic 的第一条消息即可,忽略后面的重复(它们是为了让中途加入的实时订阅者也能收到)。

本页明确说明这些值是什么、其中哪些是我们的标定真正测量的、哪些是根本没有标定的固定默认值,以及 SLAM / VIO 管线应如何使用它们。

标定 topic​

TopicSchema内容
/ego/camera/0/intrinsicsfoxglove.CameraCalibration相机 0 内参 + 畸变
/ego/camera/1/intrinsicsfoxglove.CameraCalibration相机 1 内参 + 畸变
/ego/camera/1/extrinsicsfoxglove.FrameTransformcam1 在 cam0 坐标系下的位姿
/ego/imu/0/extrinsicsfoxglove.FrameTransformimu0 在 cam0 坐标系下的位姿
/ego/imu/0/infovisio_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")

下一步​