传感器标定这个系列写到第三个专题,激光雷达终于要单独拿出来聊聊了。前两篇如果算是在跟相机标定原理较劲,这一篇我们就把精力全部放到激光雷达标定原理和实现上。很多朋友听到“点云标定”就觉得高深,其实它跟相机标定一样,核心就三个问题:测出来的点在哪儿、坐标系怎么对齐、误差怎么量化。搞清楚这三件事,不管是做自动驾驶、机器人导航还是三维重建,雷达到底准不准,心里才会真正有底。
这篇内容适合谁?适合刚接触激光雷达、手里有台雷达但不知道怎么验证精度的工程师,也适合做相机与激光雷达融合、需要先解决外参问题的同学。文章会从激光雷达原理讲起,再聊Ubuntu 20.04下的驱动准备和点云获取,接着给出一套可复现的内参与外参标定思路,最后把我在实际项目中踩过的坑整理成表,方便你直接避让。
1. 标定到底在标什么:先给激光雷达挑毛病
1.1 激光雷达原理速览:卷尺加量角器
我刚开始接触激光雷达的时候,一直觉得点云像照片一样是二维矩阵,后来才发现完全不是这样。绝大多数商用旋转式激光雷达,本质上是“一把卷尺”加“两个量角器”。测距模块测出目标距离 r,再配合水平旋转角 α 和垂直出射角 β,就能算出一个三维点。主流测距方式是飞行时间法,公式很简单:
[ d = \frac{c \cdot t}{2} ]
其中 c 是光速,t 是激光脉冲从发射到被物体反射回来的时间。这个原理跟用卷尺量距离一样,只是尺子换成了光脉冲。
除了 ToF 测距,还有三角测距和 FMCW 调频连续波方式。三角测距多用于近距离低成本雷达,FMCW 则因为能同时测角速度、抗干扰能力强,近年在车载领域很火。但从标定角度看,它们最终输出都是“距离、水平角、垂直角”这一组量。标定的本质,就是在修正这些原始测量量的系统误差。
1.2 从相机标定原理到雷达标定:不是一回事
很多人问我,激光雷达标定能不能照搬相机标定原理?结论是:思路可以借鉴,但内容完全不一样。相机标定原理的核心是内参矩阵 K、畸变系数,包括径向畸变和切向畸变,还有外参矩阵把相机坐标和世界坐标对应起来。标定时用棋盘格提取角点,利用角点在像素平面和世界平面之间的约束来解未知数。
激光雷达没有镜头,没有焦距,也没有像素坐标系。它的“内参”主要是距离补偿、水平角度零位、垂直角度安装偏差、旋转轴倾斜等。这些误差不像镜头畸变那样表现为图像弯曲,而是让点云中的平面变弧、直角变钝、同一个特征在不同角度下厚度不一样。因此雷达标定用的约束条件,通常不是“角点重投影”,而是“点到平面距离最小”或“点云特征与真值特征对齐”。
一句话总结:相机标定要的是“拍出来的图像不变形”,雷达标定要的是“打出来的点不偏、不飘、不厚”。
1.3 雷达误差有哪些:不要全怪驱动
我接触过的雷达标定项目里,很多点云异常并不是驱动问题,而是标定问题。常见的系统误差大致分三类:
- 距离误差:测距芯片线性度不够、光电信号串扰、温度漂移,会导致点云整体变近或变远,严重时形成距离拖尾。
- 角度误差:编码器零位不准、电机旋转抖动、垂直角扫描机构磨损,会让一个本应水平的墙扫出一个斜坡。
- 结构安装误差:雷达外壳与内部扫描面不平行,或者雷达装在车顶时有倾斜,都会带进点云。
这些误差对实际应用的影响很致命。比如一个 100m 远的障碍物,如果水平角度误差只有 0.1°,横向偏差就会达到约 17cm;如果距离误差是 2cm,纵向偏差就是 2cm。在自动驾驶里,这两个量会直接决定刹车判断是否越线,所以标定不是可做可不做的步骤,而是感知链路的前置条件。
2. 标定前必须弄懂的坐标变换和关键参数
2.1 点云坐标是怎么从极坐标变成直角坐标的
要标定激光雷达,首先得知道点云坐标是怎么算出来的。假设雷达返回的是距离 r、水平角 α 和垂直角 θ,在雷达坐标系中转换为三维坐标的公式是:
[ x = r \cdot \cos(\theta) \cdot \cos(\alpha) ] [ y = r \cdot \cos(\theta) \cdot \sin(\alpha) ] [ z = r \cdot \sin(\theta) ]
这里 θ 向上一般为正,有些雷达的垂直角是负值向下扫描,具体要查手册。如果知道了极坐标下的原始量,才能写标定程序,否则你只能把点云当成黑盒去调外参,效率很低。
加上误差项,一个更常见的内参修正模型长这样:
[ x = (r + \Delta r) \cdot \cos(\theta + \Delta \theta) \cdot \cos(\alpha + \Delta \alpha) + t_x ] [ y = (r + \Delta r) \cdot \cos(\theta + \Delta \theta) \cdot \sin(\alpha + \Delta \alpha) + t_y ] [ z = (r + \Delta r) \cdot \sin(\theta + \Delta \theta) + t_z ]
其中 (\Delta r) 是距离补偿量,(\Delta \theta) 和 (\Delta \alpha) 是垂直和水平角偏差,(t_x, t_y, t_z) 表示旋转中心与测距原点之间的不重合量。明白这个模型,你就知道内参标定为什么不是只调一个距离常量那么简单——它是把好几个参数放到一起联合优化。
2.2 Ubuntu 20.04 下让雷达快速转起来
很多朋友卡在第一步:雷达插到电脑上,ROS 下看不到点云。这里给一个通用排查思路,在 Ubuntu 20.04 下实测可用,适用于多数基于 ROS 的激光雷达驱动。
- 确认物理连接方式。机械式雷达很多用网口 UDP 传输,要先设置电脑 IP 到相同网段;串口转 USB 的则要确认设备节点存在。
- 查看设备节点:
ls -l /dev/ttyUSB*,如果没有,检查驱动或换 USB 口。 - 设置权限,把当前用户加入 dialout 组,或者直接
chmod 666 /dev/ttyUSB0。 - 编写 udev 规则固定设备名,避免每次重启设备号变化。
- 启动驱动 launch 文件,在 RViz 中添加 PointCloud2 话题。
# 查看 USB 转串口设备 lsusb | grep -i serial ls -l /dev/ttyUSB* # 临时授权当前用户操作串口 sudo usermod -aG dialout $USER这套操作不是标定本身,但点云都进不来,后面全白搭。我踩过最坑的一次是驱动节点启动成功,但 RViz 里没数据,最后发现是雷达和笔记本 IP 不在一个网段,UDP 包根本没收到。
2.3 标定前需要确认的参数清单
不同雷达的参数差异很大,建议在标定前先把下面这几个参数确认清楚,否则后面的误差分析会跑偏。
| 参数 | 影响 | 验证方法 |
|---|---|---|
| 垂直角分辨率 | 决定点云层数和墙面点密度 | 查看点云中 ring 字段,确认线数是否等于雷达型号 |
| 水平角分辨率 | 决定单圈点数和近处点密度 | 用rostopic hz检查帧率,解析角度增量 |
| 回波模式 | 影响测距偏差和混叠现象 | 对比强反射目标在单回波与双回波下的测距值 |
| 量程与反射率 | 影响远距离点云噪声 | 在不同距离放置漫反射标定板,记录点云标准差 |
举个例子:很多多线雷达点云自带 ring 字段,但你如果不知道它的垂直角分辨率,就没办法判断哪些点是来自同一水平线,平面拟合时也容易把不同层的点混到一起。这个准备工作值得花十分钟,能省下后面反复排查的半天时间。
3. 内参标定:从平面拟合到高精度点云
3.1 为什么平面是最好的标定对象
相机标定原理里,棋盘格提供亚像素角点;激光雷达标定里,平面则是天然的高质量靶标。原因很简单:点云中的点落在平面上时,点到平面距离可以解析计算,你能直接量出“点云压在墙上”的程度。如果点云没有任何畸变和偏移,所有点应该落在同一个平面方程上,点到面距离近似为噪声水平;如果有距离偏差或角度偏差,点会整体前移、后移或让平面变成弧形。
实际中可以使用墙面、地板或大面积标定板。关键要求是材质漫反射强、表面平整度好,不能是镜面、玻璃或深色吸光材质。镜面反射会带来假点,玻璃会把激光打到背后物体上,深色材质测距噪声会明显变大。
3.2 实验场地布置和采集要点
我常用的采集规范是:选择一面至少 3m×3m 的平整墙面,把雷达放在距离墙面约 2m 的位置,先调整雷达大致水平,然后自动旋转采集 10 秒点云。为了覆盖不同距离和入射角,我会把雷达分别放在 1m、3m、5m、8m 处,并在每个位置把雷达小幅度俯仰,让点云扫过墙面的不同区域。
采集时注意三点:
- 保证室内无强红外干扰,不要在太阳直射的窗边做标定。
- 保持场地无人走动,雷达会把人当成动态目标,最好叠加多帧静态数据。
- 关闭驱动里的运动畸变补偿功能,让算法拿到原始测量值,才能标定真实的系统误差。
3.3 基于平面拟合的标定数据处理流程
拿到静态点云后,内参标定第一步是提取平面。我习惯用 PCL 的 RANSAC 分割平面,阈值设在 1cm~2cm 之间。如果目标是地面,可以直接拟合一个平面 (z = ax + by + d),然后统计每个点到平面的残差。
import numpy as np def fit_plane(points): # points: N x 3 centroid = np.mean(points, axis=0) centered = points - centroid _, _, vh = np.linalg.svd(centered) normal = vh[-1] # 最小特征值对应的奇异向量 d = -np.dot(normal, centroid) return normal, d def plane_residual(points, normal, d): return np.abs(points @ normal + d)平面拟合之后,如果平均残差超过 2cm,说明点云里有系统性弯曲,不能只靠平移雷达来消除。此时要回到 2.1 节的修正模型,把 (\Delta r)、(\Delta \theta)、(\Delta \alpha) 作为优化变量,让点到平面的残差平方和最小。这个目标函数没有闭式解,但可以用高斯牛顿法或 L-BFGS 优化,也可以用 Ceres、g2o 等开源工具实现。
在优化前建议先手动粗调:把所有点云往墙面真实位置平移,让平均残差降到厘米内再做迭代,稳定性会好很多。直接拿随机初值起步,很容易陷到局部最优解里。
3.4 距离偏差和角度偏差的联合优化思路
平面拟合看起来简单,但有个问题:距离偏差和角度偏差都可以让点云在法线方向移动。比如一个距离系统偏移和一个小角度旋转,在某些角度下对平面残差的贡献是混叠的。要区分它们,数据里需要有不同距离和不同朝向的平面。同一块墙面在 1m 和 5m 处的点,距离偏差影响会线性增大,角度偏差影响会表现为旋转;用多面墙或多次变换雷达姿态,就能把两类误差解开。
我用的优化目标是让所有点到所属平面的均方距离最小:
[ \min_{\Delta r, \Delta \theta, \Delta \alpha} \sum_{i} \frac{1}{N_i} \sum_{j=1}^{N_i} \left( \hat{n}i \cdot p{ij}(\Delta r,\Delta \theta,\Delta \alpha) + \hat{d}_i \right)^2 ]
其中 (\hat{n}_i) 和 (\hat{d}i) 是第 i 个平面的法线和偏移,(p{ij}) 是修正后的点云。
实际项目里,我一般把优化结果和出厂参数做对比,看偏差是否在合理范围。比如 64 线雷达出厂垂直角精度通常优于 0.01°,如果你标出来的 (\Delta \theta) 是 0.2°,基本可以反推采集过程有问题,或者雷达受过撞击,而不是正常出厂漂移。
4. 外参标定:把激光雷达放进统一坐标系
4.1 外参标定到底解决什么
内参标定完后,雷达测出来的点在自己的雷达坐标系里已经比较准了。但机器人要用它,必须知道雷达在机器人什么位置、以什么姿态安装。这个变换关系就是外参。外参可以理解为雷达坐标系到底盘坐标系的刚体变换,由三维平移向量和三维旋转矩阵组成。
外参不准的典型表现是:雷达点云与相机图像对不齐,目标框和真实物体错位,或者两片点云拼接后出现重影。很多 SLAM 效果差,最后查出来不是算法不好,而是外参有一个 2° 的偏角,跑到 50m 外就成了 1.7m 的横向误差。所以外参标定不是锦上添花,而是融合感知的刚需。
4.2 对应点法求外参:SVD解算原理与代码
当你能拿到同一个特征点在两个坐标系下的坐标时,外参求解就变成一个典型的最小二乘配准问题。假设源点集 (p_i) 是雷达坐标系下的坐标,目标点集 (q_i) 是底盘坐标系下的真值坐标,我们要找旋转 R 和平移 t,使得 (R p_i + t) 与 (q_i) 的差异最小。
SVD 解法的核心是先去平移,将两个点云都中心化,再对互相关矩阵做奇异值分解,得到最优旋转。步骤如下:
- 计算两组点的质心 (\bar{p}) 和 (\bar{q})。
- 去中心化:(p'_i = p_i - \bar{p}),(q'_i = q_i - \bar{q})。
- 计算 (H = \sum p'_i q'_i^T)。
- 对 H 做 SVD:(H = U S V^T)。
- 旋转 (R = V U^T),平移 (t = \bar{q} - R \bar{p})。如果 (det(R) < 0),说明出现反射解,需要手动修复。
import numpy as np def solve_rigid_transform(src, dst): assert src.shape == dst.shape n = src.shape[0] p_bar = np.mean(src, axis=0) q_bar = np.mean(dst, axis=0) p_prime = src - p_bar q_prime = dst - q_bar H = p_prime.T @ q_prime U, _, Vt = np.linalg.svd(H) R = Vt.T @ U.T if np.linalg.det(R) < 0: Vt[-1, :] *= -1 R = Vt.T @ U.T t = q_bar - R @ p_bar return R, t很多人拿这段代码直接标外参,却忽略了输入点对的准确性。如果对应点误差达到 10cm,解出的旋转和平移就会明显偏差,所以使用前一定要可视化手动选点,至少选取 4~6 个非共面特征点。
4.3 相机-雷达联合标定:三种实用方案对比
如果项目需要雷达和相机融合,还要解决相机坐标系与雷达坐标系之间的外参。这里的标定原理和 4.2 类似,不同在于怎么建立对应关系。我整理过三种常用方案,各有适用场景:
| 方案 | 原理 | 精度 | 注意事项 |
|---|---|---|---|
| 目标板角点法 | 用棋盘格或黑白板,从相机图像提取角点,从雷达点云提取板面中心或边缘角点,形成3D-3D对应 | 高 | 雷达角点不如图像准,需要做平面分割后求交 |
| 平面法约束 | 用平面法向量和距离作为约束,构建点到平面残差 | 中高 | 至少需要三个不平行平面,适合带地面场景 |
| 自然场景ICP法 | 初始化外参后,把雷达点云投影到相机生成的三维点云,用ICP精配 | 中 | 依赖初始值,场景重叠度要求高,适合精修 |
用第一种方案时,很多人对相机标定原理已经很熟,知道相机内参要先标好。这里再提醒一句:雷达点云里的“角点”并不是真正的点,它是几个平面的交点。提取时要先对板面做平面分割,再通过板面边缘线求交点,不要直接用回波强度最大值。
4.4 外参标定完成后怎么验证
外参标定完不代表可以结束,必须做闭环验证。我常用的验证方法有四个:
- 重投影误差:把雷达点云通过外参投影到相机图像上,观察是否叠在实物边缘。平均误差小于几个像素,基本可接受。
- 点面残差:把雷达点云变换到底盘坐标,拟合已知地面或墙面,看平均残差是否在标定前的噪声水平。
- 交叉验证:用另一段不参与标定的数据做点云拼接,若障碍物轮廓出现双重边缘,说明外参仍有小角度残差。
- 里程计一致性:机器人走直线,雷达点云中的静止物体应保持不动,若出现横向漂移,多半是外参旋转有偏差。
这个验证动作会帮你发现很多无效标定。我见过不少人标完看一眼 RViz 里对得很齐,第二天实测才发现偏差依然存在,因为 RViz 的显示尺度很容易掩盖毫米级误差。
5. 常见问题与避坑实录
5.1 点云异常排查速查表
这里把我踩过的问题整理成一张表,建议收藏备用:
| 故障现象 | 可能原因 | 解决办法 |
|---|---|---|
| 点云整体偏向一侧 | 外参平移给错,或雷达安装座松动 | 重新测量或做外参标定 |
| 平整墙面出现弧面 | 距离偏移或角度零位偏差 | 用第 3 节流程重新标内参 |
| 点云中有不规则尖刺 | 镜面反射、玻璃、强太阳光 | 过滤回波模式,换测试场地 |
| 点云帧与帧之间漂移 | 运动畸变未补偿或外参受振动影响 | 低速采集,先关闭运动补偿再标定 |
| 驱动启动后无话题 | 网口 IP 或串口权限问题 | 检查网段、udev 规则、驱动日志 |
表格内容都是我在项目里真实遇到过的。点云整体偏向一侧,我一开始总以为是雷达损坏,后来发现是快拆支架换过一次,外参没有重新标。从此团队规范里加了一条:拆装过雷达之后,第一件事永远是重标外参。
5.2 数据采集阶段最容易忽略的三个坑
第一,不要用手扶着雷达采集。手动会带入抖动,相当于给数据叠了一层随机振动。第二,入射角不要太斜。激光以极斜角度入射墙面时,点云噪声会显著增大,所以布置雷达时激光与平面法线的夹角最好不要超过 70°。第三,不要把金属墙面当标定面。金属反光会产生多次回波,测距被拉长或缩短,点云会出现明显拖尾。
还有一个很隐蔽的问题:不同时间采集的数据不要直接合并。室内温度、雷达预热状态都会让测距分布发生几毫米漂移。真要对比,建议在同一次开机、同一温度环境下完成。
5.3 几条实用的标定工作流建议
最后聊几个提高标定效率的习惯,都是我踩过坑换来的。
第一,每次标定开始前先录一段 30 秒的原始 rosbag,标定失败时还能回放,不用重新摆场地。第二,把标定参数版本化,用表格记录日期、雷达序号、外参、RMSE。项目隔三个月后回来找问题,有记录比纯记忆靠谱。第三,标定完成之后拿一个已知尺寸的箱体放到不同距离测一遍点云边长,这是最直观的精度验收方式。
如果雷达是安装在车端或设备端,建议每次撞击、拆装、吊装之后都强制复标。不要嫌麻烦,很多后半夜排查的定位问题,最后都指向外参被带偏。
我再补充一点:做传感器标定,最忌讳“调出来能看就行”。激光雷达标定的价值是给感知一条可量化的精度基线。哪怕你只是做机器人小车,也建议把残差记录在案,然后轻推雷达一毫米,看看点云会不会露出缝隙,你就会明白标定精度对后面的定位和感知有多重要。