将3D向量添加到四元数

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. 角位置的变化是无限制的(因为有一个恒定的力矩,所以角速度和因此dtheta是无限制的)。当方块停止旋转时,角速度为E-4,因此我不认为这是由于浮点数不精确造成的

如果我改为施加恒定的力矩[1,0,0]或[0,1,0],则一切都可以完美地运行。这使我相信,在我的Quaternion类中,我在Z值上弄错了某些东西。然而,经过多个小时的尝试,我仍然找不到错误。

注意:在以下代码中,我使用Real类型的对象,其包含浮点数并具有将其相加和相减的方法。(这只是为了方便,如果我想要将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)方法使用了其他方法:

  • 我确信除以标量的方法已经正确实现,因为它与向量除以标量相同。
  • multiply(Quaternion)方法的公式是从书上获取的,如下所示:
  • add(Quaternion)方法如下。它将各个组件相加(w加到w,x加到x,y加到y,z加到z)

multiply(Quaternion):

您可以在此处找到公式: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 );
}

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类中还有其他简单的方法,我认为不可能出现错误(例如:getters、setters),但如果您想看到它们,请让我知道。

感谢您抽出时间阅读这篇长长的文章。我很感激。如果有任何不清楚的地方,请告诉我。帮助找到错误并解释我哪里做错了将会非常棒!

编辑 1:

入口代码: 基本上,我有一个RigidBody对象,它有一个被反复调用的方法。下面的代码是该方法中相关的角动量代码。在其中,“this”是指RigidBody对象。反转的惯性矩阵是一个3乘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:

我相信我已经修复了这个bug。我把错误缩小到了multiply(Quaternion)函数。这是第一个位置,其中反转z分量的符号可以纠正该bug。我知道四元数乘法不是交换律,所以我尝试交换它们。我将

Quaternion changeInAngularPosition = quaternionOmega.multiply( initialAngularPosition ).divide( Real.TWO );

改为

Quaternion changeInAngularPosition = initialAngularPosition.multiply( quaternionOmega ).divide( Real.TWO );

这恰好纠正了该bug并通过了我的其他测试。然而,我想知道为什么交换被乘数和乘积会解决问题,或者如果它没有解决问题,我的测试用例是否遗漏了什么。


你能在代码中添加入口点吗?你的七步问题描述中的步骤1-3的实现在哪里? - Nathaniel Waisbrot
我想不出任何情况下将3维向量逐分量加到四元数中会有意义。您可能需要重新考虑代码的这一部分应该做什么,我怀疑存在某种概念上的错误... - Jim Lewis
@Jim Lewis 对不起,如果我表达不清楚。add(Vector3D)方法通过应用我在上面写的Javadoc中的公式将Vector3D添加到四元数中。这会进行一些乘法和其他操作,导致方向发生变化的四元数。然后,add(Quaternion)方法逐个组件地添加两个四元数。 - user2570465
@Nathaniel Waisbrot 我在编辑中添加了入口点代码。如果还有其他需要提供的信息以使事情更清晰,请告诉我。 - user2570465
我相信我现在已经修复了这个 bug,但我仍然想知道为什么我的修复方法有效。请查看编辑 2。 - user2570465
1个回答

1
请仔细查看您的乘法函数最后一行的结尾:
.subtract( y1.multiply( x2 ).add( z1.multiply( w2 ) ) );

有一个括号问题。尝试使用以下代码代替:

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

哇,好棒的发现。那真的是问题所在。非常感谢你指出来。这比我乱搞代码找到的随机解决方案要好得多。 - user2570465

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接