围绕x,y,z轴旋转模型,没有万向节锁定,输入数据始终为x,y,z轴角度旋转

时间:2012-09-05 23:55:37

标签: math graphics 3d rotation quaternions

我有一个输入设备,可以给我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);
}

以避免万向节锁定的方式。

2 个答案:

答案 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)/thetanormalize(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中的旋转以相同的方式完成。

如果您向我们提供有关您希望实现的实际使用情况的更多信息,我们可能会获得更好的想法。