欧拉角&四元数&旋转矩阵小记

您所在的位置:网站首页 欧拉角的转动顺序 欧拉角&四元数&旋转矩阵小记

欧拉角&四元数&旋转矩阵小记

2024-06-09 11:44| 来源: 网络整理| 查看: 265

写在前

以下公式都是前辈们的智慧,自己也是工作期间做个总结。有些是看视频时的一些截图有些是看文章时的一些笔记,就没有一一贴引用。大佬们请包涵

因我目前对Unity相对更熟悉,所以本文内很多数据是基于Unity的规则推导和计算。

以下内容中欧拉角旋转顺序以YXZ,Unity的坐标系【左手】,矩阵列主序,使用4X4 & 3X3矩阵,矩阵相乘顺序为从左到右。代码演示为Unity C#

但其他的软件,其他的坐标系都可以采用文档内的方法进行推算。只不过推导的过程会有点伤头发罢了。文档内的很多公式在一些图形学、渲染类的书里可能有,但各位看时要注意下欧拉角旋转顺序之类的前提条件

编程可有意思了欧拉角简介

1、Euler,欧拉角相关Wiki:https://en.wikipedia.org/wiki/Euler_angles;百度百科:https://baike.baidu.com/item/%E6%AC%A7%E6%8B%89%E8%A7%92/1626212?fr=aladdin

2、通常认为欧拉角有12种定义方式,三轴旋转如XYZ等,共3的阶层6种:XYZ,XZY,YXZ,YZX,ZXY,ZYX; 二轴旋转如XYX,也是6种:XYX,XZX,YXY,YZY,ZXZ,ZYZ。所以欧拉角一定要看其定义的旋转方向

3、Unity & Unreal中的欧拉角。Unity中欧拉角旋转顺序为ZXY【但要注意矩阵相乘最终会写为MatrixRotateY * MatrixRotateX * MatrixRotateZ】。Unreal中,世界坐标系和局部坐标系中均为XYZ。

4、如何确定欧拉角顺序,很简单,选好World&Local坐标系后,先旋转X轴,再旋转Y轴,看对象第二次旋转是否沿Y轴。如果沿Y轴,那么证明Y方向旋转在X之后;如果第二次旋转的轴向诡异并不是Y轴方向,则证明Y方向旋转应该在X之前。以此方法依次判断XY;YZ;XZ等先后顺序自然能算出其旋转顺序。例如我自己,我只知道Unity的旋转顺序,Unreal World&Local的旋转顺序就是这么测出来的

5、欧拉角的好处就是非常直观且只需要存储3个轴的旋转信息,需要3个数据可以节省内存空间,所以在编辑器模式是非常常用的表示旋转的手段

旋转矩阵简介

1、Rotation Matrix,旋转矩阵相关Wiki:https://en.wikipedia.org/wiki/Rotation_matrix;百度百科:https://baike.baidu.com/item/%E6%97%8B%E8%BD%AC%E7%9F%A9%E9%98%B5/3265181?fr=aladdin

2、旋转矩阵的强大之处更多在于它是矩阵。意味着它可以跟其他的变换很方便的结合&组合

四元数简介

1、Quaternion,四元数相关Wiki:https://en.wikipedia.org/wiki/Quaternion;百度百科:https://baike.baidu.com/item/%E5%9B%9B%E5%85%83%E6%95%B0/5795379?fr=aladdin

2、欧拉角一方面因为要定义旋转顺序;一方面存在万向锁【如大家可以试试在Unity让X轴转90°,然后转Y轴和Z轴看会发生什么】;再者也有不方便做插值等问题。

3、而矩阵虽然强大,但通常要3*4,4*4等存储方法所以会占用比较多空间。且其也不方便做插值,所以游戏中通常还会用四元数来存储旋转信息

欧拉角互转

如Unity和Unreal均是基于左手系但默认如下:

Unity默认坐标轴朝向

把Unity转两下就可以变成这样,跟UE4一样了:

UE默认坐标轴朝向

欧拉角=>旋转矩阵

1、4*4的两个矩阵相乘还是4*4且会将对象的变换效果累积,所以Unity中旋转矩阵 = RotationMatrix_YXZ = RotationMatrix_Y * RotationMatrix_X * RotationMatrix_Z

2、RotationMatrix_Y即:(cosY简写为cY; sinY简写为sY; 纯旋转矩阵后面简写为3 * 3)

cY     0       sY     0

0       1       0       0

-sY    0      cY      0

0       0       0       1

RotationMatrix_X即:

RotationMatrix_Z即:

故而:RotationMatrix_YX = RotationMatrix_Y * RotationMatrix_X

故而:RotationMatrix_YXZ = RotationMatrix_YX * RotationMatrix_Z

代码【unity c#】如下:

Vector3 eulerAngle = transform.localEulerAngles;

float cY = Mathf.Cos(eulerAngle.y * Mathf.Deg2Rad);

float sY = Mathf.Sin(eulerAngle.y * Mathf.Deg2Rad);

float cX = Mathf.Cos(eulerAngle.x * Mathf.Deg2Rad);

float sX = Mathf.Sin(eulerAngle.x * Mathf.Deg2Rad);

float cZ = Mathf.Cos(eulerAngle.z * Mathf.Deg2Rad);

float sZ = Mathf.Sin(eulerAngle.z * Mathf.Deg2Rad);

Matrix4x4 rotationMatrixYXZ = Matrix4x4.identity;

rotationMatrixYXZ.m00 = cY * cZ + sY * sX * sZ;

rotationMatrixYXZ.m01 = cY * -sZ + sY * sX * cZ;

rotationMatrixYXZ.m02 = sY * cX;

rotationMatrixYXZ.m03 = 0;

rotationMatrixYXZ.m10 = cX * sZ;

rotationMatrixYXZ.m11 = cX * cZ;

rotationMatrixYXZ.m12 = -sX;

rotationMatrixYXZ.m13 = 0;

rotationMatrixYXZ.m20 = -sY * cZ + cY * sX * sZ;

rotationMatrixYXZ.m21 = sY * sZ + cY * sX * cZ;

rotationMatrixYXZ.m22 = cY * cX;

rotationMatrixYXZ.m23 = 0;

rotationMatrixYXZ.m30 = 0;

rotationMatrixYXZ.m31 = 0;

rotationMatrixYXZ.m32 = 0;

rotationMatrixYXZ.m33 = 1;

另外:Wiki链接里面有12种旋转方式旋转矩阵结果

旋转矩阵=>欧拉角

float yAngle;

yAngle = Mathf.Atan(rotationMatrixYXZ.m02 / rotationMatrixYXZ.m22) * Mathf.Rad2Deg;

float xAngle;

xAngle = Mathf.Atan(-rotationMatrixYXZ.m12 / Mathf.Sqrt(1 - rotationMatrixYXZ.m12 * rotationMatrixYXZ.m12)) * Mathf.Rad2Deg;

float zAngle;

zAngle = Mathf.Atan(rotationMatrixYXZ.m10 / rotationMatrixYXZ.m11) * Mathf.Rad2Deg;

需要注意的是,其中xAngle还有另一个求法:Mathf.Asin(-rotationMatrixYXZ.m12);其实是一样的。

可能大家注意到y,x,z三轴旋转角度求取过程中存在除法,那除数肯定是不能为0的,否则会报错,

其实这也是万向锁在数据上的原因

欧拉角=>四元数[比较复杂]

1、首先要知道四元数的乘法表示两次旋转的叠加。

2、其次看四元数的乘法。四元数q = (v, w),其中v = xi + yj + zk;   i,j,k为基向量

3、那么四元数q1 * q2 = [(v1 * v2 + w1 * v2 + w2 * v1), (w1*w2 + v1v2)],其中v1 * v2为向量的叉乘,v1v2为向量的点乘

附:叉乘展开:v1 * v2 = (y1z2 - y2z1)jk, (z1x2 - z2x1)ki, (x1y2 - x2y1)ij

附:点乘展开:v1v2 = (x1x2)ii + (y1y2)jj + (z1z2)kk

且四元数基向量相乘满足如下规则:(此规则是根据左手坐标系,检验方法也很简单,左手握拳,四指从旋转的开始轴指向结束轴,大拇指朝向决定正负)

即叉乘结果v1 * v2 = (y1z2 - y2z1)i, (z1x2 - z2x1)j, (x1y2 - x2y1)k

unity基向量相乘

简单以Unity演示i, j, k相乘:

比如从z轴->x轴的握法就是这样:此时大拇指朝上,与y轴同方向,即k * i = j,即图中第四行第二列。(愚蠢的拍屏主义者-1000)

以上面的公式的乘法,向量点乘,向量叉乘全部展开后可以得到如下结果:

q1 * q2 = (w1x2 + w2x1 + y1z2 - y2z1)i , (w1y2 + w2y1 + z1x2 - z2x1)j , (w1z2 + w2z1 + x1y2 - x2y1)k , (w1w2 - x1x2 - y1y2 -z1z2)

即:

q_result.x = (w1x2 + w2x1 + y1z2 - y2z1)

q_result.y = (w1y2 + w2y1 + z1x2 - z2x1)

q_result.z = (w1z2 + w2z1 + x1y2 - x2y1)

q_result.w = (w1w2 - x1x2 - y1y2 -z1z2)

得到了四元数相乘的的计算方式后,再进行欧拉角转四元数的计算就简单[Doge]了。并且跟欧拉角转旋转矩阵的计算方式类似。

即绕y轴旋转的四元数 * 绕x轴旋转的四元数 * 绕z轴旋转的四元数 带入以上q1 * q2公式 =

q1 * q2

即:quat_Y * quat_X的结果再跟quat_Z相乘【如何相乘参考上文q1 * q2 = q_result】

最终结果如下代码【unity c#】

Quaternion quat;  // 要注意参与四元数运算的弧度数为欧拉角旋转度数的一半,勿搞错

float cHalfY = Mathf.Cos(transform.localEulerAngles.y * 0.5f * Mathf.Deg2Rad);

float sHalfY = Mathf.Sin(transform.localEulerAngles.y * 0.5f * Mathf.Deg2Rad);

float cHalfX = Mathf.Cos(transform.localEulerAngles.x * 0.5f * Mathf.Deg2Rad);

float sHalfX = Mathf.Sin(transform.localEulerAngles.x * 0.5f * Mathf.Deg2Rad);

float cHalfZ = Mathf.Cos(transform.localEulerAngles.z * 0.5f * Mathf.Deg2Rad);

float sHalfZ = Mathf.Sin(transform.localEulerAngles.z * 0.5f * Mathf.Deg2Rad);

quat.w = sHalfY * sHalfX * sHalfZ + cHalfY * cHalfX * cHalfZ;

quat.x = sHalfY * sHalfZ * cHalfX + sHalfX * cHalfY * cHalfZ;

quat.y = sHalfY * cHalfX * cHalfZ - sHalfX * sHalfZ * cHalfY;

quat.z = -sHalfY * sHalfX * cHalfZ + sHalfZ * cHalfY * cHalfX;

可以看到欧拉角转四元数的推导运算是相当复杂的,点乘,叉乘,虚数相乘,类似矩阵相乘……,此处的推导是最麻烦的。

四元数=>欧拉角

直接上代码【unity c#】,不再赘述【直接记的公式】

int axisCount = 3;

float[] axisRotatorRadins = new float[axisCount];

string eulerRotationOrder = "yxz";

float r11 = 2 * (quat.x * quat.z + quat.w * quat.y);  // 三角函数运算展开后是矩阵的m02

float r12 = quat.w * quat.w - quat.x * quat.x - quat.y * quat.y + quat.z * quat.z;  // 矩阵的m22

float r21 = -2 * (quat.y * quat.z - quat.w * quat.x);  // 三角函数运算展开后是矩阵的-m12

float r31 = 2 * (quat.x * quat.y + quat.w * quat.z);  // 三角函数运算展开后是矩阵的m10

float r32 = quat.w * quat.w - quat.x * quat.x + quat.y * quat.y - quat.z * quat.z;  // 矩阵的m11

axisRotatorRadins[0] = Mathf.Atan2(r11, r12);

axisRotatorRadins[1] = Mathf.Asin(r21);

axisRotatorRadins[2] = Mathf.Atan2(r31, r32);

for (int i = 0; i < axisCount; i++)

{

   Debug.Log($"绕{eulerRotationOrder[i].ToString()}轴旋转了{(axisRotatorRadins[i] * Mathf.Rad2Deg).ToString()}度");

}

四元数=>旋转矩阵

直接上代码【unity c#】,不再赘述【直接记的公式】

Matrix4x4 calMatrix = Matrix4x4.identity;

calMatrix.m00 = 1 - 2 * quat.y * quat.y - 2 * quat.z * quat.z;

calMatrix.m01 = 2 * quat.x * quat.y - 2 * quat.w * quat.z;

calMatrix.m02 = 2 * quat.x * quat.z + 2 * quat.w * quat.y;

calMatrix.m03 = 0;

calMatrix.m10 = 2 * quat.x * quat.y + 2 * quat.w * quat.z;

calMatrix.m11 = 1 - 2 * quat.x * quat.x - 2 * quat.z * quat.z;

calMatrix.m12 = 2 * quat.y * quat.z - 2 * quat.w * quat.x;

calMatrix.m13 = 0;

calMatrix.m20 = 2 * quat.x * quat.z - 2 * quat.w * quat.y;

calMatrix.m21 = 2 * quat.y * quat.z + 2 * quat.w * quat.x;

calMatrix.m22 = 1 - 2 * quat.x * quat.x - 2 * quat.y * quat.y;

calMatrix.m23 = 0;

calMatrix.m30 = 0;

calMatrix.m31 = 0;

calMatrix.m32 = 0;

calMatrix.m33 = 1;

旋转矩阵=>四元数

直接上代码【unity c#】,不再赘述【直接记的公式】

Matrix4x4 matrixRotator = Matrix4x4.Rotate(transform.localRotation);

Quaternion quat = Quaternion.identity;

quat.w = Mathf.Sqrt(matrixRotator.m00 + matrixRotator.m11 + matrixRotator.m22 + 1) / 2;

quat.x = Mathf.Sqrt(matrixRotator.m00 - matrixRotator.m11 - matrixRotator.m22 + 1) / 2;

quat.y = Mathf.Sqrt(matrixRotator.m11 - matrixRotator.m00 - matrixRotator.m22 + 1) / 2;

quat.z = Mathf.Sqrt(matrixRotator.m22 - matrixRotator.m00 - matrixRotator.m11 + 1) / 2;

Debug.Log($"({quat.x.ToString()}, {quat.y.ToString()}, {quat.z.ToString()}, {quat.w.ToString()})");

此处其实还有另一种计算方法,其实可以看到矩阵运算种:

calMatrix.m21 = 2 * quat.y * quat.z + 2 * quat.w * quat.x;

calMatrix.m12 = 2 * quat.y * quat.z - 2 * quat.w * quat.x;

也就是说m21 - m12 = 4 * quat.w * quat.x

那么知道了quat.w后,两边都除以4 * quat.w就得到了quat.x;同理y和z

本文结构和部分知识参考了知乎文章:https://zhuanlan.zhihu.com/p/45404840?from=groupmessage。但此文中有些地方会变换坐标系,左右手系等,感兴趣可看链接文章

另附

1977年的论文,里面给了12种欧拉角旋转方式相关的矩阵,四元数计算结果【没有过程,且只有欧拉角=>旋转矩阵;旋转矩阵=>欧拉角;欧拉角=>四元数】。但注意右手系,1977年就已经推导好了这些信息,图形学发展日新月异.gif

链接如下:https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770019231.pdfntrs.nasa.gov

Henderson, D.M.. Euler angles, quaternions, and transformation matrices for space shuttle analysis[C]//NASA, Jun 09, 1977



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3