首先,说明几点需要注意的问题:
a.左手坐标系与右手坐标系:既然是在坐标系中进行变换,首先就要了解坐标系的类别。同样的数据在左手坐标系和右手坐标系会有不同的呈现结果。所以,所有涉及到坐标系的问题,不说明基于左手坐标系还是右手坐标系就有可能带着小白走弯路。下面的文章将分别讨论在右手坐标系和左手坐标系下的两套转换。
b.欧拉角顺规:欧拉角定义了一组物体的旋转次序,绕某个轴旋转多少度。很多人会忽略旋转顺序的问题,很多书上或者博客叫做顺规,可以理解为欧拉角旋转顺序的规定。(α, β, γ)
在不同的旋转顺序下会有不同的结果,先绕X轴旋转α,还是先绕y轴旋转β,最后的结果是不一样的。欧拉角的顺规有很多,例如Z-X-Y, X-Y-Z, X-Y-X, Z-X-Y ···
,其拥有众多的排列组合。所以,涉及到欧拉角旋转的问题,不说明使用的顺序规定就又有可能带着小白走弯路。下面的文章将使用常见Z-X-Y等顺序,具体会注明。
c.旋转的具体形式:物体旋转有多种,绕某个定点进行旋转,绕坐标轴进行旋转,绕任意轴进行旋转。不同的旋转需要用不同的数学方式来表达。网上的大部分文章都是默认讨论坐标轴旋转的,绕点旋转和绕任意轴旋转相对更复杂一点。敲黑板,注意,本文讨论的是三维中绕坐标轴的旋转。
2.右手坐标系下的转换容我自行定义两个概念,方便之后的叙述。
基础旋转矩阵:绕某单一坐标轴进行旋转对应的矩阵,称之为基础旋转矩阵。
组合旋转矩阵:多次旋转组合对应的矩阵,称之为组合旋转矩阵。
其中,基础旋转矩阵的推导过程可以参考文章中三维坐标系下的旋转章节,此处直接使用对应的矩阵。
2.1右手坐标系下基础旋转矩阵
右手坐标系下的基础旋转矩阵包括以下三个:
//在右手系中绕X轴旋转p° 对应的矩阵Rx
| 1 0 0 |
Rx= | 0 cosp -sinp|
| 0 sinp cosp|
//在右手系中绕Y轴旋转h° 对应的矩阵Ry
| cosh 0 sinh|
Ry= | 0 1 0 |
|-sinh 0 cosh|
//在右手系中绕Z轴旋转b° 对应的矩阵Rz
|cosb -sinb 0 |
Rz= |sinb cosb 0 |
| 0 0 1 |
如果物体只是发生了单一的旋转,例如欧拉角(p,0,0)、(0,h,0)、(0,0,b)这样的只是单次旋转,上面对应的矩阵就可以完成欧拉角→旋转矩阵的转化
2.2右手坐标系下组合旋转矩阵
根据组合旋转矩阵的定义也可以了解,多次旋转组合在一起,例如Z-X-Y顺规的欧拉角(30°,62°,28°)就是先绕Z轴旋转28°,然后绕X轴旋转30°,最后绕Y轴旋转62°。单一的旋转矩阵无法满足这样的组合旋转,无法完成对应。此时就需要利用旋转变换的性质:物体绕轴旋转对应的矩阵具有可累乘的性质,即多一次旋转就是多一次矩阵乘法。
因此,组合旋转矩阵就是对应顺序的基础旋转矩阵的乘积。例如Z-X-Y顺规的欧拉角对应的组合旋转矩阵就是R = Rz*Rx*Ry
。如果是X-Y-Z顺规的欧拉角,对应的组合旋转矩阵就是R = Rx*Ry*Rz
。依据这个乘法法则即可获得欧拉角→旋转矩阵的转换。
举一个例子,按照2.1节中的基础旋转矩阵,计算Z-X-Y顺规下对应的旋转矩阵,其结果如下:
//欧拉角(p,h,b)在右手系中对应的旋转矩阵(Z-X-Y顺规)
|cosbcosh-sinbsinpsinh -sinbcosp cosbsinh+sinbsinpcosh|
R = Rz*Rx*Ry = |sinbcosh+cosbsinpsinh cosbcosp sinbsinh-cosbsinpcosh|
| -cospsinh sinp cospcosh |
2.3 由旋转矩阵计算特定顺规的欧拉角
旋转矩阵一般是指3*3
的正交矩阵,如果是4*3的运动矩阵,取前三行也就是旋转矩阵,给定旋转矩阵R
| m11 m12 m13 |
M=| m21 m22 m23 |
| m31 m32 m33 |
需要计算哪一种顺规的欧拉角,就使得M=R
列方程进行求解。下面以Z-X-Y最常用的顺规来举例说明求解过程中的技巧:
| m11 m12 m13 | |cosbcosh-sinbsinpsinh -sinbcosp cosbsinh+sinbsinpcosh|
M=| m21 m22 m23 | = |sinbcosh+cosbsinpsinh cosbcosp sinbsinh-cosbsinpcosh| = R=Rz*Rx*Ry
| m31 m32 m33 | | -cospsinh sinp cospcosh |
不同顺规欧拉角对应的组合旋转矩阵通常都会有一列和一行数据的形式相对简单,在上面的例子中是第2列和第3行的数据形式相对简单。求解三个角度值p h b
从该行该列的数据入手。
通过sinp = m32
求解角度p = asin(m32)
;
通过m31 m33
求解角度h = atan2(-m31/cosp , m33/cosp)
,值得注意的只与两个参数的比例值有关系,与绝对大小没有关系,所以进一步化简为h = atan2(-m31, m33)
;
通过m12, m22
求解角度b = atan2(-m12/cosp, m22/cosp)
,同理化简为h = atan2(-m12, m22)
。
注:float atan2(float param1_sin , float param2_cos)
是C++的函数,后面的参数分别是正弦值和余弦值,同时可以同比例缩放,该函数返回值该正弦值和余弦值对应的角度值。可以在网上查到相关的资料。
讲旋转矩阵转化为其他顺规的欧拉角也是与上面的例子一样,只是采取的特定的矩阵不一样。计算的技巧和思路是一样的。
3.左手坐标系的转换左手坐标系与右手坐标系下的转换的思路是一样的,不同的地方是基础旋转矩阵,基础旋转矩阵的不同也就造成了对应的组合旋转矩阵不同。下面将直接给出相应的数据,思路可以参考2.右手坐标系相关的讲解。
3.1左手系下的基础旋转矩阵
基础旋转矩阵的差异体现在sin值的取正负的问题上。仔细分析,这是可以理解的。在右手坐标系下绕X轴旋转p°,相当于左手系中旋转-p°,而sin(-p) = - sin(p) ; cos(p) = cos(-p)
,所以有下面的差异。
//在左手系中绕X轴旋转p° 对应的矩阵Rx
| 1 0 0 |
Rx= | 0 cosp sinp|
| 0 -sinp cosp|
//在左手系中绕Y轴旋转h° 对应的矩阵Ry
| cosh 0 -sinh|
Ry= | 0 1 0 |
| sinh 0 cosh|
//在左手系中绕Z轴旋转b° 对应的矩阵Rz
|cosb sinb 0 |
Rz= |-sinb cosb 0 |
| 0 0 1 |
3.2左手系下的组合旋转矩阵
组合矩阵的计算方式依旧是矩阵连乘,由于基础旋转矩阵发生了变化,对应的组合旋转矩阵也将发生变化。此处以Z-X-Y顺规的欧拉角为例,其对应的组合旋转矩阵为R1,可以看出与右手系下的区别也体现在某些项的正负号,也与上面的sin变号的原理相同。
//欧拉角(p,h,b)在右手系中对应的旋转矩阵(Z-X-Y顺规)
|cosbcosh+sinbsinpsinh sinbcosp -cosbsinh+sinbsinpcosh|
R1 = Rz*Rx*Ry = |-sinbcosh+cosbsinpsinh cosbcosp sinbsinh+cosbsinpcosh|
| cospsinh -sinp cospcosh |
3.3 左手系中矩阵转化成欧拉角
求解的方式与右手系下的过程一致,只是最后的结果在正负号上有差异。
| m11 m12 m13 | |cosbcosh+sinbsinpsinh sinbcosp cosbsinh+sinbsinpcosh|
M=| m21 m22 m23 | = |-sinbcosh+cosbsinpsinh cosbcosp sinbsinh+cosbsinpcosh| = R=Rz*Rx*Ry
| m31 m32 m33 | | cospsinh -sinp cospcosh |
通过-sinp = m32
求解角度p = asin(-m32)
;
通过m31 m33
求解角度h = atan2(m31/cosp , m33/cosp)
,值得注意的只与两个参数的比例值有关系,与绝对大小没有关系,所以进一步化简为h = atan2(m31, m33)
;
通过m12, m22
求解角度b = atan2(-m12/cosp, m22/cosp)
,同理化简为h = atan2(-m12, m22)
。
在由欧拉角计算对应的旋转矩阵的时候,套用对应的矩阵公式即可。在由旋转矩阵方向计算欧拉角的时候需要特别注意万向锁的情况。上面所有的计算过程都没有考虑这个问题,此处单独列出来说明。
万向锁又叫Gimble lock,很多人可能都听说过,但是不清楚为什么在由旋转矩阵计算欧拉角的时候要考虑万向锁。在我们上面2.3和3.2节求解中,我们都将cosp
作为分母,默认cosp !=0
。
下面单独考虑cosp ==0
的情况。当cosp =0
的时候sinp → ±1
,也就是p = ±90°
。百度百科这次非常靠谱的介绍了90°与万向锁的关系,引用至此。
p=±90°的时候,如果采用的是动态欧拉角就会出现万向锁。此时的处理方法就是:在计算欧拉角的时候,首先判断sinp的取值是不是无限接近于1,如果是采用《3D数学基础:图形与游戏开发》10.6.2章节中的修正内容,即在使用R=M
中将R矩阵中做替换cosp=0; b=0; sinb=0;cosb=1
。修正的方法相对简单,也不是本文的重点,此处不具体讲。