我有一个输入设备,可以给我3个角度 - 围绕x,y,z轴旋转。
现在我需要使用这些角度来旋转3D空间,而不需要万向节锁定。我以为我可以转换为Quaternions,但是apparently since I'm getting the data as 3 angles this won't help?
如果是这样的话,我怎样才能正确旋转空间,记住我的输入数据只是x,y,z轴的旋转角度,所以我不能只是“避免”那个。类似地,围绕轴旋转顺序移动也无济于事 - 无论如何都将使用所有轴,因此对周围的顺序进行改组将无法完成任何操作。但肯定有办法做到这一点吗?
如果有帮助,那么实现此功能几乎可以减少问题:
void generateVectorsFromAngles(double &lastXRotation,
double &lastYRotation,
double &lastZRotation,
JD::Vector &up,
JD::Vector &viewing) {
JD::Vector yaxis = JD::Vector(0,0,1);
JD::Vector zaxis = JD::Vector(0,1,0);
JD::Vector xaxis = JD::Vector(1,0,0);
up.rotate(xaxis, lastXRotation);
up.rotate(yaxis, lastYRotation);
up.rotate(zaxis, lastZRotation);
viewing.rotate(xaxis, lastXRotation);
viewing.rotate(yaxis, lastYRotation);
viewing.rotate(zaxis, lastZRotation);
}
以避免万向节锁定的方式。
答案 0 :(得分:2)
如果你的设备给你绝对的X / Y / Z角度(这意味着类似于实际的万向节),它将有一些特定的序列来描述旋转发生的顺序。
既然你说“顺序无关紧要”,这表明你的设备就像(几乎可以肯定的话)一个3轴速率陀螺仪,而你正在获得不同的角度。在这种情况下,您希望将3个差异角度组合成旋转矢量,并使用它来更新方向四元数,如下所示:
given differential angles (in radians):
dXrot, dYrot, dZrot
and current orientation quaternion Q such that:
{r=0, ijk=rot(v)} = Q {r=0, ijk=v} Q*
construct an update quaternion:
dQ = {r=1, i=dXrot/2, j=dYrot/2, k=dZrot/2}
and update your orientation:
Q' = normalize( quaternion_multiply(dQ, Q) )
请注意,dQ只是单位四元数的粗略近似值(这使得normalize()
操作比平时更重要)。但是,如果您的差分角度不大,那么它实际上是一个很好的好近似值。即使您的差分角度 大,这种简单的近似也比您可以做的许多其他事情少得多。如果您遇到大差分角度问题,可以尝试添加二次校正以提高准确性(如第三部分所述)。
然而,一个更可能的问题是任何类似的重复更新都会因为累积的算术错误(如果没有别的话)而漂移。此外,您的物理传感器会有偏差 - 例如,您的速率陀螺仪将具有偏移量,如果不进行校正,将导致您的方向估计值Q
缓慢进动。如果这种漂移对您的应用很重要,那么如果您想维护一个稳定的系统,则需要一些方法来检测/纠正它。
如果确实存在大差分角度的问题,则存在用于计算精确更新四元数dQ
的三角公式。假设总旋转角度应与输入矢量的大小成线性比例;鉴于此,您可以按如下方式计算精确更新四元数:
given differential half-angle vector (in radians):
dV = (dXrot, dYrot, dZrot)/2
compute the magnitude of the half-angle vector:
theta = |dV| = 0.5 * sqrt(dXrot^2 + dYrot^2 + dZrot^2)
then the update quaternion, as used above, is:
dQ = {r=cos(theta), ijk=dV*sin(theta)/theta}
= {r=cos(theta), ijk=normalize(dV)*sin(theta)}
请注意,直接计算sin(theta)/theta
或normalize(dV)
是单数接近零,但接近零的向量ijk
的限制值只是ijk = dV = (dXrot,dYrot,dZrot)
,如近似从第一节开始。如果你以这种方式计算更新四元数,那么直接的方法是检查这个,并使用小theta
的近似值(这是极良好的近似值!)。 / p>
最后,另一种方法是对cos(theta)
和sin(theta)/theta
使用泰勒展开式。这是一种中间方法 - 一种改进的近似,可以提高准确度范围:
cos(x) ~ 1 - x^2/2 + x^4/24 - x^6/720 ...
sin(x)/x ~ 1 - x^2/6 + x^4/120 - x^6/5040 ...
因此,第一部分提到的“二次校正”是:
dQ = {r=1-theta*theta*(1.0/2), ijk=dV*(1-theta*theta*(1.0/6))}
Q' = normalize( quaternion_multiply(dQ, Q) )
附加术语将扩展近似的精确范围,但如果每次更新需要超过+/- 90度,则应该使用第二部分中描述的精确触发功能。您还可以将泰勒展开与精确的三角解决方案结合使用 - 允许您在近似值和精确公式之间无缝切换可能会有所帮助。
答案 1 :(得分:0)
我认为'万向节锁'不是计算/数学的问题,而是一些物理设备的问题。
鉴于您可以用XYZ旋转表示任何方向,那么即使在“万向节锁定点”, 也是 XYZ表示,用于任何可以想象的方向变化。你的物理万向节可能无法以这种方式旋转,但数学仍然有效:)。
这里唯一的问题是你的输入设备 - 如果它是云台,那么它可以锁定,但你没有提供任何细节。
编辑:好的,所以在你添加一个功能之后我觉得我知道你需要什么。功能完全正确。但遗憾的是,您无法使用XYZ轴旋转获得一种简单,连续的方向编辑方式。即使在专业的3D软件包中,我也没有看到过这样的解决方案。
我唯一想到的就是将你的输入视为飞机转向 - 你只需要一些初始方向,你可以绕X,Y或Z轴旋转一定量。然后存储新方向并清除输入。 3DMax / Maya / Blender中的旋转以相同的方式完成。
如果您向我们提供有关您希望实现的实际使用情况的更多信息,我们可能会获得更好的想法。