3. 坐标系与空间变换:世界坐标系、相机坐标系、图像坐标系、像素坐标系、刚体变换与投影变换
多摄像头系统里,最绕不开的一个基础问题就是——坐标统一。
说白了,每个摄像头都有自己的“小世界”,它们各自看到的东西,位置、角度都不一样。你要把这些信息拼在一起,就必须先搞清楚:一个点,在哪个坐标系里?怎么从一个坐标系变到另一个?
我个人习惯把这一整套东西叫做“坐标系的六脉神剑”。你掌握了它,后面做标定、做融合,心里就有底了。
3.1 四个核心坐标系
我们先从最熟悉的开始,一步步往像素走。
| 坐标系 | 单位 | 原点 | 维度 | 典型坐标 |
|---|---|---|---|---|
| 世界坐标系 | 米 / 毫米 | 人为定义(如地面一角) | 3D (Xw, Yw, Zw) | 物体在真实世界的位置 |
| 相机坐标系 | 米 / 毫米 | 相机光心 | 3D (Xc, Yc, Zc) | 以相机为原点的3D坐标 |
| 图像坐标系 | 毫米 | 图像平面中心(主点) | 2D (x, y) | 物理成像平面上的位置 |
| 像素坐标系 | 像素 | 图像左上角 | 2D (u, v) | 你在图像上看到的行列号 |
核心记忆点:世界→相机是刚体变换(旋转+平移),相机→图像是投影变换(小孔成像),图像→像素是离散化(缩放+平移)。
3.2 刚体变换:世界坐标系 → 相机坐标系
刚体变换,就是只改变物体的位置和朝向,不改变它的形状和大小。你想想看,你把一个杯子从桌上拿到手里,杯子本身没变形,这就是刚体变换。
数学上,它由一个旋转矩阵 R 和一个平移向量 t 组成。
// 刚体变换公式
// Pc = R * Pw + t
// 其中:
// Pw = [Xw, Yw, Zw]^T —— 世界坐标系下的点
// Pc = [Xc, Yc, Zc]^T —— 相机坐标系下的点
// R = 3x3 旋转矩阵
// t = 3x1 平移向量
我在项目中遇到过一个问题:两个摄像头安装角度差了不到1度,但融合出来的点云就是有重影。后来发现,就是旋转矩阵 R 的精度不够。所以,刚体变换的精度,直接决定了多摄像头融合的成败。
避坑指南:我曾经在标定的时候,把旋转矩阵和平移向量的顺序搞反了。记住,是先旋转,再平移。顺序错了,结果就是天差地别。
3.3 投影变换:相机坐标系 → 图像坐标系
这一步,就是小孔成像模型。相机坐标系下的3D点,通过光心投影到成像平面上。
// 投影变换公式(理想针孔模型)
// x = f * Xc / Zc
// y = f * Yc / Zc
// 其中:
// f —— 焦距(单位:毫米)
// (Xc, Yc, Zc) —— 相机坐标系下的3D点
// (x, y) —— 图像坐标系下的2D点
这里有个关键点:Zc 不能为0。你想想看,如果物体正好在相机光心所在的平面上,那它在图像上就成一个点了,这在实际中几乎不可能,但数学上要小心。
3.4 离散化:图像坐标系 → 像素坐标系
图像坐标系是连续的物理坐标(毫米),而像素坐标系是离散的整数坐标(像素)。这一步就是把物理尺寸映射到像素网格上。
// 离散化公式
// u = x / dx + u0
// v = y / dy + v0
// 其中:
// dx, dy —— 每个像素在x和y方向上的物理尺寸(毫米/像素)
// (u0, v0) —— 主点(图像坐标系原点)在像素坐标系中的位置
嗯,这里要注意:dx 和 dy 不一定相等。如果摄像头传感器的像素不是正方形,那就会导致图像在横纵方向上的缩放比例不同。我在做鱼眼摄像头标定时,就遇到过这种非正方形像素的情况,处理起来要格外小心。
3.5 完整变换链路
把上面三步串起来,就是一个点从世界坐标到像素坐标的完整旅程。
// 完整变换(齐次坐标形式)
// s * [u, v, 1]^T = K * [R | t] * [Xw, Yw, Zw, 1]^T
// 其中:
// K —— 相机内参矩阵(包含fx, fy, u0, v0)
// [R | t] —— 相机外参矩阵(刚体变换)
// s —— 尺度因子(其实就是 Zc)
我的习惯:在实际代码中,我通常会用齐次坐标来写,这样可以把旋转和平移合并成一个4x4的矩阵,计算起来非常方便。OpenCV 和 Eigen 库都支持这种操作。
3.6 知识体系结构图
下面这张图,把整个坐标系变换的流程和关系画清楚了。你可以把它当作一张“地图”,随时回来查阅。
3.7 多摄像头场景下的坐标系统一
在多摄像头系统中,每个摄像头都有自己的相机坐标系。你要做融合,就必须把所有摄像头的数据都统一到同一个世界坐标系下。
我常用的做法是:
- 选择一个主摄像头,把它作为世界坐标系的原点。
- 标定其他摄像头相对于主摄像头的刚体变换(R, t)。
- 所有数据都先变换到主摄像头的坐标系下,再统一处理。
实战经验:我在做自动驾驶的多摄像头环视系统时,就是用的这个方法。先把四个鱼眼摄像头都标定到车身坐标系(世界坐标系)下,然后做鸟瞰图变换。这样,不管哪个摄像头看到障碍物,最终都能在车身坐标系下找到它的准确位置。
3.8 常见问题与避坑
- 坐标系方向搞反:旋转矩阵有正负之分,平移向量也有方向。我曾经因为一个符号错误,导致融合后的点云全部跑到车底下去。所以,标定完一定要做可视化验证。
- 内参不准:相机内参 K 矩阵如果标定不准,后面的所有变换都会出错。我建议每半年重新标定一次,特别是摄像头有震动或温度变化大的场景。
- 尺度因子 s 忽略:在齐次坐标变换中,s 就是 Zc。如果你忘了除以 s,那得到的像素坐标就是错的。这个坑,我踩过不止一次。
重要提醒:坐标系变换是数学,但更是工程。不要只背公式,一定要动手写代码、画图、验证。我见过太多人,公式背得滚瓜烂熟,一上手就出错。