kramann.info
© Guido Kramann

Login: Passwort:










kramann.info
© Guido Kramann

Login: Passwort:




Musterlösung für die Implementierung eines Schwarms in Java basierend auf gleitenden Scheiben

  • Sämtliche der folgenden Umsetzungen können als .zip-File heruntergeladen werden:
java_puck2.zip - Download der folgenden Java-Programme

Dynamisches Modell einer passiven gleitenden Scheibe

Simulationsmodell für eine passive gleitende Scheibe mit Dämpfung der Bewegung

Bild 0-1: Simulationsmodell für eine passive gleitende Scheibe mit Dämpfung der Bewegung

  • Obige dynamische Gleichungen repräsentieren eine passive gleitende Scheibe mit Dämpfung.
  • Im folgenden wird ein Java-Simulationsprogramm für einen Schwarm dieser Elemente realisiert.
  • Dazu wird einfach für jede Scheibe ein Modell-Objekt erzeugt und ein Integrator-Objekt, in dem die jeweilige Scheibe registriert wird.
  • Bei zufälligen unterschiedlichen Anfangsbedingungen gleiten die Scheiben zunächst ohne sich gegenseitig zu beeinflussen solange in eine Richtung, bis die Dämpfung für ihren Stillstand sorgt.

Objektorientierte Umsetzung ohne Visualisierung mit Java unter Verwendung eines Runge-Kutta-Integrators

  • Um den Überblick über die Grundstruktur eines Schwarm-Simulators zu geben, dient folgende einfache Umsetzung ohne Visualisierung:
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

  • Durch javac Hauptprogramm.java läßt sich der Quellcode kompilieren.
  • Durch java Hauptprogramm > test.txt läßt sich die Simulation in einer Textdatei speichern.
  • Durch folgendes Scilab-Skript können die Simulationsergebnisse visualisiert werden:
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.

Zurückgelegter Weg im Betrag von Puck Nr. 0 (Es ist die Dämpfung erkennbar)

Bild 0-2: Zurückgelegter Weg im Betrag von Puck Nr. 0 (Es ist die Dämpfung erkennbar)

Zurückgelegter Weg aller 10 simulierten Puck-Elemente in der Ebene

Bild 0-3: Zurückgelegter Weg aller 10 simulierten Puck-Elemente in der Ebene

Ergänzen von Kollision

  • Die folgende Modellgleichung stellt dar, wie eine linear vom Abstand der Puckmittelpunkte abhängige Kraftwirkung bei Kollision mit einem anderen Puck modelliert werden kann.
  • Neu wurde der Puckradius eingeführt.
  • Es muß nun für jeden Puck untersucht werden, inwieweit er von einem anderen Puck durchdrungen wird.
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.

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.

  • Vorbereitend wurde in der Modellklasse dafür gesorgt, dass hier der letzte Bewegungszustand gespeichert wird.
  • Damit nicht vor dem nächsten Zeitschritt schon einzelne Pucks verschoben werden und so für nachfolgend untersuchte Pucks der Zustand verfälscht wird, wird noch in der Modellklasse yneu eingeführt und dort die neu berechneten Werte durch den Integrator gespeichert.
  • Erst abschliessend, wenn bei einem Zeitschritt alle Objekte durchgegangen wurden, wird für alle Pucks yneu nach yalt kopiert.
  • Dies kann nun ausgenutzt werden, um Kollisuionen zu erkennnen.
  • Indem jedes Modell eine Referenz auf das Array mit allen Modellobjekten erhält, kann nun jedes Puck-Objekt auf alle anderen Puckobjekte zugreifen und Kollisionen erfassen.
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

Resultat nach Ergänzung der Kollisionskräfte im Programm.

Bild 0-5: Resultat nach Ergänzung der Kollisionskräfte im Programm.