4. 单相机外参标定:旋转矩阵与平移向量、相机位姿表示

外参标定,说白了就是搞清楚相机在世界坐标系里到底「站」在哪、朝哪个方向看。我刚开始做多相机系统时,总觉得内参更重要,外参随便算算就行。结果有一次把两个相机的旋转方向搞反了,重建出来的点云直接拧成了麻花……嗯,从那以后我再也不敢小看外参了。

这一章我们聚焦单相机的外参。你理解了它,四相机联合标定就完成了一半。

4.1 外参到底在描述什么?

外参包含两个部分:旋转矩阵 R 和 平移向量 t。它们共同完成一个任务——把世界坐标系下的点 Pw 转换到相机坐标系下:

P_c = R * P_w + t

你可以这样理解:

  • R:描述相机坐标系相对于世界坐标系的姿态(朝向)
  • t:描述相机坐标系原点在世界坐标系中的位置

我个人习惯把外参看作「一个刚体变换」。相机固定在一个支架上,它相对于世界坐标系的旋转和平移,就是一组外参。

关键点:外参是 3×4 矩阵 [R|t] 的前 3 列是旋转,第 4 列是平移。它把世界点映射到相机坐标系,而不是反过来。

4.2 旋转矩阵:不只是 3×3 的数字

旋转矩阵 R 是一个 3×3 的正交矩阵,满足 RTR = I,且 det(R) = 1。它每一列都是单位向量,且两两正交。

我在项目中遇到过一个问题:用优化算法直接估计 R 的 9 个元素,结果矩阵不满足正交约束,重建出来的场景全是扭曲的。所以实际工程中,我们很少直接优化 R 的 9 个参数。

表示方式 自由度 优点 缺点
旋转矩阵 R 9(实际 3) 直观、易组合 需满足正交约束
欧拉角 3 参数少、直观 万向锁、不连续
四元数 4 无奇点、易插值 需归一化
轴角 3 紧凑、几何意义明确 有奇点

4.3 欧拉角:直观但暗藏陷阱

欧拉角用三个角度表示旋转:绕 X 轴(roll)、绕 Y 轴(pitch)、绕 Z 轴(yaw)。听起来很简单对吧?

但这里有个坑——万向锁。当 pitch 接近 ±90° 时,roll 和 yaw 会失去一个自由度,导致旋转退化。我曾经在无人机云台标定中遇到过这个问题,pitch 角一过 85°,姿态解算就开始乱跳。

避坑指南:如果你用欧拉角做优化变量,一定要限制 pitch 范围在 ±80° 以内,或者干脆换用四元数。我曾经因为没注意这个,浪费了整整两天调试时间。

另外,欧拉角的旋转顺序也很关键。常见的顺序有 ZYX、XYZ、ZYZ 等。不同顺序得到的角度值完全不同。你想想看,如果标定程序用 ZYX,而你的算法用 XYZ,那结果肯定对不上。

4.4 四元数:SLAM 工程师的最爱

四元数 q = (w, x, y, z) 是一种超复数,满足 w² + x² + y² + z² = 1。它用 4 个参数表示 3 个自由度的旋转,没有奇点,而且插值平滑。

我个人在 SLAM 项目中几乎只用四元数。原因很简单:

  • 优化时不需要额外约束(只要归一化)
  • 组合旋转用乘法,比矩阵快
  • 插值(Slerp)非常平滑

四元数到旋转矩阵的转换公式:

R = [
  [1-2(y²+z²), 2(xy-wz),   2(xz+wy)],
  [2(xy+wz),   1-2(x²+z²), 2(yz-wx)],
  [2(xz-wy),   2(yz+wx),   1-2(x²+y²)]
]

小技巧:实际编码时,直接用 Eigen 或 OpenCV 的 Rodrigues 函数转换,别手写矩阵。我见过太多人把符号写反了。

4.5 平移向量:别忽略尺度

平移向量 t = (tx, ty, tz)T 表示相机光心在世界坐标系中的位置。单位通常和标定板尺寸一致(比如毫米)。

这里有个容易忽略的点:平移向量的尺度。如果你用棋盘格标定,格子大小是 30mm,那 t 的单位就是 mm。如果你用二维码标定,尺寸是 5cm,那 t 的单位就是 cm。混用单位会导致重建结果完全不对。

我记得有一次帮同事排查问题,他标定出来的外参总是偏大 10 倍。查了半天,原来是标定板尺寸写成了 3cm,实际是 30mm。嗯,这种低级错误其实很常见。

4.6 本章知识结构

下面这张图帮你理清外参标定的核心逻辑:

单相机外参标定知识结构 单相机外参标定知识结构 外参 [R|t] 旋转矩阵 R 平移向量 t 欧拉角 四元数 轴角 尺度一致性 核心:外参描述相机在世界坐标系中的位置和朝向 优化时优先使用四元数,避免欧拉角的万向锁问题

4.7 实战:从棋盘格计算外参

实际标定时,我们通常用 PnP(Perspective-n-Point)算法从 2D-3D 对应点求解外参。OpenCV 提供了 solvePnP 函数:

// 输入:3D 世界点、2D 图像点、内参矩阵、畸变系数
// 输出:旋转向量 rvec、平移向量 tvec
cv::Mat rvec, tvec;
cv::solvePnP(objectPoints, imagePoints, cameraMatrix, distCoeffs, rvec, tvec);

// 将旋转向量转为旋转矩阵
cv::Mat R;
cv::Rodrigues(rvec, R);

注意:solvePnP 默认使用迭代法,对初始值敏感。如果标定板在图像边缘,建议先用 solvePnPRansac 做鲁棒估计。

我个人习惯在标定前先检查一下 2D 点提取的精度。如果角点检测有 1 个像素的误差,外参的旋转误差可能达到 0.1°。对于四相机系统,0.1° 的误差在 2 米外就会产生 3.5mm 的偏差——这已经很大了。

4.8 避坑总结

  • 旋转矩阵:别直接优化 9 个参数,用四元数或轴角
  • 欧拉角:注意万向锁,限制 pitch 范围
  • 四元数:记得归一化,否则矩阵不正交
  • 平移向量:统一单位,别混用 mm 和 cm
  • PnP 求解:用 RANSAC 版本抗噪

外参标定是四相机联合标定的基石。你把这个单相机的外参搞清楚了,后面把四个相机的外参统一到一个世界坐标系下,就只是矩阵乘法的事了。