2、坐标系与传感器基础:相机坐标系、激光雷达坐标系、IMU坐标系、外参标定基础、内参基础

说实话,做BEV感知这几年,我踩过最大的坑就是坐标系搞混了。你想想看,一辆车上装了相机、激光雷达、IMU,每个传感器都有自己的"小脾气"——它们各自定义自己的原点在哪、哪个方向是正。如果不把这些坐标系对齐,后面做融合、做感知,全是白搭。

这一章,我们就来把这些坐标系彻底捋清楚。我保证,看完之后你至少能少走三个月弯路。

2.1 相机坐标系:从像素到世界的桥梁

相机坐标系,说白了就是描述"相机看到了什么"的坐标系。它分三层:像素坐标系、图像坐标系、相机坐标系。

像素坐标系:就是图像上每个像素的位置,原点在左上角,u轴向右,v轴向下。单位是像素。

图像坐标系:把像素坐标转换成物理尺寸,原点在图像中心(主点),x轴向右,y轴向下。单位是毫米。

相机坐标系:以相机光心为原点,Z轴指向相机前方,X轴向右,Y轴向下。这才是真正的三维坐标系。

这三者之间的转换,靠的就是内参矩阵K。我习惯把内参理解成"相机自己的尺子"——它告诉你一个像素点对应真实世界中的多大角度。

核心公式:

// 像素坐标 -> 归一化坐标
[x_norm, y_norm, 1]^T = K^{-1} * [u, v, 1]^T

// 归一化坐标 -> 相机坐标
X_cam = z * x_norm
Y_cam = z * y_norm
Z_cam = z

我的经验:实际项目中,内参矩阵一定要自己做标定,别用厂家给的默认值。我曾经在某个量产项目里,直接用厂家标定文件,结果测距误差差了15%。后来自己用棋盘格重新标了一遍,问题就解决了。

2.2 激光雷达坐标系:点云的"老家"

激光雷达坐标系相对简单。通常以激光雷达的旋转中心为原点,X轴指向车辆前方,Y轴指向左侧,Z轴指向上方。单位是米。

每个激光点包含(x, y, z, intensity)四个信息。其中intensity是反射强度,在BEV感知中可以用来区分车道线和路面。

嗯,这里要注意:不同厂家的激光雷达坐标系定义可能不一样。比如Velodyne和Hesai的坐标系就有差异。我建议你拿到点云数据后,先可视化一下,确认坐标轴方向对不对。

避坑指南:我曾经在融合相机和激光雷达时,发现投影后的点云总是偏了30度。查了两天才发现——激光雷达的Y轴是朝左的,而我代码里默认朝右。这种低级错误,一旦犯过就再也不会忘了。

2.3 IMU坐标系:车身姿态的"裁判"

IMU(惯性测量单元)测量的是加速度和角速度。它的坐标系通常定义为:X轴指向车辆前方,Y轴指向右侧,Z轴指向下方(符合右手定则)。

IMU坐标系和车身坐标系基本重合,但安装时会有小偏差。这个偏差需要通过外参标定来补偿。

IMU的数据频率很高(100Hz以上),但存在漂移。所以在BEV感知中,我们通常用IMU做短时间内的姿态预测,用相机或激光雷达做长时间修正。

传感器 坐标系原点 X轴方向 Y轴方向 Z轴方向
相机 光心 向右 向下 向前
激光雷达 旋转中心 向前 向左 向上
IMU IMU芯片中心 向前 向右 向下

2.4 内参基础:相机自己的"基因"

内参描述了相机内部的几何特性。主要包括:

  • 焦距:fx, fy,单位是像素。决定了图像的缩放程度。
  • 主点:cx, cy,单位是像素。通常是图像中心,但实际会有偏移。
  • 畸变系数:k1, k2, p1, p2, k3。径向畸变和切向畸变。

内参标定常用的方法是张正友标定法。你只需要打印一张棋盘格,从不同角度拍20张照片,然后用OpenCV的calibrateCamera函数就能算出来。

代码示例:

import cv2
import numpy as np

# 准备棋盘格角点
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
objp = np.zeros((6*9, 3), np.float32)
objp[:,:2] = np.mgrid[0:9, 0:6].T.reshape(-1,2)

# 遍历所有图片
objpoints = []  # 世界坐标系中的点
imgpoints = []  # 图像坐标系中的点

for fname in images:
    img = cv2.imread(fname)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    ret, corners = cv2.findChessboardCorners(gray, (9,6), None)
    if ret:
        objpoints.append(objp)
        corners2 = cv2.cornerSubPix(gray, corners, (11,11), (-1,-1), criteria)
        imgpoints.append(corners2)

# 标定
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(
    objpoints, imgpoints, gray.shape[::-1], None, None
)

我的建议:标定完成后,一定要做重投影误差检查。如果误差超过0.5个像素,说明标定质量不行,需要重新拍图。我一般要求重投影误差在0.1像素以内才放心使用。

2.5 外参标定基础:让传感器"说同一种语言"

外参描述的是不同传感器之间的相对位置和姿态。比如激光雷达坐标系到相机坐标系的旋转矩阵R和平移向量t。

外参标定常用的方法有:

  • 目标物法:在场景中放置标定板,同时被相机和激光雷达看到,然后求解R和t。
  • 自然场景法:利用场景中的特征点(如角点、边缘)进行匹配,然后优化求解。
  • 在线标定:车辆行驶过程中,利用SLAM或结构光方法实时估计外参。

我个人最推荐目标物法,精度高、鲁棒性好。你只需要一个棋盘格或者特制的标定板,采集几组数据就能搞定。

避坑指南:我曾经在标定激光雷达到相机的外参时,发现投影误差总是很大。后来发现是标定板太小,激光雷达点云在板子上只有几十个点,根本拟合不出平面。换了大标定板后,问题就解决了。标定板的尺寸至少要在1米×1米以上。

外参标定的数学本质是求解一个刚体变换:

P_cam = R * P_lidar + t

其中P_cam是相机坐标系下的点,P_lidar是激光雷达坐标系下的点。R是3x3的旋转矩阵,t是3x1的平移向量。

求解这个方程,至少需要3对不共线的对应点。实际中我们会用几十对点,然后用最小二乘法或RANSAC来求解,提高鲁棒性。

2.6 坐标系统一:BEV感知的"地基"

在BEV感知中,所有传感器的数据最终都要转换到同一个坐标系下——通常是自车坐标系(车身坐标系)。

转换流程如下:

  1. 将相机像素坐标转换到相机坐标系(利用内参)
  2. 将相机坐标系下的点转换到自车坐标系(利用相机外参)
  3. 将激光雷达坐标系下的点转换到自车坐标系(利用激光雷达外参)
  4. 将IMU数据转换到自车坐标系(利用IMU外参)

这样,所有传感器数据都在同一个"世界"里了。后续的BEV特征提取、目标检测、轨迹预测,都基于这个统一坐标系。

核心要点:坐标系转换的精度,直接决定了BEV感知的上限。如果外参标定误差1度,在50米处的目标就会偏移0.87米。这个误差在高速场景下是致命的。

好了,这一章的内容就到这里。坐标系和传感器基础是BEV感知的"地基",地基不牢,地动山摇。下一章我们会讲如何把相机图像转换成BEV视角,也就是IPM(逆透视变换)和LSS(Lift-Splat-Shoot)方法。到时候你会发现,坐标系转换的功底有多重要。