刚体仿真摩擦力

Tom*_*ith 5 c++ 3d physics rigid-bodies

我目前正在研究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);
Run Code Online (Sandbox Code Playgroud)

谢谢

编辑:这是集成代码.

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

Mik*_*son 6

我不得不说,这是你所拥有的一段相当糟糕的代码,而且我已经这么做了十多年了.你应该得到一本关于动力学的基础教科书(比如Hibbeler).

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

这个等式看起来像是在尝试从影响中计算归还冲动(虽然计算错误).首先,你必须明白冲动与力量不是一回事.冲动是在一定时间间隔内力的积分.在撞击过程中,您可以假设这段时间非常小,这就是您执行瞬时速度变化的原因.这就是为什么你应该指定集成代码与碰撞计算无关,因为它在那一瞬间被旁路,或者至少应该是你做一个基于脉冲的计算.这是实际计算应该是这样的:

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;
Run Code Online (Sandbox Code Playgroud)

这实际上简化为一行如下:

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);
Run Code Online (Sandbox Code Playgroud)

但是,如果你假设地板有无限质量(或者比身体大得多),那么你只需要:

Real body_rel_vel_after = -e * Rvector::dotProduct(relativeVelocities, floorNormal);
Real body_vel_after = Rvector::dotProduct(floorVelocity, floorNormal) + body_rel_vel_after;
Run Code Online (Sandbox Code Playgroud)

就这么简单.但是,在这种假设下,你没有动力守恒.但无论如何,影响的归还冲动可以计算如下:

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

现在,因为恢复冲动是在小的冲击时间段内法向力的积分,所以冲击过程中摩擦力的冲动可以从恢复冲击中计算出来.摩擦力等于法向力的"μ"倍,即|Ff| = mu * |Fn|,这对于脉冲也是有效的,即|If| = mu * |In|.所以,你可以直接计算它:

Real friction_impulse = mu * fabs(impulse);
Run Code Online (Sandbox Code Playgroud)

但这只是摩擦冲动的大小.它的方向与相对切向速度相反,即:

Rvector tangent_rel_vel = relativeVelocities - Rvector::dotProduct(relativeVelocities, floorNormal) * floorNormal;
Run Code Online (Sandbox Code Playgroud)

它的方向是:

Rvector dir_rel_vel = tangent_rel_vel;
dir_rel_vel.normalize();
Run Code Online (Sandbox Code Playgroud)

(请注意,我需要保持切向速度不变,因为稍后需要它)

此时,你可以计算冲击后的切向速度如下(同样,在无限质量底板的假设下,否则,它比这更复杂):

Rvector tangent_rel_vel_after = tangent_rel_vel - dir_rel_vel * friction_impulse / m_Bodies[i].mass;
Run Code Online (Sandbox Code Playgroud)

但是,如果摩擦脉冲导致切向相对速度变为零怎么办?这是一个问题,因为,根据上面的公式,部分摩擦力脉冲可以扭转切向相对速度的方向,这意味着在冲击的后半部分,摩擦力实际上是朝向速度(不好).最摩擦可以做的是停止相对运动.所以,你需要检查这个条件:

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.
Run Code Online (Sandbox Code Playgroud)

在这一点上,你需要做的就是结合两个最终的速度:

m_Bodies[i].Vel = floorVelocity + tangent_rel_vel_after + body_rel_vel_after * floorNormal;
Run Code Online (Sandbox Code Playgroud)

这至少就是这个非常简单的问题(地板的无限质量).实际上,当你使事情变得复杂时,这种基于脉冲的方法变得越来越难以处理:两个有限质量的物体,多个物体和实际的刚体动力学(因为你这里只是做粒子动力学).除了简单的校园球在地板上弹跳的例子之外,很少见到基于冲动的方法.顺便说一句,你不应该把它称为"刚体"模拟器,因为你所有人都在做粒子动力学(3D刚体动力学比这更复杂).此外,你的整合法很糟糕,但这是一个完全不同的故事.