4、二维平移与齐次坐标:为什么引入齐次坐标?用矩阵统一表示旋转和平移
好,咱们接着聊坐标变换。
前面我们讲了旋转矩阵,也讲了平移向量。但你有没有发现一个问题——旋转可以用矩阵乘法搞定,但平移却要加一个向量。这就好比你去食堂打饭,旋转是刷卡,平移是掏现金,两种支付方式混着用,特别别扭。
我刚开始做机器人控制那会儿,就吃过这个亏。写代码时旋转和平移分开处理,结果一个不小心,平移忘了加,机械臂直接撞上了工件。嗯,从那以后我就下定决心,必须找个办法把旋转和平移统一起来。
4.1 问题出在哪?
先看看我们现在的处境。
对于一个二维点 (x, y),旋转可以写成:
[x'] [cosθ -sinθ] [x]
[y'] = [sinθ cosθ] [y]
平移呢?
[x'] [x] [tx]
[y'] = [y] + [ty]
你看,一个是乘法,一个是加法。这就像你左手拿筷子右手拿勺子,虽然也能吃饭,但效率低啊。
更麻烦的是,如果你要做一连串的变换——先旋转、再平移、再旋转——代码就会变成这样:
// 先旋转
x1 = x * cosθ - y * sinθ
y1 = x * sinθ + y * cosθ
// 再平移
x2 = x1 + tx
y2 = y1 + ty
// 再旋转
x3 = x2 * cosφ - y2 * sinφ
y3 = x2 * sinφ + y2 * cosφ
这代码看着就头疼。而且每次都要记住当前是旋转还是平移,特别容易出错。
4.2 齐次坐标:一个巧妙的"升维"
那怎么解决呢?
我个人的习惯是——遇到问题先想想能不能"升维"。说白了,就是给坐标多加一个维度。
我们把二维点 (x, y) 变成三维点 (x, y, 1)。这个多出来的 1 就是齐次坐标的"齐次分量"。
你可能会问:这不是把简单问题搞复杂了吗?
别急,看看效果。
现在,平移可以写成矩阵乘法了:
[x'] [1 0 tx] [x]
[y'] = [0 1 ty] [y]
[1 ] [0 0 1] [1]
旋转也一样:
[x'] [cosθ -sinθ 0] [x]
[y'] = [sinθ cosθ 0] [y]
[1 ] [0 0 1] [1]
看到了吗?现在旋转和平移都是矩阵乘法了!
核心思想:齐次坐标通过增加一个维度,把加法操作"伪装"成了乘法操作。这样所有变换都可以用矩阵乘法统一表示。
4.3 为什么是"齐次"?
这个名字其实有讲究。
在齐次坐标下,点 (x, y, 1) 和 (2x, 2y, 2) 表示的是同一个二维点。因为你可以把齐次坐标除以最后一个分量,得到笛卡尔坐标:
笛卡尔坐标 (x, y) = 齐次坐标 (x*w, y*w, w) / w
当 w=1 时,就是我们最常用的形式。当 w=0 时,表示无穷远处的点——这在计算机图形学里很有用,但咱们做控制暂时用不上。
小技巧:我写代码时习惯把齐次坐标的最后一个分量始终设为 1。这样既简单又不容易出错。如果你发现某个点的 w 分量不是 1,记得先归一化再使用。
4.4 复合变换:矩阵连乘
有了齐次坐标,复合变换就变得特别优雅。
假设你要先旋转 θ 度,再平移 (tx, ty),再旋转 φ 度。对应的变换矩阵分别是 R1、T、R2。那么总的变换矩阵就是:
M = R2 * T * R1
注意顺序!矩阵乘法不满足交换律。从右往左读:先应用 R1,再应用 T,最后应用 R2。
代码实现就简单多了:
// 定义变换矩阵
R1 = [[cosθ, -sinθ, 0],
[sinθ, cosθ, 0],
[0, 0, 1]]
T = [[1, 0, tx],
[0, 1, ty],
[0, 0, 1]]
R2 = [[cosφ, -sinφ, 0],
[sinφ, cosφ, 0],
[0, 0, 1]]
// 复合变换
M = R2 * T * R1
// 应用变换
p' = M * p
你看,所有变换都统一成了矩阵乘法。代码简洁,逻辑清晰,还不容易出错。
4.5 知识结构图
下面这张图展示了本章的核心逻辑:
4.6 避坑指南
我在实际项目中踩过几个坑,分享给你:
- 矩阵乘法顺序:我曾经把旋转和平移的顺序搞反了,结果机械臂画了个奇怪的弧线。记住:先旋转再平移,和先平移再旋转,结果完全不同。
- 齐次坐标归一化:有些库返回的齐次坐标 w 分量不是 1,记得手动归一化。我见过有人直接用 w=2 的坐标做运算,结果点全跑偏了。
- 浮点数精度:多次矩阵连乘后,误差会累积。我建议每做几次变换就重新归一化一次,保证 w=1。
特别注意:齐次坐标的最后一个分量 w 不能为 0(除非你处理无穷远点)。如果 w=0,你无法得到有效的笛卡尔坐标。我在调试时遇到过这种问题,查了半天才发现是某个矩阵把 w 变成了 0。
4.7 小结
齐次坐标是坐标变换中的一个经典技巧。它通过增加一个维度,把旋转和平移统一成了矩阵乘法。这样一来,复合变换就变成了矩阵连乘,代码实现简单,逻辑清晰,不容易出错。
我个人觉得,这是控制入门必须掌握的一个思想。后面我们会看到,三维空间的变换也是同样的套路——用 4x4 的齐次矩阵统一表示旋转和平移。
好了,这一章就到这里。记住:遇到不统一的操作,试试升维,往往有奇效。
一句话总结:齐次坐标让旋转和平移"握手言和",从此矩阵乘法一统天下。