Musterlösung für die Implementierung eines Schwarms in Java basierend auf gleitenden Scheiben
|
Dynamisches Modell einer passiven gleitenden Scheibe
Bild 0-1: Simulationsmodell für eine passive gleitende Scheibe mit Dämpfung der Bewegung
|
Objektorientierte Umsetzung ohne Visualisierung mit Java unter Verwendung eines Runge-Kutta-Integrators
|
import java.io.*; import java.util.*; public class Hauptprogramm { private int puck_anzahl = 10; private double t = 0.0; private double dt = 0.1; private int schritte = 10000; public Modell[] modell; public Integrator[] integrator; private int iabs(int x) { if(x<0) return -x; else return x; } public void init() { //Erzeugen aller Modelle modell = new Modell[puck_anzahl]; for(int i=0;i<puck_anzahl;i++) modell[i] = new Modell(); Random random = new Random(0); //Zufällige Anfangsbedingungen für die Pucks setzen: for(int i=0;i<puck_anzahl;i++) { modell[i].yalt[0] = (double)(50 + iabs(random.nextInt())%400); modell[i].yalt[1] = 0.001*(double)(iabs(random.nextInt())%2000 - 1000); modell[i].yalt[2] = (double)(50 + iabs(random.nextInt())%400); modell[i].yalt[3] = 0.001*(double)(iabs(random.nextInt())%2000 - 1000); } //Erzeugen der zugehörigen Integratoren integrator = new Integrator[puck_anzahl]; for(int i=0;i<puck_anzahl;i++) integrator[i] = new Integrator(modell[i]); } private void zeitschritt() { for(int i=0;i<puck_anzahl;i++) //alle Pucks durchgehen { integrator[i].zeitschritt(t,dt); } } public void simulieren() { for(int i=0;i<schritte;i++) { System.out.print(""+t); for(int k=0;k<puck_anzahl;k++) { //Für Scilab Zeit und dann alle Koordinaten zeilenweise ausgeben System.out.print(" "+modell[k].yalt[0]+" "+modell[k].yalt[2]); integrator[k].zeitschritt(t,dt); } System.out.println(); t+=dt; } } public static void main(String[] args) { Hauptprogramm hp = new Hauptprogramm(); hp.init(); hp.simulieren(); } }
Code 0-1: Quellcode zu Hauptprogramm.java in puck01_passiv
public class Modell { public double[] param = {1.0,0.001}; public double[] yalt; //Im Modell den letzten Zustand merken private int anzahl=4; private double m; private double D; private double f; public Modell() { yalt = new double[anzahl]; } public double rechteSeite(double[] y,double t, int zeile) { m = param[0]; D = param[1]; switch(zeile) { case 0: f = y[1]; break; case 1: f = -(D/m)*y[1]; break; case 2: f = y[3]; break; default: f = -(D/m)*y[3]; break; } return f; } public int holeAnzahlGleichungen() { return anzahl; } }
Code 0-2: Quellcode zu Modell.java in puck01_passiv
//Integrator, der ein Runge-Kutta-Verfahren verwendet. //Bei Aufruf des Konstruktors wird ein Zeiger auf das zu integrierende Modell im //Integrator-Objekt gespeichert. public class Integrator { private Modell modell; private double[] yneu; private double[] yhilf; private double[] k1,k2,k3,k4; public Integrator(Modell modell) { this.modell = modell; yneu = new double[modell.holeAnzahlGleichungen()]; yhilf = new double[modell.holeAnzahlGleichungen()]; k1 = new double[modell.holeAnzahlGleichungen()]; k2 = new double[modell.holeAnzahlGleichungen()]; k3 = new double[modell.holeAnzahlGleichungen()]; k4 = new double[modell.holeAnzahlGleichungen()]; } void zeitschritt(double t,double dt) { for(int i=0;i<modell.holeAnzahlGleichungen();i++) { k1[i] = modell.rechteSeite(modell.yalt,t,i); yhilf[i] = modell.yalt[i] + 0.5*dt*k1[i]; } for(int i=0;i<modell.holeAnzahlGleichungen();i++) { k2[i] = modell.rechteSeite(yhilf,t,i); yhilf[i] = modell.yalt[i] + 0.5*dt*k2[i]; } for(int i=0;i<modell.holeAnzahlGleichungen();i++) { k3[i] = modell.rechteSeite(yhilf,t,i); yhilf[i] = modell.yalt[i] + dt*k3[i]; } for(int i=0;i<modell.holeAnzahlGleichungen();i++) { k4[i] = modell.rechteSeite(yhilf,t,i); } for(int i=0;i<modell.holeAnzahlGleichungen();i++) { yneu[i] = modell.yalt[i] + (dt/6.0)*(k1[i]+2.0*k2[i]+2.0*k3[i]+k4[i]); } //Neuen y-Wert als alten im Modell speichern: for(int i=0;i<modell.holeAnzahlGleichungen();i++) modell.yalt[i] = yneu[i]; } }
Code 0-3: Quellcode zu Integrator.java in puck01_passiv
|
m=fscanfMat('test.txt'); //plot(m(:,1),sqrt(m(:,2).*m(:,2)+m(:,5).*m(:,5))); plot(m(:,2),m(:,3),m(:,4),m(:,5), ... ,m(:,20),m(:,21));
Code 0-4: Skript, um folgende Ergebnisse zu zeigen.
Bild 0-2: Zurückgelegter Weg im Betrag von Puck Nr. 0 (Es ist die Dämpfung erkennbar)
Bild 0-3: Zurückgelegter Weg aller 10 simulierten Puck-Elemente in der Ebene
Ergänzen von Kollision
|
Bild 0-4: Ergänzung der dynamischen Gleichungen durch einen Term, der für jeden Puck bei durchdringen eines anderen Pucks proportional zu der Durchdringungstiefe eine Gegenkraft erzeugt.
|
public class Modell { public Modell[] puck; public static int zaehler=0; //Zählt wieviele Pucks bereits erzeugt wurden. public int nr; //Nummer des aktuellen Pucks public double[] param = {1.0,0.001,30.0,20.0}; public double[] yalt; //Im Modell den letzten Zustand merken public double[] yneu; //Veränderten Zustand innerhalb eines Zeitschritts auch merken. private int anzahl=4; private double m; private double D; private double K; private double R; //Puckradius private double f; public Modell(Modell[] puck) { this.puck = puck; //Alle Pucks in jedem Puck-Objekt registrieren. yalt = new double[anzahl]; yneu = new double[anzahl]; nr=zaehler; //Aktuell erstelltem Puck eine Nummer zuweisen. zaehler++; //In statischer Variablen mitzaehlen, wieviele Pucks schon erstellt wurden. } public double rechteSeite(double[] y,double t, int zeile) { m = param[0]; D = param[1]; K = param[2]; R = param[3]; //Durch Kollisionen entstehende Zusatzkraft bestimmen. //Alle anderen Pucks durchgehen. double x_koll=0.0; double y_koll=0.0; for(int i=0;i<puck.length;i++) { if(i!=nr) //nur wenn Auswahl nicht aktueller Puck selbst ist. { double x1 = yalt[0]; double y1 = yalt[2]; double x2 = puck[i].yalt[0]; double y2 = puck[i].yalt[2]; double betrag = Math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)); //Distanz der Puck-Mittelpunkte nr und i if(betrag<2.0*R && betrag!=0.0) { //Richtungsvektor bestimmen: double ex = (x1-x2)/betrag; double ey = (y1-y2)/betrag; //Kraft hinzuaddieren: x_koll += K*(2.0*R-betrag)*ex; y_koll += K*(2.0*R-betrag)*ey; } } } switch(zeile) { case 0: f = y[1]; break; case 1: f = -(D/m)*y[1]+x_koll/m; break; case 2: f = y[3]; break; default: f = -(D/m)*y[3]+y_koll/m; break; } return f; } public int holeAnzahlGleichungen() { return anzahl; } }
Code 0-5: Modell.java in puck02_kollision
import java.io.*; import java.util.*; public class Hauptprogramm { private int puck_anzahl = 10; private double t = 0.0; private double dt = 0.1; private int schritte = 10000; public Modell[] modell; public Integrator[] integrator; private int iabs(int x) { if(x<0) return -x; else return x; } public void init() { //Erzeugen aller Modelle modell = new Modell[puck_anzahl]; for(int i=0;i<puck_anzahl;i++) modell[i] = new Modell(modell); Random random = new Random(0); //Zufällige Anfangsbedingungen für die Pucks setzen: for(int i=0;i<puck_anzahl;i++) { modell[i].yalt[0] = (double)(50 + iabs(random.nextInt())%400); modell[i].yalt[1] = 0.001*(double)(iabs(random.nextInt())%2000 - 1000); modell[i].yalt[2] = (double)(50 + iabs(random.nextInt())%400); modell[i].yalt[3] = 0.001*(double)(iabs(random.nextInt())%2000 - 1000); } //Erzeugen der zugehörigen Integratoren integrator = new Integrator[puck_anzahl]; for(int i=0;i<puck_anzahl;i++) integrator[i] = new Integrator(modell[i]); } public void simulieren() { for(int i=0;i<schritte;i++) { System.out.print(""+t); for(int k=0;k<puck_anzahl;k++) { //Für Scilab Zeit und dann alle Koordinaten zeilenweise ausgeben System.out.print(" "+modell[k].yalt[0]+" "+modell[k].yalt[2]); integrator[k].zeitschritt(t,dt); } //alle neuen Zustaende als alte umkopieren vorbereitend auf den nächsten Integrationsschritt: for(int ii=0;ii<puck_anzahl;ii++) //alle Pucks durchgehen { for(int k=0;k<modell[ii].yneu.length;k++) modell[ii].yalt[k] = modell[ii].yneu[k]; } System.out.println(); t+=dt; } } public static void main(String[] args) { Hauptprogramm hp = new Hauptprogramm(); hp.init(); hp.simulieren(); } }
Code 0-6: Hauptprogramm.java in puck02_kollision
//Integrator, der ein Runge-Kutta-Verfahren verwendet. //Bei Aufruf des Konstruktors wird ein Zeiger auf das zu integrierende Modell im //Integrator-Objekt gespeichert. public class Integrator { private Modell modell; private double[] yneu; private double[] yhilf; private double[] k1,k2,k3,k4; public Integrator(Modell modell) { this.modell = modell; yneu = new double[modell.holeAnzahlGleichungen()]; yhilf = new double[modell.holeAnzahlGleichungen()]; k1 = new double[modell.holeAnzahlGleichungen()]; k2 = new double[modell.holeAnzahlGleichungen()]; k3 = new double[modell.holeAnzahlGleichungen()]; k4 = new double[modell.holeAnzahlGleichungen()]; } void zeitschritt(double t,double dt) { for(int i=0;i<modell.holeAnzahlGleichungen();i++) { k1[i] = modell.rechteSeite(modell.yalt,t,i); yhilf[i] = modell.yalt[i] + 0.5*dt*k1[i]; } for(int i=0;i<modell.holeAnzahlGleichungen();i++) { k2[i] = modell.rechteSeite(yhilf,t,i); yhilf[i] = modell.yalt[i] + 0.5*dt*k2[i]; } for(int i=0;i<modell.holeAnzahlGleichungen();i++) { k3[i] = modell.rechteSeite(yhilf,t,i); yhilf[i] = modell.yalt[i] + dt*k3[i]; } for(int i=0;i<modell.holeAnzahlGleichungen();i++) { k4[i] = modell.rechteSeite(yhilf,t,i); } for(int i=0;i<modell.holeAnzahlGleichungen();i++) { yneu[i] = modell.yalt[i] + (dt/6.0)*(k1[i]+2.0*k2[i]+2.0*k3[i]+k4[i]); } //Neuen y-Wert als neuen im Modell speichern (später umkopieren): for(int i=0;i<modell.holeAnzahlGleichungen();i++) modell.yneu[i] = yneu[i]; } }
Code 0-7: Integrator.java in puck02_kollision
Bild 0-5: Resultat nach Ergänzung der Kollisionskräfte im Programm.