一、标定基础与坐标系

各位同行,今天咱们聊聊多传感器融合里最基础、也最绕不开的话题——坐标系。说实话,我见过太多项目在标定环节翻车,十有八九都是坐标系没搞明白。我自己刚入行那会儿,也在这上面栽过跟头。

1.1 传感器标定的核心概念

标定是什么?说白了,就是给传感器「对表」。你想想看,车上有摄像头、激光雷达、IMU,每个传感器都有自己的「视角」和「语言」。摄像头看到的是像素,激光雷达看到的是点云,IMU感受到的是加速度和角速度。它们各自为政,怎么协同工作?

标定要解决两个问题:

  • 内参标定:搞清楚传感器自己内部的参数。比如相机的焦距、畸变系数,IMU的零偏、尺度因子。
  • 外参标定:搞清楚传感器之间的相对位置和姿态。比如激光雷达装在车顶哪个位置,相机朝哪个方向。

核心思想:标定的本质是求解一个变换矩阵,把不同传感器的数据统一到同一个坐标系下。

我习惯把标定比作「翻译」。每个传感器都在说自己的方言,标定就是那个翻译官,让它们能互相听懂。

1.2 世界坐标系

世界坐标系,也叫全局坐标系。它是我们描述物体位置的「绝对参考系」。在自动驾驶里,通常用东北天坐标系(ENU)或者地心地固坐标系(ECEF)。

举个例子:你站在天安门广场,你的经纬度坐标就是世界坐标系下的位置。车上的GPS给出的就是世界坐标。

为什么要用世界坐标系?因为它是所有传感器数据的「最终归宿」。激光雷达扫描到的障碍物,最终要映射到世界坐标系下,才能知道它在真实世界中的位置。

我的经验:在室外场景,我建议用ENU坐标系。它直观,而且计算方便。室内场景则常用自定义的直角坐标系,比如以房间某个角落为原点。

1.3 相机坐标系

相机坐标系,是以相机光心为原点,Z轴指向镜头前方(或者后方,取决于具体定义)。

这里有个容易混淆的点:相机坐标系和图像坐标系不一样。图像坐标系是二维的,单位是像素。相机坐标系是三维的,单位是米(或毫米)。

我记得有一次,团队里新来的同事把相机坐标系和图像坐标系搞混了,结果投影出来的点全偏了。排查了半天才发现是坐标系定义的问题。

相机坐标系到图像坐标系的转换,靠的是内参矩阵:

// 内参矩阵 K
// fx, fy 是焦距,cx, cy 是主点偏移
K = [[fx,  0, cx],
     [ 0, fy, cy],
     [ 0,  0,  1]]

// 3D点 (X, Y, Z) 投影到像素 (u, v)
// u = fx * X / Z + cx
// v = fy * Y / Z + cy

注意:不同相机厂商的坐标系定义可能不同。有的把Z轴朝前,有的朝后。标定前一定要确认清楚。

1.4 IMU坐标系

IMU(惯性测量单元)坐标系,通常以IMU芯片的中心为原点。它的三个轴:X轴朝前,Y轴朝左,Z轴朝上(右手系)。

IMU输出的是加速度和角速度。但注意,这些数据是在IMU自身坐标系下的。如果你要把它转换到车体坐标系,就需要知道IMU的安装角度。

我曾经在一个项目中,IMU装歪了2度。结果车辆直线行驶时,IMU测出的角速度一直有微小偏差。积分出来的姿态误差越来越大,最后定位完全跑偏。嗯,这就是标定不仔细的代价。

IMU坐标系和车体坐标系的关系,通常用欧拉角表示:

// 欧拉角 (roll, pitch, yaw)
// roll: 绕X轴旋转(侧倾)
// pitch: 绕Y轴旋转(俯仰)
// yaw: 绕Z轴旋转(航向)

// 旋转矩阵 R = Rz(yaw) * Ry(pitch) * Rx(roll)

1.5 激光雷达坐标系

激光雷达坐标系,以激光雷达的旋转中心为原点。通常Z轴朝上,X轴朝前。

激光雷达输出的点云,每个点包含 (x, y, z) 坐标,单位是米。这些坐标就是在激光雷达坐标系下的。

不同品牌的激光雷达,坐标系定义可能略有差异。比如Velodyne和Hesai的坐标系就不完全一样。标定前一定要看数据手册。

避坑指南:我曾经遇到过激光雷达的坐标系定义和官方文档不一致的情况。后来发现是固件版本不同导致的。所以,拿到传感器后,先打印几个点的坐标,手动验证一下坐标系方向。

1.6 刚体变换与欧拉角

刚体变换,就是旋转+平移。它描述了一个刚体(比如传感器)在空间中的位置和姿态变化。

数学上,刚体变换用4x4的齐次变换矩阵表示:

// 齐次变换矩阵 T
// R 是 3x3 旋转矩阵,t 是 3x1 平移向量
T = [[R, t],
     [0, 1]]

// 点 P 从坐标系A变换到坐标系B
// P_b = T_ab * P_a

欧拉角是描述旋转的一种方式。它把旋转分解为绕三个轴的依次旋转。常见的顺序有:ZYX、XYZ等。

我个人习惯用ZYX顺序(先绕Z轴转yaw,再绕Y轴转pitch,最后绕X轴转roll)。这个顺序在自动驾驶里最常用。

但欧拉角有个问题:万向锁。当pitch接近±90度时,yaw和roll会失去一个自由度。这时候用四元数更稳定。

总结一下:刚体变换是标定的数学基础。欧拉角直观但有限制,四元数稳定但不够直观。实际项目中,我通常用欧拉角做可视化调试,用四元数做内部计算。

1.7 坐标系转换实战

说了这么多理论,咱们来点实际的。假设你有一个激光雷达点云,要投影到相机图像上。怎么做?

  1. 把点从激光雷达坐标系转换到车体坐标系(用外参T_lidar_to_vehicle)
  2. 把点从车体坐标系转换到相机坐标系(用外参T_vehicle_to_camera)
  3. 把点从相机坐标系投影到图像平面(用内参K)
// 伪代码
function project_lidar_to_image(point_lidar, T_lidar_to_vehicle, T_vehicle_to_camera, K):
    // 1. 转换到车体坐标系
    point_vehicle = T_lidar_to_vehicle * point_lidar
    
    // 2. 转换到相机坐标系
    point_camera = T_vehicle_to_camera * point_vehicle
    
    // 3. 投影到图像
    u = K.fx * point_camera.x / point_camera.z + K.cx
    v = K.fy * point_camera.y / point_camera.z + K.cy
    
    return (u, v)

你看,整个流程就是一连串的坐标系变换。每一步都依赖准确的标定参数。任何一个环节出错,投影结果都会偏。

重要提醒:坐标系变换的顺序不能搞反。先旋转后平移,还是先平移后旋转?答案是:先旋转,后平移。因为旋转是绕原点转的,平移后再旋转,结果就不对了。

好了,这一章的内容就到这里。坐标系是标定的基石,一定要吃透。下一章咱们聊聊具体的标定方法和工具。

我的建议:初学者可以先在纸上画坐标系,手动推导一遍变换过程。别嫌麻烦,这一步做好了,后面会省很多事。