2016-05-13 11 views
7

Heute Morgen hatte ich das Gefühl, ein nutzloses Programm zu schreiben, und ich endete mit dieser extremely minimal astronomic simulator in processing. MCVE-Version des Codes am Ende des Beitrags angehängt.Meine Planeten gingen verrückt - eine Java-bezogene Ausgabe

Dann habe ich einige Planeten hinzugefügt, lehne mich zurück und machte mich bereit, einige Planeten zu beobachten, die umeinander kreisen. Es stellte sich jedoch heraus, dass es ein Problem war.

Das Problem ist, zwei ursprünglich statische Planeten, die einander zu nahe kommen, neigen dazu, sich gegenseitig auf super ultra hohe Geschwindigkeit zu schleudern und sie werden beide für immer aus dem Bildschirm verschwinden. Dies ist offensichtlich gegen das Prinzip der Dynamik. Planeten tun das nicht. Ich fange an zu bezweifeln, dass etwas mit meiner Newton'schen Gesetzesimplementierung nicht stimmt. Aber manchmal funktionieren die Planeten gut, vorausgesetzt, sie halten eine "sichere" Distanz.

Wenn Sie in dieses Problem schauen wollen, habe ich zwei Setups für Sie sorgfältig aufgenommen. Die erste, fügt zwei Planeten hinzu und ist schön stabil. (Sie können dies tun, indem Sie jede Zeile auskommentieren, die 'Drei' enthält.) Sie können die zweite Zeile testen, indem Sie einfach den Standardcode eingeben. Hier werden Planeten verrückt.

Dieses Problem scheint wie ein Mysterium, dass stammt aus einer Computer-Domäne, die ich nie erforscht habe (ich bin ein Noob obwohl). Wegen meiner Haare würde ich es sehr schätzen, wenn jemand es erklären könnte.

Start of code:

PVector planetOneLocation = new PVector(300, 200); 
PVector planetOneSpeed = new PVector(0, -.1); 
float planetOneMass = 1; 

PVector planetTwoLocation = new PVector(100, 200); 
PVector planetTwoSpeed = new PVector(0, .1); 
float planetTwoMass = 1; 

PVector planetThreeLocation = new PVector(200, 200); 
PVector planetThreeSpeed = new PVector(0, 0); 
float planetThreeMass = 10; 

float g = 5; 

void setup() { 
    size(500, 500); 
} 

void draw() { 

    updatePlanetOne(); 
    updatePlanetTwo(); 
    updatePlanetThree(); 

    planetOneLocation.add(planetOneSpeed); 
    planetTwoLocation.add(planetTwoSpeed); 
    planetThreeLocation.add(planetThreeSpeed); 

    background(0); 

    ellipse(planetOneLocation.x, planetOneLocation.y, 10*planetOneMass, 10*planetOneMass); 
    ellipse(planetTwoLocation.x, planetTwoLocation.y, 10*planetTwoMass, 10*planetTwoMass); 
    ellipse(planetThreeLocation.x, planetThreeLocation.y, 10*planetThreeMass, 10*planetThreeMass); 
} 

void updatePlanetOne() { 
    PVector accDir = PVector.sub(planetTwoLocation, planetOneLocation); 
    float a = g * planetTwoMass/(accDir.mag() * accDir.mag()); 
    accDir.normalize(); 
    PVector acceleration = accDir.mult(a); 
    planetOneSpeed.add(acceleration); 

    accDir = PVector.sub(planetThreeLocation, planetOneLocation); 
    a = g * planetThreeMass/(accDir.mag() * accDir.mag()); 
    accDir.normalize(); 
    acceleration = accDir.mult(a); 
    planetOneSpeed.add(acceleration); 
} 

void updatePlanetTwo() { 
    PVector accDir = PVector.sub(planetOneLocation, planetTwoLocation); 
    float a = g * planetOneMass/(accDir.mag() * accDir.mag()); 
    accDir.normalize(); 
    PVector acceleration = accDir.mult(a); 
    planetTwoSpeed.add(acceleration); 

    accDir = PVector.sub(planetThreeLocation, planetTwoLocation); 
    a = g * planetThreeMass/(accDir.mag() * accDir.mag()); 
    accDir.normalize(); 
    acceleration = accDir.mult(a); 
    planetTwoSpeed.add(acceleration); 
} 

void updatePlanetThree() { 
    PVector accDir = PVector.sub(planetOneLocation, planetThreeLocation); 
    float a = g * planetOneMass/(accDir.mag() * accDir.mag()); 
    accDir.normalize(); 
    PVector acceleration = accDir.mult(a); 
    planetThreeSpeed.add(acceleration); 

    accDir = PVector.sub(planetTwoLocation, planetThreeLocation); 
    a = g * planetTwoMass/(accDir.mag() * accDir.mag()); 
    accDir.normalize(); 
    acceleration = accDir.mult(a); 
    planetThreeSpeed.add(acceleration); 
} 

-Update: Nach einiger Mühe wechsle ich Variablen schweben zu verdoppeln. Aber meine Planeten hüpfen immer noch aus dem Bildschirm. Ich denke neben dem Double/Float-Problem gibt es tatsächlich einige Probleme mit der Auflösung. Ich habe keinen Zeitschritt definiert, der insbesondere bei hoher Geschwindigkeit zu einem ungenauen Verhalten beiträgt.

Update 2: Die Einrichtung des Zeitschritts hilft sehr. Einige Setups, die ohne Zeitschritt verrückt geworden sind, funktionieren jetzt gut. Aber solange es die Möglichkeit gibt, dass das Zentrum von zwei Planeten extrem nah ist, besteht die Möglichkeit, dass das System wieder in den Hintergrund tritt. Um dies zu lösen, wird ein besserer Integrator benötigt.

Updata 3: Als Antwort auf @ Kevin-Workman portierte ich hier seinen schönen Code, um den ursprünglichen Projektcode zu ersetzen. Derselbe dritte Planet im ursprünglichen Post wird hinzugefügt und die entsprechende Newton-Mathematik wird aktualisiert. Widersprüchlich zu seinen Tests sieht es auch dann aus, wenn mv.add(p.speed.mult(p.mass)); auskommentiert ist, der dritte Planet immer noch verrückt wird (Da ich jetzt den Demo-Code auf eine Minimal-Version geändert habe, gibt es keine solche Zeile mehr). Ich denke, der von mult() eingeführte Fehler trägt zwar dazu bei, aber konkret spielt auch der instabile Integrator eine große Rolle.

+1

Bitte den entsprechenden Code in die Frage selbst stellen. Niemand möchte Code durch einen Link verfolgen. – computerfreaker

+0

Okay. Code ist beigefügt: D – shi

+0

Sie sind sicher, dass dies Java ist? Ich bezweifle das ... –

Antwort

10

Dieses Problem hat nichts mit float vs double Genauigkeit zu tun. Float hat mehr als genug Genauigkeit für diese, und in der Tat Verarbeitung verwendet float für alles standardmäßig, so dass alle double Werte, die Sie versuchen zu verwenden, nur verloren gehen wird sowieso.

Alle Ihre Probleme sind durch diese Linie verursacht:

for (Planet p: planets) { 
    mv.add(p.speed.mult(p.mass)); 
} 

Besonders dieses Bit:

p.speed.mult(p.mass) 

Diese Zeile jedes Planeten vervielfacht durch seine Masse Geschwindigkeit.

Man könnte denken, dass p.speed.mult(p.mass) werden die ursprünglichen p.speedPVector unverändert lassen, aber das ist nicht der Fall. PVector ist nicht unveränderbar, so dass der Aufruf der mult() Funktion die zugrunde liegende PVector Instanz ändert.

Ihre ersten zwei Planeten haben eine mass von 1, so dass diese Linie sie nicht wirklich betrifft. Aber dein dritter Planet hat einen mass von 10, was bedeutet, dass du seine Geschwindigkeit mit 10 jeden einzelnen Frame multiplizierst.

Sie können dies testen, indem Sie einfach die Masse eines Ihrer ursprünglichen Planeten auf 10 oder sogar 2 ändern, oder indem Sie die Masse des dritten Planeten auf 1 ändern.

Also, um Ihr Hauptproblem zu beheben, nur diese Zeile loswerden, oder ändern Sie es so, dass es die p.speedPVector nicht ändert.

Weitere Informationen finden Sie in the Processing reference for PVector#mult() finden:

Multipliziert einen Vektor mit einem Skalar. Die Version der Methode, die einen Float verwendet, wirkt direkt auf den Vektor, auf dem sie aufgerufen wird (wie im obigen Beispiel ). Die Versionen, die sowohl einen PVector als auch einen float als Argumente erhalten, sind statische Methoden und jeder gibt einen neuen PVector zurück, der das Ergebnis der Multiplikationsoperation ist.

Grundsätzlich könnte man diese Linie dies ändern:

PVector.mult(p.speed, p.mass) 

Die p.speed unverändert und gibt eine Kopie mit dem multiplizierten Wert verlassen.

Wenn Sie einen Schritt zurückgehen, werden Sie ein anderes Problem haben, weil Ihre Planeten beliebig nahe beieinander sein können. Mit anderen Worten, ihre Entfernung kann sich Null annähern (oder gleich sein). Dies passiert nicht im wirklichen Leben, und wenn es so ist, kannst du wetten, dass die Dinge verrückt werden. Du wirst also verrückte "Gravity Assists" haben, wenn die Dinge zu nahe kommen. Sie könnten in Betracht ziehen, ihre Abstände zu begrenzen.

Wenn Sie weitere Fragen haben, wird es einfacher sein, Ihnen zu helfen, wenn Sie von diesen MCVE arbeiten, anstatt Ihr ganzes Projekt der Entsendung:

PVector planetOneLocation = new PVector(300, 200); 
PVector planetOneSpeed = new PVector(0, -.1); 
float planetOneMass = 1; 

PVector planetTwoLocation = new PVector(100, 200); 
PVector planetTwoSpeed = new PVector(0, .1); 
float planetTwoMass = 10; 

float g = 5; 

void setup() { 
    size(500, 500); 
} 

void draw() { 

    updatePlanetOne(); 
    updatePlanetTwo(); 

    planetOneLocation.add(planetOneSpeed); 
    planetTwoLocation.add(planetTwoSpeed); 

    background(0); 

    ellipse(planetOneLocation.x, planetOneLocation.y, 10*planetOneMass, 10*planetOneMass); 
    ellipse(planetTwoLocation.x, planetTwoLocation.y, 10*planetTwoMass, 10*planetTwoMass); 
} 

void updatePlanetOne() { 
    PVector accDir = PVector.sub(planetTwoLocation, planetOneLocation); 
    float a = g * planetOneMass/(accDir.mag() * accDir.mag()); 
    accDir.normalize(); 
    PVector acceleration = accDir.mult(a); 
    planetOneSpeed.add(acceleration); 
} 

void updatePlanetTwo() { 
    PVector accDir = PVector.sub(planetOneLocation, planetTwoLocation); 
    float a = g * planetTwoMass/(accDir.mag() * accDir.mag()); 
    accDir.normalize(); 
    PVector acceleration = accDir.mult(a); 
    planetTwoSpeed.add(acceleration); 
} 
+0

auswählte Hey Kevin danke für den tollen Beitrag! Dies erklärt einige der Fehler! Schön gemacht. Allerdings muss ich sagen, dass, wenn Sie die Anfangsgeschwindigkeit beider Planeten auf 0 setzen, sie immer noch verrückt werden (auch 0,01). Warte einen Moment, bevor du sagst: "Hey, ich habe das Distanzding erwähnt und sie werden verrückt", denke über dieses Modell nach, wo zwei rigi singuläre Massen im wirklichen Leben in unmittelbarer Nähe interagieren, egal wie sie sich bewegen, sie enden dem Newtonschen Gesetz folgend (wir gehen hier nicht weiter in astronomische Fortschritte, weil das nicht unser Modell ist). Und das ist nicht der Fall, den dein/mein Skript reflektiert. – shi

+0

Durch das Lesen der Kommentare anderer weiß ich, dass meine Integrations-Engine nicht so stabil ist. Es muss also noch etwas getan werden, damit dieses Skript ordnungsgemäß funktioniert. Deshalb habe ich Ihren Beitrag neu gewählt, aber ich habe ihn nicht akzeptiert. P.S. Danke für den Tipp von MCVE. Werde das nächste Mal tun: D – shi

+0

Dies beantwortet die Frage, warum dein dritter Planet verrückt wird. Wenn Sie eine weitere Frage zu zwei Planeten haben, die nahe beieinander sind und verrückt spielen, erstellen Sie bitte einen zweiten Beitrag, der sich genau auf dieses Problem konzentriert. Stellen Sie sicher, dass ein [mcve] enthalten ist. –

0

Das Problem ist in dem integrierenden Teil. Der verwendete Euler-Integrator ist nicht sehr genau. Die Verlet-Integration wird normalerweise für die Physiksimulation verwendet.

Ilmari Karonen's Answer Zitiert kann Verlet implementiert werden als:

acceleration = force(time, position)/mass; 
time += timestep; 
position += timestep * (velocity + timestep * acceleration/2); 
newAcceleration = force(time, position)/mass; 
velocity += timestep * (acceleration + newAcceleration)/2;