私は物理エンジンについて説明している本を使用しています。C++ を使用していますが、私は Java を使用しているため、コピーと貼り付けは機能しません (そして、私もそうしませんでした)。
私が気づいた問題のある領域の 1 つは、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 ] を適用すると、すべてが完全に機能します。これにより、Quaternion クラスのどこかで、Z 値を間違えたと思います。しかし、何時間も経っても間違いを見つけることができませんでした。
注: 次のコードでは、実数型のオブジェクトを使用しています。これには、float と、それらを加算および減算するためのメソッドが含まれています。(floatをdoubleにアップグレードしたい場合に便利にするためです)
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 ) メソッドは、他のいくつかのメソッドを使用します。
- スカラー法による除算は、スカラーによるベクトル除算と同じであるため、正しく実装されていると確信しています。
- 乗算 ( Quaternion ) メソッドの式は、本によってスプーンで与えられたもので、以下のとおりです。
- add( Quaternion ) メソッドは以下のとおりです。それぞれのコンポーネントを互いに追加します( w から w 、 x から x 、 y から y 、 z から z )
乗算 (クォータニオン):
ここで数式を見つけることができます: http://en.wikipedia.org/wiki/Quaternion#Ordered_list_form (数式が正しく適用されていることを確認するために、この関数を約 10 回チェックしました)
/**
* @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 );
}
add(クォータニオン):
バグを見つけようとして費やした時間の中で、「修正」を見つけました。次の方法でそれぞれの 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 クラスには、エラーを含む可能性がないと思われる他の単純なメソッド (getter、setter など) がありますが、それらを確認したい場合はお知らせください。
この巨大なテキスト ブロックをお読みいただき、ありがとうございます。それは有り難いです。何か不明な点がありましたらお知らせください。バグを見つけて、私が何を間違えたのかを説明する助けがあれば素晴らしいでしょう!
編集1:
エントリ ポイント コード: 基本的に、私は RigidBody オブジェクトを持っており、それには何度も呼び出されるメソッドがあります。次のコードは、そのメソッドに関連する角運動量コードです。その中で、「これ」は 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 );
たまたまバグを修正し、他のテストに合格しました。ただし、被乗数を乗数に切り替えると問題が解決する理由、または問題が解決せず、テスト ケースで何かが見落とされた場合に興味があります。