我一直在使用一本讨论物理引擎的书。它使用 C++,但我使用 Java,所以复制粘贴不起作用(我也不会这样做)。
我注意到的一个有问题的区域是在 Quaternion 类的 add(Vector3D) 函数中,我无法找出错误。我刚刚从书中了解到四元数(这本书还手握了一些数学知识),所以我对四元数的体验并不好。
这是问题所在:
- 我有一个对象,它的方向由归一化(幅度 = 1)四元数表示。
- 我在物体上应用这个恒定扭矩 [ 0 , 0 , 1 ](所以只有 z 方向的扭矩)
- 扭矩引起角加速度,引起角速度,引起角位置的变化,通过在其方向上添加 3D 矢量来修改。物体似乎可以很好地旋转 0 到 ~60 度
- 在约 60 度时,旋转减慢
- 当物体旋转大约 90 度时,它停止旋转
- println() 语句显示当旋转接近 90 度时,对象的方向四元数接近 [ sqrt(2) , 0 , 0 , -sqrt(2) ] 并卡在那里。
- changeInAngularPosition 是无界的(因为有一个恒定的扭矩,所以角速度和因此 dtheta 是无界的)。当块停止旋转时,角速度在功率E-4上,所以我不相信这是由于浮点不准确造成的
如果我改为应用恒定扭矩 [ 1 , 0 , 0 ] 或 [ 0 , 1 , 0 ],一切正常。这让我相信在我的四元数课程的某个地方,我搞砸了 Z 值。然而,几个小时后,我找不到错误。
注意:在下面的代码中,我使用了一个 Real 类型的对象,其中包含浮点数和加减它们的方法。(如果我想将浮动升级为双倍,这只是为了方便)
这是 add( Vector3D ) 函数:
/**
* updates the angular position using the formula
* <p>
* theta_f = theta_i + dt/2 * omega * theta_i
* <p>
* where theta is position and omega is angular velocity multiplied by a dt ( dt = 1/1000 currently ).
*
* @param omega angular velocity times a change in time (dt)
* @return
*/
public Quaternion add( Vector3D omega ) {
//convert the omega vector into a Quaternion
Quaternion quaternionOmega = new Quaternion( Real.ZERO , omega.getX() , omega.getY() , omega.getZ() );
//calculate initial theta
Quaternion initialAngularPosition = this;
//calculate delta theta, which is dt/2 * omega * theta_i
Quaternion changeInAngularPosition = quaternionOmega.multiply( initialAngularPosition ).divide( Real.TWO );
//System.out.println( "dtheta = " + changeInAngularPosition );
//System.out.println( "theta = " + this );
//System.out.println( "Quaternion omega = " + quaternionOmega );
//add initial theta to delta theta
Quaternion finalAngularPosition = initialAngularPosition.add( changeInAngularPosition );
//System.out.println( finalAngularPosition );
//return the result
return finalAngularPosition;
}
add( Vector3D ) 方法使用其他一些方法:
- 我确信标量方法的除法是正确实现的,因为它与标量向量除法相同。
- 乘法(四元数)方法的公式是书里灌输的,如下
- add( Quaternion ) 方法如下。它将各自的组件相互添加( w 到 w , x 到 x , y 到 y 和 z 到 z )
乘法(四元数):
你可以在这里找到公式:http ://en.wikipedia.org/wiki/Quaternion#Ordered_list_form (我还检查了这个函数大约十次以确保我正确应用了公式)
/**
* @param multiplier <code>Quaternion</code> by which to multiply
* @return <code>this * Quaternion</code>
*/
public Quaternion multiply( Quaternion multiplier ) {
Real w1 = this.m_w;
Real w2 = multiplier.getW();
Real x1 = this.m_x;
Real x2 = multiplier.getX();
Real y1 = this.m_y;
Real y2 = multiplier.getY();
Real z1 = this.m_z;
Real z2 = multiplier.getZ();
Real productW = w1.multiply( w2 ).subtract( x1.multiply( x2 ) ).subtract( y1.multiply( y2 ) ).subtract( z1.multiply( z2 ) );
Real productX = w1.multiply( x2 ).add( x1.multiply( w2 ) ).add( y1.multiply( z2 ) ).subtract( z1.multiply( y2 ) );
Real productY = w1.multiply( y2 ).subtract( x1.multiply( z2 ) ).add( y1.multiply( w2 ) ).add( z1.multiply( x2 ) );
Real productZ = w1.multiply( z2 ).add( x1.multiply( y2 ) ).subtract( y1.multiply( x2 ).add( z1.multiply( w2 ) ) );
return new Quaternion( productW , productX , productY , productZ );
}
添加(四元数):
在我试图找到错误的几个小时内,我确实找到了一个“修复”。如果我在以下方法中减去而不是添加相应的 z 值,则旋转完全可以正常工作-但是在一次旋转多个维度时会弄乱事情。这可能表明存在符号错误,但这主要发生在手动完成的计算中,您删除了负号。我了解物理学(这本书很简单),但不了解四元数。我几乎可以肯定这个错误是在这个类中。
/**
* adds this <code>Quaternion</code> to the <code>augend</code> by
* adding respective components
*
* [ w1 , x1 , y1 , z1 ] + [ w2 , x2 , y2 , z2 ] = [ w1 + w2 , x1 + x2 , y1 + y2 , z1 + z2 ]
*
* @param augend <code>Quaternion</code> to add
* @return <code>this + augend </code>
*/
public Quaternion add( Quaternion augend ) {
Real newW = this.m_w.add( augend.getW() );
Real newX = this.m_x.add( augend.getX() );
Real newY = this.m_y.add( augend.getY() );
//TODO UNEXPLAINABLE - why must this be subtract
Real newZ = this.m_z.add( augend.getZ() );
return new Quaternion( newW , newX , newY , newZ );
}
Quaternion 类中还有其他一些简单的方法,我认为它们不会包含错误(即 getters、setters ),但如果您想查看它们,请告诉我。
感谢您花时间阅读这个巨大的文本块。我很感激。让我知道是否有任何不清楚的地方。任何有助于发现错误并解释我做错了什么都会很棒!
编辑1:
入口点代码:基本上,我有一个 RigidBody 对象,它有一个反复调用的方法。以下代码是该方法中的相关角动量代码。其中,“this”指的是 RigidBody 对象。反惯性矩是一个矩阵(3 x 3)。
`
//calculate angular acceleration from torque = I * alpha
//or alpha = torque / I
Vector3D angularAcceleration = this.m_invMomentOfInertia.transform( this.getNetTorque() );
//adjust angular velocity
this.setAngularVelocity( this.getAngularVelocity().add( angularAcceleration.multiply( duration ) ) );
//modify angular position
Vector3D deltaTheta = this.getAngularVelocity().multiply( duration );
this.setOrientation( this.getOrientation().add( deltaTheta ) );
`
编辑2:
我相信我现在已经修复了这个错误。我将错误缩小到乘法(四元数)函数。这是第一个反转 z 分量的符号纠正错误的位置。我知道四元数乘法不是可交换的,所以我试着把它们换掉。我变了
Quaternion changeInAngularPosition = quaternionOmega.multiply( initialAngularPosition ).divide( Real.TWO );
至
Quaternion changeInAngularPosition = initialAngularPosition.multiply( quaternionOmega ).divide( Real.TWO );
这恰好纠正了错误并通过了我的其他测试。但是,我很好奇为什么用乘数切换被乘数会解决问题,或者如果它没有解决问题并且我的测试用例遗漏了一些东西。