四元数到达万向节锁

时间:2015-02-28 00:52:14

标签: c++ rotation transformation quaternions euler-angles

为了在执行旋转时避免角度锁定,我试图切换到四元数。不知怎的,我仍然设法达到万向节锁。

我不确定它是否由于我已经实现的数学或设计错误,所以请指出我是否应该改变我的对象坐标方法。

我的每个物体都拥有X,Y,Z值以及俯仰,偏航,滚动值。 当我更改旋转值时,对象将根据上述信息重新计算其顶点。这个逻辑如下:

    // vertex array
    vertices[x] -= /*Offset by origin point*/;

    // Quat.'s representing rotation around xyz axes
    Quaternion q1 = Quaternion(glm::vec3(1,0,0),pitch);
    Quaternion q2 = Quaternion(glm::vec3(0,1,0),yaw); 
    Quaternion q3 = Quaternion(glm::vec3(0,0,1),roll); 

    // total rotation
    Quaternion TotalRot = ( (q3 * q2) * (q1) ); 

    // conversion of original coordinates to quaternion
    Quaternion Point1 = Quaternion(0, vertices[x].x(), vertices[x].y(), vertices[x].z()); 

    // resulting rotated point
    Quaternion Point2 = Quaternion( (TotalRot * Point1) * TotalRot.inverse() );

    // placing new point back into vertices array
    vertices[x] = QVector3D(round(Point2.v.x),round(Point2.v.y),round(Point2.v.z));
    vertices[x]+= /*Undo origin point offset*/;

“vertices []”是对象的顶点数组。上面注释掉的原点偏移只是为了使对象围绕正确的原点旋转,因此它相对于0,0,0移位,因为在该点周围发生旋转(右?)。

我有一个我的问题的图形表示,我首先偏航90°,俯仰45°,然后滚动-90,但滚动轴与俯仰轴平行:

problemexample

修改

我尝试将这些3轴四元数相乘,然后乘以4x4矩阵,然后乘以我的​​顶点,但我仍然是万向节锁定/达到奇点!

    Quaternion q1 = (1,0,0,pitch);
    Quaternion q2 = (0,1,0,yaw);
    Quaternion q3 = (0,0,1,roll);
    Quaternion qtot = (q1*q2)*q3;
    Quaternion p1(0, vertices[x].x(), vertices[x].y(), vertices[x].z());
    QMatrix4x4 m;
    m.rotate(qtot);
    QVector4D v = m*p1;
    vertices[x] = QVector3D(v.x(),v.y(),v.z());

2 个答案:

答案 0 :(得分:5)

您的问题是,即使您使用四元数,您仍然存储三个俯仰,偏航和滚动值,而不是四元数,以表示对象的方向。

以下是使用四元数进行轮换的方法:

  1. 而不是为每个对象存储X,Y,Z,俯仰,偏航,滚动,而是存储X,Y,Z,orientation 在每个对象中,orientation是从初始值(0,0,0,1)开始的四元数,表示没有旋转。为每个物体存储俯仰,偏航和滚动很容易受到奇点(万向节锁定)的影响,因为当添加小的变化时,其中一个中间旋转(例如,俯仰)可能导致物体平行于旋转轴(例如,偏航轴),以便围绕该轴的下一次旋转可能失败。

  2. 然后,在旋转对象时,确定在该帧期间发生的该对象的俯仰,偏航和滚动(假设您的输入设备以该形式提供旋转),将其转换为四元数,然后将该四元数预先乘以对象的orientation四元数。这种方法不太容易受到奇点的影响,因为预计每一帧的旋转变化都非常小。

  3. 更改方向后,请勿直接修改对象的X,Y和Z(您的verticies数组)。相反,当对象的方向发生变化时,创建一个新的旋转矩阵作为对象世界变换矩阵的一部分(连同缩放和平移;为了获得最佳结果,将世界变换计算为translation * rotation * scaling)。

  4. 每隔几帧,您应该对orientation四元数进行标准化,以避免由于舍入错误而导致的方向发生意外变化。

答案 1 :(得分:3)

如果你采用欧拉角表示并将其转换为四元数只是为了进行矢量旋转,那么你仍然只是使用欧拉角。只要你在任何地方都有欧拉角,云台锁问题就会一直存在。你需要完全切换到四元数,如果你想完全消除这个问题,就不要在任何地方进行欧拉角表示。

这样做的基本方法是你可以在计算的远端使用欧拉角(作为原始输入或作为最终输出),如果这对你更方便(例如,欧拉角往往更多"人类可读"表示)。但是,对其他一切使用四元数(并且你可以偶尔转换为旋转矩阵,因为它们对旋转矢量更有效),并且永远不会对任何"坏"进行中间转换。旋转表示(欧拉角,轴角等)。 "万向节锁定"四元数和旋转矩阵的好处仅适用于在所有计算过程中坚持使用这两个表示的情况。

所以,一方面,你有单一的表达方式(" gimbal lock" 奇点)的正式用语:

  • 欧拉角(任何种类,其中有12个,顺便说一下);和
  • 轴角。

另一方面,你有无奇点的表示:

  • 四元数(更有效的记忆和组合操作);和
  • 旋转矩阵(更有效地将旋转应用于矢量)。

无论何时操作(如将几个旋转放在一起,或移动一个旋转的物体)都有一个单一的表示,你将不得不担心每次计算中的奇点。当您使用无奇点表示执行相同的操作时,您不必担心(但您必须担心约束,这是四元数的单位范数约束和正确的正交性旋转矩阵)。当然,无论何时转换为单一表示形式,您都必须担心奇点。这就是为什么你只应该在计算的远端进行转换(进入或离开时),并在计算中坚持使用非单数表示。

欧拉角的唯一好处是人类可读性,时期。