1

I have a problem in collision resolution between balls. Actually collision responses are very realistic but the momentum is not conserved, why?

I use an algorithm based on this document: http://www.vobarian.com/collisions/2dcollisions2.pdf.

In Java code my algorithm is:

/**
* 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;
}

Thank you!

VanDir
  • 1,980
  • 3
  • 23
  • 41

2 Answers2

2

The equations themselves will converse momentum, assuming exact math. So the natural suspect is floating point errors. Under normal circumstances, the errors will be very small, though they can still accumulate over time. However, division by small numbers can magnify the errors.

When you normalize a very small vector, you may end up with something whos magnitude is not close to 1, thanks to magnified errors in the division of each component. This in turn will change the momentum greatly. In fact, the way your code is written could give you infinities or NaNs, though I assume you would have noticed if that were the case.

In fact, in some cases you don't even normalize the vector at all (when both of the components are exactly 0). But you still blindly continue with the bogus vector.

Edit: I just noticed your vectors are mutable. I'd highly recommend making them immutable instead. It simplifies the code and reduces the scope for bugs from missing copies.

Antimony
  • 37,781
  • 10
  • 100
  • 107
  • Regarding question: In your opinion can change something the use of float instead of double? In this question http://stackoverflow.com/questions/5008252/2d-ball-collision-problem-no-conservation-of-energy?rq=1 the user affirm to obtain the conservation of momentum using the same algorithm (with float)... – VanDir Apr 22 '13 at 17:28
  • Then how the user in question above has obtained the conservation? – VanDir Apr 22 '13 at 17:36
  • First off, what kind of errors are you seeing? Small errors that build up over time, or large errors after a single timestep? And if it's the latter, what kind of displacement vectors do you have? Did you check the magnitudes after normalization? – Antimony Apr 22 '13 at 18:36
  • For example I'm seeing that the 2 balls start with velocities length of 24 and 0 therefore momentum is 24(the masses are both 1), after first collision the momentum is 34, after another collisions 28, ---> 32 ---> 33 ---> 32. I check magnitudes of uN vector after normalization and it's almost always 1.0 and sometimes 0.9999999999999999. – VanDir Apr 23 '13 at 10:02
  • Are those velocities all in the same direction? Because if so, that really shouldn't be happening. Trying printing out the numbers for each variable and working through the first collision to see where the mistake is coming from. It might also be helpful to show how you're calculating momentum. – Antimony Apr 23 '13 at 12:30
  • I did a test like you said and the result of each variable is correct. I'm beginning to think that the algorithm is slightly wrong. To calculate momentum I sum the velocity of each ball( in my test the balls are two) since masses are 1. – VanDir Apr 23 '13 at 14:24
  • If every variable is correct, then by definition the momentum is correct as well. If you're seeing a change, there must be some point at which the momentum changes. Anyway, the equations are correct, but your implementation might have bugs somewhere. Perhaps you could construct a minimal executable example demonstrating the problem and post it so we can see exactly what's happening? – Antimony Apr 23 '13 at 15:13
1

I had a similar problem with my program where I have a user defined amount of balls with random masses, radii, and speeds constantly colliding with one another. The problem arises due to decimal rounding errors, which are impossible to avoid.

The solution I thought of which has kept my momentum consistently within plus or minus about 1% of the starting momentum is as follows:

Make a boolean called firstRun, and two doubles totalMomentum and totalMomentum2. Calculate the total momentum of the first run through the loop and save it to totalMomentum. Then, every other run, calculate the total momentum and save it to totalMomentum2.

Then, for every run after the first, multiply the ball's velocities by the ratio (totalMomentum/totalMomentum2).

This way, if the momentum is too high, it will make the total lower. If it is too low, it will make it higher.

I have a setup currently with 200 balls colliding with eachother with an initial momentum of about 45000. My range stays between 44500 and 45500 consistently.

Hope this helps!

EDIT: When I reduce the amount of balls to 5, and increased the mass, giving a total momentum of about 22500, the momentum was conserved almost perfectly using this method. The fluctuation would not even happen most of the time, and if it did, it would go from 22500 to 22499 then right back to 22500.