4、传感器空间标定:内参标定与外参标定

说到多传感器融合,有个绕不开的硬骨头——空间标定。说白了,就是让不同传感器知道彼此在哪儿、长什么样。我见过不少项目,算法模型调得漂漂亮亮,一上实车就崩,查到最后往往是标定没做好。嗯,这活儿看着基础,但真踩过坑的人都知道,它决定了整个系统的天花板。

4.1 内参标定:让传感器认识自己

内参标定,是让传感器搞清楚「我是谁」。每个传感器出厂时都有个理论参数,但实际装好后,镜头畸变、安装公差、温度漂移都会让理论值失效。我习惯把内参标定比作「给传感器配眼镜」——度数不对,看啥都是花的。

4.1.1 相机内参标定

相机内参包括焦距、主点坐标、畸变系数。常用的方法是张正友标定法,用棋盘格拍个十几张照片就行。我个人习惯用 OpenCV 的 calibrateCamera 函数,但要注意几点:

  • 棋盘格要贴平:贴在硬质板上,别用软布。我曾经用亚克力板贴棋盘格,结果反光太强,角点检测全废了。
  • 角度要丰富:前后左右、倾斜旋转,覆盖整个视场。只拍正面的,标定出来的畸变参数会偏。
  • 图片数量:15-20张就够了,太多反而引入冗余噪声。

核心代码片段(Python + OpenCV):

import cv2
import numpy as np

# 准备棋盘格角点坐标
pattern_size = (9, 6)  # 内角点数量
objp = np.zeros((pattern_size[0]*pattern_size[1], 3), np.float32)
objp[:, :2] = np.mgrid[0:pattern_size[0], 0:pattern_size[1]].T.reshape(-1, 2)

# 收集所有图片的角点
objpoints = []  # 世界坐标系中的点
imgpoints = []  # 图像坐标系中的点

for fname in image_files:
    img = cv2.imread(fname)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    ret, corners = cv2.findChessboardCorners(gray, pattern_size, None)
    if ret:
        objpoints.append(objp)
        imgpoints.append(corners)

# 标定
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(
    objpoints, imgpoints, gray.shape[::-1], None, None
)

小技巧:标定完成后,一定要做重投影误差检查。误差小于0.5像素算合格,超过1像素就得重新拍了。我一般会画个误差分布直方图,一眼就能看出哪些图片拖后腿。

4.1.2 IMU内参标定

IMU的内参包括零偏、尺度因子、轴间对准误差。这东西比相机麻烦,因为IMU的误差会随时间漂移。我记得第一次标定IMU时,以为跟相机一样拍几张图就行,结果数据全是乱的。

IMU标定通常用六面法或转台法:

  • 六面法:把IMU静止放在六个正交方向上,采集加速度计和陀螺仪数据。每个方向至少保持30秒。
  • 转台法:用高精度转台提供已知角速度,反推陀螺仪误差。精度高,但设备贵。

我个人在项目里常用 Allan 方差分析来评估IMU噪声特性。它能帮你区分出量化噪声、角度随机游走、速率随机游走、零偏不稳定性等。说白了,就是告诉你这个IMU到底有多「晃」。

注意:IMU标定对环境温度极其敏感。我曾经在空调房里标定完,拿到室外40度的停车场,零偏直接翻了一倍。所以,有条件的话要在目标温度下标定,或者做温度补偿模型。

4.1.3 激光雷达内参标定

激光雷达的内参主要是激光发射器与接收器之间的对准误差、测距偏移、角度偏移。对于机械式雷达,每个激光器都有自己的参数,需要逐个标定。

常用的方法是:

  • 平面法:用一块已知平面的标定板,让雷达扫描。理想情况下,所有点应该落在同一个平面上。实际会有偏差,通过拟合平面反推每个激光器的角度和距离误差。
  • 球体法:用标准球体,通过点云拟合球心位置,评估测距精度。

我遇到过最头疼的问题,是雷达的「鬼影点」——明明前面是空地,却扫出一堆点。后来发现是某个激光器的发射功率衰减了,导致信噪比下降。嗯,内参标定不只是算参数,还得会诊断传感器健康状态。

4.2 外参标定:让传感器认识彼此

外参标定,是让不同传感器搞清楚「你在哪儿、我在哪儿」。说白了,就是求一个旋转矩阵和平移向量,把不同坐标系下的数据对齐到同一个坐标系里。

4.2.1 Camera-LiDAR 外参标定

这是最常用的外参标定之一。相机看到的是像素,雷达看到的是点云,两者要融合,必须先对齐。

常用方法:

  • 棋盘格法:在棋盘格上,相机能检测到角点,雷达能检测到棋盘格平面上的点云。通过角点和平面点的对应关系,求解外参。
  • 边缘对齐法:利用场景中的边缘特征(比如墙角、柱子),让相机图像中的边缘和雷达点云中的边缘对齐。

我推荐用棋盘格法,因为它稳定、可重复。具体步骤:

  1. 放置棋盘格标定板,同时采集图像和点云。
  2. 从图像中提取棋盘格角点,得到像素坐标。
  3. 从点云中提取棋盘格平面上的点,拟合平面方程。
  4. 构建约束方程,求解旋转矩阵 R 和平移向量 t。

核心思路(数学表达):

对于棋盘格上的每个角点 P_w(世界坐标):
  相机投影:p_cam = K * [R_cam | t_cam] * P_w
  雷达投影:p_lidar = [R_lidar | t_lidar] * P_w
  
  外参关系:p_cam = K * [R_ext | t_ext] * p_lidar
  
  其中 [R_ext | t_ext] 就是我们要标定的 Camera-LiDAR 外参。

避坑指南:我曾经用一块小棋盘格标定,结果雷达点云在棋盘格上只有几十个点,平面拟合误差很大。后来换成1.2米×0.9米的大标定板,点云密度上去了,标定精度明显提升。标定板尺寸建议覆盖雷达视场的30%以上。

4.2.2 Camera-IMU 外参标定

Camera-IMU 外参标定,本质上是求相机坐标系和IMU坐标系之间的刚体变换。这个标定通常和联合标定一起做,因为IMU的角速度和加速度可以辅助相机做运动估计。

常用工具:

  • Kalibr:ETH Zurich 开源的标定工具,支持多相机、多IMU的联合标定。我用了好几年,很成熟。
  • 联合标定流程:让设备做丰富的运动(平移、旋转、急停),同时采集相机图像和IMU数据。Kalibr 会通过优化方法,同时估计相机内参、IMU内参、以及两者之间的外参。

我个人习惯在标定前先做一次「预对齐」——用手大致测量一下相机和IMU的安装位置,给优化算法一个好的初值。否则,算法可能收敛到局部最优,标定结果看着合理,实际用起来却对不上。

4.2.3 LiDAR-IMU 外参标定

LiDAR-IMU 外参标定,通常和 SLAM 系统绑定在一起。因为IMU可以提供高频的姿态信息,帮助雷达点云做运动畸变矫正。

标定方法:

  • 基于运动约束:让设备做旋转运动,雷达点云中的静止物体应该形成闭合的环。如果外参不准,环会断开。通过最小化闭合误差,反推外参。
  • 基于平面约束:用标定板或地面,同时利用IMU的重力方向约束,求解外参。

我记得有一次,LiDAR-IMU 外参标定结果总是跳变,排查了两天才发现是IMU的采样时间戳和雷达的时间戳没对齐。时间同步没做好,外参标定就是空中楼阁。所以,做外参标定之前,一定先确认时间同步精度。

4.3 标定板设计与自动化标定流程

标定板是标定的「尺子」,设计得好不好,直接影响标定精度和效率。

4.3.1 标定板设计要点

  • 材质:亚克力板或铝板,表面哑光处理,避免反光。我见过有人用打印纸贴泡沫板,结果一碰就弯,标定数据全废。
  • 图案:棋盘格或圆点阵。棋盘格适合相机标定,圆点阵适合雷达标定(因为雷达点云中圆点更容易识别)。
  • 尺寸:根据传感器视场和分辨率决定。对于激光雷达,标定板边长至少是雷达角分辨率的100倍以上。
  • 多模态兼容:如果要做 Camera-LiDAR 联合标定,标定板最好同时被两种传感器清晰识别。我常用黑白棋盘格,雷达能扫到黑白交界处的边缘,相机能检测到角点。

推荐标定板参数(参考):

传感器组合 标定板类型 推荐尺寸 图案
单相机 棋盘格 A3纸大小 9×6 内角点
Camera-LiDAR 棋盘格(大尺寸) 1.2m×0.9m 12×9 内角点
多相机 圆点阵 根据视场角定制 对称或非对称圆点

4.3.2 自动化标定流程

手动标定太累了,而且容易引入人为误差。我建议搭建自动化标定流程,把重复劳动交给机器。

一个典型的自动化标定流程包括:

  1. 数据采集:自动控制标定板或传感器运动,采集多组数据。可以用机械臂或转台,也可以用人工手持但配合脚本触发。
  2. 特征提取:自动检测图像中的角点、点云中的平面、IMU中的静止区间。
  3. 初值估计:用闭式解或PnP方法快速估计外参初值。
  4. 非线性优化:用Levenberg-Marquardt或高斯-牛顿法,最小化重投影误差或点面距离。
  5. 结果验证:用独立的验证集评估标定精度,输出残差统计。

我的经验:自动化流程中最容易出问题的是「异常数据剔除」。我曾经写了个全自动标定脚本,结果有一次标定板被风吹歪了,算法还傻乎乎地往里算,最后标定结果偏了5度。后来我加了个残差阈值判断,超过3倍中位数的数据直接扔掉,效果好了很多。

嗯,空间标定这块内容,说难不难,说简单也不简单。关键是要理解每个参数背后的物理意义,以及它们在实际系统中会怎么影响融合效果。我见过太多人拿着工具跑一遍标定,看到「success」就以为完事了,结果融合出来的数据一塌糊涂。记住:标定不是一次性工作,传感器会老化、安装会松动、温度会变化,定期复标是保证系统长期稳定运行的关键。