第二章 坐标系与变换:世界坐标系、机器人坐标系、齐次变换矩阵、四元数与旋转矩阵

做SLAM这么多年,我见过太多新手在坐标系上栽跟头。说句实话,坐标系搞不清楚,后面所有定位算法都是空中楼阁。今天咱们就把这块硬骨头啃下来。

2.1 世界坐标系与机器人坐标系

先问个问题:你站在房间里,怎么描述你手机的位置?

你可能会说「手机在桌上,离我右手边半米」。这个描述里其实藏着两个坐标系:一个是房间的固定坐标系(世界坐标系),一个是以你身体为原点的坐标系(机器人坐标系)。

世界坐标系,说白了就是整个场景的绝对参考系。在SLAM里,我们通常把第一帧相机的位置设为世界坐标系原点。这个坐标系一旦定下来,就不动了。

机器人坐标系,是跟着机器人走的。我习惯叫它「body frame」,原点在机器人中心,x轴朝前,y轴朝左,z轴朝上。嗯,这里要注意,不同厂家的定义可能不一样,做融合的时候一定要看清楚。

核心关系:机器人坐标系里的测量值,必须通过变换才能放到世界坐标系里。这个变换,就是我们要讲的位姿。

我在项目中遇到过一件事:有个同事把激光雷达的数据直接当世界坐标用,结果建出来的地图全是歪的。排查了半天,才发现是忘了做坐标系变换。从那以后,我每次拿到传感器数据,第一件事就是确认它是在哪个坐标系下表达的。

2.2 齐次变换矩阵

齐次变换矩阵,说白了就是用一个4x4的矩阵,同时搞定旋转和平移。你想想看,如果分开做旋转和平移,公式写起来多麻烦?

它的形式长这样:

T = [ R  t ]
    [ 0  1 ]

其中R是3x3的旋转矩阵,t是3x1的平移向量。最后一行是[0,0,0,1],这是齐次坐标的固定格式。

为什么用齐次坐标?因为可以把连续的变换写成矩阵乘法。比如从机器人坐标系到世界坐标系的变换:

p_world = T * p_robot

如果你有多个变换,直接乘起来就行:

T_world_to_camera = T_world_to_robot * T_robot_to_camera

我的习惯:在代码里,我通常用Eigen库的Isometry3d来存储变换矩阵。它比手动拼4x4矩阵安全得多,不容易出错。

2.3 旋转矩阵

旋转矩阵是3x3的正交矩阵,行列式为+1。它描述了一个坐标系相对于另一个坐标系的朝向。

举个例子,绕z轴旋转θ角:

R_z(θ) = [ cosθ  -sinθ  0 ]
         [ sinθ   cosθ  0 ]
         [ 0      0     1 ]

绕x轴和y轴的类似,我就不写了。你记住一个规律:绕哪个轴转,哪个轴对应的行和列就不变。

我曾经踩过的坑:旋转矩阵的乘法顺序很重要!R1 * R2 和 R2 * R1 结果完全不同。在SLAM里,我们通常用右乘表示当前坐标系下的旋转,左乘表示世界坐标系下的旋转。这个搞反了,轨迹会乱成一团。

2.4 四元数

四元数,很多人一听就头大。其实它没那么可怕。说白了,四元数就是用四个数来表示旋转:一个实部加三个虚部。

q = w + xi + yj + zk

其中w是实部,x,y,z是虚部。约束条件是w² + x² + y² + z² = 1。

为什么不用旋转矩阵?因为旋转矩阵有9个参数,但只有3个自由度,存在冗余。做优化的时候,这9个参数还要满足正交约束,很麻烦。四元数只有4个参数,一个归一化约束,用起来方便多了。

我个人的经验:在代码里,我几乎只用四元数做旋转的插值和优化。只有在最后可视化的时候,才转成旋转矩阵给OpenGL用。

2.5 四元数与旋转矩阵的互转

这个转换公式,我建议你直接背下来,或者存成代码片段。每次手推太浪费时间了。

四元数转旋转矩阵:

R = [ 1-2(y²+z²)   2(xy-wz)   2(xz+wy) ]
    [ 2(xy+wz)     1-2(x²+z²) 2(yz-wx) ]
    [ 2(xz-wy)     2(yz+wx)   1-2(x²+y²) ]

旋转矩阵转四元数:

w = sqrt(1 + R[0][0] + R[1][1] + R[2][2]) / 2
x = (R[2][1] - R[1][2]) / (4 * w)
y = (R[0][2] - R[2][0]) / (4 * w)
z = (R[1][0] - R[0][1]) / (4 * w)

避坑指南:当w接近0时,这个公式会不稳定。我一般会判断一下,如果w太小,就改用其他三个元素来算。Eigen库的Quaterniond::fromRotationMatrix()已经处理好了,直接用就行。

2.6 本章知识体系

下面这张图,是我自己总结的坐标系与变换的核心逻辑。你看一遍,应该能理清思路。

世界坐标系 固定参考系 机器人坐标系 随机器人移动 齐次变换矩阵 T 包含旋转R + 平移t 旋转矩阵 (3x3) 9参数,有正交约束 四元数 (w,x,y,z) 4参数,归一化约束 四元数→旋转矩阵 旋转矩阵→四元数 核心:所有变换最终统一到齐次变换矩阵 旋转用四元数存储和优化,用旋转矩阵计算和可视化

2.7 实战建议

说了这么多理论,最后给几条实在的建议:

  • 统一坐标系定义:项目一开始,就把所有坐标系的定义写清楚。x轴朝哪,y轴朝哪,z轴朝哪。别等到代码写了一半才发现定义不一致。
  • 用库,别手写:Eigen、Sophus、Ceres这些库都封装好了变换操作。我见过有人手写四元数乘法,结果符号搞反了,调了三天bug。何必呢?
  • 可视化验证:每次做完坐标系变换,都可视化出来看一眼。RViz或者Matlab都行。眼睛一看,就知道对不对。
  • 单元测试:写一个简单的测试用例,比如把点从机器人坐标系变换到世界坐标系,再变换回来,看是不是回到原点。这个测试能帮你发现90%的坐标系错误。

最后说一句:坐标系变换是SLAM的基石。这块搞明白了,后面的轨迹评估、回环检测、图优化,学起来都会顺畅很多。别急,慢慢来,把基础打牢。

可可精品资料库