3. 传感器标定
说到感知融合,有个绕不开的硬骨头——传感器标定。尤其是相机和激光雷达的外参标定,说白了就是搞清楚:激光雷达扫到的那个点,到底对应图像上的哪个像素。
我刚开始做融合的时候,觉得算法模型才是核心,标定嘛,差不多就行了。结果有一次路测,障碍物框在点云里明明很准,投影到图像上却偏了半个车身。嗯,从那以后,我再也不敢小看标定了。
为什么需要外参标定?
相机和激光雷达,一个拍2D图像,一个扫3D点云。它们各自有各自的坐标系。
- 激光雷达坐标系:以雷达中心为原点,x轴朝前,y轴朝左,z轴朝上。
- 相机坐标系:以相机光心为原点,z轴朝前(沿着光轴),x轴朝右,y轴朝下。
外参标定要做的,就是求出一个旋转矩阵 R 和一个平移向量 t。有了它们,就能把点云从雷达坐标系,变换到相机坐标系下。
核心公式:Pcam = R × Plidar + t
其中 Plidar 是雷达坐标系下的3D点,Pcam 是相机坐标系下的3D点。
你想想看,如果 R 和 t 不准,那点云投影到图像上,就跟鬼影一样,飘来飘去。融合出来的障碍物位置,自然也是错的。
标定的两种主流思路
我个人习惯把标定方法分成两类:目标物标定和无目标物标定。
1. 目标物标定法
这是最传统、也最可靠的方法。你需要一个特制的标定板,通常是棋盘格或者带圆孔的平板。
具体步骤是这样的:
- 采集数据:让车辆停在标定板前,换个角度多拍几组。我建议至少拍10组,覆盖不同距离和角度。
- 提取特征:
- 在图像里,用角点检测找到棋盘格的角点坐标。
- 在点云里,提取标定板平面的点云,拟合出平面方程。
- 建立对应关系:标定板上的同一个物理点,在图像里是像素(u, v),在点云里是3D点(x, y, z)。
- 求解外参:用PnP(Perspective-n-Point)算法,或者直接优化重投影误差,解出 R 和 t。
我的小技巧:标定板最好用哑光材质,别反光。我曾经用了一块亮面亚克力板,激光雷达打上去全是噪点,拟合出来的平面歪了5度,折腾了一下午才发现是板子的问题。
2. 无目标物标定法
如果场景里不方便放标定板,比如在高速公路上,那就得靠自然特征了。
核心思路是:找场景中共同的边缘或平面。比如路沿、车道线、建筑物墙面。
- 在图像里,用边缘检测提取这些特征。
- 在点云里,用RANSAC拟合直线或平面。
- 然后最小化两者之间的投影误差,迭代优化外参。
这种方法的好处是灵活,但精度不如目标物标定。我一般用它做在线微调,也就是车辆跑起来之后,实时修正外参的漂移。
标定精度的衡量标准
标定完了,怎么知道好不好?我一般看两个指标:
| 指标 | 说明 | 我常用的阈值 |
|---|---|---|
| 重投影误差 | 点云投影到图像后,与真实像素位置的偏差 | < 2 像素 |
| 距离误差 | 在3D空间中,标定板角点的测量值与真值的偏差 | < 5 cm(10米内) |
如果重投影误差超过3个像素,我建议重新标定。别凑合,凑合出来的数据,后面做目标关联时全是坑。
一个简单的标定流程示意图
下面这张图,是我自己画的外参标定核心流程。你可以看到,数据采集、特征提取、求解优化,这三步是闭环的。
避坑指南
做标定这么多年,踩过的坑不少。我挑几个最常见的说说:
坑一:标定板太小
我曾经用了一块A4纸大小的棋盘格,结果激光雷达在10米外只能扫到几个点,平面拟合的方差大得离谱。后来换了1.2m×0.9m的大板子,精度一下就上来了。
建议:标定板尺寸至少占激光雷达视场角的1/3以上。
坑二:忽略时间同步
相机和激光雷达的采集时间如果不一致,车辆稍微一晃,点云和图像就对不上了。我见过有人标定结果很好,但一上路就飘,查了半天发现是时间戳没对齐。
建议:用硬件触发同步,或者至少做软件插值对齐。
坑三:初始值给得太差
PnP算法对初始值敏感。如果你随便给个R和t,优化很容易掉进局部最优。我习惯先用粗略的手动测量给个初值,再让算法去微调。
代码片段:重投影验证
标定完了,我习惯写一小段代码,把点云投影到图像上,肉眼看一下对齐效果。下面是个简单的Python示例:
import numpy as np
import cv2
def project_lidar_to_image(points_3d, R, t, camera_matrix, dist_coeffs):
"""
将激光雷达点云投影到图像上
:param points_3d: (N, 3) 雷达坐标系下的点
:param R: (3, 3) 旋转矩阵
:param t: (3, 1) 平移向量
:param camera_matrix: (3, 3) 相机内参
:param dist_coeffs: 畸变系数
:return: (N, 2) 像素坐标
"""
# 1. 变换到相机坐标系
points_cam = (R @ points_3d.T + t).T
# 2. 只保留相机前方的点(z > 0)
mask = points_cam[:, 2] > 0
points_cam = points_cam[mask]
# 3. 投影到像素平面
points_2d, _ = cv2.projectPoints(
points_cam,
np.zeros((3,1)), # 已经变换过了,这里不再旋转
np.zeros((3,1)),
camera_matrix,
dist_coeffs
)
return points_2d.reshape(-1, 2), mask
跑完这段代码,把投影点画在图像上。如果标定准确,你会看到点云轮廓和图像里的物体边缘完美贴合。如果偏差超过2个像素,嗯,回去检查标定数据吧。
外参标定这件事,看起来是个预处理步骤,但它决定了整个感知融合系统的上限。标定做不好,后面的目标关联、跟踪、预测,全是空中楼阁。我个人建议,每次传感器拆装之后,都重新做一次标定,别偷懒。
可可精品资料库