7

私は現在、3Dリジッドボディシミュレーションプログラムに取り組んでいます。私は現在、剛体を床に衝突させ、衝撃を使用して正しく跳ね返らせることができました。ただし、私の問題は、バウンスすると、摩擦ベクトルを使用して速度を落とそうとしているにもかかわらず、絶えず加速することです。

これはあなたが地面に着いたときのコードです

Rvector fDirection(m_Bodies[i].Vel.x,0.0,m_Bodies[i].Vel.z);
Rvector relativeVelocities = m_Bodies[i].Vel - floorVelocity;
fDirection.normalize();


Real impulse = -(1+e) * (Rvector::dotProduct(relativeVelocities,floorNormal)) 
/ (1/m_Bodies[i].mass + floorMass);
Rvector friction = fDirection*mu*gravity.length()*m_Bodies[i].mass;

Rvector collision_forces = Rvector(0,1,0)*impulse;
collision_forces += friction ;

m_Bodies[i].Vel += (collision_forces/m_Bodies[i].mass);

ありがとう

編集:これが統合コードです。

void RigidBodySimulation::eulerIntegration(float dTime)
{
    Rvector newVel;
    Rvector newPos;
    Rvector zero(0.0, 0.0, 0.0);
    Real one_over_mass;
    Rvector accel;
    for( unsigned int i = 0 ; i < m_Bodies.size(); i++)
    {   
        one_over_mass = 1/m_Bodies[i].mass;
        newVel = m_Bodies[i].Vel + m_Bodies[i].force*one_over_mass*dTime;
        newPos = m_Bodies[i].Pos + m_Bodies[i].Vel*dTime;
        accel = m_Bodies[i].force / m_Bodies[i].mass;
        m_Bodies[i].acceleration = accel;
        m_Bodies[i].newPos = newPos;  
        m_Bodies[i].Vel = newVel;
        m_Bodies[i].Pos = newPos;
    }
}
4

2 に答える 2

8

言わなければならないのは、これはあなたがそこに持っているかなりひどいコードであり、私はこれを10年以上行っています。ダイナミクスに関する基本的な教科書(Hibbelerなど)を入手する必要があります。

Real impulse = -(1+e) * (Rvector::dotProduct(relativeVelocities,floorNormal))
               / (1/m_Bodies[i].mass + floorMass);

この方程式は、衝撃から復元インパルスを計算しようとしているように見えます(計算は間違っていますが)。まず、衝動は力と同じものではないことを理解する必要があります。インパルスは、特定の時間間隔での力の積分です。衝撃の間、あなたはその期間が本当に短いと仮定することができます、そしてそれはあなたが瞬間的な速度変化を実行する理由です。そのため、統合コードは衝突計算とは関係がないことを指定する必要があります。これは、その瞬間にバイパスされるためです。少なくとも、インパルスベースの計算を行う場合はバイパスされるはずです。実際の計算は次のようになります。

Real momentum_before = Rvector::dotProduct(m_Bodies[i].Vel * m_Bodies[i].mass + floorVelocity * floorMass, floorNormal);
Real rel_vel_after = -e * Rvector::dotProduct(relativeVelocities,floorNormal);
// conservation of momentum in normal direction gives this:
Real body_vel_after = (momentum_before + floorMass * rel_vel_after) / (m_Bodies[i].mass + floorMass);
Real floor_vel_after = body_vel_after - rel_vel_after;

これは実際には次のように1行に簡略化されます。

Real body_vel_after = ( (m_Bodies[i].mass - e * floorMass) * Rvector::dotProduct(m_Bodies[i].Vel, floorNormal)
                      + (1.0 + e) * floorMass * Rvector::dotProduct(floorVelocity, floorNormal) 
                      ) / (m_Bodies[i].mass + floorMass);

ただし、床の質量が無限大(または体の質量よりはるかに大きい)であると仮定すると、次のようになります。

Real body_rel_vel_after = -e * Rvector::dotProduct(relativeVelocities, floorNormal);
Real body_vel_after = Rvector::dotProduct(floorVelocity, floorNormal) + body_rel_vel_after;

とても簡単です。しかし、その仮定の下では、勢いの保存はありません。しかし、いずれにせよ、衝撃からの回復インパルスは次のように計算できます。

Real impulse = m_Bodies[i].mass * (body_vel_after - Rvector::dotProduct(m_Bodies[i].Vel, floorNormal));

ここで、復元インパルスは、衝撃の短い期間にわたる垂直力の積分であるため、衝撃中の摩擦からのインパルスは、その復元衝撃から計算できます。摩擦力は、垂直抗力の「mu」倍に等しくなります。つまり|Ff| = mu * |Fn|、これは力積に対しても有効です|If| = mu * |In|。したがって、直接計算できます。

Real friction_impulse = mu * fabs(impulse);

しかし、それは摩擦インパルスの大きさです。その方向は、相対接線速度とは反対です。

Rvector tangent_rel_vel = relativeVelocities - Rvector::dotProduct(relativeVelocities, floorNormal) * floorNormal;

そしてその方向性は次のとおりです。

Rvector dir_rel_vel = tangent_rel_vel;
dir_rel_vel.normalize();

(後で必要になるため、接線速度をそのまま維持する必要があることに注意してください)

この時点で、次のように衝撃後の接線速度を計算できます(ここでも、無限の質量の床を想定しています。それ以外の場合は、それよりも複雑です)。

Rvector tangent_rel_vel_after = tangent_rel_vel - dir_rel_vel * friction_impulse / m_Bodies[i].mass;

しかし、摩擦インパルスによって接線方向の相対速度がゼロになるとどうなりますか?これは問題です。上記の式では、摩擦インパルスの一部が接線方向の相対速度の方向を反転させる可能性があるためです。つまり、衝撃の後半では、摩擦力が実際には次の方向に作用します。速度(良くない)。最も摩擦ができるのは、相対運動を止めることです。したがって、その状態を確認する必要があります。

Real tang_rel_vel_change = friction_impulse / mBodies[i].mass;
Rvector tangent_rel_vel_after = tangent_rel_vel - dir_rel_vel * tang_rel_vel_change;

if ( tang_rel_vel_change > tangent_rel_vel.length() ) 
    tangent_rel_vel_after = Rvector(0.0, 0.0, 0.0);   // stop relative motion.

この時点で、あなたがする必要があるのは、2つの最終的な速度を組み合わせるだけです。

m_Bodies[i].Vel = floorVelocity + tangent_rel_vel_after + body_rel_vel_after * floorNormal;

そして、少なくとも、この非常に単純な問題(床の無限の質量)についてはこれで終わりです。実際には、このインパルスベースのアプローチは、2つの有限質量オブジェクト、複数のオブジェクト、および実際の剛体ダイナミクス(ここでは粒子ダイナミクスを実行しているため)を複雑にするにつれて、処理がますます困難になります。インパルスベースのアプローチは、ボールが床で跳ねるという単純な校庭の例を超えて、めったに見られません。ところで、実際にパーティクルダイナミクスを実行しているので、これを「リジッドボディ」シミュレータと呼ぶべきではありません(3Dリジッドボディダイナミクスはこれよりもはるかに複雑です)。また、あなたの統合法はひどいですが、それはまったく別の話です。

于 2013-01-23T17:46:40.770 に答える
0

摩擦は逆ですよね?

 Rvector fDirection(m_Bodies[i].Vel.x,0.0,m_Bodies[i].Vel.z);

これは速度の方向です。次に、それをいくつかの定数で乗算し、それを速度に追加します(インパルスとともに)

collision_forces += friction ;
 m_Bodies[i].Vel += (collision_forces/m_Bodies[i].mass);

そのため、床に沿った速度を上げるように機能します。


他にも奇妙なことがいくつかあり
ます。インパルス項には次 のようなものがあります。(1/m_Bodies[i].mass + floorMass) これは、質量に1/質量を追加することです。あるべきではない(1/(m_Bodies[i].mass + floorMass))

次に、積分器で、力から加速度を計算しますが、それを積分することはありません。また、速度に直接力を適用します。では、メンバーは何のaccelerationためにあるのでしょうか?

于 2013-01-23T16:25:44.507 に答える