kramann.info
© Guido Kramann

Login: Passwort:










kramann.info
© Guido Kramann

Login: Passwort:




Simulation eines aktiven Puckschwarms

java_puck3.zip - Puck mit Antrieb, Projekt puck03_antrieb und puck05_schwarm2
  • Nun sollen die Puckmodelle mit einem aktiven Antrieb versehen werden.
  • Im Anschluß soll ein möglichst einfaches Verhalten implementiert werden, das dazu führt, dass der Schwarm sich in einer geordneten Bahn bewegt.

Der Antrieb

  • Der Antrieb ist eine weitere Kraft, deren Richtung frei wählbar ist.
  • Der Einfachheit halber wird wirklich die Kraft auf einen konstanten Wert Kant eingestellt.
  • Als Winkel für die Wirkrichtung der Kraft wird ein Winkel mit der Bezeichnung α eingeführt.
  • Die Antriebskraft Fant ergibt sich dann wie folgt:
Antriebskraft der Pucks

Bild 0-1: Antriebskraft der Pucks

  • Dafür, dass die Pucks aufgrund der konstanten Antriebskraft nicht unendlich beschleunigen, sorgt der Dämpfungsterm in den Modellgleichungen.
  • Im folgenden ist zunächst der Antrieb implementiert, wobei aber zunächst die Antriebsrichtung auf einen zufälligen Wert gesetzt und dort festgehalten wird.
  • Die Umwelt ist nun auf ein Rechteck mit Banden der Länge 6 Meter und der Breite 4 Meter festgelegt.
  • Dieser Bereich wird in der Animation auf 600x400 Pixel skaliert.
  • Der Puckradius beträgt nun 10cm
  • Damit keine unsinnigen Anfangsbedingungen auftreten, wurde das Feld in Quadrate mit jeweils 40cm Kantenlänge untergiedert.
  • Nur im Zentrum eines solchen Quadrates werden die Pucks zu Beginn zufällig positioniert, ohne, dass zwei Pucks in das gleiche Quadrat gesetzt werden.
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 
                         //  m   D     K    R   Kant alfa
    public double[] param = {1.0,1.0, 500.0,0.1,1.0, 0.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; //Masse
    private  double D; //Dämpfung der Bewegung
    private  double K; //Verstaerkung der Kollisionskraft
    public   double R; //Puckradius
    private  double Kant; //Antriebsstaerke
    private  double alfa; //Antriebsrichtung
    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];
        Kant = param[4];
        alfa = param[5];
        //KOLLISION mit anderen Pucks
        //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;
                }
            }
        }
        //KOLLISION mit den Wänden implementieren
        //Grenzen: x-Richtung 0m und 6m
        //         y-Richtung 0m und 4m
        //Die Wirkrichtung der Wandkräfte steht immer senkrecht auf der Wand.
        if( yalt[0]>6.0-R )//Eindringen in die rechte Wand bei x==6m
            x_koll += K*( 6.0-R-yalt[0]  );
        if( yalt[0]<0.0+R )//Eindringen in die linke Wand bei x==0m
            x_koll += K*( 0.0+R-yalt[0]  );
        if( yalt[2]>4.0-R )//Eindringen in die untere Wand bei y==4m (Koordinatenursprung oben links)
            y_koll += K*( 4.0-R-yalt[2]  );
        if( yalt[2]<0.0+R )//Eindringen in die obere Wand bei y==0m
            y_koll += K*( 0.0+R-yalt[2]  );
        //ANTRIEB
        //Antriebskraft berechnen:
        double x_ant = Kant*Math.cos(alfa);
        double y_ant = Kant*Math.sin(alfa);
        switch(zeile)
        {
            case 0:
                f = y[1];
            break;
            case 1:
                f = -(D/m)*y[1]+x_koll/m+x_ant/m; 
            break;                    
            case 2:
                f = y[3];
            break;                    
            default:
                f = -(D/m)*y[3]+y_koll/m+y_ant/m; 
            break;                                                                                            
        }              
        return f; 
    }
    public int holeAnzahlGleichungen()
    {
        return anzahl;
    } 
} 

Code 0-1: Aus Projekt puck03_antrieb: Modell mit Antriebskraft, deren Richtung variiert werden kann.

  • Um in einem ersten Ansatz einen sich selbst organisierenden Schwarm zu erhalten, wurde die Antriebsrichtung nun so gewählt, dass sie 90o gegen Uhrzeigersinn gedreht zur letzten Kollisionsrichtung steht.
  • Treten mehrere Kollisionen gleichzeitig auf, wird die Richtung der Gesamtkollisionskraft zugrunde gelegt.
  • Die dahinter stehende Idee ist die, dass sich bei einem Konvoi die Einheiten senkrecht zu den Wänden (Banden) bewegen und die Kollision Auskunft darüber gibt, in welche Richtung eine Wand liegt.
  • Um das Verhalten weiter zu verbessern wurde eine zweite Kollisionskraft berechnet, bei der der doppelte Puck-Radius verwendet wurde.
  • Diese zweite Kollisionskraft wird aber nicht benutzt, sondern liefert nur vorausschauend die demnächst zu erwartende Kollisionsrichtung.
  • Es wird dabei nicht unterschieden, ob die Kollisionskraft sich aus Kollisionen mit den Wänden, oder anderen Pucks zusammensetzt.
  • Eine weitere Ergänzung, ist die Verwendung eines Umweltmodells, mit dem die Umrandung (der Parcours) als Polygonzug formulieren läßt.
  • Die Geradenstücke, aus denen das Polygon zusammengesetzt ist, werden durch deren Endpunkte beschrieben.
  • Mittels Linearer Algebra und der Cramerschen Regel wird dann die resultierende Kollisionskraft jedes Pucks mit jedem Geradenstück bestimmt.
public class Modell
{
    public Umwelt umwelt;
    public Modell[] puck;
    public static int zaehler=0; //Zählt wieviele Pucks bereits erzeugt wurden.
    public int nr; //Nummer des aktuellen Pucks 
                         //  m   D     K    R   Kant alfa
    public double[] param = {1.0,1.0, 500.0,0.1,1.0, 0.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; //Masse
    private  double D; //Dämpfung der Bewegung
    private  double K; //Verstaerkung der Kollisionskraft
    public   double R; //Puckradius
    private  double Kant; //Antriebsstaerke
    private  double alfa; //Antriebsrichtung
    private  double x_ant_alt=0.0;
    private  double y_ant_alt=0.0;
    private double f;
    public Modell(Modell[] puck,Umwelt umwelt)  
    {  
        this.puck = puck; //Alle Pucks in jedem Puck-Objekt registrieren.
        this.umwelt = umwelt; //Umwelt 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];
        Kant = param[4];
        alfa = param[5];
        //KOLLISION mit anderen Pucks
        //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;
                }
            }
        }
        //KOLLISION mit den Wänden implementieren
        //Grenzen: x-Richtung 0m und 6m
        //         y-Richtung 0m und 4m
        //Die Wirkrichtung der Wandkräfte steht immer senkrecht auf der Wand.
        //JETZT MIT SCHRÄGEN IN DEN ECKEN
        umwelt.berechneWandkraft(yalt[0],yalt[2],R,K);
        x_koll+=umwelt.Fx;
        y_koll+=umwelt.Fy;
//Zweite Dummy-Kollisionskraft mit verdoppeltem Radius, um die Richtung der
//Antriebskraft zu bestimmen:
//###########################################
        //KOLLISION mit anderen Pucks
        //Durch Kollisionen entstehende Zusatzkraft bestimmen:
        //Alle anderen Pucks durchgehen.
        double x_koll2=0.0;
        double y_koll2=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<4.0*R && betrag!=0.0)
                {
                    //Richtungsvektor bestimmen:
                    double ex = (x1-x2)/betrag;
                    double ey = (y1-y2)/betrag;
                    //Kraft hinzuaddieren:
                    x_koll2 += K*(4.0*R-betrag)*ex;
                    y_koll2 += K*(4.0*R-betrag)*ey;
                }
            }
        }
        //KOLLISION mit den Wänden implementieren
        //Grenzen: x-Richtung 0m und 6m
        //         y-Richtung 0m und 4m
        //Die Wirkrichtung der Wandkräfte steht immer senkrecht auf der Wand.
        //JETZT MIT SCHRÄGEN IN DEN ECKEN
        umwelt.berechneWandkraft(yalt[0],yalt[2],2.0*R,K);
        x_koll2+=umwelt.Fx;
        y_koll2+=umwelt.Fy;
//###########################################
        //ANTRIEB
        //Antriebskraft berechnen:
        //alfa immer 90 Grad gegen Uhrzeigersinn zu Stoß drehen:
        double stoss_betrag = Math.sqrt(x_koll2*x_koll2+y_koll2*y_koll2);
        double x_ant = x_ant_alt;
        double y_ant = y_ant_alt;
        if(stoss_betrag>0.01)
        {
            x_ant = -Kant*y_koll2/stoss_betrag;
            y_ant =  Kant*x_koll2/stoss_betrag;
        }
        x_ant_alt = x_ant;
        y_ant_alt = y_ant;
        switch(zeile)
        {
            case 0:
                f = y[1];
            break;
            case 1:
                f = -(D/m)*y[1]+x_koll/m+x_ant/m; 
            break;                    
            case 2:
                f = y[3];
            break;                    
            default:
                f = -(D/m)*y[3]+y_koll/m+y_ant/m; 
            break;                                                                                            
        }              
        return f; 
    }
    public int holeAnzahlGleichungen()
    {
        return anzahl;
    } 
}

Code 0-2: Aus Projekt puck05_schwarm2: Modell mit Antriebsrichtung um 90o gedreht bzgl. der letzten Kollision.

Vergleich zwischen dem Bewegungszustand zu Beginn und nach einiger Zeit der Simulation.

Bild 0-2: Vergleich zwischen dem Bewegungszustand zu Beginn und nach einiger Zeit der Simulation.

  • In der Volesung geklärt wird:
  • die Mathematik hinter der Umwelt-Klasse,
  • Berechnung des Winkels aus einem Kraftvektor,
  • Interpretation des Schwarmmodells.