2d проблема столкновения шара: нет сохранения энергии

Я пытаюсь написать простую физическую симуляцию, в которой шары с разным радиусом и массой отскакивают в идеально эластичной среде без трения. Я написал свой собственный код после этого ресурса: http://www.vobarian.com/collisions/2dcollisions2.pdf, и я также протестировал код отсюда: Ball to Ball Столкновение - обнаружение и устранение

ВОПРОС ОТредактирован

С помощью Рика Голдштейна и Ральфа я заставил свой код работать (была опечатка ...). Большое спасибо за вашу помощь. Однако я все еще не понимаю, почему другой алгоритм не работает для меня. Шарики отскакивают в правильном направлении, но общая энергия системы никогда не сохраняется. Скорости становятся все быстрее и быстрее, пока шары не начнут мигать в статичных положениях на экране. Я действительно хочу использовать этот код в своей программе, потому что он намного более краток, чем тот, который я написал.

Вот функциональный алгоритм, который я написал (хотя я взял первый бит из другого источника). Это в классе Bubble:

public void resolveCollision(Bubble b)
{
    // get the minimum translation distance
    Vector2 delta = (position.subtract(b.position));
    float d = delta.getMagnitude();
    // minimum translation distance to push balls apart after intersecting
    Vector2 mtd = delta.multiply(((getRadius() + b.getRadius())-d)/d); 

    // resolve intersection --
    // inverse mass quantities
    float im1 = 1 / getMass(); 
    float im2 = 1 / b.getMass();

    // push-pull them apart based off their mass
    position = position.add(mtd.multiply(im1 / (im1 + im2)));
    b.position = b.position.subtract(mtd.multiply(im2 / (im1 + im2)));

    //get the unit normal and unit tanget vectors
    Vector2 uN = b.position.subtract(this.position).normalize();
    Vector2 uT = new Vector2(-uN.Y, uN.X);

    //project ball 1 & 2 's velocities onto the collision axis
    float v1n = uN.dot(this.velocity);
    float v1t = uT.dot(this.velocity);
    float v2n = uN.dot(b.velocity);
    float v2t = uT.dot(b.velocity);

    //calculate the post collision normal velocities (tangent velocities don't change)
    float v1nPost = (v1n*(this.mass-b.mass) + 2*b.mass*v2n)/(this.mass+b.mass);
    float v2nPost = (v2n*(b.mass-this.mass) + 2*this.mass*v1n)/(this.mass+b.mass);

    //convert scalar velocities to vectors
    Vector2 postV1N = uN.multiply(v1nPost);
    Vector2 postV1T = uT.multiply(v1t);
    Vector2 postV2N = uN.multiply(v2nPost);
    Vector2 postV2T = uT.multiply(v2t);

    //change the balls velocities
    this.velocity = postV1N.add(postV1T);
    b.velocity = postV2N.add(postV2T);
}

А вот тот, который не работает

public void resolveCollision(Bubble b)
{
    // get the minimum translation distance
    Vector2 delta = (position.subtract(b.position));
    float d = delta.getMagnitude();
    // minimum translation distance to push balls apart after intersecting
    Vector2 mtd = delta.multiply(((getRadius() + b.getRadius())-d)/d); 

    // resolve intersection --
    // inverse mass quantities
    float im1 = 1 / getMass(); 
    float im2 = 1 / b.getMass();

    // push-pull them apart based off their mass
    position = position.add(mtd.multiply(im1 / (im1 + im2)));
    b.position = b.position.subtract(mtd.multiply(im2 / (im1 + im2)));

    // impact speed
    Vector2 v = (this.velocity.subtract(b.velocity));
    float vn = v.dot(mtd.normalize());

    // sphere intersecting but moving away from each other already
    if (vn > 0.0f) return;

    // collision impulse (1f is the coefficient of restitution)
    float i = (-(1.0f + 1f) * vn) / (im1 + im2);
    Vector2 impulse = mtd.multiply(i);

    // change in momentum
    this.velocity = this.velocity.add(impulse.multiply(im1));
    b.velocity = b.velocity.subtract(impulse.multiply(im2));
}

Дай мне знать, если найдешь что-нибудь. Спасибо


person Cbas    schedule 15.02.2011    source источник


Ответы (3)


Есть ли опечатка в строке, устанавливающей v1nPost? Похоже, знаменатель должен быть this.mass + b.mass, а не this.mass * b.mass.

Кроме того, поскольку вы вычисляете столкновение между this и b, проверяете ли вы, чтобы убедиться, что вы не делаете такое же столкновение между b и this, тем самым удваивая дельту, применяемую к каждому участвующему пузырю в столкновении?

person Rick Goldstein    schedule 15.02.2011
comment
Да, это была опечатка ... Должно быть, я слишком долго сидел перед своим компьютером, я проверял математику много раз. Спасибо. Однако проверьте мои изменения, я действительно хочу, чтобы другой алгоритм работал, и это определенно не та же проблема. - person Cbas; 16.02.2011
comment
Что касается вашего второго пункта, я не проверяю, разрешил ли я столкновение в моем цикле через пузыри, но я не думаю, что это имеет значение, поскольку первое, что я делаю в этом методе resolveCollision, - это их разнимать. Когда цикл действительно находит другой шар в столкновении, он больше не будет сталкиваться и больше не будет вызывать этот метод - person Cbas; 16.02.2011
comment
Когда вы вычисляете mtd.normalize(), остается ли mtd нормализованным? Если нет, думаю, вам нужно изменить расчет impulse на mtd.normalize().multiply(i). Помимо этого, вы можете наблюдать накопленные ошибки округления, особенно при переводе в систему отсчета b (расчет «скорости удара»). - person Rick Goldstein; 17.02.2011
comment
Вот и все! Теперь отлично работает. Честно говоря, я не понимал всей математики, лежащей в основе этого, так как я просто взял его из другого источника, так что большое спасибо за помощь мне в этом. Я обязательно напишу комментарий к этому другому потоку, проинформируя их, что в коде есть ошибка. - person Cbas; 17.02.2011
comment
Рад, что помог. В прошлой жизни я был физиком, поэтому пару раз решал задачи о столкновениях. Ваше здоровье! - person Rick Goldstein; 17.02.2011

Я делаю первое предположение: getMass () возвращает целое число (или int) (а не float или double)?

Если это правда, то проблема в том, что 1 / getMass() приведет к целочисленному значению (и может быть только 1 или чаще всего 0)). Чтобы исправить это, замените 1 на 1.0 или 1.0f

Поскольку общее правило простое: если у вас есть математическая операция (+, -, *, /), результирующий тип будет целочисленным, если ни один из обоих оперантов не является структурой данных с плавающей запятой (double или float)

В любом случае: может быть вторая проблема, возможно, ваши вычисления недостаточно точны. Тогда вам следует использовать double вместо float.

person Ralph    schedule 15.02.2011
comment
Это очень хороший момент. getMass () фактически возвращает байт, и до сих пор в моем коде все пузырьки имели массу 1. Если бы массы различались, я вижу, что у меня возникнет проблема. Я изменю это, большое спасибо. - person Cbas; 16.02.2011

Есть часть, которая выглядит странно:

Два расчета:

float v1nPost = (v1n*(this.mass-b.mass) + 2*b.mass*v2n)/(this.mass*b.mass);
float v2nPost = (v2n*(b.mass-this.mass) + 2*this.mass*v1n)/(this.mass+b.mass);

симметричны, кроме последней операции, в первой - *, во второй - +

person Ralph    schedule 15.02.2011
comment
Это было .. Большое спасибо. Проверьте мое редактирование, теперь я пытаюсь заставить другой алгоритм работать - person Cbas; 16.02.2011