Industry Robot II mit Java 6 - Swing und BlueJ
Der Industry Robot II - 3-Achs-Roboter : Hier am Robo
Interface, Programmierung über 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, I7,
I8
Downloads : workSpace34.zip
Klasse SwingMot : Motor M3 fährt links, rechts und aus
|
Mit dem JLabel lblStatus
und den JButtons cmdLinks, cmdAus und cmdRechts auf dem JFrame
Importe :
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import ftcomputing.robo.*;
|
Klasse und Konstruktor
public class SwingMot extends JFrame implements ActionListener {
private JLabel lblStatus;
private JButton cmdLinks;
private JButton cmdAus;
private JButton cmdRechts;
private FishFace ft;
public SwingMot() {
setTitle("Robo Interface und Swing");
setLayout(new GridLayout(4,1));
lblStatus = new JLabel("Motor an M3", JLabel.CENTER);
add(lblStatus);
cmdLinks = new JButton("Links");
cmdLinks.addActionListener(this);
add(cmdLinks);
cmdAus = new JButton("Aus");
cmdAus.addActionListener(this);
add(cmdAus);
cmdRechts = new JButton("Rechts");
cmdRechts.addActionListener(this);
add(cmdRechts);
ft = new FishFace();
ft.openInterface(0, 0);
}
Die Klasse SwingMot wird von JFrame abgeleitet und implementiert zur
Auswertung der Button-Clicks einen ActionListener.
Im Konstruktor wird nach dem Text für den Frame-Titel das Layout der
einzufügenden Controls festgelegt :
Eine einspaltige Tabelle mit Platz für vier Elemente (es gibt noch andere
Möglichkeiten).
Anschließend werden die Controls instantiiert und der Reihe nach auf dem Frame
plaziert (add). Bei den JButtons wird noch der benötigte ActionListener (in
diesem Fall der in der Klasse implementierte) hinzugefügt
(addActionListener(this)).
Zum Schluß noch ein wenig FishFace.
ActionListener
public void actionPerformed(ActionEvent e) {
Object cmd = e.getSource();
if(cmd == cmdLinks) {
ft.setMotor(Mot.M3, Dir.Left);
lblStatus.setText("M3 : Links");
}
else if(cmd == cmdRechts) {
ft.setMotor(Mot.M3, Dir.Right);
lblStatus.setText("M3 : Rechts");
}
else {
ft.setMotor(Mot.M3, Dir.Off);
lblStatus.setText("M3 : Aus");
}
}
Der ActionListener erfordert die Implementierung der Methode actionPerformed
für die Bearbeitung eines MausClicks (Maus drücken und wieder loslassen). Die
Methode wird gleichermaßen für alle JButtons eingesetzt.
In der Methode wird deswegen zunächst die Quelle des Ereignisses anhand des
Methodenparameters bestimmt und danach verzweigt:
Bei JButton cmdLinks wird Mot.M3 auf Dir.Links geschaltet und dessen Funktion in
lblStatus angezeigt. Die anderen Buttons entsprechend.
Test über Klasse MotMain
public class MotMain {
public static void main() {
SwingMot frmMain = new SwingMot();
frmMain.setSize(280, 160);
frmMain.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frmMain.setVisible(true);
}
}
Getestet wird über die zweite Klasse MotMain und deren static void main
Methode.
Hier wird zunächst eine Instanz von SwingMot erzeugt, die Größe des Frames
wird angegeben. Zusätzlich wird festgelegt das ein Schließen der Form auch die
gesamte Anwendung beendet (das ist sonst nicht der Fall, der Frame läuft dann
im Verborgenen weiter). Zum Schluß wird der Frame noch sichtbar gemacht.
Klasse SwingMouse : Motor M3 läuft wenn mousePressed
|
Mit dem JLabel lblStatus
und den JButtons cmdLinks und cmdRechts auf dem JFrame
Importe :
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import ftcomputing.robo.*; wie gehabt
Bei der Steuerung eines Industry Robots ist es angenehmer, wenn der
Motor schon beim mouseReleased abschaltet und nicht erst durch Drücken
eines weiteren Buttons. Deswegen diese Variante
|
Klasse und Konstruktor
public class SwingMouse extends JFrame implements MouseListener {
private JLabel lblStatus;
private JButton cmdLinks;
private JButton cmdRechts;
private FishFace ft;
public SwingMouse() {
setTitle("Robo Interface mit Maus");
setLayout(new GridLayout(3, 1));
lblStatus = new JLabel("Motor an M3", JLabel.CENTER);
add(lblStatus);
cmdLinks = new JButton("Links");
cmdLinks.addMouseListener(this);
add(cmdLinks);
cmdRechts = new JButton("Rechts");
cmdRechts.addMouseListener(this);
add(cmdRechts);
ft = new FishFace();
ft.openInterface(0, 0);
}
public static void main() {
SwingMouse frmMain = new SwingMouse();
frmMain.setSize(280, 160);
frmMain.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frmMain.setVisible(true);
}
Neu ist hier eigentlich nur das implements MouseListener anstelle von
ActionListener. Außerdem wurde die static void main in die Klasse integriert,
man spart dann eine (sehr kleine Extraklasse).
MouseListener
public void mousePressed(MouseEvent e) {
Object cmd = e.getSource();
if(cmd == cmdLinks) {
ft.setMotor(Mot.M3, Dir.Left);
lblStatus.setText("M3 : Links");
}
else if(cmd == cmdRechts) {
ft.setMotor(Mot.M3, Dir.Right);
lblStatus.setText("M3 : Rechts");
}
}
public void mouseReleased(MouseEvent e) {
ft.setMotor(Mot.M3, Dir.Off);
lblStatus.setText("M3 : Aus");
}
public void mouseEntered(MouseEvent e) {}
public void mouseExited (MouseEvent e) {}
public void mouseClicked(MouseEvent e) {}
Der MouseListener erfordert die Implementierung von insgesamt fünf Methoden.
Wenn nicht alle benötigt werden reicht ein leerer Prozedurrumpf.
Hier in mousePressed nur noch Motor links und rechts und bei mouseReleased Motor
aus. Vom Inhalt her wie bei SwingMot
Klasse RobTouch : Der ganze Robot wird gescheucht
|
Die Zahl der Buttons hat deutlich zugenommen.
Die dahinter liegenden Funktionen unterscheiden sich : (Greifer) AUF, ZU
und (Robot) HOME werden bei Klick auf den Button vollständig ausgeführt.
Die anderen Funktionen werden bei mouseReleased abgebrochen.
Um die Buttons flexibler auf dem Frame anordnen zu können, wurden sie auf
JPanel pnlMain untergebracht (JLabel auf pnlInfo).
Hinzugekommen ist noch ein WindowListener um endlich die FishFace-Instanz
sauber beenden zu können |
Klasse und Konstruktor
public class RobTouch extends JFrame implements MouseListener,
WindowListener {
private JLabel lblStatus;
private JButton cmdAuf;
private JButton cmdRauf;
......
private FishFace ft;
public RobTouch(int x, int y) {
super("RobTouch");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new BorderLayout());
setSize(x, y);
addWindowListener(this);
JPanel pnlInfo = new JPanel();
add(pnlInfo, BorderLayout.NORTH);
lblStatus = new JLabel("Ready", JLabel.CENTER);
pnlInfo.add(lblStatus);
JPanel pnlMain = new JPanel();
pnlMain.setLayout(new GridLayout(3, 3));
add(pnlMain, BorderLayout.SOUTH);
cmdAuf = new JButton("AUF");
cmdAuf.addMouseListener(this);
pnlMain.add(cmdAuf);
...
ft = new FishFace();
ft.openInterface(0, 0);
}
public static void main() {
JFrame frm = new RobTouch(320, 160);
frm.setLocation(300, 200);
frm.setVisible(true);
}
Aufbau (beinahe) wie gehabt, es ist aber deutlich mehr geworden. Das JFrame
wird hier mit einem BorderLayout versehen. Das erlaubt die gezielte Plazierung
der von pnlInfo und pnlMain oben (NORTH) bzw unten (SOUTH) auf dem JFrame.
pnlMain mit den JButtons hat jetzt eine 3 * 3 Gitterlayout. Im main() wird mit
setLocation(300, 200) zusätzlich die Position des JFrame auf dem Bildschirm
festgelegt.
MouseListener
public void mousePressed(MouseEvent e) {
Object cmd = e.getSource();
if(cmd == cmdAuf) {
lblStatus.setText("Greifer öffnet");
ft.setMotor(Mot.M4, Dir.Left, Speed.Full, 9999);
}
....
else if(cmd == cmdHome) {
lblStatus.setText("Robot fährt auf Home-Position");
ft.setMotor(Mot.M1, Dir.Left, Speed.Full, 9999);
ft.setMotor(Mot.M2, Dir.Left, Speed.Full, 9999);
ft.setMotor(Mot.M3, Dir.Left, Speed.Full, 9999);
ft.setMotor(Mot.M4, Dir.Left, Speed.Full, 9999);
}
else if(cmd == cmdRechts) {
lblStatus.setText("Säule dreht nach rechts");
ft.setMotor(Mot.M1, Dir.Right);
}
....
}
public void mouseReleased(MouseEvent e) {
Object cmd = e.getSource();
if(cmd == cmdAuf) ft.waitForMotors(0, Mot.M4);
....
else if(cmd == cmdHome)
ft.waitForMotors(0, Mot.M1, Mot.M2, Mot.M3, Mot.M4);
else if(cmd == cmdRechts) ft.setMotor(Mot.M1, Dir.Off);
....
lblStatus.setText("Idle");
}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
public void mouseClicked(MouseEvent e) {}
Mit der Methode mousePressed werden die Actionen angestossen und mit
mouseReleased abgeschlossen bzw. beendet. Am Verfahren hat sich weiter nichts
geändert.
WindowListener
public void windowClosing(WindowEvent e) {
ft.closeInterface();
}
public void windowClosed(WindowEvent e) {}
public void windowDeiconified(WindowEvent e) {}
public void windowIconified(WindowEvent e) {}
public void windowActivated(WindowEvent e) {}
public void windowDeactivated(WindowEvent e) {}
public void windowOpened(WindowEvent e) {}
Eigentlich nur bei windowsClosing ein schlichtes ft.closeInterface(); Hinzu
kommen dann aber noch die restlichen (Dummy) Methoden.
Klasse RobPos : RobTouch + Positionsanzeige
|
Aufbau wie gehabt, jetzt aber Anzeige der
aktuellen Position in JLabel lblStatus. |
MouseListener
public void mousePressed(MouseEvent e) {
Object cmd = e.getSource();
....
else if(cmd == cmdRauf) {
lblStatus.setText("Arm geht nach oben");
ft.setMotor(Mot.M3, Dir.Left, Speed.Full, 9999);
}
....
else if(cmd == cmdRunter) {
lblStatus.setText("Arm geht nach unten");
ft.setMotor(Mot.M3, Dir.Right, Speed.Full, 9999);
}
else if(cmd == cmdRueck) {
lblStatus.setText("Arm geht nach hinten");
ft.setMotor(Mot.M2, Dir.Left, Speed.Full, 9999);
}
}
public void mouseReleased(MouseEvent e) {
Object cmd = e.getSource();
....
else if(cmd == cmdRauf) {
ft.setMotor(Mot.M3, Dir.Off);
actPos[Mot.M3-1] -= 9999 - ft.getCounter(Inp.I6);
if(actPos[Mot.M3-1] < 0) actPos[Mot.M3-1] = 0;
lblStatus.setText("Arm vertikal : " + actPos[Mot.M3-1]);
}
....
else if(cmd == cmdRunter) {
ft.setMotor(Mot.M3, Dir.Off);
actPos[Mot.M3-1] += 9999 - ft.getCounter(Inp.I6);
lblStatus.setText("Arm vertikal : " + actPos[Mot.M3-1]);
}
else if(cmd == cmdRueck) {
ft.setMotor(Mot.M2, Dir.Off);
actPos[Mot.M2-1] -= 9999 - ft.getCounter(Inp.I4);
if(actPos[Mot.M2-1] < 0) actPos[Mot.M2-1] = 0;
lblStatus.setText("Arm horizontal : " + actPos[Mot.M2-1]);
}
}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
public void mouseClicked(MouseEvent e) {}
Bei mousePressed cmd == cmdRauf wird die Action "Arm geht nach
oben" angestoßen
Bei mouseReleased cmd == cmdRauf wird die Action durch setMotor(
Dir.Off ) beendet und der aktuelle Zählerstand (der beim Start der
Action zurückgesetzt wurde) ausgewertet. Der hypothetische Weg von 9999
Impulsen wird um die tatsächliche Impulszahl vermindert und auf die letzte
actPos addiert (Dir.Right) bzw. von der letzten actPos subtrahiert (Dir.Left).
Bei Dir.Left wird zusätzlcih noch auf 0 korrigiert wenn der Motor zulange am
Endtaster gewürgt hatte.
Klasse RobTeach : RobPos + TeachIn->Run
|
Aufbau des Robots wie gehabt. Hinzugekommen
sind die Buttons :
CLEAR : Löschen der internen Positionsliste
ADD : Hinzufügen der aktuellen Position zu robListe
RUN : Betreiben des Robots nach robListe |
mouseClicked
Wesentliches neues Element ist der Code für das bisher nur als Methodenrumpf
vorliegende Ereignis mouseClicked und die dazu gehörende private Methode
runListe.
public
void
mouseClicked(MouseEvent e) {
Object cmd = e.getSource();
if(cmd
== cmdRun)
{
runListe();
}
else
if(cmd
== cmdClear)
{
anzPos
= 0;
lblStatus.setText("robListe
gelöscht");
}
else
if(cmd
== cmdAdd)
{
for(int
i = 0; i < 4; i++) robListe[anzPos][i]
= actPos[i];
if(anzPos
< (robListe.length-1))
anzPos++;
lblStatus.setText("Länge
robListe : " + anzPos);
}
}
private
void
runListe() {
for(int
i = 0; i < anzPos;
i++) {
for(int
j = 0; j < 3; j++) {
int
zPos = robListe[i][j];
if(zPos
< actPos[j])
ft.setMotor(j+1,
Dir.Left,Speed.Full,
actPos[j]-zPos);
else
if(zPos
> actPos[j])
ft.setMotor(j+1,
Dir.Right,
Speed.Full,
zPos-actPos[j]);
}
ft.waitForMotors(0,
Mot.M1,
Mot.M2,
Mot.M3);
for(int
j = 0; j < 3; j++) actPos[j]
= robListe[i][j];
if(robListe[i][3]
== 0) {
ft.setMotor(Mot.M4,
Dir.Left,
Speed.Full,
9999);
ft.waitForMotors(0,
Mot.M4);
gripOpen
= 0;
}
else
if(gripOpen
== 0){
ft.setMotor(Mot.M4,
Dir.Right,
Speed.Full,
GripClosed);
ft.waitForMotors(0,
Mot.M4);
gripOpen
= GripClosed;
}
lblStatus.setText("Run
: " +
i);
}
}
Mit Button CLEAR wird der Zeiger auf den ersten freien Platz in robListe auf
0 zurückgesetzt
Mit ADD wird die aktuelle Position des Robots in die robListe eingetragen und
der Zeiger auf den ersten freien Platz erhöht. Aus Gründen der Einfachheit
wird bei voller robListe "auf der Stelle getreten".
RUN ruft runListe das die in robListe eingetragenen Robot-Positionen abarbeitet.
Dazu zunächst eine äußere Schleife über die Einzelpositionen von robListe.
In der Schleife Starten der Motoren für Säule, Arm vertikal und Arm
horizontal. Warten auf das Erreichen aller vorgegebenen Einzelpositionen und
notieren der neuen Positionen in actPos.
Anschließend wird sich um den Greifer gekümmert.
Das wars dann auch schon. Die neuen Variablen-Deklarationen ... kann man der
Source (diesmal aber Eclipse 3.4) entnehmen.
Wie gehts weiter : Hier nicht, aber der eigenen Phantasie und Tatkraft sind
keine Grenzen gesetzt :
- Überarbeiten der GUI : mehr Abstand, Rahmen
- Einfügen eines Listen-Widgets zur Anzeige der in robListe eingetragenen
Positionen verbunden mit der Möglichkeit einzelne Einträge zu
verändern/löschen.
- Speichern von robListe in eine Datei und Laden der Liste beim Start des
Programms
- ....
Es bleibt noch viel zu tun.
Stand : 16.04.2010
|