【问题标题】:From Euler angles to Quaternions从欧拉角到四元数
【发布时间】:2019-10-27 19:21:03
【问题描述】:

我正在模拟平面运动。目前,我使用欧拉角将“身体框架”转换为“世界框架”,效果很好。

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

四元数

如果我理解正确,四元数代表两件事。它有一个 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.我的应用程序基于数据流,这意味着每 1ms 它检查是否有新数据(平面的新方向)。

例子:

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

2。创建方向四元数 - p

在乞求时,我定义我的飞机的方向(头部)在 X 轴上。所以我的本地方向是 v=[1,0,0] 在四元数中 (my 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 之后,我从数据流中检查旋转角度(欧拉)并使用 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.计算“世界方向”

使用 Hamilton 积我计算旋转后的四元数,这是我的全局方向:

pq = HamiltonProduct(q, p); 

    P = HamiltonProduct(pq, Q);

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

5.每1ms

重复3-4

【问题讨论】:

  • 即使是 [90, -90] 也应该像这样将音高转换为半径吗?

标签: c++ quaternions euler-angles


【解决方案1】:

您的模拟似乎使用欧拉角来旋转每帧的对象。然后,您将这些角度转换为四元数。这不会解决云台锁定问题。

当您将欧拉角添加到欧拉角时,云台锁定可能随时发生。从局部空间到世界空间时,仅仅解决这个问题是不够的。您还需要模拟在帧之间使用四元数。

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

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

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


编辑回应您的编辑,解释您的应用程序是如何工作的。

在乞求俯仰/滚动/偏航 = 0 处,1 毫秒后偏航改变 10 度,因此应用程序读取俯仰 = 0,滚动 = 0,偏航 = 10。在接下来的 1 毫秒后,偏航再次改变 20 度。因此输入数据将如下所示:pitch=0, roll=0, yaw = 30。

这就是万向节锁定发生的地方。 在计算旋转之后转换为四元数。那是错的。您需要在第一步中使用四元数in。不要这样做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,其中表面上的每个点(表面空间?)都代表 3D 空间中的旋转,精美地允许在任意两个旋转之间进行平滑插值。

【讨论】:

  • 感谢您的评论,但我仍然不确定您推荐的和我的算法有什么区别。我的平面方向有局部(常数四元数) - p[0,1,0,0] 每次当模拟器改变平面方向时,我创建四元数 q(使用 toQuaternion)并使用 p'=qpq' 我计算世界空间的新方向。问题是我在前一个欧拉角上添加了新的欧拉角,是吗?您能否提供一个示例(逐步)它应该如何工作?
  • 我只能回答你提出的问题。如果您在代码方面需要帮助,您应该发布您的代码。只要我不知道您的应用程序是什么样子,我也不知道您的应用程序需要哪些步骤才能正常运行。
  • 我添加了我的应用程序的工作方式。如果你能看一下,我将不胜感激:)
  • 感谢您的回答。因此,如果我理解正确,基于我在“编辑”中放置的示例,我应该首先创建一个 0 旋转的“四元数零”(使用 toQuaternion(0,0,0))并得到结果(p') 正如我所描述的。然后,当偏航角发生变化时,我必须将 10 度偏航角转换为四元数(toQuaternion(10,0,0))并乘以“四元数零”并使用这个新的四元数得到新的 p'。然后如果偏航再次改变 20 度,我通过将 20 而不是 30 度(!)(toQuaternion(20,0,0))再次创建一个新的四元数并与前一个四元数相乘?
  • @begginer 完全正确。
猜你喜欢
  • 2012-06-21
  • 1970-01-01
  • 2013-06-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多