1. 项目概述:从“怎么转”到“怎么算”的思维跃迁
搞过机器人运动学、三维图形渲染或者无人机姿态解算的朋友,对“旋转矩阵”这个词肯定不陌生。它就像三维空间里的“万能扳手”,任何描述物体方向、进行坐标变换的活儿,几乎都绕不开它。网上教程很多,但很多要么一上来就甩给你一个最终的矩阵公式,让人知其然不知其所以然;要么推导过程跳跃太大,满篇的三角函数和向量运算,看得人头大。这次,咱们不玩虚的,就从一个最朴素的问题出发:一个三维空间里的点,绕着一根过原点的轴旋转了一个角度之后,它的新坐标到底该怎么算?目标只有一个:把“旋转矩阵”这个黑盒子打开,看看里面的齿轮是怎么咬合的,让每个步骤都清晰可见,真正做到“包看懂”。
你可能会想,我直接用某个库(比如ROS里的TF,或者三维引擎里的变换函数)不就行了?确实,对于应用来说,调用接口足矣。但当你需要自己写一个滤波器(比如ESKF,误差状态卡尔曼滤波)、推导一个控制算法(比如PMSM的FOC矢量控制中的坐标变换)、或者优化一段图形渲染的代码时,不理解底层数学,就像开车不懂交规,迟早要出问题。那些网络热词里提到的“雅可比行列式推导”、“等幅变化推导”、“罗德里格旋转公式”,其核心思想都与旋转矩阵息息相关。理解了这个基础,再看那些高级话题,就会有一种豁然开朗的感觉。
所以,这篇文章是写给需要“知其所以然”的工程师、学生和爱好者的。我们将从二维旋转这个更简单的“热身运动”开始,逐步搭建起三维旋转的直观图像和严谨数学。我会尽量用画图般的语言来描述,并穿插一些在机器人编程和图形处理中实际踩过的“坑”。放心,我们只需要基础的线性代数知识(向量、矩阵乘法)和一点点空间想象力,就能一起完成这次推导之旅。
2. 思维基石:二维旋转的温故与知新
在冲击三维之前,我们必须把二维旋转吃透。这绝非多此一举,因为三维旋转的核心思想,在二维中已经全部登场了。
2.1 单位圆的几何直观
想象一个二维平面,上面有一个点P, 它的坐标是(x, y)。现在,我们让这个点绕坐标原点O逆时针旋转一个角度θ, 到达新位置P', 坐标是(x', y')。我们的任务就是找到(x', y')和(x, y, θ)之间的关系。
最清晰的思路,是把点P看作一个从原点出发的向量。这个向量的长度是r, 与 x 轴正方向的夹角是φ。那么根据三角函数,原始坐标可以表示为:x = r * cos(φ)y = r * sin(φ)
旋转之后,向量的长度r保持不变(我们讨论的是刚体旋转,没有缩放),但夹角增加了θ, 变成了φ + θ。因此,新坐标是:x' = r * cos(φ + θ)y' = r * sin(φ + θ)
关键提示:这里就是整个推导的第一个“心法”——旋转不改变向量的长度(模长),只改变其方向。这个性质在三维乃至更高维都成立,是旋转矩阵必须是“正交矩阵”的几何根源。
2.2 三角恒等式的代数演绎
接下来,我们利用三角函数的和角公式来展开x'和y':x' = r * cos(φ + θ) = r * [cos(φ)cos(θ) - sin(φ)sin(θ)]y' = r * sin(φ + θ) = r * [sin(φ)cos(θ) + cos(φ)sin(θ)]
注意到r * cos(φ)就是原来的x,r * sin(φ)就是原来的y。我们进行代换:x' = x * cos(θ) - y * sin(θ)y' = x * sin(θ) + y * cos(θ)
看!我们得到了一个纯代数关系。旋转后的坐标(x', y')是原始坐标(x, y)的线性组合。所谓线性组合,就是新坐标的每个分量,都是旧坐标各分量乘以一个系数后再相加。这种关系,天生就是为矩阵乘法准备的。
2.3 矩阵形式的华丽登场
我们把上面的方程组写成矩阵形式:
[ x' ] = [ cos(θ) -sin(θ) ] * [ x ] [ y' ] [ sin(θ) cos(θ) ] [ y ]或者更简洁地:P' = R(θ) * P
其中,这个 2x2 的矩阵R(θ)就是二维旋转矩阵。它包含了旋转操作的全部信息。你可以这样理解矩阵的每一列:
- 第一列
[cos(θ); sin(θ)]是原始 x 轴基向量[1; 0]旋转 θ 角后的新位置。 - 第二列
[-sin(θ); cos(θ)]是原始 y 轴基向量[0; 1]旋转 θ 角后的新位置。
实操心得:这个“列向量是基向量旋转后的位置”的视角极其重要。在三维中,它将是我们理解和记忆复杂旋转矩阵的钥匙。当你记不住绕某个轴旋转的矩阵时,试着去想象这个轴的基向量旋转后的样子,往往就能自己推导出来。
2.4 二维旋转矩阵的重要性质
让我们审视一下这个矩阵R(θ):
- 正交性:计算一下R(θ)^T * R(θ)(T表示转置),你会发现结果等于单位矩阵I。这意味着旋转操作不会拉伸或压缩向量,只改变方向。矩阵的逆就是它的转置,几何上对应反向旋转:R(θ)^-1 = R(θ)^T = R(-θ)。
- 行列式为1:
det(R(θ)) = cos^2(θ) + sin^2(θ) = 1。行列式为1保证了旋转不改变面积(在二维中)或体积(在三维中),且保持了手性(不包含镜像反射)。
掌握了这些,你的二维旋转基础就非常牢固了。接下来,我们将带着这些洞察,向三维世界进发。
3. 三维旋转的核心:绕坐标轴旋转
三维空间多了一个维度,旋转似乎变复杂了,但我们可以化繁为简:任何复杂的三维旋转,都可以分解为绕三个基本坐标轴(X, Y, Z轴)的旋转组合。因此,我们先搞定这三个最简单的“原子操作”。
3.1 绕Z轴旋转:二维旋转的升维
绕Z轴旋转是最容易理解的,因为它完全发生在XY平面内,Z坐标保持不变。这简直就是二维旋转的直接扩展。
几何想象:你俯视XY平面,Z轴垂直屏幕指向你。一个点绕Z轴旋转,就像在桌面上转动一枚硬币,它的高度(Z值)不变。
推导过程:既然Z坐标不变,我们只需关心X和Y坐标的变化。直接套用二维旋转公式,并补上不变的Z坐标:
x' = x * cos(θ) - y * sin(θ) y' = x * sin(θ) + y * cos(θ) z' = z写成矩阵形式,就是绕Z轴的旋转矩阵R_z(θ):
[ x' ] [ cos(θ) -sin(θ) 0 ] [ x ] [ y' ] = [ sin(θ) cos(θ) 0 ] * [ y ] [ z' ] [ 0 0 1 ] [ z ]记忆技巧:看这个矩阵,它几乎就是二维旋转矩阵嵌入了一个3x3矩阵的左上角,右下角的1表示Z轴基向量[0; 0; 1]在绕自身旋转时保持不变。
3.2 绕X轴旋转:抓住不变的轴
绕X轴旋转,意味着X坐标不变,旋转发生在YZ平面。
几何想象:你从X轴正方向看过去,YZ平面就像一张纸,点在这张纸上绕原点旋转。
推导过程:此时,我们把Y和Z看作“新二维平面”的横纵坐标。注意,从X轴正向看,Y轴向右,Z轴向上,构成一个右手坐标系。一个点绕X轴逆时针旋转θ角(右手螺旋定则,拇指指向X轴正方向),在YZ平面内的变换,完全类比二维旋转。 因此:
x' = x y' = y * cos(θ) - z * sin(θ) // 类比 x' = x*cos - y*sin, 这里y对应x, z对应y z' = y * sin(θ) + z * cos(θ) // 类比 y' = x*sin + y*cos写成矩阵形式,得到绕X轴的旋转矩阵R_x(θ):
[ x' ] [ 1 0 0 ] [ x ] [ y' ] = [ 0 cos(θ) -sin(θ) ] * [ y ] [ z' ] [ 0 sin(θ) cos(θ) ] [ z ]记忆技巧:矩阵的第一行和第一列非常稀疏,只有(1,1)位置是1,这正对应了X轴基向量[1; 0; 0]在绕自身旋转时不变。右下角的2x2块,就是YZ平面的二维旋转矩阵。
3.3 绕Y轴旋转:最易混淆的一环
绕Y轴旋转是很多人容易记错的一个,因为它的正负号位置和X/Z轴不同。关键在于确定“从Y轴正向看过去”的二维平面是什么。
几何想象:你从Y轴正方向看过去,此时,Z轴指向你的右侧,X轴指向你的上方。注意,这不是我们习惯的“X右Y上”的平面,而是“Z右X上”的平面。
推导过程:在这个视角下,我们把 (Z, X) 当作二维平面的 (x, y) 坐标。一个点绕Y轴逆时针旋转θ角(右手螺旋,拇指指向Y正方向),在这个“ZX平面”内的变换为:
z' = z * cos(θ) - x * sin(θ) // 新“横坐标” x' = z * sin(θ) + x * cos(θ) // 新“纵坐标” y' = y // Y坐标不变我们需要把它整理回 (x', y', z') 的标准顺序。注意,上面第二式已经是x'了。所以:
x' = x * cos(θ) + z * sin(θ) // 重新排列项的顺序,更美观 y' = y z' = -x * sin(θ) + z * cos(θ) // 重新排列项的顺序写成矩阵形式,得到绕Y轴的旋转矩阵R_y(θ):
[ x' ] [ cos(θ) 0 sin(θ) ] [ x ] [ y' ] = [ 0 1 0 ] * [ y ] [ z' ] [ -sin(θ) 0 cos(θ) ] [ z ]重要注意事项:这里sin(θ)的符号是易错点。记住一个规律:在绕Y轴旋转的矩阵中,sin(θ)出现在第一行第三列(对应x'包含 +zsin)和第三行第一列(对应z'包含 -xsin),并且这两个sin的符号相反。你可以通过检查基向量来验证:Y轴基向量[0;1;0]不变,对应中间一行;X轴基向量[1;0;0]旋转后变为[cos; 0; -sin],对应第一列;Z轴基向量[0;0;1]旋转后变为[sin; 0; cos],对应第三列。
3.4 三个基本旋转矩阵的总结与验证
为了方便查阅和对比,我们把三个基本矩阵列在一起:
| 旋转轴 | 旋转矩阵 R(θ) | 记忆要点 |
|---|---|---|
| X轴 | [1, 0, 0; 0, cosθ, -sinθ; 0, sinθ, cosθ] | X不变,右下角是YZ平面旋转。注意:-sinθ在(2,3)位置。 |
| Y轴 | [cosθ, 0, sinθ; 0, 1, 0; -sinθ, 0, cosθ] | Y不变。特别注意:sinθ在(1,3)为正,(3,1)为负。这是由ZX平面的视角决定的。 |
| Z轴 | [cosθ, -sinθ, 0; sinθ, cosθ, 0; 0, 0, 1] | Z不变,左上角是XY平面旋转。 |
实操心得:在实际编程中(比如机器人学中的欧拉角变换),千万不要死记硬背。我推荐两种防错方法:1)画图法:在纸上快速画出坐标系,标出旋转轴和正方向,推导基向量的新位置。2)测试法:写一个简单的测试脚本,用一个小角度(比如30度)分别旋转单位向量
[1,0,0],[0,1,0],[0,0,1],看看结果是否符合你的空间想象。例如,绕Y轴旋转90度,[1,0,0](X轴)应该变成[0,0,-1](负Z轴),代入R_y(90°)矩阵计算一下就能验证。
4. 组合旋转与欧拉角的迷思
掌握了绕单轴旋转,我们就有能力描述任何复杂旋转了吗?理论上是的,但我们需要一个系统的方法来组合它们。这就是欧拉角(Euler Angles)登场的原因,但它也是无数困惑的源头。
4.1 欧拉角:一种直观但危险的描述
欧拉角用三个连续的绕轴旋转来描述一个刚体的最终朝向。最常见的是“ZYX”顺序,也称为“偏航-俯仰-滚转”(Yaw-Pitch-Roll),广泛用于航空航天和车辆描述。
- 偏航角 (Yaw, ψ):绕Z轴旋转。
- 俯仰角 (Pitch, θ):绕旋转后的Y轴旋转。
- 滚转角 (Roll, φ):绕两次旋转后的X轴旋转。
注意关键词:“旋转后的轴”。这意味着旋转不是绕固定的世界坐标系轴进行的,而是绕随着物体一起转动的“本体坐标系”轴进行的。这种旋转顺序是内旋(Intrinsic Rotation)。
对应的旋转矩阵是这三个基本旋转矩阵按顺序从右向左相乘:R = R_z(ψ) * R_y(θ) * R_x(φ)
为什么是从右向左?因为我们对一个向量v进行变换时,最先发生的是滚转(φ),然后是俯仰(θ),最后是偏航(ψ)。矩阵乘法是左乘,所以写作:v' = R_z(ψ) * ( R_y(θ) * ( R_x(φ) * v ) ) = (R_z(ψ) * R_y(θ) * R_x(φ)) * v。因此,组合矩阵R就是它们按操作顺序的逆序相乘。
4.2 万向节死锁:欧拉角的阿喀琉斯之踵
欧拉角非常直观,人类很容易理解“偏航30度,俯仰20度”是什么意思。但它有一个致命的缺陷:万向节死锁(Gimbal Lock)。
什么是死锁?当第二个旋转角(俯仰角θ)达到±90度时,第一次旋转(偏航)和第三次旋转(滚转)的旋转轴会重合,丢失一个自由度。从数学上看,此时组合旋转矩阵R中会出现一些项相互抵消,导致从旋转矩阵反解欧拉角时,有无穷多组解(ψ和φ可以任意变化,但效果相同)。
几何解释:想象一个飞机。偏航是机头左右转,俯仰是机头上下抬。当飞机垂直向上(俯仰90度)时,机头指向正上方。此时,你再让它“滚转”,它是在绕机头-机尾轴旋转。但如果你先让它“偏航”,由于它已经笔直向上,偏航操作变成了让它绕垂直轴“自转”,这跟“滚转”操作在物理上效果完全一样了!两个旋转轴重合,你失去了区分偏航和滚转的能力。
避坑指南:在需要连续旋转或进行姿态插值(比如动画)的场景下,尽量避免使用欧拉角。死锁会导致姿态插值路径出现剧烈跳动和不连续。在机器人学中,很多开源库(如ROS的TF)内部都用四元数或旋转矩阵来存储和计算姿态,欧拉角仅作为对人友好的显示接口。
4.3 旋转的串联:顺序至关重要
即使不死锁,旋转的顺序也绝对重要。三维旋转是不可交换的。也就是说:R_x(30°) * R_y(45°) ≠ R_y(45°) * R_x(30°)你可以用双手比划一下:先绕X轴转30度,再绕Y轴转45度;与先绕Y轴转45度,再绕X轴转30度,最终的手势朝向是完全不同的。
这意味着,当你看到一组欧拉角(ψ, θ, φ)时,必须明确其旋转顺序(是ZYX还是ZXY还是别的?)。不同的顺序定义,算出来的最终旋转矩阵天差地别。这是跨平台、跨软件进行姿态数据交换时最常见的错误来源之一。
5. 通用旋转公式:罗德里格斯的智慧
绕坐标轴旋转是特例,欧拉角有死锁问题。那么,有没有一个公式,能直接描述绕空间任意一个单位向量 k = [kx, ky, kz]^T旋转角度 θ 的变换呢?有的,这就是强大的罗德里格斯旋转公式。
5.1 公式的推导思路
推导罗德里格斯公式需要一些向量投影和叉积的知识,但其思想非常优美。我们将任意向量v分解为平行于旋转轴k的分量v_parallel和垂直于k的分量v_perp。
- 平行分量在旋转中保持不变:v_parallel' = v_parallel。
- 垂直分量在一个垂直于k的平面内旋转 θ 角。这个旋转可以看作是在该平面内,由v_perp和k × v_perp(另一个垂直于k和v_perp的向量,长度与v_perp相同)张成的二维旋转。
通过几何和向量运算,最终可以推导出旋转后的向量v'为:v' = v_parallel + cosθ * v_perp + sinθ * (k × v_perp)
利用向量公式v_parallel = (k·v)k和v_perp = v - (k·v)k,以及叉积的向量三重积性质,可以将上式整理成纯向量运算形式,并最终写成矩阵乘法形式。
5.2 最终的旋转矩阵形式
经过推导(过程略,核心是利用叉积矩阵[k]×),我们得到罗德里格斯公式的矩阵形式:v' = R * v其中旋转矩阵R为:R = I + sinθ * K + (1 - cosθ) * K²
这里:
- I是3x3单位矩阵。
- K是旋转轴单位向量k的叉积矩阵(反对称矩阵):
[ 0 -kz ky ] K = [ kz 0 -kx ] [ -ky kx 0 ]- K²是矩阵K的平方。
这个公式非常通用和强大。无论旋转轴k是什么,只要代入这个公式,就能直接得到对应的3x3旋转矩阵。
5.3 罗德里格斯公式的应用与验证
应用场景:
- 轴-角表示法:罗德里格斯公式天然就是“轴-角表示法”到“旋转矩阵”的转换工具。你只需要知道旋转轴k和角度 θ。
- 旋转插值:在三维动画或运动规划中,在两个旋转之间进行平滑插值(如SLERP,球面线性插值)通常先在轴-角或四元数空间进行,但最终需要矩阵来变换顶点。
- 微小旋转的近似:当 θ 非常小时,
sinθ ≈ θ,cosθ ≈ 1。公式简化为R ≈ I + θK。这在推导机器人学中的速度运动学(雅可比矩阵)或IMU的误差模型时非常有用。
验证与特例: 你可以用这个公式来验证之前的基本旋转矩阵。例如,绕Z轴旋转,则k = [0,0,1]^T, 代入公式计算出的R, 正是我们之前推导的R_z(θ)。这是一个很好的练习,能加深你对公式的理解。
实操心得:在代码中实现罗德里格斯公式时,注意k必须是单位向量。一个常见的错误是直接使用未归一化的旋转轴向量,这会导致错误的旋转。另外,虽然公式看起来复杂,但实现起来就是一些矩阵加法和乘法,计算效率很高。对于需要频繁进行任意轴旋转的应用(如逆向运动学求解),这个公式是必备工具。
6. 从矩阵到实践:验证、陷阱与调试
理论推导完了,但如果不落到代码和实际验证上,一切都是空中楼阁。这部分我们来聊聊如何确保你推导或使用的旋转矩阵是正确的,以及在实际项目中会遇到哪些坑。
6.1 旋转矩阵的“健康检查”
一个合法的、描述纯旋转(不含缩放和镜像)的3x3矩阵R, 必须满足两个核心性质,可以作为你代码中的断言(Assert)条件:
- 正交性:R^T * R = I(单位矩阵)。这意味着矩阵的每一行(或每一列)都是单位向量,且彼此正交。在浮点数计算中,允许一个极小的误差(如
1e-6)。 - 行列式为+1:
det(R) ≈ 1。如果行列式是-1,那说明这个矩阵包含了一个镜像反射(就像照镜子),这不是我们想要的刚体旋转。
检查方法:在初始化一个旋转矩阵后(比如从欧拉角计算而来),可以计算np.dot(R.T, R)(Python NumPy)或R.transpose() * R(Eigen C++),看是否接近单位阵。同时计算其行列式。
6.2 浮点数误差的累积与重正化
在连续进行大量旋转矩阵乘法后,即使每个单独的旋转矩阵都是完美的,累积的浮点数误差也会逐渐破坏矩阵的正交性。这会导致物体在反复旋转后发生微小的形变或缩放,在长期仿真或迭代优化中是个严重问题。
解决方案:周期性地重正化(Renormalize)旋转矩阵。一个简单有效的方法是:
- 将矩阵的第一列(或第一行)当作向量a, 将其单位化:a' = a / ||a||。
- 将矩阵的第二列当作向量b, 首先减去它在a'方向上的投影,以消除与a'的不正交分量,然后单位化:b' = (b - (b·a')a') / ||b - (b·a')a'||。
- 第三列c'由前两列的叉积得到:c' = a' × b'。这能保证行列式为+1。 然后将
[a', b', c']作为新的、正交的旋转矩阵。
更稳健的方法是使用奇异值分解(SVD)或QR分解,将近似旋转矩阵分解后,强制其成为最接近的正交矩阵(即令奇异值全为1)。很多数学库(如Eigen)提供了现成的函数来做这个。
6.3 左手系与右手系的“对撞”
这是三维开发中经典的“对不上”问题。旋转矩阵的推导依赖于坐标系的手性(Handedness)。我们上面所有推导都基于右手坐标系(Right-Handed Coordinate System):伸出右手,食指指向X轴正方向,中指指向Y轴正方向,拇指指向Z轴正方向。
然而,许多图形API(如DirectX)和部分CAD软件使用左手坐标系。在左手系中,绕轴旋转的“正方向”定义可能不同(通常用左手螺旋定则),这会导致直接套用右手系公式算出的旋转效果完全相反。
排查步骤:
- 明确环境:首先确定你使用的库、引擎或数据约定的是左手系还是右手系。
- 统一约定:在项目内部,尽量将所有数据转换到同一种手性下进行计算。通常选择右手系作为“世界计算坐标系”更常见。
- 符号检查:如果必须在左手系中使用,最可能出问题的是绕Y轴旋转的矩阵(
R_y)中sinθ的符号,以及叉积的方向。一个简单的验证方法是:在左手系中,绕Y轴旋转90度,X轴正方向应该转向Z轴正方向还是负方向?用你的矩阵乘一下[1,0,0]测试便知。
6.4 旋转矩阵与四元数的抉择
旋转矩阵(3x3)和四元数(4个数)是描述三维旋转最常用的两种方式。它们各有优劣:
| 特性 | 旋转矩阵 | 四元数 |
|---|---|---|
| 直观性 | 直观,列向量即旋转后的坐标轴方向。 | 不直观,四个数没有直接的几何意义。 |
| 存储 | 9个数,有冗余。 | 4个数,更紧凑。 |
| 组合旋转 | 直接矩阵相乘,但计算量较大(27次乘加)。 | 四元数乘法,计算量较小(16次乘加)。 |
| 插值 | 线性插值矩阵元素会导致中间状态不是纯旋转,效果差。 | 球面线性插值非常完美、高效。 |
| 累积误差 | 易失去正交性,需重正化。 | 易失去单位长度,但重正化(除以其模长)非常简单快速。 |
| 万向节死锁 | 从矩阵反解欧拉角时仍会遇到。 | 不存在万向节死锁。 |
选择建议:
- 内部计算与存储:优先使用四元数。它更紧凑、插值性能无敌、没有死锁、误差处理简单。几乎所有现代游戏引擎、机器人中间件(如ROS)内部都用四元数表示姿态。
- 与人交互或最终变换:使用欧拉角(给人看)或旋转矩阵(给GPU渲染管线用)。GPU的顶点变换天然就是矩阵乘法,所以最终传递给着色器的往往是旋转矩阵(或包含旋转的4x4变换矩阵)。
转换关系是双向的,算法都很成熟。重点在于,理解你当前在处理的数据是哪种形式,并在需要时进行正确转换。例如,从IMU读取的可能是四元数,用于滤波算法;滤波后的四元数需要转换为旋转矩阵,用于计算机器人末端执行器的位置。
7. 实战演练:一个完整的坐标变换例子
让我们通过一个具体例子,把前面的知识串起来。假设我们有一个机械臂末端执行器上点的坐标(在本体坐标系{B}中)为p_B = [0.1, 0.2, 0.3]^T米。机械臂基座坐标系{W}相对于世界坐标系是固定的。已知本体坐标系{B}相对于基座坐标系{W}的姿态由以下欧拉角描述(ZYX顺序,即Yaw-Pitch-Roll):偏航 ψ = 30°, 俯仰 θ = 20°, 滚转 φ = 10°。
任务:求该点在世界坐标系{W}中的坐标p_W。
步骤1:将欧拉角转换为旋转矩阵首先,将角度转换为弧度(计算时务必如此)。ψ = 30° = π/6 ≈ 0.5236 radθ = 20° ≈ 0.3491 radφ = 10° ≈ 0.1745 rad
然后,计算三个基本旋转矩阵:
R_z(ψ) = [cosψ, -sinψ, 0; sinψ, cosψ, 0; 0,0,1] ≈ [0.8660, -0.5000, 0; 0.5000, 0.8660, 0; 0,0,1] R_y(θ) = [cosθ, 0, sinθ; 0,1,0; -sinθ, 0, cosθ] ≈ [0.9397, 0, 0.3420; 0,1,0; -0.3420, 0, 0.9397] R_x(φ) = [1,0,0; 0, cosφ, -sinφ; 0, sinφ, cosφ] ≈ [1,0,0; 0, 0.9848, -0.1736; 0, 0.1736, 0.9848]接着,按ZYX顺序组合:R_WB = R_z(ψ) * R_y(θ) * R_x(φ)。注意顺序是从右向左乘。 我们先计算R_y(θ) * R_x(φ), 再用结果左乘R_z(ψ)。 最终得到从{B}到{W}的旋转矩阵R_WB(约数):
R_WB ≈ [0.9254, -0.3188, 0.2049; 0.1632, 0.8232, 0.5438; -0.3420, -0.4698, 0.8138]这个矩阵的每一列,分别代表本体坐标系{B}的X、Y、Z轴在世界坐标系{W}中的指向。
步骤2:进行坐标变换假设本体坐标系{B}的原点在世界坐标系{W}中的位置为t_WB = [1.0, 0.5, 0.2]^T米(即平移向量)。 那么,点从{B}到{W}的完整变换为:p_W = R_WB * p_B + t_WB
计算:
p_W ≈ [0.9254, -0.3188, 0.2049; [0.1] [1.0] 0.1632, 0.8232, 0.5438; * [0.2] + [0.5] -0.3420, -0.4698, 0.8138] [0.3] [0.2] ≈ [0.9254*0.1 + (-0.3188)*0.2 + 0.2049*0.3, ...]^T + [1.0, 0.5, 0.2]^T ≈ [0.0925 - 0.0638 + 0.0615, ...]^T + [1.0, 0.5, 0.2]^T ≈ [0.0902, 0.2796, 0.1264]^T + [1.0, 0.5, 0.2]^T ≈ [1.0902, 0.7796, 0.3264]^T所以,该点在世界坐标系中的坐标约为(1.090, 0.780, 0.326)米。
避坑技巧:在实际编程中,务必清楚你使用的数学库(如Eigen, NumPy, ROS TF)对于旋转矩阵乘法是前置乘法(Pre-multiplication)还是后置乘法(Post-multiplication)。这决定了你如何解释
R * p这个操作。在上面的例子中,我们采用的是前置乘法:R_WB * p_B表示将{B}系下的向量p_B变换到{W}系。如果你的库约定不同,可能需要转置矩阵或调整乘法顺序。最稳妥的方法是,用一组简单的测试向量(如基向量)来验证你的变换链是否正确。