c++ 四元数到达万向节锁定

3z6pesqy  于 2023-07-01  发布在  其他
关注(0)|答案(2)|浏览(149)

为了避免在执行旋转时Angular 锁定,我尝试切换到四元数。不知怎的,我还是设法达到平衡环锁定。
我不确定这是由于我实现的数学,还是设计错误,所以请指出我是否应该改变我的对象坐标方法。
我的每个对象都有一个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度,但滚转轴与俯仰轴平行:

编辑:

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

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());
70gysomp

70gysomp1#

您的问题是,即使使用四元数,您仍然要存储三个pitch、yaw和roll值,而不是一个四元数来表示对象的方向。
以下是如何使用四元数进行旋转:
1.不是存储每个对象的X,Y,Z,pitch,yaw,roll,而是在每个对象中存储X,Y,Z,orientation,其中orientation是从初始值(0,0,0,1)开始的四元数,意味着没有旋转。存储每个对象的俯仰、偏航和滚转容易受到奇点(万向节锁定)的影响,因为随着添加小的变化,中间旋转之一(例如,俯仰)可能导致对象平行于旋转轴(例如,偏航轴),使得围绕该轴的下一次旋转可能失败。
1.然后,当对象旋转时,确定该对象在该帧期间发生的俯仰、偏航和滚动(假设输入设备以该形式提供旋转),将其转换为四元数,然后将该四元数预乘为对象的orientation四元数。这种方法不太容易受到奇点的影响,因为旋转的变化预计在每一帧都非常小。
1.不要在更改方向后直接修改对象的X、Y和Z(verticies阵列)。相反,当一个对象的方向改变时,创建一个新的旋转矩阵作为对象的世界变换矩阵的一部分(沿着缩放和平移;要获得最佳结果,请将世界变换计算为translation * rotation * scaling)。
1.每隔几帧,应规格化orientation四元数,以避免由于舍入误差而导致的方向上的不希望的变化。

zbq4xfa0

zbq4xfa02#

如果你把一个欧拉角表示,并把它变成四元数,只是为了做向量旋转,那么你仍然只是使用欧拉角。只要你在任何地方涉及到欧拉角,万向节锁定问题就仍然存在。如果你想完全消除这个问题,你需要完全切换到四元数,并且永远不要在任何地方使用欧拉角表示。
基本的方法是,你可以在计算的远端使用欧拉角(作为原始输入或最终输出),如果这对你来说更方便的话(例如,欧拉角通常是一种更“人类可读”的表示)。但是,使用四元数的一切(你可以偶尔转换为旋转矩阵,因为他们是更有效的旋转向量),永远不要做中间转换到任何“坏”的旋转表示(欧拉角,轴角等)。四元数和旋转矩阵的“无万向节锁定”优点仅适用于在所有计算中坚持使用这两种表示法的情况。
所以,一方面,你有奇异表示(“万向节锁定”的正式术语是 * 奇异性 *):

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

在另一边,你有无奇异性的表示:

  • 四元数(更有效的内存方式和组合操作);和
  • 旋转矩阵(对向量应用旋转更有效)。

每当你用奇异表示进行操作(比如把几个旋转放在一起,或者移动一个旋转的对象)时,你将不得不在你所做的每一次计算中担心那个奇异性。当你用一个无奇异性的表示做同样的操作时,你就不必担心这个问题了(但是你必须担心约束,这是四元数的单位范数约束和旋转矩阵的适当正交性)。当然,任何时候你转换到或从一个奇异表示,你必须担心的奇点。这就是为什么你应该只在计算的远端(进入或离开时)进行转换,否则在整个计算过程中坚持使用非奇异表示。
欧拉角的唯一好的目的是为了人类的可读性,时期。

相关问题