10内外参与投影

本页路线图(由浅入深)
  1. 三类参数(一 ~ 六),内参 · 外参 · 内外参的相对性 · 时间参数
  2. 什么时候会做错(七 ~ 八),敏感性分析 · 诊断决策树
  3. 怎么标定(九 ~ 十二),交互演示 · 单张照片的局限 · 标定顺序 · 总结
进阶 · 几何层含 2 个实时演示

一、一句话区分:内参与外参

先看一个具体画面。

点云里每个点自带 (x, y, z):
    点云说「前方 12 m 处有个箱子」

同一个箱子在照片上是某个像素 (u, v):
    图像说「画面右侧那一小片像素是箱子」

要让这两句话对上,需要两组数字:
    相机自己怎么把光线变成像素      → 内参
    相机相对 LiDAR 在哪、朝哪        → 外参

把这两组数字写成定义:

核心定义
内参 (intrinsics) = 传感器【自身】的映射关系
                    自己的坐标系  →  自己的输出

外参 (extrinsics) = 传感器【之间 / 与世界】的空间关系
                    自己的坐标系  →  别人的坐标系

判断方法:

为什么要先分清这两个词

后面的诊断、标定顺序、验收标准全都建立在这个分类上:内参错了表现为 "中心对得上、边缘对不上",旋转外参错了表现为"整幅图均匀错位"(见第七、八节)。

二、相机内参

相机的内参描述的是一件事:一条从相机原点出发的光线,落在像平面上的哪个像素? 这条映射由 4 个参数加一组畸变系数确定:

        ⌊ fx   0   cx ⌋
    K = |  0  fy   cy |         + 畸变系数 (k1 k2 p1 p2 k3 …)
        ⌊  0   0    1 ⌋
参数物理含义
fx = 焦距(mm) / 像元尺寸(μm) × 1000水平方向的"像素焦距"
fy垂直方向(通常 ≈ fx)
cx, cy主点,光轴与像平面的交点。理想在图像中心,实际偏几十像素
畸变系数镜头的非线性误差(径向 + 切向)

视场角:FOV_h = 2·atan(W / 2fx)。fx=933、W=1920 → 91.6°。 这条式子的直觉是:fx 越大,同样画幅对应的视场角越小,这正是"长焦看窄、广角看宽"的数学形式。

三、外参:两个坐标系之间的刚体变换

相机和 LiDAR 装在车上的不同位置、朝向也不同。要把 LiDAR 系的点云画到相机图像上, 必须先做一次坐标变换,一个 3×3 旋转加一个 3×1 平移,合起来写成 4×4 矩阵:

    ⌊ R  t ⌋          R: 3×3 旋转
T = |      |          t: 3×1 平移
    ⌊ 0  0 0  1 ⌋

关键:外参永远是成对的

用 T_A_B 记号(把 B 系的点表示成 A 系,即 p_A = T_A_B · p_B):

T_cam_lidar   : 点云 → 相机   // 投影用这个
T_lidar_cam   : 相机 → 点云   // 反投影、给点云上色用这个
这两个不是转置关系,是互逆关系

只写"外参"而不写方向,这个说法就是不完整的,必须补上从哪到哪。

四、并非所有传感器都有"内参"这个概念

相机必须由用户标定内参,但这不是所有传感器的通例。逐类看,有的传感器把内参 藏在固件里、用户永远碰不到,有的则根本没有这一层:

传感器有内参吗为什么
相机✅ 必须有,且必须自己标输出是图像(2D 像素),要从像素反推 3D,必须知道针孔+畸变模型
LiDAR⚠️ 有,但厂商固件里用掉了输出直接就是 XYZ,用户层面只需要外参
IMU✅ 有(bias、scale、轴间非正交)输出是原始加速度/角速度,要校正
深度相机✅ 有两套(见第五节)内部是深度相机 + 彩色相机,各有一套内参
编码器✅ 有(零位、齿轮比)输出是脉冲计数

LiDAR 的内参藏在固件里

LiDAR 的内参确实存在,就藏在 ring 字段背后:

每条激光线的:
  • 垂直角偏差      // 装配误差,理想是 2° 间隔,实际每条都偏一点
  • 水平角偏差
  • 测距偏差/尺度因子
  • 各激光器的强度差异

这些就是 LiDAR 的内参。厂商在出厂时标定好,烧在固件里, 所以标定文件里拿到的 XYZ 已经是补偿过的结果。

用户层面通常不需要管,除非:要做逐线标定(用 ring 字段!)、 传感器摔过 / 温度漂移严重、或者要做多 LiDAR 融合需要每台的逐线参数。

对比:相机的内参暴露给用户,因为厂商给的标称值精度不够(尤其畸变), 必须自己标或至少验证。

五、内外参是相对的,不是绝对属性

看一个深度相机(RealSense)的参数构成:

RealSense D435:
  ├── 深度相机内参 K_depth           ← 内参
  ├── 彩色相机内参 K_color           ← 内参
  ├── 深度→彩色 的变换 T_color_depth  ← ???
  └── 设备→世界 的变换 T_world_dev     ← 外参

同一个 T_color_depth,换个角度看会得到相反的答案:

从【单个传感器】的角度看   → 它是【外参】(深度相机相对于彩色相机的位置)
从【整个设备】的角度看     → 它是【内参】(设备【内部】两个部件的相对关系,出厂固定)
结论

"内参/外参"不是一个绝对属性,而是「相对于所选参考系」而言的。

同一个 4×4 矩阵,取决于把"设备"的边界画在哪里,它可能是内参也可能是外参。

这解释了为什么工程上经常看到"相机内参"里混着相机之间的变换,因为对于多传感器模组,"内"的边界是整个模组。

六、三类参数:内参、外参、时间

先看一个场景:车以 10 m/s 行驶,相机和 LiDAR 的时间戳相差 33 ms。 这 33 ms 里车已经走了 33 cm,投影到图像上就是上百像素的整体错位 (第七节实算)。这类误差既不属于内参,也不属于外参。

"内参 + 外参"这个分类漏了一类,而且漏掉的这一类危害最大:

① 内参      自身映射          (K、畸变、bias…)
② 外参      空间关系          (R、t)
③ 时间参数  时间关系          (传感器之间的时间偏移、曝光时刻)

时间偏移严格说既不是内参也不是外参,但它必须在标定时一起估计 (Kalibr 就是这么做的:camera-IMU 的时间偏移和内外参一起优化)。

七、敏感性分析:各类参数错了后果多大

把四类误差各注入一点,看它们分别把投影点推偏多少像素。这四类是: 外参的旋转部分、外参的平移部分、内参 fx、以及时间偏移。 实算用一组真实的标定值:fx=933.4667、fy=934.6754、 cx=896.4692、cy=507.3557,图像 1920×1080。 下表每一格表示"该深度、该画面位置处的像素偏移量":

① 旋转误差 0.5°

      深度Z | X/Z=-0.6 | X/Z=-0.3 | X/Z=+0.0 | X/Z=+0.3 | X/Z=+0.6
      3m |   11.02px |    8.86px |    8.15px |    8.90px |   11.14px
      5m |   11.02px |    8.86px |    8.15px |    8.90px |   11.14px
     10m |   11.02px |    8.86px |    8.15px |    8.90px |   11.14px
     25m |   11.02px |    8.86px |    8.15px |    8.90px |   11.14px

注意:和距离完全无关(3 m 和 25 m 都是 11.02 px), 但越靠画面边缘越大(中心 8.15 → 边缘 11.14)。中心值正好等于 fx·θ = 933 × 0.00873 = 8.15。

② 平移误差 1 cm

      深度Z | X/Z=-0.6 | X/Z=-0.3 | X/Z=+0.0 | X/Z=+0.3 | X/Z=+0.6
      3m |    3.11px |    3.11px |    3.11px |    3.11px |    3.11px
      5m |    1.87px |    1.87px |    1.87px |    1.87px |    1.87px
     10m |    0.93px |    0.93px |    0.93px |    0.93px |    0.93px
     25m |    0.37px |    0.37px |    0.37px |    0.37px |    0.37px

正好相反:和画面位置无关,和距离成 1/Z(3 m 处 3.11 px → 25 m 处 0.37 px)。

③ 内参 fx 误差 1%

      深度Z | X/Z=-0.6 | X/Z=-0.3 | X/Z=+0.0 | X/Z=+0.3 | X/Z=+0.6
      3m |    5.60px |    2.80px |    0.00px |    2.80px |    5.60px
     25m |    5.60px |    2.80px |    0.00px |    2.80px |    5.60px

和距离无关;画面中心是 0,向边缘线性增长。

④ 时间误差 33 ms(车 10 m/s → 33 cm 位移)

时间戳相差 33 ms,车在这段时间里走了 33 cm,效果等同于一个 33 cm 的平移误差, 区别只在它随速度变化、而不随安装变化。换算到像素:

      深度Z | X/Z=-0.6 | X/Z=-0.3 | X/Z=+0.0 | X/Z=+0.3 | X/Z=+0.6
      3m |  102.68px |  102.68px |  102.68px |  102.68px |  102.68px
      5m |   61.61px |   61.61px |   61.61px |   61.61px |   61.61px
     10m |   30.80px |   30.80px |   30.80px |   30.80px |   30.80px
     25m |   12.32px |   12.32px |   12.32px |   12.32px |   12.32px

3 m 处 102.68 px,比 1 cm 标定误差大 33 倍。

八、诊断决策树:从错位形态反推病因

第七节四组数据的分布形态两两正交,互不混淆,于是可以整理成一棵判据树:

旋转误差  只取决于【画面位置】,与距离无关
平移误差  只取决于【距离】,与画面位置无关
内参误差  与距离无关,且【中心为 0】
时间误差  形态像平移误差(1/Z),但【量级随速度变化】
投影错位诊断决策树
错位现象                          结论
─────────────────────────────────────────────────────
中心对齐、边缘错位            → 【内参】问题(fx/fy/主点/畸变)
全图均匀错位(中心也错)      → 【旋转外参】问题
近处错位大、远处小            → 【平移外参】问题(或时间偏移)
错位量随运动速度变化          → 【时间同步】问题
错位量在 3D 空间随距离增大    → 旋转误差(3D 位移 = Z·θ)

这套判据可以在 30 秒内把问题归到某一类参数上,而不是盲目地重标一遍。

DEMO 3 · 误差诊断器 切换误差类型,看箭头(像素偏移)的分布形态
图像平面上的像素偏移场(箭头 = 偏移方向与大小)

常见误解:小角度误差会被距离放大

"看远处物体,因为小角度误差会随距离放大"

这个理由不成立。从第七节的数据看:

但"看远处细长物体"仍然是正确的验收方法,理由不是误差大小,是可检测性:

远处的电线杆  → 在图像上只有几像素宽
               → 点云偏 8px 就【完全跑到杆子外面】→ 一眼看出

近处的墙      → 在图像上占几百像素
               → 偏 8px 相对它的尺寸微不足道 → 看不出来

结论:远处细长物体放大了错误的可见性,而不是放大了误差本身。

九、交互演示:投影链实时

左边是 LiDAR 坐标系下的 3D 点云(可以拖动旋转),右边是实时投影结果。 调参数时注意观察 3D 场景不动、只有图像在变,以及每个参数影响图像的方式不一样。

DEMO 2 · 投影链实时 拖动左图旋转 · 调下面参数 · 试右侧预设按钮
LiDAR 坐标系(x 前 / y 左 / z 上)· 拖动旋转 · 滚轮缩放
相机图像(像素坐标)· 橙色十字 = 主点

演示里可以动手验证的四件事

操作观察什么
只调 fx画面里的一切同步放大/缩小,这就是"焦距"的含义。注意 FOV 在变
关掉"朝向约定"整个投影瞬间崩掉,点全部飞到画面外。这就是忘记 LiDAR→Camera 约定时的样子
把 pitch 调到 ±15°场景整体在图像上平移。注意平移量在画面各处一致(旋转误差的特征)
对照右侧 readout 里的 R 矩阵它按行优先逐行打印;把主序搞错(行列颠倒)相当于用转置矩阵做投影,点全部错位到荒诞的位置

十、为什么单张照片解不出内参

先看一个具体画面:同一块棋盘格,放在 2 m 处用短焦镜头拍,和放在 4 m 处用长焦镜头拍, 在照片上可以长得完全一样,格子数量相同、大小相同、位置相同。 照片里只有像素坐标、没有深度,所以从一张照片无法区分这两种情形。 这就是焦距与距离之间的尺度歧义:板子实际远一倍,在图上就小一倍,两者恰好抵消。

形式化:约束数不够

一张平面靶标只给出一个单应矩阵(8 个自由度), 它能提供的关于内参 K 的约束只有 2 个;而 K 至少有 4 个未知数 (fx、fy、cx、cy),畸变系数还没算进去。方程数少于未知数,解不唯一。

由此可以直接推出张正友标定法的两条要求:必须多张、而且姿态要散开 拍 15~30 张,且板子必须倾斜。如果每张都正对镜头,约束数始终只有那 2 个, fy 和畸变系数永远解不出来。

同理,"所有照片都拍在画面正中央"是标定禁忌,畸变系数的约束全部来自画面边缘。

十一、标定顺序:先内参,后外参

标定不是四个独立的步骤,而是一条有依赖的流水线。依赖关系如下:

外参标定依赖内参:
  求 T_cam_lidar 要先把点云投到图像上
  投影需要 K
  → K 错了,投出来的位置就错了
  → 于是优化的"外参"是在【补偿 K 的误差】
  → 结果:一个错误的内参 + 一个"学会补偿它"的外参
  → 换个距离/视角就崩

内参误差会污染外参,这就是为什么标定必须按顺序来, 而且内参的重投影误差要先验收通过(< 0.3 px)再往下走。

内参 → 外参 → 时间 → 融合质量 → 标注质量 → 模型质量
 ↑                                              │
 └──────────── 发现问题,从最上游查起 ────────────┘

十二、总结表

内参外参时间参数
描述自身映射空间关系时间关系
相机K + 畸变T_world/cam曝光时刻、与 IMU 的偏移
LiDAR逐线角偏/距偏(固件里)T_world/lidar帧时间戳、逐点时间
IMUbias, scale, 非正交T_world/imu与相机偏移
深度相机两套(深度+彩色)两层(内部+对外)—
怎么标棋盘格 / 靶标靶标或点云-图像对齐运动法 / Kalibr
验收重投影误差 < 0.3 px边缘错位 < 2 px残留偏移最小
错了的表现中心对齐、边缘错位全图均匀错位 / 近处偏随速度变化
一句话总结
内参 = 自身映射(相机:fx fy cx cy + 畸变);外参 = 空间关系(4×4)。
判断方法:描述"自己怎么读数"的是内参,描述"相对别人在哪"的是外参。
内外参是相对的(深度相机的两层外参),不是绝对属性。

分类有三类,漏掉的那类危害最大:
  内参 / 外参 / 时间参数

最反直觉的一条:
  我们花最多时间标的几毫米平移误差,往往【不是主要矛盾】。
  时间误差(33ms = 103px)和旋转误差(0.5° = 8px)的杀伤力,
  通常大于平移误差(1cm = 3px)。

诊断决策树(看错位形态反推病因):
  中心对齐边缘错 → 内参      全图均匀错 → 旋转
  近处大远处小   → 平移      随速度变化 → 时间

标定顺序必须【先内后外】—— 内参误差会污染外参,
标出一个"学会补偿内参误差"的假外参。
← 05 传感器体系
下一模块 07 投影模型与畸变 →
← 上一页 ·  下一页 →
京ICP备19013239号