Math 绕x、y、z轴旋转模型,无万向节锁,输入数据始终为x、y、z轴角度旋转

Math 绕x、y、z轴旋转模型,无万向节锁,输入数据始终为x、y、z轴角度旋转,math,graphics,3d,rotation,quaternions,Math,Graphics,3d,Rotation,Quaternions,我有一个输入设备,可以给我3个角度——绕x,y,z轴旋转 现在我需要使用这些角度来旋转3D空间,而不需要万向节锁。我以为我可以转换成四元数,但是 如果是这样的话,我如何才能正确地旋转空间,记住我的输入数据只是x,y,z轴的旋转角度,所以我不能只是“避免”它。类似地,围绕轴旋转的顺序移动也不会有帮助——所有轴都将被使用,因此无序移动顺序不会完成任何事情。但肯定有办法做到这一点吗 如果有帮助的话,问题几乎可以归结为实现此功能: void generateVectorsFromAngles(doubl

我有一个输入设备,可以给我3个角度——绕x,y,z轴旋转

现在我需要使用这些角度来旋转3D空间,而不需要万向节锁。我以为我可以转换成四元数,但是

如果是这样的话,我如何才能正确地旋转空间,记住我的输入数据只是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);
}

以避免万向节锁的方式。

我认为“万向节锁”不是一个计算/数学问题,而是一些物理设备的问题

假设您可以通过XYZ旋转来表示任何方向,那么即使在“万向节锁定点”,对于任何可想象的方向变化,都是XYZ表示。您的物理框架可能无法以这种方式旋转,但数学仍然有效:)

这里唯一的问题是你的输入设备-如果它是万向节,那么它可以锁定,但你没有给出任何细节


编辑:好的,在你添加了一个函数之后,我想我知道你需要什么了。功能完全正确。但遗憾的是,您无法使用XYZ轴旋转获得一种简单、连续的方向编辑方式。我甚至在专业3D软件包中都没有见过这样的解决方案

我想到的唯一一件事就是把你的输入当作飞机上的方向盘——你只需要一些初始方向,你就可以绕X、Y或Z轴旋转一些。然后存储新方向并清除输入。3DMax/Maya/Blender中的旋转方式相同


如果你给我们提供更多关于你想要实现的真实世界使用的信息,我们可能会得到一些更好的想法。

如果你的设备提供绝对X/Y/Z角度(这意味着类似于实际的万向节),它将有一些特定的顺序来描述旋转发生的顺序

既然你说“顺序无关紧要”,这就意味着你的设备就像(几乎可以肯定?)一个三轴速率陀螺仪,你得到的是不同的角度。在本例中,您希望将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) )
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)}
请注意,dQ只是单位四元数的粗略近似值(这使得
normalize()
操作比通常更重要)。然而,如果微分角不是很大,它实际上是一个很好的近似值。即使你的微分角很大,这个简单的近似也比你能做的其他事情少一些废话。如果您在使用大微分角度时遇到问题,可以尝试添加二次修正以提高精度(如第三节所述)

然而,一个更可能的问题是,任何类型的重复更新都倾向于漂移,这仅仅是由于累积的算术错误(如果没有其他原因的话)。此外,你的物理传感器会有偏差——例如,你的速率陀螺仪会有偏移,如果不进行校正,将导致你的方位估计
Q
缓慢前进。如果这种漂移对您的应用程序很重要,那么如果您想要保持一个稳定的系统,您需要某种方法来检测/纠正它


如果你确实对大微分角有问题,有一个三角公式可以计算精确的更新四元数
dQ
。假设总旋转角度应与输入向量的大小成线性比例;因此,您可以按如下方式计算精确的更新四元数:

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) )
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
normalize(dV)
在零附近是奇异的,但向量
ijk
在零附近的极限值只是
ijk=dV=(dXrot,dYrot,dZrot)
,与第一节的近似值相同。如果您确实以这种方式计算更新四元数,最简单的方法是检查它,并使用小的
θ的近似值(这是一个非常好的近似值!)


最后,另一种方法是对
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度以上,则可能应使用第二节中描述的精确trig函数。您还可以将泰勒展开与精确的三角解结合使用——这可能有助于您在近似值和精确公式之间无缝切换。

更改旋转顺序会产生很大的差异;通常,围绕不同轴的旋转不会相互转换。你能给我们一个简单的例子来说明你想做什么吗?@Beta:我正在开发一种方法来控制3D可视化,使用一种设备,它可以让我以旋转角度围绕三个轴旋转。更改顺序没有帮助,因为模型可以绕所有轴旋转;AFAIK只有在一个轴的运动比另外两个轴的运动更受限制时,更改顺序才有帮助…@Beta:另外,刚刚编辑了这个问题以显示我想要实现的基本功能。所以。。。您想实现
JD::Vector::rotate(JD::Vector,double)
?或者您已经有了,并且想要实现
generatevectorsfromagles(…)
,以便获得与y相同的效果