3. 坐标变换与空间同步:刚体变换、欧拉角与四元数、传感器外参标定原理、时间戳对齐方法

各位同学,欢迎来到第三章。

这一章,我们聊聊多传感器融合里最基础、也最绕不开的话题——空间同步

说白了,就是让不同传感器“看”到的世界,能对上号。

你想想看,激光雷达说“前方3米有障碍物”,摄像头说“前方2.8米有障碍物”。到底信谁的?

嗯,很可能它们说的不是同一个坐标系。这就是我们要解决的问题。

3.1 刚体变换:传感器之间的“翻译官”

刚体变换,听起来挺唬人。其实它就是描述一个物体在空间里怎么移动、怎么旋转。

一个刚体,不会变形。它从位置A到位置B,无非就是两步:旋转 + 平移

数学上,我们用旋转矩阵 R 和平移向量 t 来表示:

P_b = R * P_a + t

这里,P_a 是物体在坐标系a下的坐标,P_b 是它在坐标系b下的坐标。

R 是3x3的旋转矩阵,t 是3x1的平移向量。

核心要点:刚体变换不改变物体的形状和大小,只改变它的位姿(位置+姿态)。

我在做第一个融合项目时,就犯过傻。直接把两个传感器的数据叠加,结果点云和图像完全对不上。后来才发现,我忘了做坐标变换。嗯,从那以后,我每次拿到新传感器,第一件事就是确认它的坐标系定义。

3.2 欧拉角与四元数:旋转的两种“语言”

旋转矩阵有9个参数,但自由度只有3个。用起来有点浪费。于是就有了欧拉角和四元数。

3.2.1 欧拉角

欧拉角用三个角度来描述旋转:滚转角(Roll)俯仰角(Pitch)偏航角(Yaw)

你可以想象成:先绕X轴转一个角度,再绕Y轴转一个角度,最后绕Z轴转一个角度。

但欧拉角有个臭名昭著的问题——万向锁

当俯仰角达到±90度时,滚转和偏航会变得无法区分。说白了,就是丢失了一个自由度。

避坑指南:我曾经在无人机项目里用欧拉角做姿态控制。结果飞机做大机动时,姿态解算直接崩了。后来换成四元数,问题才解决。所以,如果你做的是全姿态运动(比如无人机、机器人),千万别用欧拉角。

3.2.2 四元数

四元数,说白了就是一个“超复数”。它用四个数来表示旋转:

q = w + x*i + y*j + z*k

其中,w是实部,x、y、z是虚部。并且满足 i² = j² = k² = ijk = -1。

四元数没有万向锁问题,而且插值平滑。在传感器融合里,我几乎只用四元数。

举个例子,用四元数旋转一个向量:

P_rotated = q * P * q_conjugate

这里,q_conjugate 是 q 的共轭四元数。

我的习惯:在代码里,我通常用Eigen库或ROS的tf库来处理四元数。手写四元数乘法容易出错,相信我,你不想debug那个。

3.3 传感器外参标定原理

外参标定,就是求两个传感器之间的刚体变换矩阵(R, t)。

说白了,就是让激光雷达和摄像头“对齐”。

常用的方法有:

  • 目标法:用标定板(比如棋盘格),让两个传感器同时观测。然后通过特征点匹配,求解R和t。
  • 自然特征法:利用环境中的自然特征(比如角点、边缘)进行匹配。适合在线标定。
  • 手眼标定法:机器人领域常用。通过机械臂的运动,间接求解传感器之间的变换。

我个人最常用的是目标法。简单、可靠。

具体步骤:

  1. 放置标定板,确保两个传感器都能看到。
  2. 从激光雷达点云中提取标定板的中心点。
  3. 从图像中提取标定板的角点。
  4. 建立对应关系,求解PnP问题。
  5. 优化:用非线性最小二乘法(比如Levenberg-Marquardt)优化结果。

注意:标定板的材质很重要。反光太强,激光雷达会打偏。我建议用哑光材质的标定板。

下面这张图,是我自己画的标定流程。你看一眼就明白了。

传感器外参标定流程 放置标定板 提取特征点 (点云中心/图像角点) 建立对应关系 (3D-2D匹配) 求解PnP (RANSAC+优化) 非线性优化 (LM算法) 输出外参(R,t) 迭代优化直到收敛

3.4 时间戳对齐方法

空间同步搞定了,还有时间同步。

不同传感器的采样频率不同。激光雷达10Hz,摄像头30Hz,IMU 200Hz。它们的数据到达时间也不一样。

如果不做时间对齐,你融合的就是“过去”和“未来”的数据。结果可想而知。

常用的方法:

  • 硬件同步:用同一个时钟源(比如GPS的PPS信号)触发所有传感器。精度最高,但硬件成本高。
  • 软件同步:用时间戳插值。比如,摄像头图像的时间戳是t1,激光雷达点云的时间戳是t2。我们找一个最近的匹配。
  • 滤波同步:在卡尔曼滤波框架里,用预测-更新机制来处理异步数据。这是最优雅的方式。

我的经验:在量产项目中,我倾向于硬件同步+软件插值。硬件同步保证时间基准一致,软件插值处理不同频率的数据。这样既可靠又灵活。

举个例子,时间戳对齐的伪代码:

def align_timestamps(lidar_data, camera_data):
    # lidar_data: [(t_l1, pcl1), (t_l2, pcl2), ...]
    # camera_data: [(t_c1, img1), (t_c2, img2), ...]
    
    aligned_pairs = []
    for t_c, img in camera_data:
        # 找到最接近的激光雷达数据
        nearest_lidar = min(lidar_data, key=lambda x: abs(x[0] - t_c))
        aligned_pairs.append((img, nearest_lidar[1]))
    
    return aligned_pairs

这段代码很简单,但实际项目中要考虑更多。比如,时间差超过阈值时要丢弃数据。我曾经因为时间戳没对齐,导致融合后的障碍物位置跳变。排查了三天才找到原因。

警告:不要依赖系统时间戳。不同传感器的时钟漂移不同。一定要用同一个时钟源,或者做时钟同步。

好了,这一章的内容就到这里。

坐标变换和空间同步,是传感器融合的基石。你想想看,如果连“对齐”都做不好,后面的融合、跟踪、决策,全是空中楼阁。

下一章,我们会聊聊滤波与状态估计。嗯,到时候见。


专注资料整理