从欧拉角到四元数

时间:2019-06-13 08:28:34

标签: c++ quaternions euler-angles

我正在模拟飞机运动。现在,我使用欧拉角将“身体框架”转换为“世界框架”,并且效果很好。

enter image description here

最近,我了解了四元数及其在旋转矩阵(万向节锁定)方面的优势,并尝试使用模拟器中的偏航角/俯仰角/横滚角来实现它。

四元数

如果我正确理解,四元数代表两件事。它具有一个 x,y, z 分量,该分量表示将围绕其旋转的轴。它还具有一个 w 分量,它表示围绕该轴发生的旋转量。简而言之,一个向量和一个浮点数。四元数可以表示为4个元素向量:

q = [w,x,y,z]

要计算结果(完全旋转之后),请使用以下公式:

p'= qpq'

其中:

p = [0,x,y,z] -方向向量

q = [w,x,y,z] -旋转

q'= [w,-x,-y,-z]

算法

  1. 创建四元数:

使用wikipedia通过绕3个轴(q)旋转来创建四元数:

Quaterniond toQuaternion(double yaw, double pitch, double roll) // yaw (Z), pitch (Y), roll (X)
{
    //Degree to radius:
    yaw = yaw * M_PI / 180;
    pitch = pitch * M_PI / 180;
    roll = roll * M_PI / 180;


    // Abbreviations for the various angular functions
    double cy = cos(yaw * 0.5);
    double sy = sin(yaw * 0.5);
    double cp = cos(pitch * 0.5);
    double sp = sin(pitch * 0.5);
    double cr = cos(roll * 0.5);
    double sr = sin(roll * 0.5);

    Quaterniond q;
    q.w = cy * cp * cr + sy * sp * sr;
    q.x = cy * cp * sr - sy * sp * cr;
    q.y = sy * cp * sr + cy * sp * cr;
    q.z = sy * cp * cr - cy * sp * sr;
    return q;
}
  1. 定义平面方向(航向)矢量:

    p = [0,1,0,0]

  2. 计算Hamilton product

    p'= qpq'

    q'= [w,-qx,-qy,-qz]

    p'=(H(H(q,p),q')

Quaterniond HamiltonProduct(Quaterniond u, Quaterniond v)
{
    Quaterniond result;

    result.w = u.w*v.w - u.x*v.x - u.y*v.y - u.z*v.z;
    result.x = u.w*v.x + u.x*v.w + u.y*v.z - u.z*v.y;
    result.y = u.w*v.y - u.x*v.z + u.y*v.w + u.z*v.x;
    result.z = u.w*v.z + u.x*v.y - u.y*v.x + u.z*v.w;

    return result;

}

结果

我的结果将是矢量:

v = [p'x,p'y,p'z]

它工作正常,但与欧拉角旋转(万向锁定)相同。是因为我在这里也使用欧拉角吗?我真的不知道如何在不绕3轴旋转的情况下工作。我是否应该分别绕每个轴旋转?

我将非常感谢您提出的任何建议和帮助,以帮助您理解这一问题。

编辑(应用程序的工作方式)

1。我的应用程序基于数据流,这意味着每隔1毫秒就会检查是否有新数据(平面的新方向)。

示例:

以乞求的 pitch / roll / yaw = 0 ,在 1ms 偏航角改变10度后,应用程序读取 pitch = 0,roll = 0,偏航= 10 。在下一个1毫秒后,偏航再次改变20度。因此,输入数据将如下所示: pitch = 0,roll = 0,偏航= 30

2。创建方向四元数- p

在乞求时,我将平面的方向(头)定义为X轴。所以我的当地方向是 v = [1,0,0] 四元数(我的 p )是 p = [0,1,0,0]

Vector3 LocalDirection, GlobalDirection; //head
    Quaterniond p,P,q, Q, pq; //P = p', Q=q'


    LocalDirection.x = 1;
    LocalDirection.y = 0;
    LocalDirection.z = 0;

    p.w = 0;
    p.x = direction.x;
    p.y = direction.y;
    p.z = direction.z;

3。创建旋转

每隔 1ms 我检查一次数据流的旋转角度(Euler),并使用 toQuaternion

计算 q
q = toQuaternion(yaw, pitch, roll); // create quaternion after rotation


    Q.w = q.w;
    Q.x = -q.x;
    Q.y = -q.y;
    Q.z = -q.z;

4。计算“世界方向”

使用 汉密尔顿积 ,我计算旋转后的四元数,这是我的总体方向:

pq = HamiltonProduct(q, p); 

    P = HamiltonProduct(pq, Q);

    GlobalDirection.x = P.x;
    GlobalDirection.y = P.y;
    GlobalDirection.z = P.z;

5。每 1ms

重复3-4次

1 个答案:

答案 0 :(得分:2)

您的模拟似乎使用欧拉角来旋转每帧物体。然后,您可以将这些角度转换为四元数。那不会解决万向节锁定问题。

将欧拉角添加到欧拉角时,万向节锁定可能随时发生。从本地空间转到世界空间不足以解决此问题。您还需要进行模拟以在帧之间使用四元数。

基本上,每次您的对象改变其旋转角度时,将当前旋转角度转换为四元数,乘以新的旋转角度增量,然后将结果转换回Euler角度或用于存储旋转角度的任何角度即可。

我建议重写您的应用程序以仅使用和叙述四元数。每当用户进行输入或游戏的某些其他逻辑想要旋转某些内容时,您都将立即将其转换为四元数并将其输入模拟中。

有了toQuaternionHamiltonProduct,您便拥有了所需的所有工具。


编辑,以回应您的编辑,说明您的应用程序如何工作。

  

在乞求的俯仰/侧倾/偏航= 0时,将1ms偏航更改10度后,应用程序将读取音高= 0,侧倾= 0,偏航=10。在接下来的1ms偏航中,再次更改20度。因此输入数据将如下所示:pitch = 0,roll = 0,yaw = 30。

这就是万向节锁定发生的地方。计算旋转后,您将转换为四元数。那是错的。您需要在第一步中使用使用四元数。不要执行after 1ms yaw is changed by 10 degree so application reads pitch=0, roll=0, yaw = 10,请执行以下操作:

  1. 将旋转存储为四元数,而不是欧拉角;
  2. 将10度偏航角转换为四元数;
  3. 将存储的四元数和10度偏航四元数相乘;
  4. 存储结果。

要澄清一下:您的步骤2、3和4很好。问题出在步骤1。


在旁注:

  

它具有x,y和z分量,代表将围绕其旋转的轴。它也有一个w分量,它代表将围绕该轴发生的旋转量

并不完全正确。四元数的分量不直接是轴和角度,它们是sin(angle/2) * axiscos(angle/2)(这是您的toQuaternion方法产生的)。这很重要,因为它为您提供了一个很好的单位四元数,这些四元数形成了4D sphere,其中表面(surspace?)上的每个点都代表3D空间中的旋转,精美地实现了任意两个旋转之间的平滑插值。