Warum über Akkumulation integrieren?


14

Ich fange an, das Erlernen der DIY-Physik zu erlernen, und ich habe eine Frage zur Implementierung der Integration auf der grundlegendsten Ebene (dh dies ist keine Frage von Euler vs. RK4).

Fast jedes Beispiel, auf das ich integrate()stoße, hat eine Funktion, die den Zeitschritt seit dem letzten Update und die Beschleunigung (und / oder Geschwindigkeit und / oder Position) seit dem letzten Update aktualisiert.

In der einfachsten Form: position += velocity * deltaTime

Ich verstehe jedoch nicht, warum es sich so ansammelt, wenn es genauso leicht durch Ändern einer Funktion erhalten werden kann . Zum Beispiel: getPosition = makeNewFunction()Was etwas zurückgeben könnte, das die Signatur von hat Time -> Position, und das Innenleben dieser Funktion werden über die entsprechende mathematische Formel generiert.

Auf diese Weise gibt es keine Akkumulation ... wann immer die Position abgerufen werden muss, ruft es diese Funktion mit der aktuellen Zeit auf.

Mein Neuling Verständnis ist, dass dies auch die Fehler, die von Akkumulation kommen, vermeiden würde ... also warum funktioniert das nicht, was fehle ich?

(Fwiw Ich habe einen grundlegenden Proof of Concept für diese Idee zusammengestellt - obwohl gleichzeitig einige andere Dinge getestet werden, ist dies nicht das sauberste Beispiel: https://github.com/dakom/ball-bounce-frp )

EDIT 1: Wie in den Kommentaren erwähnt, ist es wahrscheinlich wichtig darauf hinzuweisen, dass ich noch nichts über das Ändern der Beschleunigung oder den Umgang mit Ruck und anderen Dingen gelernt habe, die eine Integration höherer Ordnung erfordern als eine konstante Beschleunigung.

EDIT 2: hier einige grundlegende Beispielcode von der Idee, und Pseudo-JavaScript - Syntax - Note , die getKinematicPositionsich teilweise angewandt , so wird eine neue Funktion von nur Zeit Rückkehr -> Position:

Ich halte an meiner Position fest, aber es könnte auch etwas anderes sein, getVelocitydenke ich ...

getKinematicPosition = initialVelocity => acceleration => time => 
  ((.5 *acceleration) * (time * time)) + (initialVelocity * time);

getPosition = getKinematicPosition ([0,0,0]) (GRAVITY);

onTick = totalTime => {
   position = getPosition (totalTime);
   onCollision = () => {
     getPosition = changeTheFunction(totalTime);
     //changeTheFunction uses totalTime to base updates from 0
     //it could use getKinematicPosition or something else entirely
   }
}

1
Was würde Ihre Funktion tun, wenn Sie eine nicht konstante Geschwindigkeit / Beschleunigung hätten?
Linaith

Ich habe keine Ahnung! : D Wenn das der Grund ist - z. B. habe ich die Beschleunigung noch nicht geändert. Ich würde mich sehr über eine ausführlichere Erklärung freuen, wo dies als Antwort fehlschlagen würde. Andernfalls könnte ich diese funktionale Straße verlassen und in eine Sackgasse geraten !)
davidkomer

6
Nun, wenn sich Ihr Objekt nur in einem Kreis bewegt, ist es sicher ... was ist, wenn der Spieler auf eine Kiste drückt? Wenn Sie getPosition (jetzt + 100) aufrufen, sagt es die Zukunft voraus, um zu wissen, wann der Spieler aufhören wird, es zu drücken? Wenn Sie getPosition (now-1000) aufrufen, muss es sich an die Vergangenheit erinnern?
user253751

Antworten:


34

... das Innenleben dieser Funktion wird über die entsprechende mathematische Formel erzeugt ...

Dies funktioniert für bestimmte Problemklassen, und der Schlüsselbegriff, nach dem gesucht werden muss, ist eine geschlossene Lösung. Beispielsweise wird im Kerbal Space Program die Bewegung eines Raumfahrzeugs im Orbit auf diese Weise berechnet. Leider haben die meisten nicht-trivialen Probleme (z. B. der Wiedereintritt des Raumfahrzeugs in die Atmosphäre) keine bekannte Lösung in geschlossener Form. Daher sind mathematisch einfachere numerische Näherungen (dh integrate()über die Zeit) erforderlich .


Ahh ... großartig! Wenn Sie eine Minute Zeit haben - was halten Sie davon, diesen funktionalen Ansatz anzustreben, und greifen Sie dann auf das Akkumulieren zurück, wenn ich nicht herausfinden kann, wie er funktioniert (z. B. wenn ich mit einem nicht geschlossenen Formularproblem zu tun habe oder Ich kann nicht herausfinden, wie ich es in eins verwandeln soll. Ich mag die Idee, Funktionen zu generieren, da sie 1: 1 in die Mathematik
passen

8
@davidkomer Warum willst du überhaupt weiterhin Funktionen generieren? Wenn Sie dies schaffen, können Sie einfach die gesamte Flugbahn vorberechnen und aufzeichnen! Natürlich machen die Leute das schon: Es heißt Animation und hat einen Teil an Feinheiten.
Joker_vD

Die Funktionen ändern sich je nach Laufzeitdynamik ... siehe zum Beispiel den FRP-Ball-Bounce
davidkomer

Eigentlich muss ich dieses Beispiel so aktualisieren, dass es eher wie Pong
aussieht

10

Das Problem bei Ihrer Vorgehensweise ist, dass Sie keine Historie Ihres Objekts haben. Sie können die Position berechnen, wenn Sie sich in eine Richtung bewegen, aber was passiert, wenn Sie etwas treffen und zurückprallen?
Wenn Sie sich von Ihrer letzten bekannten Position angesammelt haben, können Sie den Aufprall bewältigen und von dort aus fortfahren. Wenn Sie versuchen, es von Anfang an zu berechnen, müssen Sie den Aufprall jedes Mal neu berechnen oder als neue Startposition festlegen.

Ihr Beispiel erinnerte mich an ein Rennspiel. (Ich weiß nicht, ob die Position von der Physik-Engine gesteuert wird, aber ich denke, es funktioniert gut zu erklären)
Wenn Sie mit Ihrem Auto fahren, können Sie beschleunigen und verlangsamen. Sie können Ihre Position nicht berechnen, ohne zu wissen, wie das Geschwindigkeitsprofil Ihres Autos von Anfang an ausgesehen hat. Die Akkumulation der Distanz ist viel einfacher als das Speichern der Geschwindigkeit, die Sie von Anfang an in jedem Frame hatten.

Haftungsausschluss: Ich habe bis jetzt noch keine Spielphysik geschrieben. Genau so sehe ich das Problem.

Bearbeiten:
In diesem Diagramm können Sie sehen, wie sich die Werte im Laufe der Zeit ändern.
rot = Beschleunigung (vom Anfahren bis zum Abbremsen)
grün = Geschwindigkeit (vom Anfahren bis zum Anhalten)
blau = Ihr Weg.

Die Gesamtgeschwindigkeit ist das Integral der Beschleunigung von Ihrem Startpunkt zu Ihrer tatsächlichen Protokollierung. (Der Bereich zwischen Linie und Achse)
Der Weg ist das Integral Ihrer Geschwindigkeit.
Wenn Sie die Werte für Ihre Beschleunigung kennen, können Sie die anderen Werte berechnen. Aber wenn ich mich nicht irre, werden Integrale auch durch Akkumulation auf PCs berechnet. Und es ist viel mehr Aufwand, alle Beschleunigungswerte zu speichern.
Außerdem ist es wahrscheinlich zu viel, um in jedem Frame berechnet zu werden.

Beschleunigungs- / Geschwindigkeits- / Weg-Zeit-Diagramm

Ich weiß, meine Malfähigkeiten sind großartig. ;)

Edit 2:
Dieses Beispiel ist für lineare Bewegungen. Richtungswechsel erschweren dies zusätzlich.


msgstr "oder als neue Startposition festlegen." - ja, aber ich sehe das Problem damit nicht :) Beispiel Auto ... Ich habe das starke Gefühl, dass ich wirklich anfangen muss, mit so etwas Komplexem zu spielen, um intuitiv zu verstehen, wo dies fehlschlägt. .
davidkomer

das setzen einer neuen position ist wahrscheinlich kein problem. Ich habe das Autoteil bearbeitet
Linaith

1
In einem Autospiel stelle ich mir eine noch komplexere Beschleunigung vor. Es würde wahrscheinlich Sprünge und Stacheln geben, und dies kann von der Geschwindigkeit abhängen. (ZB sinkt die Beschleunigung auf 0, wenn sich das Auto der Höchstgeschwindigkeit nähert.)
jpmc26

3
@davidkomer kümmert sich nicht einmal um ein Auto (es sei denn, Sie möchten), ein grundlegender Plattformer reicht aus. Wie funktioniert mario.getPosition (Time) in Super Mario Bros?
user253751

8

Ich verstehe jedoch nicht, warum es sich so ansammelt, wenn es genauso leicht durch Ändern einer Funktion erhalten werden kann. Beispiel: getPosition = makeNewFunction (), die etwas zurückgeben könnte, das die Signatur von Time -> Position hat, und das Innenleben dieser Funktion wird über die entsprechende mathematische Formel generiert.

Du kannst!

Es wird mit einer analytischen oder geschlossenen Lösung aufgerufen . Dies hat den Vorteil, dass es genauer ist, da es keine Rundungsfehler gibt, die sich im Laufe der Zeit ansammeln.

Dies funktioniert jedoch nur, wenn Sie ein solches geschlossenes Formular vorher kennen. Bei Spielen ist dies oft einfach nicht der Fall.

Spielerbewegung ist unberechenbar und kann einfach nicht in eine vorberechnete Funktion umgewandelt werden. Der Spieler kann und wird seine Geschwindigkeit und Orientierung ziemlich oft ändern.

NPCs könnten möglicherweise Lösungen in geschlossener Form verwenden, und tatsächlich tun sie dies manchmal. Dies hat jedoch einige andere Nachteile. Denken Sie an ein einfaches Rennspiel. Jedes Mal, wenn Ihr Fahrzeug mit einem anderen Fahrzeug kollidiert, müssen Sie Ihre Funktion ändern. Vielleicht bewegt sich das Auto je nach Untergrund schneller. Dann wird es ziemlich schwierig sein, eine solche geschlossene Lösung zu finden. In der Tat gibt es wahrscheinlich mehr Fälle, in denen das Auffinden einer solchen geschlossenen Form entweder unmöglich oder so kompliziert ist, dass es einfach nicht durchführbar ist.

Ein gutes Beispiel für die Verwendung einer geschlossenen Lösung ist das Kerbal Space Program. Sobald sich Ihre Rakete in der Umlaufbahn befindet und nicht mehr unter Schub steht, kann KSP sie "auf Schienen" setzen. Umlaufbahnen sind im Zweikörpersystem vorbestimmt und periodisch. Solange die Rakete keinen Schub mehr ausübt, wissen Sie bereits, wo die Rakete sein wird, und können einfach anrufen getPositionAtTime(t)(es ist nicht genau so benannt, aber Sie haben die Idee).

In der Praxis ist die schrittweise Integration jedoch oft viel praktischer. Aber wenn Sie eine Situation sehen, in der es eine geschlossene Lösung gibt und die einfach zu berechnen ist, entscheiden Sie sich dafür! Es gibt keinen Grund, es nicht zu benutzen.

Wenn Ihr Charakter beispielsweise auf eine Kanone zielt, können Sie den vorhergesagten Aufprallpunkt der Kanonenkugel mit einer geschlossenen Lösung leicht anzeigen. Und wenn Ihr Spiel nicht zulässt, dass der Kurs der Kanonenkugel geändert wird (z. B. kein Wind), können Sie sie sogar zum Bewegen der Kanonenkugel verwenden. Beachten Sie, dass Sie besonders auf Hindernisse achten müssen, die sich in den Weg Ihrer Kanonenkugel bewegen.

Es gibt viele ähnliche Situationen. Wenn Sie ein rundbasiertes Spiel erstellen, gibt es wahrscheinlich weitaus geschlossenere Lösungen als beim Erstellen eines RTS-Spiels, da Sie alle Parameter im Voraus kennen und mit Sicherheit sagen können, dass sie sich nicht ändern (nichts bewegt sich plötzlich) in diesen Weg, zum Beispiel).

Beachten Sie, dass es Techniken gibt, um dann numerische Ungenauigkeiten der schrittweisen Integration zu bekämpfen. Beispielsweise können Sie den akkumulierten Fehler verfolgen und einen Korrekturterm anwenden, um den Fehler in Schach zu halten, z. B. Kahan Summation


8

Im Falle eines einfachen springenden Balls ist es einfach, Lösungen mit geschlossener Form zu finden. Bei komplexeren Systemen ist jedoch in der Regel die Lösung einer gewöhnlichen Differentialgleichung (ODE) erforderlich. Numerische Löser sind erforderlich, um alle Fälle außer den einfachsten zu behandeln.

Es gibt in der Tat zwei Klassen numerischer ODE-Löser: explizit und implizit. Explizite Löser bieten eine geschlossene Näherung für Ihren nächsten Zustand, während implizite Löser die Lösung einer Gleichung erfordern. Was Sie für Ihren springenden Ball beschreiben, ist eigentlich ein impliziter ODE-Löser, ob Sie es wussten oder nicht!

Implizite Löser haben den Vorteil, dass sie viel größere Zeitschritte verwenden können. Für Ihren Bouncing Ball-Algorithmus kann Ihr Zeitschritt mindestens so lang sein wie die Dauer bis zur nächsten Kollision (was Ihre Funktion ändern würde). Dadurch kann Ihr Programm viel schneller ausgeführt werden. Im Allgemeinen können wir jedoch nicht immer gute implizite Lösungen für die ODEs finden, an denen wir interessiert sind. Wenn dies nicht möglich ist, greifen wir auf die explizite Integration zurück.

Der große Vorteil, den ich bei der expliziten Integration sehe, ist, dass die Fallstricke bekannt sind. Sie können jedes Lehrbuch aus den 60er Jahren öffnen und alles lesen, was Sie über die kleinen Macken wissen müssen, die bei bestimmten Integrationstechniken auftreten. Ein Entwickler lernt diese Fähigkeiten also einmal und muss sie nie wieder erlernen. Wenn Sie implizite Integration durchführen, ist jeder Anwendungsfall leicht unterschiedlich, mit leicht unterschiedlichen Fallstricken. Es ist etwas schwieriger, das, was Sie von einer Aufgabe gelernt haben, auf die nächste anzuwenden.


1

pos (t) = v (t) * t

funktioniert nur, wenn pos (0) = 0 und v (t) = k

Sie können die Position nicht mit der Zeit in Beziehung setzen, ohne die Anfangsbedingung und die gesamte Geschwindigkeitsfunktion zu kennen. Die Gleichung ist also eine Annäherung an das Integral

pos (t) = Integral von v (t) dt von 0 bis t

EDIT _________

Hier ist ein kleiner Beweis für die Kommentare (unter der Annahme, dass pos (0) = 0)

sei v (t) = 4

Gleichung 1: pos (t) = 4 * t (richtig)

Gleichung 2: pos (t) = c + 4 * t von 0 bis t = 4 * t (richtig)

sei v (t) = 2 * t

Gleichung 1: pos (t) = 2 * t ^ 2 (falsch)

Gleichung 2: pos (t) = c + t ^ 2 von 0 bis t = t ^ 2 (richtig)

Ich sollte hinzufügen, dass Ihre Gleichung bereits eine konstante Beschleunigung berücksichtigt (dh Ihre Gleichung ist Gleichung 2, wobei v (t) = v0 + a * t und die Integrationsgrenzen t0 und t sind), sodass Ihre Gleichung so lange funktionieren sollte, wie Sie sie aktualisieren Ausgangsposition, Anfangsgeschwindigkeit und Beschleunigung bleiben konstant.

EDIT2 ________

Ich sollte auch hinzufügen, dass Sie auch die Position mit der Anfangsposition, der Anfangsgeschwindigkeit, der Anfangsbeschleunigung und dem konstanten Ruck berechnen können. Mit anderen Worten, Sie können eine Funktion auf der Grundlage von Gleichung 2 erstellen, die die Position gegen die Zeit darstellt, indem Sie sie in ihre Ableitungen, dh Geschwindigkeit, Ruck, was auch immer als nächstes kommt, usw. usw., aufteilen. Sie werden jedoch in Ihrer Gleichung nur dann genau sein, wenn v (t) kann auf diese Weise modelliert werden. Wenn v (t) nicht nur mit Geschwindigkeit, Beschleunigung, konstantem Ruck usw. modelliert werden kann, müssen Sie zu einer Näherung von Gleichung 2 zurückkehren, die häufig auftritt, wenn Dinge springen, Luftwiderstand, Wind usw .


eine Konstante. es bedeutet nur, dass v (t) nicht im Laufe der Zeit variieren darf
Kyy13

Ich muss mich damit auseinandersetzen und herausfinden, warum es wahr ist ... erstmal stimmen :) Ich habe ein Codebeispiel in der Frage gepostet, falls sich etwas ändert
davidkomer

Kein Problem, wieder mit besseren Worten aktualisiert :)
Kyy13
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.