【引用】四元组与旋转矩阵

2023-11-19

【引用】四元组与旋转矩阵   

2011-09-22 17:13:39|  分类: DirectX资料|举报|字号 订阅

       
下载LOFTER客户端
本文转载自ericyang1231 《四元组与旋转矩阵 》

在3D程序中,通常用quaternion来计算3D物体的旋转角度,与Matrix相比,quaternion更加高效,占用的储存空间更小,此外也更便于插值。在数学上,quaternion表示复数w+xi+yj+zk,其中i,j,k都是虚数单位: 

 

i*i = j*j = k*k= -1

i*j = k, j*i = -k


可以把quaternion看做一个标量和一个3D向量的组合。实部w表示标量,虚部表示向量标记为V,或三个单独的分量(x,y,z)。所以quaternion可以记为[ w, V]或[ w,(x,y,x)]。对quaternion最大的误解在于认为w表示旋转角度,V表示旋转轴。正确的理解应该是w与旋转角度有关,v与旋转轴有关。例如,要表示以向量N为轴,轴旋α度,相对的quaternion应该是:



q = [ cos(α/ 2) , sin(α/ 2) N]

 =[ cos(α/ 2) , ( sina(α/ 2) Nx, sin(α/ 2)Ny, sin(α/ 2)Nz ) ]



为了计算方便,一般要求N为单位矢量。对quaternion来说使用四个值就能记录旋转,而不是Matrix所需的十六个值。为什么用quaternion来计算旋转很方便呢?先说过quaternion是一个复数,如果你还记得一点点复数的知识,那么应该知道复数乘法(叉乘)的几何意义实际上就是对复数进行旋转。对最简单的复数p= x + yi来说,和另一个复数q = ( conα,sinα)相乘,则表示把p沿逆时针方向旋转α:



p’ = pq 





当然,x+yi的形式只能表示2D变换,对3D变换来说就需要使用 quaternion了,而且计算也要复杂一点。为了对3D空间中的一个点p(x,y,z)进行旋转,需要先把它转换为quaternion形式p = [0, ( x, y, z)],接下来前面讨论的内容,定义q = cos(α/ 2) , sin(α/ 2) N为旋转quaternion,这里N为单位矢量长度的旋转轴,α为旋转角度。那么旋转之后的点p’则为:



       p’ = qpq-1
                                                    09-07-27


Re: 如何从四元组得到绕各个轴的旋转角度?       

         void QuaternionToEuler(Quaternion* q, Vector3* vOut) 

double q0 = q->w; 
double q1 = q->x; 
double q2 = q->y; 
double q3 = q->z; 

vOut->x = float(atan2( 2 * (q2*q3 + q0*q1), (q0*q0 - q1*q1 - q2*q2 + q3*q3))); 
vOut->y = float(asin( -2 * (q1*q3 - q0*q2))); 
vOut->z = float(atan2( 2 * (q1*q2 + q0*q3), (q0*q0 + q1*q1 - q2*q2 - q3*q3))); 
}

好吧,我必须承认到目前为止我还没有完全理解四元数,我一度把四元数理解为轴、角表示的4维向量,也就在下午我才从和同事的争辩中理解了四元数不完全是角、轴这么简单,为此写点心得给那些同我一样搞了2年3D游戏的还不清楚四元数的朋友。

为什么使用四元数

为了回答这个问题,先来看看一般关于旋转(面向)的描述方法-欧拉描述法。它使用最简单的x,y,z值来分别表示在x,y,z轴上的旋转角度,其取值为0-360(或者0-2pi),一般使用roll,pitch,yaw来表示这些分量的旋转值。需要注意的是,这里的旋转是针对世界坐标系说的,这意味着第一次的旋转不会影响第二、三次的转轴,简单的说,三角度系统无法表现任意轴的旋转,只要一开始旋转,物体本身就失去了任意轴的自主性,这也就导致了万向轴锁(Gimbal Lock)的问题。

还有一种是轴角的描述方法(即我一直以为的四元数的表示法),这种方法比欧拉描述要好,它避免了Gimbal Lock,它使用一个3维向量表示转轴和一个角度分量表示绕此转轴的旋转角度,即(x,y,z,angle),一般表示为(x,y,z,w)或者(v,w)。但这种描述法却不适合插值。

那到底什么是Gimbal Lock呢?正如前面所说,因为欧拉描述中针对x,y,z的旋转描述是世界坐标系下的值,所以当任意一轴旋转90°的时候会导致该轴同其他轴重合,此时旋转被重合的轴可能没有任何效果,这就是Gimbal Lock,这里有个例子演示了Gimbal Lock,点击这里下载。运行这个例子,使用左右箭头改变yaw为90°,此时不管是使用上下箭头还是Insert、Page Up键都无法改变Pitch,而都是改变了模型的roll。

那么轴、角的描述方法又有什么问题呢?虽然轴、角的描述解决了Gimbal Lock,但这样的描述方法会导致差值不平滑,差值结果可能跳跃,欧拉描述同样有这样的问题。

什么是四元数

四元数一般定义如下:

q=w+xi+yj+zk

其中w是实数,x,y,z是虚数,其中:

i*i=-1

j*j=-1

k*k=-1

也可以表示为:

q=[w,v]

其中v=(x,y,z)是矢量,w是标量,虽然v是矢量,但不能简单的理解为3D空间的矢量,它是4维空间中的的矢量,也是非常不容易想像的。

四元数也是可以归一化的,并且只有单位化的四元数才用来描述旋转(面向),四元数的单位化与Vector类似,

首先||q|| = Norm(q)=sqrt(w2 + x2 + y2 + z2)

因为w2 + x2 + y2 + z2=1

所以Normlize(q)=q/Norm(q)=q / sqrt(w2 + x2 + y2 + z2)

说了这么多,那么四元数与旋转到底有什么关系?我以前一直认为轴、角的描述就是四元数,如果是那样其与旋转的关系也不言而喻,但并不是这么简单,轴、角描述到四元数的转化:

w = cos(theta/2)

x = ax * sin(theta/2)

y = ay * sin(theta/2)

z = az * sin(theta/2)

其中(ax,ay,az)表示轴的矢量,theta表示绕此轴的旋转角度,为什么是这样?和轴、角描述到底有什么不同?这是因为轴角描述的“四元组”并不是一个空间下的东西,首先(ax,ay,az)是一个3维坐标下的矢量,而theta则是级坐标下的角度,简单的将他们组合到一起并不能保证他们插值结果的稳定性,因为他们无法归一化,所以不能保证最终插值后得到的矢量长度(经过旋转变换后两点之间的距离)相等,而四元数在是在一个统一的4维空间中,方便归一化来插值,又能方便的得到轴、角这样用于3D图像的信息数据,所以用四元数再合适不过了。

关于四元数的运算法则和推导这里有篇详细的文章介绍,重要的是一点,类似与Matrix的四元数的乘法是不可交换的,四元数的乘法的意义也类似于Matrix的乘法-可以将两个旋转合并,例如:

Q=Q1*Q2

表示Q的是先做Q2的旋转,再做Q1的旋转的结果,而多个四元数的旋转也是可以合并的,根据四元数乘法的定义,可以算出两个四元数做一次乘法需要16次乘法和加法,而3x3的矩阵则需要27运算,所以当有多次旋转操作时,使用四元数可以获得更高的计算效率。

为什么四元数可以避免Gimbal Lock

在欧拉描述中,之所以会产生Gimbal Lock是因为使用的三角度系统是依次、顺序变换的,如果在OGL中,代码可能这样:

glRotatef( angleX, 1, 0, 0)

glRotatef( angleY, 0, 1, 0)

glRotatef( angleZ, 0, 0, 1)

注意:以上代码是顺序执行,而使用的又是统一的世界坐标,这样当首先旋转了Y轴后,Z轴将不再是原来的Z轴,而可能变成X轴,这样针对Z的变化可能失效。

而四元数描述的旋转代码可能是这样:

TempQ = From Eula(x,y,z)

FinalQ =CameraQ * NewQ

theta, ax, ay, az = From (FinalQ)

glRotatef(theta, ax, ay, az);

其中(ax,ay,az)描述一条任意轴,theta描述了绕此任意轴旋转的角度,而所有的参数都来自于所有描述旋转的四元数做乘法之后得到的值,可以看出这样一次性的旋转不会带来问题。这里有个例子演示了使用四元数不会产生Gimbal Lock的问题。

关于插值

使用四元数的原因就是在于它非常适合插值,这是因为他是一个可以规格化的4维向量,最简单的插值算法就是线性插值,公式如:

q(t)=(1-t)q1+t q2

但这个结果是需要规格化的,否则q(t)的单位长度会发生变化,所以

q(t)=(1-t)q1+t q2 / || (1-t)q1+t q2 ||

如图:

四元组与旋转矩阵  - ericyang1231 - ericyang1231的博客

尽管线性插值很有效,但不能以恒定的速率描述q1到q2之间的曲线,这也是其弊端,我们需要找到一种插值方法使得q1->q(t)之间的夹角θ是线性的,即θ(t)=(1-t)θ1+t*θ2,这样我们得到了球形线性插值函数q(t),如下:

q(t)=q1 * sinθ(1-t)/sinθ + q2 * sinθt/sineθ

如果使用D3D,可以直接使用D3DXQuaternionSlerp函数就可以完成这个插值过程。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【引用】四元组与旋转矩阵 的相关文章

随机推荐

  • java九九乘法表

    package wo public class fi public static void main String args TODO Auto generated method 这串代码可以在创建的时候不勾选 以下是乘法表代码 for i
  • 目标检测综述-P1-yolov1(20201017初稿)

    目标检测综述 P1 yolov1 写在最初 一 yolov1的提出 二 yolov1如何进行检测 三 yolov1的loss函数 四 yolov1训练过程 1 训练前处理 2 五 yolov1的优缺点 写在最初 关于基于深度学习的目标检测相
  • maven工程提示pom.xml无法解析org.apache.maven.plugins:maven-resources-...

    错误信息出现在pom头的project标签 project标签内容是
  • Python命令行参数定义及注意事项

    在命令行中运行python代码是很常见的 下面介绍如何定义命令后面跟的参数 常规用法 Python代码中主要使用下面几行代码来定义并获取需要在命令行中赋值的参数 import argparse parser argparse Argumen
  • STM32(七)DMA总结库函数串口使用DMA

    系列文章目录 文章目录 系列文章目录 前言 一 配置步骤 二 代码实例 前言 DMA 全称为 Direct Memory Access 即直接存储器访问 DMA 传输方式无需 CPU 直接 控制传输 也没有中断处理方式那样保留现场和恢复现场
  • 百度网盘搜索网站

    目录 小白盘 https www xiaobaipan com 56网盘 https www 56wangpan com 盘搜搜 https www pansoso com 如风搜 http www rufengso net 及搜盘 htt
  • L2-045 堆宝塔PTA

    堆宝塔游戏是让小朋友根据抓到的彩虹圈的直径大小 按照从大到小的顺序堆起宝塔 但彩虹圈不一定是按照直径的大小顺序抓到的 聪明宝宝采取的策略如下 首先准备两根柱子 一根 A 柱串宝塔 一根 B 柱用于临时叠放 把第 1 块彩虹圈作为第 1 座宝
  • lauyi实现表格内显示文件名称,点击实现下载功能。

    定义监听事件 a class layui btn layui btn warm i class layui icon layui icon export i 导出 a js代码 field fileName title 文件名称 width
  • 数据结构之递归的应用场景迷宫问题

    递归是什么 简单的说 递归就是方法自己调用自己 每次调用的时传入不同的变量 递归有助于开发解决复杂的问题 同时可以使代码变的简洁 递归调用机制 直接上代码 用案例说明 我觉得在学校老师好像讲过很多遍了 但是过一端时间自己又写不出来 打印问题
  • DC/DC:闭环控制的隔离型反激变换电路设计及实验仿真(文章底部含仿真程序获取方式)

    反激变换电路在开关管导通时电源将电能转为磁场能储存在变压器中 当开关管关断时再将磁能转变为电能传送到负载 单端反激变换电路是由升降压 Buck Boost 变换电路派生而来的 电路图如图所示 反激变换电路的原理设计可参考文章 DC DC 单
  • 玩转树莓派 一、为你的树莓派烧录系统镜像

    准备工作 1 一台烧录镜像用的电脑 Windows Mac Linux 2 树莓派 3 显示器 高清连接线 根据不同型号需要不同的接口 4 键盘鼠标 5 Micro SD 读卡器 Micro SD 卡 16 128G 6 网线 不使用wif
  • 利用github.io(githubPages)免费托管个人静态网站/个人博客

    我们的个人博客或者静态网站可以托管到github就能通过github域名访问 git仓库配置 我采用的是自己编写一个html文件 githubPages搭建 首先需要在GitHub上创建Github Pages服务 具体步骤如下图 点击之后
  • 11 个Python教程来炫耀你的高级技能

    如果你可以以 Python 式的方式使用 Python 那么 Python 是一种优雅的语言 但不管你有多资深 真正用 Python 写代码都需要一些时间 本文将向你分享 11 个 Pythonic 技巧 让你的 Python 技能提升到一
  • hexo d时提示错误ssh: Could not resolve hostname e. coding. net: Name or service not known解决方案

    步骤1 命令符ping github com 得出的IP github com添加到 etc hosts hosts文件在C Windows System32 drivers etc目录 如拒绝修改 可右键添加用户完全控制权限
  • vue 项目全局修改element-ui的样式

    引入了element ui 但是和我们自己的样式颜色有很大的不同 官网自定义主题 点击查看 修改例子 在src文件下创建 element var scss 代码如下 color primary yellow 修改按钮primary的颜色 改
  • windows MongoDB安装和配置

    一 MongoDB安装和配置 1 进入官网下载你所需要的安装版本 点击直通官网 Step1 进入官网后 将看到如下界面 点击上方导航栏Products 找到Community Server Step2 选择自己需要的版本 系统和压缩方式 2
  • centos启动停留在started GNOME display manager

    Centos启动卡死进不去界面 停留在started GNOME display manager 在安装Centos7 9系统成功后 需要安装显卡驱动 显卡驱动有一个驱动程序自带这图形化界面 安装该驱动程序后 系统一直处于started G
  • Python连接MySQL数据库

    一 准备模块 python连接SQL数据库首先需要用到 pymysql 模块 这里使用pip install指令来安装步骤如下 1 在安装的python的路径下找到Scripts文件夹并打开 在路径上面写成 cmd 后回车 2 进入这个界面
  • springboot配置自定义数据源(Druid德鲁伊)的步骤。

    今天和大家分享下在Springboot中配置自定义数据源Druid的两种方法及步骤 方法一 1 在pom xml配置依赖 注释里面的内容 2 配置自己的数据源设置 我是在yaml文件中配置的 顺便提醒一下 在配置yaml文件的时候缩进问题一
  • 【引用】四元组与旋转矩阵

    引用 四元组与旋转矩阵 2011 09 22 17 13 39 分类 DirectX资料 举报 字号 订阅 下载LOFTER客户端 本文转载自ericyang1231 四元组与旋转矩阵 在3D程序中 通常用quaternion来计算3D物体