有几件事情可能正在发生。你没有提到重新规范化该四元数。如果你不这样做,肯定会发生坏事。您也不会说在将三角形四元数组件添加到原始四元数之前,它们已经通过了dt
的时间量。如果角速度是以弧度/秒为单位的,但你只是向前走几分之一秒,那么你会走得太远。然而,即便如此,由于您正在经历一段时间,试图假装它无限小,奇怪的事情将会发生,特别是如果您的时间步或角速度很大。
物理引擎ODE提供了从角速度更新物体旋转的选项,就好像它正在采取无限小步骤或使用有限大小的步骤进行更新。有限步骤更精确,但涉及一些触发。功能等等有点慢。可以看到相关的ODE源代码here, lines 300-321,代码查找delta-quaternion here, line 310。
float wMag = sqrt(wx*wx + wy*wy + wz*wz);
float theta = 0.5f*wMag*dt;
q[0] = cos(theta); // Scalar component
float s = sinc(theta)*0.5f*dt;
q[1] = wx * s;
q[2] = wy * s;
q[3] = wz * s;
其中sinc(x)
是:
if (fabs(x) < 1.0e-4) return (1.0) - x*x*(0.166666666666666666667);
else return sin(x)/x;
这可以让你避免除以零的问题,仍然是非常精确的。
然后,将四元数q
预乘到现有的身体方向的四元数表示上。然后,重新正常化。
编辑 - 当这个公式来源于:
考虑初始四元数q0
和最终四元q1
与角速度w
旋转为dt
量的时间之后产生的。我们在这里所做的就是将角速度矢量改变成四元数,然后用四元数旋转第一个方向。四元数和角速度都是轴角表示的变化。 正文围绕单位轴[x,y,z]
围绕theta
的标准方向旋转将具有以下四元数的方向表示:q0 = [cos(theta/2) sin(theta/2)x sin(theta/2)y sin(theta/2)z]
。 身体是旋转theta/s
围绕单位轴[x,y,z]
将有角速度w=[theta*x theta*y theta*z]
。因此,为了决定在dt
秒内将发生多少旋转,我们首先提取角速度的大小:theta/s = sqrt(w[0]^2 + w[1]^2 + w[2]^2)
。然后我们找到实际的角度乘以dt
(为方便起见,同时除以2,将其转换为四元数)。由于我们需要对轴线[x y z]
进行归一化,因此我们也除以theta
。这就是sinc(theta)
部分的来源。 (因为theta
有一个额外的0.5*dt
从它的规模,我们乘以返回)。当x
很小时,sinc(x)
函数只是使用函数的泰勒级数近似,因为它在数值上是稳定的,而且更准确。使用这个方便的功能的能力是为什么我们不仅仅是除以实际量值wMag
。旋转速度不快的物体将具有非常小的角速度。由于我们预计这很常见,我们需要一个稳定的解决方案。我们最终得到的是一个四元数,表示旋转的单步时间步dt
。
你说你没有得到预期的结果。什么似乎出错? – JCooper 2012-01-13 20:51:55
我正在使用模型进行跟踪,并且它不稳定 – 2012-01-16 07:46:21
“w”向量是否已经乘以“delta time”?只有“增量时间”很小时,该等式才能正常工作。 – minorlogic 2014-05-21 08:48:39