3

我一直在使用一本讨论物理引擎的书。它使用 C++,但我使用 Java,所以复制粘贴不起作用(我也不会这样做)。

我注意到的一个有问题的区域是在 Quaternion 类的 add(Vector3D) 函数中,我无法找出错误。我刚刚从书中了解到四元数(这本书还手握了一些数学知识),所以我对四元数的体验并不好。

这是问题所在:

  1. 我有一个对象,它的方向由归一化(幅度 = 1)四元数表示。
  2. 我在物体上应用这个恒定扭矩 [ 0 , 0 , 1 ](所以只有 z 方向的扭矩)
  3. 扭矩引起角加速度,引起角速度,引起角位置的变化,通过在其方向上添加 3D 矢量来修改。物体似乎可以很好地旋转 0 到 ~60 度
  4. 在约 60 度时,旋转减慢
  5. 当物体旋转大约 90 度时,它停止旋转
  6. println() 语句显示当旋转接近 90 度时,对象的方向四元数接近 [ sqrt(2) , 0 , 0 , -sqrt(2) ] 并卡在那里。
  7. 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 );

这恰好纠正了错误并通过了我的其他测试。但是,我很好奇为什么用乘数切换被乘数会解决问题,或者如果它没有解决问题并且我的测试用例遗漏了一些东西。

4

1 回答 1

1

仔细查看乘法函数最后一行的末尾:

.subtract( y1.multiply( x2 ).add( z1.multiply( w2 ) ) );

括号有问题。尝试,而不是:

.subtract( y1.multiply( x2 ) ).add( z1.multiply( w2 ) );  
于 2013-07-18T03:37:46.650 回答