2013-01-23 18 views
5

Attualmente sto lavorando a un programma di simulazione 3D rigido del corpo. Al momento sono riuscito a far entrare in collisione i corpi rigidi con il pavimento e a rimbalzare correttamente usando l'impulso. Tuttavia, il mio problema è che una volta rimbalzato accelerano costantemente nonostante l'uso di un vettore di attrito per cercare di rallentarli.Attrito simulazione corpo rigido

Questo è il codice quando si colpisce il terreno

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); 

Grazie

Edit: Ecco il codice di integrazione.

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; 
    } 
} 
+0

Sei sicuro che 'fDirection.normalize()' influenza fDirection? Potrebbe essere che questa funzione restituisca solo una versione normalizzata del vettore senza influenzare 'fDirection' stessa? – Joey

+0

Se i corpi accelerano dopo essere rimbalzati, sarebbe bello vedere il pezzo di codice in cui ci si integra nel tempo. –

+0

Sì, l'ho appena controllato e sicuramente lo riguarda direttamente. –

risposta

6

Devo dire che questo è un codice piuttosto terribile che hai lì, e lo faccio da più di 10 anni. Dovresti ottenere un manuale di base sulla dinamica (come Hibbeler).

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

Questa equazione sorta di sguardi che si sta tentando di calcolare l'impulso restituzione per l'impatto (anche se il calcolo è sbagliato). Primo, devi capire che un impulso non è la stessa cosa di una forza. Un impulso è l'integrale della forza per un certo intervallo di tempo. Durante un impatto, puoi assumere che il periodo di tempo sia veramente piccolo ed è per questo che esegui un cambio di velocità istantaneo. Ed è per questo che dovresti aver specificato che il codice di integrazione non ha nulla a che fare con il calcolo della collisione, perché è by-passato per quell'istante, o almeno dovrebbe essere se fai un calcolo basato sull'impulso. Questo è ciò che il calcolo effettivo dovrebbe essere simile:

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; 

che semplifica in realtà ad una riga come segue:

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); 

Tuttavia, se si assume il piano di avere massa infinita (o molto più grande di quello del corpo), quindi si avrebbe semplicemente:

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

È così semplice. Ma, sotto quell'ipotesi, non hai la conservazione della quantità di moto.Ma in ogni caso, l'impulso restituzione per l'impatto può essere calcolato come:

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

Ora, perché l'impulso restituzione è l'integrale della forza normale sul piccolo periodo di tempo di impatto, la impulso dal l'attrito durante l'impatto può essere calcolato dall'impatto di restituzione. La forza di attrito è uguale a "mu" volte la forza normale, cioè |Ff| = mu * |Fn|, questo vale anche per l'impulso, cioè |If| = mu * |In|. Quindi, puoi calcolarlo direttamente:

Real friction_impulse = mu * fabs(impulse); 

Ma questa è solo la grandezza dell'impulso di attrito. E 'direzione è opposta dalla velocità tangenziale relativa, che è:

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

Ed è direzione è:

Rvector dir_rel_vel = tangent_rel_vel; 
dir_rel_vel.normalize(); 

(Si noti che ho bisogno di mantenere la velocità tangenziale intatto, perché sarà necessario in seguito)

a questo punto, si potrebbe calcolare la velocità tangenziale dopo l'impatto nel modo seguente (ancora una volta, sotto l'ipotesi di un piano infinito di massa, in caso contrario, è più complicato di così):

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

Tuttavia, cosa succede se l'impulso di attrito fa sì che la velocità relativa tangenziale arrivi a zero? Questo è un problema, perché, con la formula di cui sopra, parte dell'impulso di attrito potrebbe finire per invertire la direzione della velocità relativa tangenziale, il che significherebbe che durante l'ultima parte dell'impatto, la forza di attrito sta effettivamente agendo nella direzione di la velocità (non buona). La maggior parte dell'attrito può fare è fermare il moto relativo. Quindi, è necessario verificare la presenza di tale condizione:

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. 

A questo punto, tutto quello che dovete fare è combinare le due velocità finali:

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

E questo è tutto, almeno, proprio per questo problema semplice (massa infinita del pavimento). In realtà, questo approccio basato sull'impulso diventa sempre più difficile da gestire mentre complicate le cose: due oggetti a massa finita, oggetti multipli e dinamiche di corpo rigido effettive (perché qui state facendo solo la dinamica delle particelle). L'approccio basato sull'impulso si vede raramente ovunque, al di là dei semplici esempi di palle da campo rimbalzanti sul pavimento. A proposito, non dovresti davvero chiamarlo simulatore "corpo rigido" poiché tutto ciò che stai facendo è una dinamica particellare (e la dinamica 3D del corpo rigido è molto più complicata di così). Inoltre, la tua legge sull'integrazione è terribile, ma questa è una storia completamente diversa.

+0

Forse puoi attenuarlo un po ', ma +1 per un'attenta analisi. Inoltre, è bene sottolineare che il metodo di Eulero è la peggiore scelta possibile. Forse un link a http://en.wikipedia.org/wiki/Numerical_ordinary_differential_equations sarebbe d'aiuto. –

+0

Grazie. Il povero codice non è per me, è attualmente un lavoro per un modulo universitario e ci sono stati dati il ​​quadro e molti altri del codice. Sto lottando con esso così tanto perché è così male! La prossima volta che ci starò di fronte userò il tuo consiglio. Grazie per la tua risposta. –

+0

Grazie mille per il tuo aiuto, ora funziona tutto bene. –

0

L'attrito è indietro, non è vero?

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

Questo è nella direzione della velocità. È quindi moltiplicarlo per alcune costanti poi aggiungerlo alla velocità (insieme con l'impulso)

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

In modo che agirà per aumentare la velocità lungo il pavimento.


ci sono alcune altre cose strane:
vostro termine impulso ha: (1/m_Bodies[i].mass + floorMass) Questo è l'aggiunta di 1/massa di massa. Non dovrebbe essere (1/(m_Bodies[i].mass + floorMass))

E poi nell'integratore, si calcola l'accelerazione dalla forza, ma poi non si integra mai, si applica anche la forza alla velocità direttamente. Allora, qual è il membro acceleration?

+0

Sì, ci ho provato e ho avuto qualche strana forma di jittering. Si muovono all'indietro abit abitualmente e poi continuano a scorrere in avanti. –

+0

1) sei sicuro che il codice di collisione sia chiamato solo una volta per collisione? 2) dove imposti 'body.force'? – AShelly

+0

Sì, la collisione si chiama definitivamente solo una volta per collisione. corpo.la forza è impostata su un vettore zero sull'inizializzazione degli oggetti. –