3. 传感器标定

说到感知融合,有个绕不开的硬骨头——传感器标定。尤其是相机和激光雷达的外参标定,说白了就是搞清楚:激光雷达扫到的那个点,到底对应图像上的哪个像素。

我刚开始做融合的时候,觉得算法模型才是核心,标定嘛,差不多就行了。结果有一次路测,障碍物框在点云里明明很准,投影到图像上却偏了半个车身。嗯,从那以后,我再也不敢小看标定了。

为什么需要外参标定?

相机和激光雷达,一个拍2D图像,一个扫3D点云。它们各自有各自的坐标系。

  • 激光雷达坐标系:以雷达中心为原点,x轴朝前,y轴朝左,z轴朝上。
  • 相机坐标系:以相机光心为原点,z轴朝前(沿着光轴),x轴朝右,y轴朝下。

外参标定要做的,就是求出一个旋转矩阵 R 和一个平移向量 t。有了它们,就能把点云从雷达坐标系,变换到相机坐标系下。

核心公式:Pcam = R × Plidar + t

其中 Plidar 是雷达坐标系下的3D点,Pcam 是相机坐标系下的3D点。

你想想看,如果 R 和 t 不准,那点云投影到图像上,就跟鬼影一样,飘来飘去。融合出来的障碍物位置,自然也是错的。

标定的两种主流思路

我个人习惯把标定方法分成两类:目标物标定和无目标物标定。

1. 目标物标定法

这是最传统、也最可靠的方法。你需要一个特制的标定板,通常是棋盘格或者带圆孔的平板。

具体步骤是这样的:

  1. 采集数据:让车辆停在标定板前,换个角度多拍几组。我建议至少拍10组,覆盖不同距离和角度。
  2. 提取特征:
    • 在图像里,用角点检测找到棋盘格的角点坐标。
    • 在点云里,提取标定板平面的点云,拟合出平面方程。
  3. 建立对应关系:标定板上的同一个物理点,在图像里是像素(u, v),在点云里是3D点(x, y, z)。
  4. 求解外参:用PnP(Perspective-n-Point)算法,或者直接优化重投影误差,解出 R 和 t。

我的小技巧:标定板最好用哑光材质,别反光。我曾经用了一块亮面亚克力板,激光雷达打上去全是噪点,拟合出来的平面歪了5度,折腾了一下午才发现是板子的问题。

2. 无目标物标定法

如果场景里不方便放标定板,比如在高速公路上,那就得靠自然特征了。

核心思路是:找场景中共同的边缘或平面。比如路沿、车道线、建筑物墙面。

  • 在图像里,用边缘检测提取这些特征。
  • 在点云里,用RANSAC拟合直线或平面。
  • 然后最小化两者之间的投影误差,迭代优化外参。

这种方法的好处是灵活,但精度不如目标物标定。我一般用它做在线微调,也就是车辆跑起来之后,实时修正外参的漂移。

标定精度的衡量标准

标定完了,怎么知道好不好?我一般看两个指标:

指标 说明 我常用的阈值
重投影误差 点云投影到图像后,与真实像素位置的偏差 < 2 像素
距离误差 在3D空间中,标定板角点的测量值与真值的偏差 < 5 cm(10米内)

如果重投影误差超过3个像素,我建议重新标定。别凑合,凑合出来的数据,后面做目标关联时全是坑。

一个简单的标定流程示意图

下面这张图,是我自己画的外参标定核心流程。你可以看到,数据采集、特征提取、求解优化,这三步是闭环的。

相机与激光雷达外参标定流程 ① 数据采集 多角度拍摄标定板 ② 特征提取 图像角点 + 点云平面 ③ 求解外参 PnP / 优化重投影 ④ 精度验证 重投影误差 < 2px 不满足精度 → 重新采集 输出:R, t 矩阵

避坑指南

做标定这么多年,踩过的坑不少。我挑几个最常见的说说:

坑一:标定板太小

我曾经用了一块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个像素,嗯,回去检查标定数据吧。

外参标定这件事,看起来是个预处理步骤,但它决定了整个感知融合系统的上限。标定做不好,后面的目标关联、跟踪、预测,全是空中楼阁。我个人建议,每次传感器拆装之后,都重新做一次标定,别偷懒。

可可精品资料库