在球之间的碰撞解决之后,动量不守恒

时间:2013-04-22 16:52:45

标签: physics

我在球之间的碰撞分辨率方面存在问题。 实际上碰撞反应是非常现实的,但动量是不守恒的,为什么呢?

我使用基于此文档的算法:http://www.vobarian.com/collisions/2dcollisions2.pdf

在Java代码中,我的算法是:

/**
* Resolve the collision creating new velocities according to physical laws. 
*/
public void resolveCollisionWith(Ball ball) {
    //First resolve intersection for calculate correct new velocities.
    resolveIntersectionWith(ball);

    //Unit normal vector uN is the unit-vector that links the two centers.
    Vector uN = mPosition.subtraction(ball.getPosition()).normalize();

    //Unit tangent vector uT is the unit-vector normal to uN. It's tangent to both the    two balls.
    Vector uT = new Vector(-uN.getY(), uN.getX());

    //Project the two balls velocities onto the collision axis(uT and uN vectors).
    double v1n = uN.dot(mVelocity), v1t = uT.dot(mVelocity);
    double v2n = uN.dot(ball.getVelocity()), v2t = uT.dot(ball.getVelocity());

    //Calculate the post collision normal velocities (tangent velocities don't change).
    double v1nPost = (v1n*(mMass-ball.getMass()) + 2*ball.getMass()*v2n)/(mMass+ball.getMass());
    double v2nPost = (v2n*(ball.getMass()-mMass) + 2*mMass*v1n)/(mMass+ball.getMass());

    //Convert scalar velocities to vectors.
    Vector postV1N = uN.multiplication(v1nPost), postV1T = uT.multiplication(v1t);
    Vector postV2N = uN.multiplication(v2nPost), postV2T = uT.multiplication(v2t);

    //Change the balls velocities.
    mVelocity.set(postV1N.addition(postV1T));
    ball.getVelocity().set(postV2N.addition(postV2T));
}

 /**
 * When two balls collide can occur an intersection(the distance between the centers
 * is less than the sum of the radii) that dephases the response. 
 * The method fix this situation bringing back the two ball according to their mass.
 */
private void resolveIntersectionWith(Ball ball){
    Vector n = mPosition.subtraction(ball.getPosition());
    // How much the distance between centers is less than the radii's sum.
    double offset = getRadius() + ball.getRadius() - n.length();
    n.normalize();
    n.multiply(offset);
    // Bring back the two ball according to their mass.
    mPosition.add(n.multiplication(ball.getMass() * 1.0 / (mMass + ball.getMass())));
    ball.getPosition().subtract(n.multiplication(mMass * 1.0 / (mMass + ball.getMass())));
}

 /**
 * Normalizes and returns this vector.
 */
 // ***INSIDE VECTOR CLASS***
public Vector normalize() {
    //Avoid division by zero.
    if (mX != 0 || mY != 0) {
        double lenght = length();
        mX /= lenght;
        mY /= lenght;
    }
    return this;
}

谢谢!

2 个答案:

答案 0 :(得分:2)

假设精确的数学运算,方程式本身将与动量相反。所以自然怀疑是浮点错误。在正常情况下,错误将非常小,尽管它们仍然会随着时间的推移而累积。但是,按小数除法可以放大错误。

当你对一个非常小的矢量进行标准化时,由于每个分量的分割中出现放大的误差,你可能会得到幅度不接近1的东西。这反过来将极大地改变这种势头。实际上,编写代码的方式可以为你提供无穷大或NaN,不过我认为如果是这样你就会注意到。

事实上,在某些情况下,你甚至根本不对矢量进行标准化(当两个组件都正好为0时)。但你仍然盲目地继续伪造矢量。

编辑:我刚注意到你的载体是可变的。我强烈建议让它们不变。它简化了代码并减少了丢失副本的错误范围。

答案 1 :(得分:1)

我的程序有类似的问题,我有一个用户定义的球量随机质量,半径和速度不断相互碰撞。由于十进制舍入误差而产生问题,这是不可避免的。

我认为的解决方案使我的动力始终保持在起始动量的正负1%左右,如下所示:

创建一个名为firstRun的布尔值,并将两个totalMomentum和totalMomentum2加倍。计算通过循环的第一次运行的总动量并将其保存到totalMomentum。然后,每隔一次运行,计算总动量并将其保存到totalMomentum2。

然后,对于第一次之后的每次跑步,将球的速度乘以比率(totalMomentum / totalMomentum2)。

这样,如果动量太高,就会使总量降低。如果它太低,它会使它更高。

我目前有200个球相互碰撞的设置,初始动量约为45000.我的范围一直保持在44500到45500之间。

希望这有帮助!

编辑:当我将球的数量减少到5并增加质量时,总动量约为22500,使用这种方法几乎完美地保留了动量。波动甚至不会发生在大多数时间,如果确实如此,它将从22500变为22499然后再变回22500.