结论

我直接抛出结论:

Gimbal Lock 产生的原因不是欧拉角也不是旋转顺序,而是我們的思维方式和程序的执行逻辑没有对应,也就是说是我们的观念导致这个情况的发生。

他人解释

首先我们看一下欧拉角的定义:

用一句话说,欧拉角就是物体绕坐标系三个坐标轴(x,y,z轴)的旋转角度。

在这里,坐标系可以是世界坐标系,也可以是物体坐标系,旋转顺序也是任意的,可以是xyz,xzy,yxz,zxy,yzx,zyx中的任何一种,甚至可以是xyx,xyy,xzz,zxz等等等等。。。。。。所以说欧拉角多种多样。欧拉角可分为两种情况:

1,静态:即绕世界坐标系三个轴的旋转,由于物体旋转过程中坐标轴保持静止,所以称为静态。

2,动态:即绕物体坐标系三个轴的旋转,由于物体旋转过程中坐标轴随着物体做相同的转动,所以称为动态。

网上的文章,一般都是这样解释的:

是指物体的两个旋转轴指向同一个方向。实际上,当两个旋转轴平行时,我们就说万向节锁现象发生了,换句话说,绕一个轴旋转可能会覆盖住另一个轴的旋转,从而失去一维自由度

通常说来,万向节锁发生在使用Eular Angles(欧拉角)的旋转操作中,原因是Eular Angles按照一定的顺序依次独立地绕轴旋转。让我们想象一个具体的旋转场景,首先物体先绕转X轴旋转,然后再绕Y轴,最后绕Z轴选择,从而完成一个旋转操作(飘飘白云译注:实际是想绕某一个轴旋转,然而Eular Angle将这个旋转分成三个独立的步骤进行),当你绕Y轴旋转90度之后万向节锁的问题就出现了,因为X轴已经被求值了,它不再随同其他两个轴旋转,这样X轴与Z轴就指向同一个方向(它们相当于同一个轴了)。

看得懂吗?我是看不太懂~

我的理解

我们先来考虑一下,旋转到底是怎么个旋转法。

静态的情况很好理解,怎么旋转都不会有问题,万向节的问题是不会出现在静态的旋转过程中的。但是你想像一下动态的旋转,动态的旋转,这里会有两个坐标系,看清楚了,两个坐标系!

  1. 世界坐标系
  2. 物体坐标系

那么这两者是什么关系呢?

一开始,这两个坐标系是重合的,但是旋转开始以后,世界坐标系不会变化,物体坐标系随着旋转就发生变化了。

亲爱的读者,你们先想想,这两个坐标系的关系,你们觉得物体旋转是绕着那个坐标系旋转的?

你会说:

你刚刚不是说了嘛!是绕着物体的坐标系旋转的!

对,没有错,那么在物体旋转的时候,物体的坐标系是不是一直在变化呢?是的!那么我們在给他旋转的参数的时候考虑到这个问题了吗?没有!

就是说我给他的旋转的参数是基于一种假设:每一次旋转都是以物体的坐标系为参考来进行的。就是说我是希望它每一次旋转前,都能够将旋转参数在物体坐标系上进行计算。很简单,一架飞机,作为机长,每次旋转以后他跟着飞机旋转了,后面的旋转操作自然是基于新的物体坐标系来的。

但是实际上,程序解析我给的数据的时候,只是简单地将三个轴的旋转一个个的相乘,也就是说,总的来说还是在最开始的那个坐标系(也就是一直不动的世界坐标系)下面计算的。而且需要注意的是: 每一次进行计算的顺序是确定不变的! 这也是为什么有人会说万向节问题是因为旋转顺序导致的樂。

看一下在OpenGL 实现旋转的代码:

void configRotateTrans(GLfloat radX, GLfloat radY, GLfloat radZ) {
GLfloat xTrans[4][4] = {0};
GLfloat yTrans[4][4] = {0};
GLfloat zTrans[4][4] = {0};
GLfloat tempMatrix[4][4] = {0}; xTrans[3][3] = 1;
xTrans[0][0] = 1;
xTrans[1][1] = cosf(radX);
xTrans[1][2] = -sinf(radX);
xTrans[2][2] = cosf(radX);
xTrans[2][1] = sinf(radX); yTrans[3][3] = 1;
yTrans[0][0] = cosf(radY);
yTrans[0][2] = sinf(radY);
yTrans[2][2] = cosf(radY);
yTrans[2][0] = -sinf(radY);
yTrans[1][1] = 1; zTrans[3][3] = 1;
zTrans[2][2] = 1;
zTrans[0][0] = cosf(radZ);
zTrans[0][1] = -sinf(radZ);
zTrans[1][0] = sinf(radZ);
zTrans[1][1] = cosf(radZ); // Multiply the 3 matrix
// rotateTrans = xTrans * yTrans * zTrans
multiMatrix(xTrans, yTrans, tempMatrix);
multiMatrix(tempMatrix, zTrans, rotateTrans);

看懂了吗,物体最终在哪个位置是简单粗暴地将绕xyz三个旋转的矩阵连续相乘得到的,计算的顺序是x->y->z,那么比如用户先输入绕y轴转90度,再输入绕x轴转90度。其实程序执行的时候,还是会先将x轴的数据进行计算,再计算y轴的数据。但是如果用户先输入绕y轴转90度,再输入绕z轴转90度,程序还是按照x-y-z的顺序来,只是正好用户也是这样输入。

现在我們明确了两点:

  1. 物体的旋转是以世界坐标系为参考的。
  2. 物体旋转的顺序是确定的,和用户输入的旋转的顺序无关。

那么还是刚刚那两种情况:

操作A:

用户第一次输入: 绕Y轴转90度,第二次输入:绕X轴转90度。

实际程序运行:先繞X轴转90度,再绕Y轴转90度。

操作B:

用户第一次输入: 绕Y轴转90度,第二次输入:绕Z轴转90度。

实际程序运行:先繞Y轴转90度,再绕Z轴转90度。

现在发挥一下想象力,当物体绕Y轴转动90度以后,物体坐标系的X轴和世界坐标系的Z轴是不是变成了同一个轴?好的,那么这个时候,用户无论输入的是绕X轴转还是绕Z轴转,最终物体转动是不是都是绕着这个轴(世界Z轴/物体X轴)。上面的操作A和操作B的结果是一样的!

这就是Gimbal Lock,这并不是什么缺陷,陷阱,而是我們的思维方式是错误的,所以导致这个问题的出现。

参考资料:GimbalLock万向节锁与四元数旋转

万向节锁(Gimbal Lock)的理解的更多相关文章

  1. 使用四元数解决万向节锁(Gimbal Lock)问题

    问题 使用四元数可以解决万向节锁的问题,但是我在实际使用中出现问题:我设计了一个程序,显示一个三维物体,用户可以输入绕zyx三个轴进行旋转的指令,物体进行相应的转动. 由于用户输入的是绕三个轴旋转的角 ...

  2. 万向节死锁 gimbal lock

    ,如下图一,把灰色箭头想象成是一架飞机,红,绿蓝三个圈看作是三个外围控制器,外圈带动所有里圈运动,里圈的运动不影响外圈. 1,首先,绕Y轴旋转(旋转绿圈),来确定前进的方向.这时红圈与蓝圈都跟着旋转. ...

  3. Gimbal Lock欧拉角死锁问题

    技术背景 在前面几篇跟SETTLE约束算法相关的文章(1, 2, 3)中,都涉及到了大量的向量旋转的问题--通过一个旋转矩阵,给定三个空间上的欧拉角\(\alpha, \beta, \gamma\), ...

  4. Gimbal Lock

    [Gimbal Lock] 万向锁源于欧拉角的是有序处理的.U3D中的序列为: y->x->z.当旋转y时,local坐标系与世界坐标系重合,所以y等于永远按惯性坐标旋转.当x旋转+/-9 ...

  5. 【转】锁(lock)知识及锁应用

    sql server锁(lock)知识及锁应用转自:http://blog.csdn.net/huwei2003/article/details/4047191 关键词:锁提示,锁应用 提示:这里所摘 ...

  6. volatile和synchronized与lock的理解

    volatile 特征: a:可见性:一个线程修改了某个共享变量的值,其他线程能够立马得知这个修改. b:禁止特定的处理器重排序. volatile的内存语义: 1.当写一个volatile变量的时候 ...

  7. Python3学习之路~9.3 GIL、线程锁之Lock\Rlock\信号量、Event

    一 Python GIL(Global Interpreter Lock) 全局解释器锁 如果一个主机是单核,此时同时启动10个线程,由于CPU执行了上下文的切换,让我们宏观上看上去它们是并行的,但实 ...

  8. java里的锁总结(synchronized隐式锁、Lock显式锁、volatile、CAS)

    一.介绍 首先, java 的锁分为两类: 第一类是 synchronized 同步关键字,这个关键字属于隐式的锁,是 jvm 层面实现,使用的时候看不见: 第二类是在 jdk5 后增加的 Lock ...

  9. java面试-公平锁/非公平锁/可重入锁/递归锁/自旋锁谈谈你的理解

    一.公平锁/非公平锁/可重入锁/递归锁/自旋锁谈谈你的理解 公平锁:多个线程按照申请的顺序来获取锁. 非公平锁:多个线程获取锁的先后顺序与申请锁的顺序无关.[ReentrantLock 默认非公平.s ...

随机推荐

  1. HTML5 语义元素(二)文本内容

    上一篇介绍的是关于页面结构方面的语义元素,本篇介绍文本内容方面,包含:<bdi>.<details>.<summary>.<mark>.<outp ...

  2. ExtJS 4.2 组件介绍

    目录 1. 介绍 1.1 说明 1.2 组件分类 1.3 组件名称 1.4 组件结构 2. 组件的创建方式 2.1 Ext.create()创建 2.2 xtype创建 1. 介绍 1.1 说明 Ex ...

  3. Yeoman 官网教学案例:使用 Yeoman 构建 WebApp

    STEP 1:设置开发环境 与yeoman的所有交互都是通过命令行.Mac系统使用terminal.app,Linux系统使用shell,windows系统可以使用cmder/PowerShell/c ...

  4. 立即执行函数表达式(IIFE)

    原文地址:benalman.com/news/2010/11/immediately-invoked-function-expression/ 译者:nzbin 也许你还没有注意到,我是一个对术语比较 ...

  5. lua 学习笔记(1)

    一.lua函数赋值与函数调用         在lua中函数名也是作为一种变量出现的,即函数和所有其他值一样都是匿名的,当要使用某个函数时,需要将该函数赋值给一个变量,这样在函数块的其他地方就可以通过 ...

  6. Linux 常用命令(持续补充)

    常用命令: command &:将进程放在后台执行 ctrl + z:暂停当前进程 并放入后台 jobs:查看当前后台任务 bg( %id):将任务转为后台执行 fg( %id):将任务调回前 ...

  7. PHP获取上个月最后一天的一个容易忽略的问题

    正常来说,PHP是有一个很方便的函数可以获取上个月时间的 strtotime (PHP 4, PHP 5, PHP 7) strtotime - 将任何英文文本的日期时间描述解析为 Unix 时间戳 ...

  8. TemplateMethod(模块方法模式)

    /** * 模块模式 * @author TMAC-J * 将一个完整的算法分离,分成不同的模块 * 用于有很多步骤的时候,可能以后这些步骤还会增加,把这些步骤分离 * 将有共性的部分放在抽象类中 * ...

  9. AndroidStudio — Error:Failed to resolve: junit:junit:4.12错误解决

    原博客:http://blog.csdn.net/u013443865/article/details/50243193 最近使用AndroidStudio出现以下问题: 解决:打开app下的buil ...

  10. 周末聊聊IT人员的人脉观:关于帮妹子找兼职有感

    背景: 前几天,有个认识了好几年的网友,现在是大学生,在厦门读大一,说和她同学要一起到广州找兼职,看我有没有介绍. 像我这么积极热心善良的人,就说帮她找找看,结果问了几次,没消息,只好诚实的回复人家, ...