Java 6 BlueJ IR2

ftComputing : Programme für die fischertechnik-Interfaces und -konstruktionskästen
  
ftComputing.de
Home
Back
Sitemap
Index
Links
Impressum
Mail

 

Programmierung der Industry Robot mit Java 6 und BlueJ

Eine Auswahl der Industry Robots. Hier zur Programmierung über das Robo Interface und ftcomputing.robo.jar.

Aufbau der Robots nach Bauanleitung mit Säule an M1, Endtaster I1, Impulstaster I2, 
Arm waagerecht M2, I3, I4 und Arm senkrecht M3, I5, I6. Der Greifer an M4 kommt später

Downloads : workSpace34.zip

Klasse RobTut : Säule fährt Home und dann nach rechts

Import, Klasse und Konstruktor

import ftcomputing.robo.*;
public class RobTut {
  private FishFace ft;

  public RobTut() {
    ft = new FishFace();
    ft.openInterface(0, 0);
  }

Import des genutzten Packages, in der Klasse die globalen Variable ft mit dem FishFace Objekt. Im Konstruktor wird ft instanziiert. Zusätzlich wird eine Verbindung zum ersten Robo Interface an USB hergestellt.

Säule fährt linksdrehend an ihre Home-Position

public void goHome() {
  System.out.println("Motor M1 Home, wartet auf I1");
  ft.setMotor(Mot.M1, Dir.Left);
  ft.waitForInput(Inp.I1);
  ft.setMotor(Mot.M1, Dir.Off);
  System.out.println("--- Zu Hause ---");
}

System... zur Anzeige der aktuellen Aktivität auf der Konsole.
setMotor : Start des Säulenmotors
waitForInput : Warten auf das Erreichen der Home-Position
setMotor Off : Abschalten des Säulenmotors

Säule dreht rechts um die angegebene Schrittzahl

public void goRight(int Inc) {
  System.out.println("Säule dreht rechts um : " + Inc);
  ft.setMotor(Mot.M1, Dir.Right, Speed.Full, Inc);
  ft.waitForMotors(0, Mot.M1);
  System.out.println("--- Säule angekommen ---");

setMotor mit zusätzlichem Parameter Impulszahl. Die Methode wird asynchron ausgeführt. Bei Erreichen der vorgegebenen Impulszahl wird der Motor wieder abgeschaltet. Das Programm läuft derweil weiter. Da hier sonst nichts zu tun ist, wird hier schlicht gewartet (waitForMotors). Der Parameter 0 steht dabei für Warten bis fertig.

Testen mit BlueJ

Auf das Klassensymbol von RobTut rechtsklicken und ein neues Objekt anlegen. Darauf klicken und die interessierende Methode aufrufen. Achtung : Nach Änderungen in der Source muß das BlueJ vorher noch zurückgesetzt werden (Rechtsklick auf das Run-Symbol).

Alternativ können die interessierenden Befehle oder Methoden auch im Direkteingabe-Fenster eingegeben werden. Als erster Befehl muss dann eine RobTut = ft new RobTut() Instanz angelegt werden.

Testen mit BlueJ : Methode Main

public class RobMain {
  public static void main() {
    RobTut rob = new RobTut();
    System.out.println("Robot gestartet");
    rob.goHome();
    rob.goRight(33);
    rob.goHome();
    rob.goRight(112);
    System.out.println("--- Das war's ---");
  }
}

Eine zweite Klasse mit der statischen Methode main() anlegen und in ihr die interessierenden Befehle unterbringen.

RobTutZwei : Der gesamte Robot wird gescheucht

Import, Klasse und Konstruktor

import ftcomputing.robo.*;
public class RobTutZwei {
  private FishFace ft;
  private int[] actPos = new int[4];

  public RobTutZwei() {
    ft = new FishFace();
  }

  public void start() {
    ft.openInterface(0, 0);
  }
  public void ende() {
    ft.closeInterface();
  }

Es wird ein Array angelegt, der die aktuelle Position der Robot-Komponenten in Impulsen ab Home (Endtaster) enthält. Die Verbindung zum Interface wird jetzt in einer eigenen Methode hergestellt. Hinzu kommt eine weitere Methode für das Beenden der Verbindung. Macht zwar im Testbetrieb etwas mehr Arbeit, erleichtert aber auch die Arbeit.

Anfahren der Home-Position

public void driveHome(int motNr) {
  int endT = motNr*2-1;
  System.out.println("Motor M" + motNr + " -> Home, wartet auf I" + endT);
  ft.setMotor(motNr, Dir.Left);
  ft.waitForInput(endT);
  ft.setMotor(motNr, Dir.Off);
  actPos[motNr-1] = 0;
  System.out.println("M" + motNr + "--- Zu Hause ---");
}

Angefahren wird die Home-Position einer Komponente. Die zugehörende Motornummer wird als Parameter übergeben. Die Nummer des Endtasters wird aus der Motornummer ermittelt. Die eigentliche Home-Funktion entspricht dem schon bekannten goHome. Hinzugekommen ist noch ein Null-Setzen der aktuellen Position.

Anfahren einer vorgegebenen Position - Impulse ab Home

public void driveTo(int motNr, int pos) {
  System.out.println("Motor M" + motNr + " fährt auf Position : " + pos);
  if (pos < actPos[motNr-1]) ft.setMotor(motNr, Dir.Left, Speed.Full, 
    actPos[motNr-1] - pos);
  else if (pos > actPos[motNr-1]) ft.setMotor(motNr, Dir.Right, Speed.Full, 
    pos - actPos[motNr-1]);
  while(ft.waitForMotors(1111, motNr) == Wait.Time)
    System.out.print(" - " + ft.getCounter(motNr*2));
  System.out.println();
  actPos[motNr-1] = pos;
  System.out.println("Auf Position : " + pos);
}

Die Methode ist komfortabler als die bekannte goRight, aber auch komplizierter. Mit dem if-Konstrukt wird zunächst einmal die erforderliche Drehrichtung bestimmt. Nach links, wenn die neue Position kleiner ist als die aktulle, nach rechts, wenn sie größer ist. Gar nicht, wenn Zielposition und aktuelle Position übereinstimmen. In Verbindung mit der Drehrichtungsbestimmung wird dann der zugehörenden Fahrbefehl wie in goRight aufgerufen. Beim ebenfalls wieder erforderlichen waitForMotors wird jetzt ein wenig aufwandgetrieben, um die laufende Aktion zu "visualisieren" : Anzeige des aktuellen Counterstandes alle 1,111 Sekunden. Zu Schluß wird noch die aktuelle Position in actPos aktualisiert. Die Verwendung von motNr in setMotor und motNr-1 bei actPos kann ein wenig verwirren. Die M-Ausgänge zählen entsprechend der Beschriftung auf dem Interface ab 1, bei Arrays hat das erste Element aber den Index 0.

Testen mit Main

public class RobMainZwei {
  static RobTutZwei rob = new RobTutZwei();
  public static void main() {
    System.out.println("Robot gestartet");
    rob.start();
    HinUndHer();
    //AlleDrei();
    //RobGo();
    rob.ende();
    System.out.println("--- Das war's ---");
  }
  private static void HinUndHer() {
    rob.driveHome(1);
    rob.driveTo(1, 44);
    rob.driveTo(1, 22);
    rob.driveTo(1, 112);
  }

Das Main ist etwas aufwändiger geworden. static : main() muß eine static Methode sein um startbar zu sein. Wenn main() auf globale Elemente zugreifen will, müssen diese auch static sein. Das gilt für Object rob und die Methoden der Klasse.
In main wird hier mit rob.start() die Verbindung zum Interface hergestellt und mit rob.ende() wieder aufgehoben. dazwischen gibt es nur einen Aufruf der jeweils genutzten Methode. Hier HinUndHer().
In HinUndHer() wird zunächst das Home der Säule angefahren und dann wird die Säule ein wenig hin und her gedreht.

Alternativ kann man natürlich auch weiterhin mit den direkten Testmethoden von BlueJ arbeiten (s.o.). Unter Menü Ansicht des Hauptfensters von BlueJ kann man zusätzlich ein Debugger-Fenster einschalten.

Und nun : Alle

private static void AlleDrei() {
  for(int i = 1; i <= 3; i++) rob.driveHome(i);
  rob.driveTo(1, 112);
  rob.driveTo(2, 44);
  rob.driveTo(3, 66);
}

Zunächst werden die Home-Positionen aller beteiligten Komponenten nacheinander angefahren. Anschließend wird eine Komponente nach der anderen in die gewünschte Position gebracht.

Simultan : Alles auf einmal

public void moveHome() {
  System.out.println("Es geht nach Hause");
  for(int i = 1; i <= 3; i++) ft.setMotor(i, Dir.Left, Speed.Full, 9999);
  ft.waitForMotors(0, Mot.M1, Mot.M2, Mot.M3);
  for(int i = 0; i < 3; i++) actPos[i] = 0;
  System.out.println("--- wir sind zu Hause ---");
}

Das Anfahren Home mit allen Komponenten gleichzeitig ist eigentlich recht simpel. Zunächst einmal werden die beteiligten Motoren in Richtung links für 9999 Impulse geschaltet. Hier wird eine zusätzliche Eigenheit von setMotor genutzt : Der Motor wird abgeschaltet, wenn der zugehörende Endtaster erreicht wird - 9999 wird nie erreicht. Anschließend wird in Ruhe auf das Ende der Operation gewartet. waitForMotors hat hier eine Liste aller beteiligten Motoren als Parameter. Zu Schluß dann actPos = 0;

public void moveTo(int... pos) {
  System.out.println("Robot fährt auf neue Position");
  for(int i = 0; i < pos.length; i++) {
    if(pos[i] < actPos[i]) ft.setMotor(i+1, Dir.Left, Speed.Full, actPos[i] - pos[i]);
    else if(pos[i] > actPos[i]) ft.setMotor(i+1, Dir.Right, Speed.Full, pos[i] - actPos[i]);
  }
  ft.waitForMotors(0, 1, 2, 3);
  for(int j = 0; j < pos.length; j++) actPos[j] = pos[j];
  System.out.println("--- Mir san doa ---");
}

Auch hier Start aller beteiligten Motor in einer for-Schleife. Hier wird aber nach links / rechts unterschieden wie schon bei driveTo. Der Parameter int... pos von moveTo steht für variable Anzahl von Argumenten. Es müssen also nur die Motoren von "vorne (ab 1)" angegeben werden, deren Position sich ändert. Gewartet wird aber wieder auf alle Beteiligten.

Test : RobGo - RobListe

private static void RobGo() {
  rob.moveHome();
  rob.moveTo(112, 33, 66);
  rob.moveTo(100, 22, 33);
}

Ist eigentlich schon ein wenig mickrig.

private static void RobListe() {
  int[][] posListe = new int[][]{{55, 33, 66},{10, 22, 66},{112, 11, 11}};
  rob.moveHome();
  for(int i = 0; i < posListe.length; i++) 
      rob.moveTo(posListe[i][0], posListe[i][1], posListe[i][2]);
}

Deswegen nochmal, aber listengesteuert. Hier wird erstmal eine Liste der Positionen aufgebaut, die dann in einer Schleife abgearbeitet wird.

Klasse RobIR2 : Betrieb des Säulenrobots

Ist primär eine Überarbeitung der Klasse RobTutZwei ohne deren Struktur deutlich zu verändern.

Import, Klasse und Konstruktor

import ftcomputing.robo.*;
public class RobIR2 {
  private FishFace ft;
  private int[] actPos = new int[3];
  private int gripOpen;

  static final int GripClosed = 26;
  static final int[] RobMot = new int[]{1, 2, 3};
  static final int GripMot = 4;

  public RobIR2() {
    ft = new FishFace();
  }
  public void start() {
    ft.openInterface(0, 0);
    for(int pos : actPos) pos = 0;
    gripOpen = 0;
  }
  public void ende() {
    ft.closeInterface();
  }

Neu ist hier die klare Trennung des eigentlichen Robot von seinen Werkzeugen : Liste RobMot mit einer Aufzählung der Robotmotoren und GripMot mit der Nummer des Greifermotor. Dazu gehören actPos mit der aktuellen Position des Robots und gripOpen für die Stellung des Greifers. In start() werden die Werte zurückgesetzt. Hinweis for(int pos: actPos) pos = 0; ist eine "foreach"-Schleife, die es seit Java 1.5 bzw. 5 gibt. Sie erlaubt eine deutlich kürzere Schreibweise bei Aufzählungen als mit der bekannten for-Schleife. Hier werden alle Elemente des Arrays actPos auf 0 gesetzt.

moveHome : Überarbeitet

public void moveHome() {
  System.out.println("Home simultan");
  for(int mot : RobMot)ft.setMotor(mot, Dir.Left, Speed.Full, 9999);
  ft.waitForMotors(0, RobMot);
  gripper(0);
  for(int pos : actPos) pos = 0;
}

In RobIR2 nur noch die simultane Methode, die auch den Greifer berücksichtigt. Bei waitForMotors kann jetzt als Argument die Liste der beteiligten Motoren verwendet werden.

moveTo : Neuausgabe

public void moveTo(int... pos) {
  System.out.print("Robot fährt auf neue Position");
  for(int p : pos) System.out.print(" - " + p);
  for(int i = 0; i < actPos.length; i++) {
    if(pos[i] < actPos[i]) 
      ft.setMotor(RobMot[i], Dir.Left, Speed.Full, actPos[i] - pos[i]);
    else if(pos[i] > actPos[i]) 
      ft.setMotor(RobMot[i], Dir.Right, Speed.Full, pos[i] - actPos[i]);
    }
  ft.waitForMotors(0, RobMot);
  for(int i = 0; i < actPos.length; i++) actPos[i] = pos[i];
}

Wie gehabt, jetzt mit Anzeige der anzufahrenden Position.

gripper : Extrawurst für den Greifer

public void gripper(int open) {
  if(open == 0) { 
    System.out.println(" : Greifer öffnet");
    ft.setMotor(GripMot, Dir.Left, Speed.Full, 9999);
    ft.waitForMotors(0, GripMot);
    gripOpen = 0;
  }
  else if (gripOpen == 0) { 
    System.out.println(" : Greifer schließt");
    ft.setMotor(GripMot, Dir.Right, Speed.Full, GripClosed);
    ft.waitForMotors(0, GripMot);
    gripOpen = GripClosed;
  }
}

Da der Greifer erst dann öffnet oder schließt, wenn der Robot steht, auch eine separate Methode dafür. sie lehnt sich an das bekannt driveHome/To an. Da der Greifer keine "Zwischentöne" kennt sondern nur "auf" / "zu" hier auch als Parameter die Werte 0 für auf und 1 für zu. Was zu in Impulsen heißt, sagt die Konstante GripClosed. Zuätzlich wird bei zu abgefragt, ob der Greifer schon zu ist, so kann man den Robot mit gleicher Greiferstellung zwischen mehreren Robot-Positionen verfahren. 

Testen von RobIR2

import ftcomputing.robo.*;
public class RobmainIR2 {
  static RobIR2 rob = new RobIR2();
  public static void main() {
    System.out.println("Robot gestartet");
    rob.start();
    RobListe(new int[][]{{112, 21, 33, 1},
                         {22, 42, 22, 0},
                         {112, 11, 21, 1}});
    rob.ende();
    System.out.println("--- Das war's ---");
  }
  private static void RobListe(int[][] posListe) {
    rob.moveHome();
    for(int[] pos : posListe) { 
      rob.moveTo(pos);
      rob.gripper(pos[3]);
    }
  }
}

Eigentlich auch schon bekannt. Die Methode RobListe hat jetzt einen Parameter mit der um die Greiferstellung erweiterten posListe bekommen. Nach dem Anfahren der Robot-Position mit moveTo wird der Greifer gesteuert. Die um einen Eintrag (Greifer) längere posListe macht dem moveTo nichts, da es sich intern an actPos orientiert.

Stand : 18.03.2010