我正在模拟飞机运动。现在,我使用欧拉角将“身体框架”转换为“世界框架”,并且效果很好。
最近,我了解了四元数及其在旋转矩阵(万向节锁定)方面的优势,并尝试使用模拟器中的偏航角/俯仰角/横滚角来实现它。
如果我正确理解,四元数代表两件事。它具有一个 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]
使用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;
}
定义平面方向(航向)矢量:
p = [0,1,0,0]
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
计算 qq = 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次答案 0 :(得分:2)
您的模拟似乎使用欧拉角来旋转每帧物体。然后,您可以将这些角度转换为四元数。那不会解决万向节锁定问题。
将欧拉角添加到欧拉角时,万向节锁定可能随时发生。从本地空间转到世界空间不足以解决此问题。您还需要进行模拟以在帧之间使用四元数。
基本上,每次您的对象改变其旋转角度时,将当前旋转角度转换为四元数,乘以新的旋转角度增量,然后将结果转换回Euler角度或用于存储旋转角度的任何角度即可。
我建议重写您的应用程序以仅使用和叙述四元数。每当用户进行输入或游戏的某些其他逻辑想要旋转某些内容时,您都将立即将其转换为四元数并将其输入模拟中。
有了toQuaternion
和HamiltonProduct
,您便拥有了所需的所有工具。
编辑,以回应您的编辑,说明您的应用程序如何工作。
在乞求的俯仰/侧倾/偏航= 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
,请执行以下操作:
要澄清一下:您的步骤2、3和4很好。问题出在步骤1。
在旁注:
它具有x,y和z分量,代表将围绕其旋转的轴。它也有一个w分量,它代表将围绕该轴发生的旋转量
并不完全正确。四元数的分量不直接是轴和角度,它们是sin(angle/2) * axis
和cos(angle/2)
(这是您的toQuaternion
方法产生的)。这很重要,因为它为您提供了一个很好的单位四元数,这些四元数形成了4D sphere,其中表面(surspace?)上的每个点都代表3D空间中的旋转,精美地实现了任意两个旋转之间的平滑插值。