2013-01-23 14 views
5

Obecnie pracuję nad programem symulacji 3D Rigid Body. W tej chwili udało mi się zmusić ciała sztywne do kolizji z podłogą i podskakując poprawnie za pomocą impulsu. Jednak mój problem polega na tym, że po odbiciu się ciągle przyspieszają pomimo użycia wektora tarcia, aby spróbować je spowolnić.Tarcie symulacji sztywnego ciała

Jest to kod, gdy uderzył o ziemię

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

Dzięki

EDIT: Oto kod integracja.

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

Czy jesteś pewien, że 'fDirection.normalize()' wpływa na fDirection? Czy to możliwe, że ta funkcja po prostu zwraca znormalizowaną wersję wektora bez wpływu na 'fDirection'? – Joey

+0

Jeśli ciała przyspieszają po odbiciu, byłoby świetnie zobaczyć fragment kodu, z którym integrujesz się z czasem. –

+0

Tak, właśnie to sprawdziłem i to zdecydowanie wpływa na to bezpośrednio. –

Odpowiedz

6

Muszę powiedzieć, że jest to dość przerażający fragment kodu, który tam masz, i robię to od ponad 10 lat. Powinieneś dostać podstawowy podręcznik dynamiki (jak Hibbeler).

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

Równanie to rodzaj wygląda próbujesz obliczyć impuls restytucji od wpływu (mimo że wyliczenie jest źle). Po pierwsze, musisz zrozumieć, że impuls nie jest tym samym co siła. Impuls jest całką siły w określonym przedziale czasu. Podczas uderzenia możesz założyć, że ten czas jest naprawdę mały i dlatego dokonujesz natychmiastowej zmiany prędkości. I dlatego powinieneś był sprecyzować, że kod integracji nie ma nic wspólnego z obliczaniem kolizji, ponieważ jest on ominięty w tej chwili, a przynajmniej powinien być, jeśli wykonujesz obliczenia oparte na impulsach. To, co rzeczywiste kalkulacja powinna wyglądać następująco:

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; 

Które rzeczywiście upraszcza do jednej linii, co następuje:

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

Jeśli jednak zakładać podłogę mieć nieskończoną masę (lub znacznie większy niż ciała), wtedy po prostu masz:

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

To takie proste. Ale przy tym założeniu nie zachowujesz dynamiki.Ale w każdym przypadku, impuls restytucja od uderzenia może być obliczona jako:

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

Teraz, ponieważ impuls restytucja jest całka siły normalnej nad małym okresie czasu uderzeniu impuls od tarcie podczas uderzenia można obliczyć na podstawie tego wpływu na zadośćuczynienie. Siła tarcia jest równa "mu" razy większa od siły normalnej, tj. |Ff| = mu * |Fn|, jest to również ważne dla impulsu, tj. |If| = mu * |In|. Możesz więc obliczyć go bezpośrednio:

Real friction_impulse = mu * fabs(impulse); 

Ale to tylko wielkość impulsu tarcia. Jest to kierunek jest odwrotny od względnej prędkości obwodowej, który jest:

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

I to kierunek jest:

Rvector dir_rel_vel = tangent_rel_vel; 
dir_rel_vel.normalize(); 

(Zauważ, że muszę utrzymać prędkość styczną nienaruszone, ponieważ będzie ona potrzebna później)

w tym momencie, można obliczyć prędkość styczną po uderzeniu w następujący sposób (znów przy założeniu nieskończonej masowej piętrze, w przeciwnym razie jest to bardziej skomplikowane):

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

Co jednak, jeśli impuls tarcia spowoduje, że styczna prędkość względna dojdzie do zera? To problem, ponieważ dzięki powyższej formule część impulsu tarcia mogłaby skończyć odwracając kierunek stycznej prędkości względnej, co oznaczałoby, że podczas drugiej części uderzenia siła tarcia działa w kierunku prędkość (niedobra). Największym tarciem może być zatrzymanie ruchu względnego. Więc trzeba sprawdzić dla tego warunku:

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. 

W tym momencie, wszystko co musisz zrobić, to połączyć dwa końcowe prędkości:

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

I to, co najmniej, za to bardzo prosty problem (nieskończona masa podłogi). W rzeczywistości to podejście oparte na impulsach staje się coraz trudniejsze do opanowania, gdy komplikujesz rzeczy: dwie obiekty o skończonej masie, wiele obiektów i faktyczną dynamikę ciała sztywnego (ponieważ właśnie robisz dynamikę cząstek tutaj). Podejście oparte na impulsie rzadko spotyka się poza zwykłymi szkolnymi przykładami piłek odbijających się od podłogi. Btw, nie powinieneś nazywać tego symulatorem "sztywnego ciała", ponieważ właściwie robisz dynamikę cząstek (a dynamika ciała sztywnego 3D jest o wiele bardziej skomplikowana niż ta). Także twoje prawo integracyjne jest straszne, ale to zupełnie inna historia.

+0

Być może możesz nieco go zmniejszyć, ale +1 dla dokładnej analizy. Dobrze też zauważyć, że metoda Eulera jest najgorszym możliwym wyborem. Być może pomógłby link do http://en.wikipedia.org/wiki/Numerical_ordinary_differential_equations. –

+0

Dzięki. Kiepski kod nie jest mi potrzebny, obecnie pracuje nad modułem uniwersyteckim, a ramy i wiele kodu zostały nam przekazane. Zmagam się z tym tak bardzo, ponieważ jest tak źle! Następnym razem, gdy będę z nim, skorzystam z twojej rady. Dzięki za odpowiedź. –

+0

Dzięki za pomoc, wszystko działa dobrze teraz. –

0

Tarcie jest cofnięte, prawda?

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

To jest w kierunku prędkości. Następnie pomnożyć przez niektórych stałych następnie dodać go do prędkości (wraz z impulsem)

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

tak, że będzie działać, aby zwiększyć prędkość po podłodze.


Są też inne dziwne rzeczy:
Twój impuls termin ma: (1/m_Bodies[i].mass + floorMass) to dodanie 1/masa do masy. Jeśli nie, to w integratorze oblicza się przyspieszenie z siły, ale nigdy nie integruje się z nim, a także bezpośrednio stosuje się siłę do prędkości. Więc do czego służy element acceleration?

+0

Tak, próbowałem tego i dostaję jakiś dziwny rodzaj jittering. W pewnym sensie przeskakują w tył, a następnie przesuwają się do przodu. –

+0

1) Czy jesteś pewien, że kod kolizji jest wywoływany tylko raz na kolizję? 2) gdzie ustawiasz 'body.force'? – AShelly

+0

Taka kolizja jest zdecydowanie nazywana tylko raz na kolizję. ciało.siła jest ustawiona na wektor zerowy przy inicjalizacji obiektów. –