2、坐标系与空间变换:车辆坐标系、相机坐标系、图像坐标系、坐标系转换基础
做BEV感知,说白了就是解决一个问题:如何让车看懂周围的世界。
车上有好几个摄像头,每个摄像头看到的都是二维图像。但车需要知道——那个行人到底在车的左前方几米?那辆车是在我的正后方还是右后方?
这就绕不开坐标系和空间变换。我刚开始接触BEV时,被各种坐标系绕得头晕。后来踩了几个坑,才慢慢理清楚。今天咱们就把这事彻底讲明白。
2.1 为什么需要多个坐标系?
你想想看,每个传感器都有自己的“视角”。
- 摄像头看到的是像素点,它活在图像坐标系里。
- IMU和GPS关心车本身的姿态,它们活在车辆坐标系里。
- 而BEV感知最终要输出的是“鸟瞰图”,那是世界坐标系的事。
所以,我们需要一套方法,把不同坐标系下的数据统一起来。这就是坐标系转换的由来。
核心思想:所有传感器数据最终都要转换到同一个坐标系下,才能做融合和决策。
2.2 车辆坐标系(Vehicle Coordinate System)
这是最常用的参考系。我习惯把它想象成:车就是世界的中心。
- 原点:通常在后轴中心,或者车辆质心。不同车厂定义略有差异,但原理一样。
- X轴:车头方向为正。
- Y轴:车身左侧为正(注意,有些定义是右侧为正,一定要确认清楚)。
- Z轴:车顶方向为正。
我在项目中遇到过一个问题:某款车的IMU安装在车尾,而摄像头在前保险杠。如果不做坐标系对齐,融合出来的目标位置直接偏了半米。嗯,这坑我踩过。
小技巧:实际开发中,建议把车辆坐标系的原点统一到后轴中心。这样做的好处是——车辆转弯时,后轴中心基本不动,计算更稳定。
2.3 相机坐标系(Camera Coordinate System)
相机坐标系是以摄像头为原点的。它和车辆坐标系的关系,就是我们要解决的第一个转换问题。
- 原点:相机光心(镜头中心)。
- Z轴:镜头朝向为正(也就是相机看的方向)。
- X轴:相机右侧为正。
- Y轴:相机下方为正(注意,这里是向下!)。
为什么会这样?因为图像坐标系的原点在左上角,Y轴向下为正。为了和图像坐标系保持一致,相机坐标系的Y轴也朝下。说白了,这是为了数学上方便。
注意:相机坐标系和车辆坐标系的Y轴方向是相反的!车辆坐标系Y轴朝左,相机坐标系Y轴朝下。转换时千万别搞反了。
2.4 图像坐标系(Image Coordinate System)
图像坐标系就是我们看到的像素坐标。它有两个层次:
- 像素坐标系:单位是像素,原点在左上角,u轴向右,v轴向下。
- 物理坐标系:单位是毫米,原点在图像中心,x轴向右,y轴向下。
两者之间通过内参矩阵转换。内参矩阵里包含了焦距、主点位置这些参数。每个相机出厂时都会标定好,直接拿来用就行。
// 像素坐标 (u, v) 转物理坐标 (x, y)
// 假设内参矩阵 K = [fx, 0, cx; 0, fy, cy; 0, 0, 1]
x = (u - cx) / fx
y = (v - cy) / fy
我记得第一次做相机标定时,发现标定出来的内参和官方文档差了0.5个像素。当时没在意,结果投影出来的3D点全偏了。后来重新标定了一遍,问题才解决。所以,内参一定要自己标定,别偷懒用现成的。
2.5 坐标系转换基础
坐标系转换,说白了就是旋转+平移。用数学语言说,就是刚体变换。
2.5.1 旋转矩阵
旋转矩阵是一个3x3的正交矩阵。它描述了从一个坐标系到另一个坐标系的旋转关系。
// 绕Z轴旋转θ角
R_z = [cosθ, -sinθ, 0;
sinθ, cosθ, 0;
0, 0, 1]
实际项目中,我们很少手动写旋转矩阵。一般用欧拉角或者四元数来表示旋转。我个人习惯用四元数,因为它没有万向锁问题。
2.5.2 平移向量
平移向量就是两个坐标系原点之间的偏移量。比如相机在车辆坐标系中的位置,就是一个平移向量。
2.5.3 外参矩阵
外参矩阵 = [R | t],其中R是旋转矩阵,t是平移向量。它把相机坐标系下的点,转换到车辆坐标系下。
// 相机坐标系下的点 P_cam,转换到车辆坐标系 P_veh
P_veh = R * P_cam + t
关键点:外参矩阵是每个相机独有的。安装位置不同,外参就不同。而且车辆行驶过程中,外参会因为震动而轻微变化。所以,定期标定外参是很有必要的。
2.6 从图像到BEV的完整流程
好了,现在我们把所有知识串起来。从摄像头图像到BEV鸟瞰图,需要经过以下步骤:
- 图像坐标系 → 相机坐标系:利用内参矩阵,把像素坐标转为相机坐标。
- 相机坐标系 → 车辆坐标系:利用外参矩阵,把相机坐标转为车辆坐标。
- 车辆坐标系 → BEV坐标系:把车辆坐标投影到地面(Z=0平面),得到BEV坐标。
你可能会问:为什么最后要投影到Z=0平面?因为BEV是鸟瞰图,它只关心地面上的位置,不关心高度。当然,如果你要做3D检测,那就要保留Z坐标。
实战建议:在写代码时,建议把坐标系转换封装成独立的函数。比如 cam_to_vehicle()、vehicle_to_bev()。这样调试起来方便,也容易复用。
2.7 常见坑点与避坑指南
- 坐标系方向搞反:我曾经把相机坐标系的Y轴方向搞反了,结果投影出来的目标全在车的另一侧。排查了半天才发现是符号问题。
- 外参标定不准:有一次外参标定误差大了0.1度,导致30米外的目标偏移了0.5米。这在高速场景下是致命的。
- 忽略镜头畸变:鱼眼相机的畸变很大,如果不做去畸变,投影出来的BEV图像会扭曲变形。
避坑指南:我曾经因为外参标定文件搞混了,导致两个相机的数据对调。结果车看到左边的障碍物,却显示在右边。从那以后,我每次标定完都会做一次交叉验证——用标定板在几个已知位置拍照,验证投影误差是否在合理范围内。
2.8 小结
坐标系与空间变换,是BEV感知的基石。你想想看,如果坐标系都没对齐,后面的融合、检测、跟踪全是白搭。
我个人习惯在项目初期,先把所有坐标系的关系图画出来。标清楚每个坐标系的原点、轴方向、以及它们之间的转换关系。这样后面写代码时,思路会清晰很多。
下一章,我们会聊到相机模型与标定。到时候会深入讲解内参、外参的标定方法,以及如何用OpenCV做去畸变。嗯,那才是真正动手的时候。
一句话总结:坐标系转换就是“旋转+平移”,但细节决定成败。每个符号、每个参数,都值得你认真对待。