2. 图像基础与坐标系

做车道线感知,说白了就是解决一个问题:怎么让车看懂路。

但摄像头拍到的是一张二维图片,而车道线是三维世界里的物理标记。从二维到三维,中间隔着一整套坐标系变换。我刚开始接触这部分时,也觉得坐标系来回倒腾很烦。但后来发现,坐标系就是自动驾驶感知的“通用语言”——不懂它,你连标定数据都看不懂。

这一节,我们就把这几种坐标系和它们之间的变换关系彻底讲透。

2.1 四大坐标系:从像素到世界

先记住一句话:所有坐标变换,都是为了把图像上的一个点,映射回真实世界中的位置。

这中间要经过四个坐标系:

  • 像素坐标系:图像左上角为原点,单位是像素 (u, v)
  • 图像坐标系:图像中心为原点,单位是毫米 (x, y)
  • 相机坐标系:相机光心为原点,单位是米 (Xc, Yc, Zc)
  • 世界坐标系:自定义参考系,单位是米 (Xw, Yw, Zw)

嗯,这里要注意:像素坐标系和图像坐标系都在图像平面上,但原点不同、单位不同。很多初学者把这两个搞混,标定出来的参数全是错的。

核心关系图:

像素坐标系 (u,v) 图像坐标系 (x,y) 相机坐标系 (Xc,Yc,Zc) 世界坐标系 (Xw,Yw,Zw) 内参 内参 外参 从像素到世界:像素坐标 → 图像坐标 → 相机坐标 → 世界坐标 从世界到像素:世界坐标 → 相机坐标 → 图像坐标 → 像素坐标

2.2 像素坐标系 → 图像坐标系

这是第一步,也是最简单的一步。

像素坐标系的原点在左上角,u轴向右,v轴向下。图像坐标系的原点在图像中心,x轴向右,y轴向上。

转换公式很简单:

x = (u - cx) * dx
y = (v - cy) * dy

其中 (cx, cy) 是图像中心在像素坐标系下的坐标,dx、dy 是每个像素的物理尺寸(单位:mm/pixel)。

我的经验: 实际项目中,dx 和 dy 通常从相机 datasheet 里查,或者通过标定得到。我遇到过有人直接用 1.0 代替,结果投影出来的车道线位置偏了半个车身——嗯,这种坑踩过一次就记住了。

2.3 图像坐标系 → 相机坐标系

这一步用到了小孔成像模型。

假设相机光心在 (0,0,0),图像平面在 Z = f 处(f 是焦距)。那么图像上的点 (x, y) 对应相机坐标系下的射线:

Xc = x * Zc / f
Yc = y * Zc / f
Zc = 深度值(未知)

这里有个关键问题:单目相机无法直接得到 Zc。所以从图像坐标到相机坐标,其实是一条射线,而不是一个点。

那车道线感知怎么解决?

我们通常假设车道线在地面平面上。结合地面方程(Zc = 某个值),就能解出具体的 3D 坐标。

注意: 地面假设在平坦路面上成立。遇到坡道或颠簸路面,这个假设会引入误差。我曾经在高速上坡路段测试,车道线投影偏差达到了 30 厘米——后来加了 IMU 数据做补偿才解决。

2.4 相机坐标系 → 世界坐标系

这一步用外参矩阵。

外参矩阵描述了相机在世界坐标系中的位置和朝向。它由旋转矩阵 R(3x3)和平移向量 t(3x1)组成:

[Xw]     [R    t] [Xc]
[Yw]  =  [      ] [Yc]
[Zw]     [0 0 0 1] [Zc]
[1 ]               [1 ]

说白了,就是把相机坐标系下的点,先旋转、再平移,映射到世界坐标系。

我个人习惯把外参矩阵理解为“相机在世界中的位姿”。你想想看,如果相机装在车顶,外参矩阵就描述了相机相对于车体坐标系的安装位置和角度。

2.5 内参矩阵:把三维投影到二维

内参矩阵 K 是 3x3 的矩阵:

K = [fx  0  cx]
    [0  fy  cy]
    [0   0   1]

其中 fx、fy 是焦距在像素单位下的值,cx、cy 是图像中心坐标。

内参矩阵的作用:把相机坐标系下的 3D 点投影到像素坐标系。

u = fx * Xc / Zc + cx
v = fy * Yc / Zc + cy

这个公式在车道线感知中太常用了。比如你检测到一条车道线,想把它投影到图像上显示,就用这个公式。

避坑指南: 我曾经在标定内参时,用了错误的棋盘格尺寸,结果 fx、fy 全偏了。后来发现,标定板的格子尺寸必须精确到毫米级,差 1 毫米都不行。

2.6 畸变模型与校正

现实中的相机镜头不是完美的。光线经过镜头时会发生弯曲,导致图像边缘出现畸变。

畸变主要分两种:

  • 径向畸变:光线弯曲程度与距离中心的半径有关。表现为“桶形畸变”或“枕形畸变”
  • 切向畸变:镜头与成像平面不平行导致。表现为“梯形畸变”

畸变模型用多项式描述:

x_distorted = x * (1 + k1*r^2 + k2*r^4 + k3*r^6) + 2*p1*x*y + p2*(r^2 + 2*x^2)
y_distorted = y * (1 + k1*r^2 + k2*r^4 + k3*r^6) + p1*(r^2 + 2*y^2) + 2*p2*x*y

其中 k1、k2、k3 是径向畸变系数,p1、p2 是切向畸变系数,r^2 = x^2 + y^2。

校正过程就是:

  1. 对每个像素 (u, v),计算对应的归一化坐标 (x, y)
  2. 用畸变模型计算畸变后的坐标 (x_distorted, y_distorted)
  3. 再映射回像素坐标,得到校正后的图像

我的建议: 车道线感知中,畸变校正一定要做。尤其是鱼眼相机,边缘畸变非常严重。我见过一个项目,没做畸变校正,结果车道线在图像边缘直接弯成了弧形——这要是用来控制方向盘,车就画龙了。

2.7 完整变换流程:从像素到世界

把上面所有步骤串起来,从像素坐标到世界坐标的完整流程是:

1. 像素坐标 (u, v) → 图像坐标 (x, y)     [用内参 cx, cy, dx, dy]
2. 图像坐标 (x, y) → 相机坐标 (Xc, Yc, Zc)  [用小孔模型 + 深度假设]
3. 相机坐标 (Xc, Yc, Zc) → 世界坐标 (Xw, Yw, Zw)  [用外参 R, t]

反过来,从世界坐标到像素坐标:

1. 世界坐标 (Xw, Yw, Zw) → 相机坐标 (Xc, Yc, Zc)  [用外参的逆]
2. 相机坐标 (Xc, Yc, Zc) → 图像坐标 (x, y)     [用内参的逆]
3. 图像坐标 (x, y) → 像素坐标 (u, v)        [用内参 cx, cy, dx, dy]

你想想看,整个流程其实就是矩阵乘法的串联。在实际代码中,我们通常把内参和外参合并成一个 3x4 的投影矩阵 P:

P = K * [R | t]

这样一步就能把世界坐标投影到像素坐标。

总结一下:

  • 四大坐标系:像素 → 图像 → 相机 → 世界
  • 内参矩阵:描述相机本身的属性(焦距、中心、畸变)
  • 外参矩阵:描述相机在世界中的位姿(旋转、平移)
  • 畸变校正:消除镜头带来的图像变形
  • 完整流程:矩阵乘法串联,一步到位

这些坐标系和矩阵,是车道线感知的数学基础。后面讲车道线检测、跟踪、融合,每一步都离不开它们。我个人建议你把这一节的内容亲手推导一遍,写代码实现一下——只有亲手做过,才能真正理解。

最后提醒: 标定参数不是一成不变的。相机受到震动、温度变化,内参可能会漂移。我建议每 3-6 个月重新标定一次,或者加入在线标定模块实时更新。

可可精品资料库