Selbstständiges Fahren - 4. Entscheidungstabelle

Mit der Programmlogik konnten wir unser Problem lösen. Wir hatten ein einfaches Problem und konnten dieses einfache Problem durch nachdenken lösen. Was aber, wenn das Problem nicht mehr so einfach ist? Wenn wir 4 und 3 addieren benutzen wir auch keinen Taschenrechner. Vorteile sind erst sichtbar, wenn das Problem sich verschärft. Aber wir haben aus Absicht ein leichtes Problem erstellt, damit es leichter ist das Programm nachzuvollziehen.

Unsere nächste Aufgabe ist ein Programm, dass selbstständig lernen soll durch die Strecke zu fahren. Etwas zu lernen bedeutet, dass wir es am Anfang noch nicht können, d.h. viele Fehler machen und irgendwann in der Lage sein werden die gestellte Aufgabe zu erfüllten. Unser Fahrzeug wird Fehler machen. Es fährt gegen den Untergrund und hält an. Wir lehnen uns an die Evolution an und lassen viele Fahrzeuge fahren. Das beste Fahrzeug vervielfältigen wir und änderen diese Fahrzeug etwas ab. So schreiten wir langsam auf dem Weg der kleinen Verbesserungen voran.

Aus der EDV kennt man das EVA-Modell (Eingabe, Verarbeitung, Ausgabe). Die Eingabe sind unsere Sensordaten. Von diesen Daten müssen wir auf Ausgabedaten kommen. Wir wollen an dieser Stelle keine komplizierte Berechnung durchführen. Gleiche Eingabewerte sollen zu gleichen Ausgabewerte führen. Wir benötigen eine Tabelle in der die Eingabewerte zu bestimmten Ausgabewerten führen:


Die Eingabewerte können Zahlwerte von 0 bis 65535 annehmen. Für jeden Wert benötigen wir einen Ausgabewert. D.h. wir benötigen eine Tabelle mit 65536 Einträgen. In jeder Zelle der Tabelle steht ein Ausgabewert. Als Ausgabewerte haben wir Werte zwischen 0 und 8. Die Tabelle wird mit zufällen Ausgabewerten initialisiert.

Jetzt muss das Fahrzeug nur noch verändert werden. Hierzu dient die Methode mutant(). In dieser Methode werden 10% aller Felder mit zufälligen Ausgabewerten belegt.


package car;

import java.util.Arrays;

public class RacerBrain extends Racer {

  
   // das "Gehirn" enthält die Tabelle der Ausgabewerte
   protected byte[] brain;

   public RacerBrain(int n) {
      super(n);
      brain = new byte[65536];
      initBrain();
   }
   /**
    * Die Entscheidungstabelle initialisieren mit Werten zwischen 0 und 8.
    */

   private void initBrain() {
      for (int i=0; i < brain.length; i++) brain[i] = (byte)(Math.random() * 9);
   }

   /*
    * Liefert die Entscheidungstabelle
    */
   public byte[] getBrain() {
      return this.brain;
   }
   /**

    * Setzt die Entscheidungstabelle
    */
    public void setBrain(byte[] b) {
        this.brain = Arrays.copyOf(b, b.length);
    }
    /**
     * Die Werte in der Entscheidungstabelle werden mutiert (10%)

     *
     */

   public void mutant() {
      int anzmut;
      anzmut = Math.floorDiv(brain.length,10); // 10% mutieren
      for (int i=0; i < anzmut; i++) {
         brain[((int)(Math.random() * brain.length))] = 

                                                 (byte)(Math.random() * 9);
      }
   }
   /**
    * Setzt Geschwindigkeit und Richtung
    *
    * Die Daten werden aus dem "Gehirn" ermittelt.
    *
    * Insgesamt gibt es neu Ausgänge. Diese neun Aktionen setzen sich aus der

    * Geschwindigkeit und der Richtungsänderung zusammen.
    *
    * Die Geschwindigkeit kann auf 2, 4 oder 6 Pixel gesetzt werden

    * (3 Möglichkeiten)
    * Die Richtung kann links herum -5, rechts herum 5 oder geradeaus 0 sein 

    * (3 Möglichkeiten)
    *
    * Die Kombination ergibt  3x3 = 9 Möglichkeiten
    *
    * Die Geschwindigkeit ist im Array s gespeichert.

    * Die Richtung im Array d.
    * Beide Arrays zusammen bilden alle Möglichkeiten ab.
    */

   private void setSpeedDirection() {
      int[] s = {2, 2, 2, 4, 4, 4, 6, 6, 6};
      int[] d = {0, -5, 5, 0, -5, 5, 0, -5, 5};
      int v;
      if (stop) return;
      getLook(); // sehwert berechnen
      v = brain[sehwert];
      speed = s[v];
      direction = d[v];
      degree += direction;
      if (degree < 0) degree += 360;
      if (degree > 360) degree -= 360;

    }   
    /**
     * Hauptroutine, die das Fahrzeug bewegt.
     */

    public void drive() {

        // wenn das Fahrzeug gestoppt wurde, wird es nicht mehr bewegt.
        if (stop) return;
        setSpeedDirection();
        super.drive();
    }
}


Da wir das meiste in der Klasse Cars vorbereitet haben ist unsere Klasse CarsBrain recht klein und beinhaltet nur zwei wichtige Methoden:

  • initRacer
    um die Fahrzeuge anzulegen. Wir schicken zeitgleich drei Fahrzeuge auf die Strecke.
  • development
    um neue Fahrzeuge zu entwickeln.
Bei der Entwicklung von neuen Fahrzeugen wird die Entscheidungstabelle des besten Fahrzeugs in die anderen beiden Fahrzeuge kopiert und anschließend wird die kopierte Tabelle zufällig verändert (mutant).

package car;

import java.io.IOException;

public class CarsBrain extends Cars {

   public CarsBrain() {
      super("Cars-Brain");
   }
   /**
    * Fahrzeuge erzeugen und zur Rennstrecke hinzufügen.
    */

   public void initRacer() {
      raceCar = new RacerBrain[3];
      for (int i=0; i < this.raceCar.length; i++) {
         this.raceCar[i] = new RacerBrain(i);
         this.raceCar[i].setGround(ground);
      }
      ground.setRacer(raceCar);       
   }
   /**
    * Weiterentwickeln der Fahrzeuge
    */

   public void development() {

      RacerBrain rb = (RacerBrain) raceCar[carindex];
      // Mutationen durchführen
      for (int j=0; j < raceCar.length; j++) {
         if (j != carindex) {

            // Ende, wenn ein Fahrzeug die Ziellinie erreicht hat
            if (raceCar[j].isWin()) finished=true;

            // Die Entscheidungstabelle des besten Fahrzeugs setzen
            ((RacerBrain)raceCar[j]).setBrain(rb.getBrain());

            // und die Tabelle verändern
            ((RacerBrain)raceCar[j]).mutant();
         }
      }       
   }
   /**
    * Hauptmethode
    *
    * @param args
    */

   public static void main(String[] args) {
      CarsBrain c = new CarsBrain();
      try {
         c.go("Strecke0.png");
      } catch (IOException e) {
         e.printStackTrace();
      }
   }
}



Führen wir das Programm 10 mal bis zum Ende aus, dann erreicht das Fahrzeug das Ziel nach ca.   Runden.

Runden: 83, 472, 293,

Was ist das Besondere an unserem Fahrzeug? Wir haben es nicht programmiert, wie es am besten durch die Strecke kommt. Es hat es selbst "herausgefunden".

Kommentare

Beliebte Posts