ifm O2D500 Objekterkennungssensor

Der O2D500 Objekterkennungssensor erm�glicht durch eine kombinierbare 2D-Pr�fung von Konturen und Fl�chen eine maximale Qualit�tssicherung.

1. Einleitung

Mittels dem Programm "ifm Vission Assistant" kann der Sensor sehr einfach konfiguriert werden. Folgende Erkennungsmodelle stehen zur Verf�gung: BLOB-Analyse, Kontur-Erkennung sowie Kontur Lagenachf�hrung. Er eignet sich haupts�chlich f�r Pr�faufgaben. Seit der Firmware 1.27.9941 kann er zudem zur Objektlokalisierung in Verbindung mit einem Roboter eingesetzt werden.

Mit dem Sensor kann per digitalen IOs oder per TCP/IP kommuniziert werden. F�r Pr�faufgaben reicht in den meisten F�llen das digitale Triggern sowie eine digitale R�ckmeldung des Sensors bez�glich den Pr�fergebnissen aus. Wird er zur Objektlokalisierung eingesetzt, k�nnen die Objektkoordinaten per TCP/IP zu horstFX �bertragen werden.

Im folgenden Artikel wird die Kommunikation mittels digitalen IOs sowie mittels TCP/IP beschrieben. Die Kommunikation mittels TCP/IP wird anhand eines Beispielprogramms aufgezeigt, bei welchem der Roboter Objekte durch die Kamera erkennt und greift. Das horstFX Programm sowie die notwendige Schnittstellenkonfiguration f�r den ifm Vission Assistant k�nnen am Ende des Artikels heruntergeladen werden.

Versionshinweise:

Firmwarestand Kamera: 1.28.10310

ifm Vission Assistand Version 2.5.26.0

horstFX Version 2022.07

2. Einrichtung des Systems

Die Einrichtung sowie Verdrahtung des Sensors ist sehr gut in dem Programm ifm Vission Assistant sowie dem Benutzerhandbuch des Sensors beschrieben. Diese k�nnen auf der Homepage von ifm heruntergeladen werden.

Damit der ifm Vission Assistant die Kamera findet, m�ssen sich der PC sowie die Kamera im selben Bereich des Netzwerks befinden. Standardm��ig wird der Sensor mit der IP-Adresse 192.168.0.69 ausgeliefert. Damit der PC mit der Kamera kommunizieren kann, kann der PC somit z.B. folgende IP-Adresse besitzen: 192.168.0.1 

3. Kalibrierung

Die Kalibrierung des Systems kann in dem ifm Vission Assistant bei der Erstellung einer Anwendung unter dem Tab Bilder und Trigger bei der Anwendungskonfiguration durchgef�hrt werden. Der Wizzard leitet einen in wenigen Schritten durch die Kalibrierung. Ja ne Applikation stehen unterschiedliche Kalibriermethoden zur Verf�gung. Sollen beispielsweise nur Teile �berpr�ft werden, reicht eine der "Mess-Kalibrierungen" aus. Sollen Objektpositionen mit dem Roboter angefahren werden (d.h. Teile gegriffen werden), kann die Roboter-Sensor-Kalibrierung durchgef�hrt werden. Im Verlauf des Artikels wird diese Kalibriermethode f�r das Beispiel verwendet.

Beim Einlernen eines Objekts wird empfohlen, das Objekt mit dem Roboter zu greifen und einen Rotationswert in RZ von 0� anzufahren. Das Objekt wird anschlie�end mit diesem Winkel unter der Kamera abgelegt und kann in dieser Position eingelernt werden. Der ifm Vission Assistant wei�t beim Einlernen des Objekts diesem auch eine RZ Orientierung von 0� zu. Bei anschlie�enden Rotations�nderungen im realen Ablauf, stimmt so die Orientierungs�nderung direkt mit der Verarbeitung in horstFX �berein.

4. Kommunikation

Die Kommunikation mit dem Sensor kann digital oder mittels TCP/IP erfolgen. Im Folgenden werden beide Kommunikationsarten beschrieben:

4.1. Digitale Kommunikation

F�r die meisten Pr�faufgaben wird die digitale Kommunikation ausreichend sein. In dem ifm Vission Assistant kann zur Hilfe der Verdrahtung eine passende �bersicht zu dem jeweiligen Anschlusskabel aufgerufen werden. 

4.1.1. Digitales Triggern

Um den Sensor digital zu Triggern, muss im ifm Vission Assistant bei der Erstellung einer Anwendung der Triggermodus auf Positive Flanke eingestellt werden. Ebenso muss in dem Fenster der Button auf alle Trigger reagieren angew�hlt werden.

Durch das Schalten des passenden digitalen Ausgangs in horstFX, kann der Trigger nun ausgel�st werden.

output_literal( "OUTPUT_1", 1.0 );
sleep(100);
output_literal( "OUTPUT_1", 0.0 );

4.1.2. Digitale Ergebnis�bertragung

Im ifm Vission Assistant kann in dem Tab Logik eine Logik-Konfiguration erstellt werden. Die digitalen Ausg�nge k�nnen so mit den passenden Zust�nden belegt werden.

4.2. TCP/IP Kommunikation

Neben der digitalen Kommunikation kann f�r komplexere Anwendungsf�lle mittels TCP/IP mit der Kamera kommuniziert werden. Der Roboter muss hierzu, mittels dem Netzwerkkabel, mit der RJ45 Buchse des Roboter-Schaltschranks verbunden werden. Zudem m�ssen sich die IP-Adressen der beiden Teilnehmer im selben Bereich befinden. Im ifm Vission Assistant k�nnen unter dem Tab Ger�te-Konfiguration Netzwerk- sowie Schnittstelleneinstellungen vorgenommen werden. Wie man die IP-Adresse des Roboters einstellen kann, ist in folgendem Artikel beschrieben: IP-Adresse �ndern. In folgendem Beispiel besitzt die Kamera die IP-Adresse 192.168.2.15 und der Roboter die IP-Adresse 192.168.2.2

4.2.1. Herstellen der Verbindung

Die beiden Teilnehmer kommunizieren �ber Netzwerk Sockets miteinander. Detailliertere Informationen finden sich unter Sockets

Die Verbindung zur Kamera kann mit folgenden Befehlen in horstFX hergestellt werden:

var IP = "192.168.2.15";                // IP ifm Sensor
var Port = 50010;                       // Port ifm Sensor
var Terminator = "\r\n";                // Zeilenumbruch Carriage Return + Linefeed beim Schreiben einer Nachricht
var Delimiter = "\r\n".charCodeAt(0);   // Zeilenumbruch Carriage Return + Linefeed beim Einlesen einer Nachricht
 
try
{
    var socket = new java.net.Socket();
    socket.connect(new  java.net.InetSocketAddress(IP, Port), 10000); //10s timeout
    showHint("Connection to the sensor established");
 
    // 10 sek Timeout beim Einlesen der Nachricht ueber einen Socket
    socket.setSoTimeout(10000);
}
catch (e)
{
    show_info("A connection with the ifm sensor could not be established!");
}
 
try
{
    // Programmcode
    // ...
}
 
finally
{
    // Schliesse Socket
    socket.close();
}

Die IP-Adresse sowie der Port der Kamera muss evtl. an die Einstellungen im ifm Vission Assistant angepasst werden. Der Delimiter sowie Terminator beschreibt das Ende eines Befehls an bzw. vom Sensor. 

horstFX versucht mit dem eingestellten Timeout von 10 Sekunden eine Verbindung mit dem Sensor herzustellen. Sollte in der Zeit keine Verbindung hergestellt werden k�nnen, wird eine Informationsmeldung ausgegeben. 

Der eigentliche Programmcode des Programms befindet sich in dem try{} Block. Wird hierbei durch ein Fehler das Programm beendet, wird durch den finally{} Block sichergestellt, dass der Socket auch wieder geschlossen wird. 

4.2.2. Zusammenstellung der TCP/IP Befehle 

Wie die TCP/IP Befehle zur Kommunikation mit der Kamera zusammengesetzt sind, ist in dem ifm programmers guide beschrieben. Diese Dokumentation kann von der ifm Homepage heruntergeladen werden.

ifm bietet 2 unterschiedliche Protokolle an. Diese k�nnen unter dem Tab Ger�te-Konfiguration ? Schnittstellen eingestellt werden. Die Standard Einstellung ist die Version 3. Auf diese wird auch im Folgenden eingegangen.

 

ifm_TCP_ProtokollBei dem Protokoll V3 setzt sich ein TCP/IP Befehl aus 2 Unterbefehlen zusammen. Da das Protokoll bei der Maschinen-zu-Maschinen-Kommunikation verwendet wird, unterst�tzt es asynchrone Befehle. Der erste Unterbefehl besteht demnach aus einer zu vergebenen Ticketnummer (character string bestehend aus 4 Zahlen zwischen 1000 und 9999) sowie der L�nge (character string beginnend mit 'L' sowie 9 folgenden Zahlen) des eigentlichen folgenden Befehls. Der zweite Unterbefehl besteht aus der selben Ticketnummer sowie dem eigentlichen Befehl an die Kamera. Beide Unterbefehle werden mit einem carriage return sowie linefeed beendet. <Ticket><Length>CR LF<Ticket><Content>CR LF

Der Befehl zum Triggern der Kamera sieht somit folgenderma�en aus:

Befehl 1: <1234><L000000007>CR LF 

Befehl 2: <1234><t>CR LF 

4.2.3. Triggern der Kamera via TCP/IP

Um die Kamera per TCP/IP Triggern zu k�nnen, muss in dem ifm Vission Assistant bei der Anwendungskonfiguration unter dem Tab Bilder & Trigger der Triggermodus auf Prozess-Schnittstelle gestellt werden. Ebenso muss der Button auf alle Trigger reagieren angew�hlt werden. 

ifm_Trigger_TCP

Folgend wird beschrieben, wie obiger Befehl zum Triggern der Kamera von horstFX aus an die Kamera gesendet werden kann.

Zum Senden einer Nachricht an die Kamera kann folgende Funktion verwendet werden. 

var Terminator = "\r\n";        // Zeilenumbruch Carriage Return + Linefeed beim Schreiben einer Nachricht
 
// Nachricht ueber Socket schreiben
function writeMessage(socket, message)
{
    var printWriter =
        new java.io.PrintWriter(
            new java.io.OutputStreamWriter(
                socket.getOutputStream()));
    printWriter.print(message += Terminator);  
    printWriter.flush();
}

Wie oben beschrieben, setzt sich ein Befehl an die Kamera aus 2 Unterbefehlen zusammen. Somit wird obige Funktion auch 2 mal hintereinander �ber folgende Codezeilen aufgerufen:

// Ticketnummer "1234" + Laenge des naechsten Befehls "L000000007"
var Message = "1234L000000007";
writeMessage(socket, Message);
     
// Trigger Befehl: Ticketnummer "1234" + Befehl "t"
var Message = "1234t";
writeMessage(socket, Message);

Damit die Kamera auf einen Trigger-Befehl reagieren kann, muss sie sich im Test- oder Runmodus befinden.

4.2.4. Ergebnis�bertragung per TCP/IP

Der Ausgabe-Character-String der Kamera kann im ifm Vision Assistant bei der Anwendungskonfiguration in dem Tab Schnittstellen erstellt werden. Die Konfiguration von diesem Beispiel kann am Ende von diesem Artikel heruntergeladen und in den ifm Vision Assistant importiert werden.

image2022-12-21_15-40-55

Die Zeichenkette aus diesem Beispiel hat folgendes Format:

star;"Anzahl Kontur�bereinstimmung";"ObjektkoordinateX";"ObjektkoordinateY;"ObjektkoordinateZ";"ObjektwinkelRX";stop"

Wird ein Objekt erkannt, sieht die Zeichenkette somit folgenderma�en aus:

star;1;0.000;0.000;0.000;2.63;stop

Wird kein Objekt erkannt, sieht die Zeichenkette somit folgenderma�en aus: star;0;stop

Folgend wird beschrieben, wie obige Antwort der Kamera in horstFX eingelesen und genutzt werden kann.

Zum Einlesen einer Nachricht wird folgende Funktion verwendet. Diese liest eine Kameranachricht bis zu dem carriage return sowie linefeed ein.

var Delimiter = "\r\n".charCodeAt(0);   // Zeilenumbruch Carriage Return + Linefeed beim Einlesen einer Nachricht
 
// Nachricht ueber Socket lesen
function readMessage(socket, msgSperator)
{
    var ByteHelper = Java.type("de.fruitcore.robot.core.util.ByteHelper");
    var ByteArray = Java.type("byte[]");
    var dIn = new java.io.DataInputStream(socket.getInputStream());
    var full_msg = new ByteHelper();;   
    var buffer = new ByteArray(1);
         
    while(1)
    {
            dIn.readFully(buffer, 0, buffer.length);
            full_msg.putByte(buffer[0]);
            if(msgSperator == buffer[0])
        {
                    break;
            }
    }
    var message = new java.lang.String(full_msg.toBytes(), java.nio.charset.StandardCharsets.UTF_8);
    return message.substring(0, message.length - 1);
}

Da wir das asynchrone Protokoll nutzen, antwortet die Kamera auf einen Befehl mit 2 Antworten. Die erste beinhaltet die Ticketnummer sowie L�nge der folgenden Antwort. Die zweite Antwort beinhaltet die Ticketnummer sowie ein Zeichen welches den Erfolg oder Misserfolg des gesendeten Befehls beschreibt. Bei einem '*' wurde der Befehl erfolgreich gesendet. In horstFX k�nnen gesendete Kamerabefehle somit mit folgenden Codezeilen bzw. mit dem Aufruf von folgender Funktion �berpr�ft werden.

// Ueberpuefe gesendete Nachricht
function checkSentMessage()
{
    var CamMsg = null;
 
    // Ticketnummer und Laenge in Bytes der Kameraantwort:
    cam_msg = readMessage(socket,Delimiter);
    show_info("Ticketnummer und Laenge der Kameraantwort: " + CamMsg);
 
    // Ticketnummer der Kameraantwort. Bei "*" war der Befehl erfolgreich
    CamMsg = readMessage(socket,Delimiter);
    show_info("Ticketnummer und Erfolgsmeldung: " + CamMsg);
 
    CamMsg = CamMsg.toString();
    if (CamMsg.indexOf("*") == -1)
    {
        show_info("Der Befehl an die Kamera war nicht erfolgreich. Befindet sich die Kamera im Run oder Test Modus? \nTicketnummer + Fehler: " + CamMsg);
        // Schliesse Socket
        socket.close();
        // Beende Programm
        exitRobotScript();
    }
}

Bei einem Misserfolg wird in diesem Beispiel der socket geschlossen und das Programm beendet. Dies sollte an die jeweilige Anwendung angepasst werden.

Neben diesen beiden Antworten antwortet die Kamera, je nach gesendetem Befehl, zus�tzlich noch mit weiteren Antworten. Beispielweise wird von der Kamera bei dem Triggerbefehl noch die oben definierte Zeichenkette (beinhaltet Objektkoordinaten) geschickt. Auch diese Antwort setzt sich aus 2 Teilantworten zusammen. Die erste beinhaltet wieder die L�nge der folgenden eigentlichen Kameraantwort. Die zweite beinhaltet die oben definierte Zeichenkette. Mit folgenden Codezeilen k�nnen die Antworten in horstFX ausgelesen werden:

// Lese Kameradaten aus. Notwendig sobald ein Befehl gesendet wird, der eine Antwort von der Kamera binhaltet
function getCameraData()
{
    // Laenge der Kameraantwort in Bytes
    var CamMsg = readMessage(socket,Delimiter);
    show_info("Laenge der Kameraantwort: " + CamMsg);
 
    // Eigentliche Kameraantwort: Abhaengig von der Schnittstellenkonfiguration im ifm Vision Assistant
    var camResult = readMessage(socket,Delimiter);
    show_info(camResult);
 
    return camResult;  
}

Die eigentliche Kameraantwort, in diesem Beispiel mit den Objektkoordinaten, wird in der Variablen camResult gespeichert und von der Funktion zur�ckgegeben.

5.  Verarbeitung der Daten

Folgend wird beschrieben, wie die per TCP/IP erhaltenen Kameradaten auf das passende Format gebracht werden und wie anschlie�end die Objektposition angefahren werden kann.

5.1.  Erhalten der richtigen Objektkoordinaten

Um die eingelesenen Kameradaten verarbeiten zu k�nnen, m�ssen sie noch in das f�r horstFX passende Format gebracht werden. Das geschieht mittels folgender Funktion.

// Erhalte Objektkoordinaten
function getObjectCoordinates()
{
    //Ueberpuefe gesendete Nachricht
    checkSentMessage();
    // Erhalte Kameradaten
    var CamReport = getCameraData();
 
    // Aufsplitten der Kameranachricht
    // Abhaengig von der Schnittstellenkonfiguration im ifm Vision Assistant.
    // Bei dieser Konfiguration: start;AnzahlKonturuebereinstimmung;X;Y;Z;Rz
    var SplittedData = CamReport.split(";");
    var Result = parseFloat(SplittedData[1]);       // Result = 1 bei Treffer, Result = 0 bei keiner Uebereinstimmung
     
    if (Result == 0)
    {
        // Kein Objekt erkannt
        showHint("Kein Objekt erkannt");
        return {Result: Result};
    }  
 
    else
    {
        var ObjectX = parseFloat(SplittedData[2])/1000; // Objektkoordinate X
        var ObjectY = parseFloat(SplittedData[3])/1000; // Objektkoordinate Y
        var ObjectZ = parseFloat(SplittedData[4])/1000; // Objektkoordinate Z
        var ObjectRz = parseFloat(SplittedData[5]); // Orientierung Rz
         
        //show_info("Cam X: " + ObjectX + "\nCam Y: " + ObjectY + "\nCam Z: " + ObjectZ + "\nCam Rz : " + ObjectRz);
 
        if (UseFixedObjectHeight == 1)
        {
            ObjectZ = ObjectHeight;
        }
 
        // Falls fest vorgegebene Rz Orientierung genutzt werden soll. Z.b. bei runden Objekten
        if (UseFixedRZCoord == 1)
        {
            ObjectRz = FixedRZCoord;
        }
 
        // Bildung des Normalenvektors (notwendig wenn Orientierung in RZ oder RX vorgegeben wird)
        var ObjectRx = FixedRXCoord;
        var ObjectRy = FixedRYCoord;
        Normal = getSurfaceNormalByEuler(ObjectRx, ObjectRy, ObjectRz);
 
        return {Result: Result, ObjectX: ObjectX, ObjectY: ObjectY, ObjectZ: ObjectZ, ObjectRx: ObjectRx, ObjectRy: ObjectRy, ObjectRz: ObjectRz}
    }
}

Zuerst werden mittels oben beschriebener Funktionen die gesendete Kameranachricht auf Erfolg �berpr�ft sowie die Kameradaten eingelesen. Anschlie�end wird �berpr�ft, ob ein Objekt von der Kamera erkannt wird. Ist dies der Fall, werden die Objektkoordinaten in X,Y,Z sowie die Orientierung um RZ auf das passende Format gewandelt. Falls die Objekte einen Winkel in RX bzw. RY aufweisen, wird der Normalenvektor gebildet. Dadurch kann der St�tzpunkt, welcher sich �ber dem Objektpunkt befindet, mit der selben Orientierung angefahren werden. Die Linearbewegung zu der endg�ltigen Greifposition findet somit schon in der passenden Orientierung statt.

5.2.  Anfahren der Objektpositionen

Folgender Code beschreibt, wie die Objektposition �ber einen St�tzpunkt angefahren werden kann.

// Fahre Stuetzpunkt an
            move({
                    'Coord': 'CARTESIAN_BASIS',
                    'MoveType': 'JOINT',
                    'PoseRelation': 'ABSOLUTE',
                    'anyconfiguration': false,
                'blendradius.orient': 180.0,
                    'blendradius.xyz': 0.02,
                    'speed.ratio': 1.0,
                    'target': {'xyz+euler': [CamData.ObjectX - (moveFaktor*Normal.getX()), CamData.ObjectY - (moveFaktor*Normal.getY()), CamData.ObjectZ - (moveFaktor*Normal.getZ()), CamData.ObjectRx, CamData.ObjectRy, CamData.ObjectRz]},
                    'tool': 'No Tool'
            }, "Stuetzpunkt");
 
            // Fahre Objektposition an
            move({
                    'Coord': 'CARTESIAN_BASIS',
                    'MoveType': 'LINEAR',
                    'PoseRelation': 'ABSOLUTE',
                    'anyconfiguration': false,
                    'speed.ratio': 0.5,
                    'target': {'xyz+euler': [CamData.ObjectX, CamData.ObjectY, CamData.ObjectZ, CamData.ObjectRx, CamData.ObjectRy, CamData.ObjectRz]},
                    'tool': 'No Tool'
            }, "Objektposition");  
             
            // Schliesse Greifer
            closeGripper();
 
            // Fahre Stuetzpunkt an
            move({
                    'Coord': 'CARTESIAN_BASIS',
                    'MoveType': 'JOINT',
                    'PoseRelation': 'ABSOLUTE',
                    'anyconfiguration': false,
                'blendradius.orient': 180.0,
                    'blendradius.xyz': 0.02,
                    'speed.ratio': 1.0,
                    'target': {'xyz+euler': [CamData.ObjectX - (moveFaktor*Normal.getX()), CamData.ObjectY - (moveFaktor*Normal.getY()), CamData.ObjectZ - (moveFaktor*Normal.getZ()), CamData.ObjectRx, CamData.ObjectRy, CamData.ObjectRz]},
                    'tool': 'No Tool'
            }, "Stuetzpunkt");

6. Kompletter Programmablauf

Folgend wird der komplette Programmablauf eines Beispielprogramms dargestellt. Der Roboter greift hierbei in einer Schleife ein von der Kamera detektiertes Objekt und legt es anschlie�end an einer definierten Position ab. Das komplette horstFX Programm kann am Ende des Artikels heruntergeladen werden.

//*************************************** Variablendeklaration ***************************************
 
// Objekterkennung
var UseFixedObjectHeight = 1;           // Falls vorgegebene Objekthoehe -> Wert = 1. Ansonsten wird bei ifm Kalibirerung genutzte Hoehe verwendet
var ObjectHeight = 0.310;               // Hoehe Z-Achse mit welcher Objekt gegriffen wird falls Variable UseFixedObjectHeight = 1. Ansonsten wird bei ifm Kalibirerung genutzte Hoehe verwendet
var UseFixedRZCoord = 0;                // Wert = 1 falls keine RZ Orientierung von Kamera vorgegeben werden soll. Z.b. bei runden Objekten
var FixedRXCoord = 180;                 // Vorgegebene RX-Orientierung in Grad mit welcher Teile gegriffen werden. Standard = 180 -> Flansch parallel zur Robotermontageflaeche
var FixedRYCoord = 0;                   // Vorgegebene RY-Orientierung in Grad mit welcher Teile gegriffen werden. Standard = 0 -> Flansch parallel zur Robotermontageflaeche
var FixedRZCoord = 0;                   // Vorgegebene RZ-Orientierung in Grad mit welcher Teile gegriffen werden. Falls "UseFixedRZCoord" = 1
var moveFaktor = 0.05;                  // Entfernung des Anfahrpunktes zu Pickposition. Wird mittels Faktor von Normalenvektor errechnet
var Normal = null;                      // Normalenvektor der Abgreifposition
var CamData = null;
 
// Applikationsnummer
var ApplicationNumber = "01";           // Applikationsnummer im ifm VissionAssistant: Muss zwingend aus 2 Zahlen bestehen
 
// TCP/IP Socket Communication
var IP = "192.168.2.15";                // IP ifm Sensor
var Port = 50010;                       // Port ifm Sensor
var Terminator = "\r\n";                // Zeilenumbruch Carriage Return + Linefeed beim Schreiben einer Nachricht
var Delimiter = "\r\n".charCodeAt(0);   // Zeilenumbruch Carriage Return + Linefeed beim Einlesen einer Nachricht
 
//********************************************* Programm *********************************************
 
// Aufbau der Verbindung zum ifm Sensor
try
{
    var socket = new java.net.Socket();
    socket.connect(new  java.net.InetSocketAddress(IP, Port), 10000); //10s timeout
    showHint("Connection to the sensor established");
 
    // 10 sek Timeout beim Einlesen der Nachricht ueber einen Socket
    socket.setSoTimeout(10000);
}
catch (e)
{
    show_info("A connection with the ifm sensor could not be established!");
}
 
// Startpunkt
move({
    'Coord': 'JOINT',
    'MoveType': 'JOINT',
    'PoseRelation': 'ABSOLUTE',
    'anyconfiguration': false,
    'blendradius.orient': 180.0,
    'blendradius.xyz': 0.06,
    'speed.ratio': 1.0,
    'target': {'joints': [23.027008, 8.307049, 33.134277, 0.000000, 48.596222, 180]},
    'tool': 'No Tool'
}, "Startpunkt");
 
// Initialisiere Variablen
initVars();
 
// oeffne Greifer
openGripper();
 
try
{
    // Aktiviere Applikation
    activateApplication(ApplicationNumber);
 
    while (true)
    {
 
        // trigger Kamera
        triggerCamera();
 
        // Erhalte Objektkoordinaten
        CamData = getObjectCoordinates();
 
        if (CamData.Result != 0)   
        {
            // Fahre Stuetzpunkt an
            move({
                    'Coord': 'CARTESIAN_BASIS',
                    'MoveType': 'JOINT',
                    'PoseRelation': 'ABSOLUTE',
                    'anyconfiguration': false,
                'blendradius.orient': 180.0,
                    'blendradius.xyz': 0.02,
                    'speed.ratio': 1.0,
                    'target': {'xyz+euler': [CamData.ObjectX - (moveFaktor*Normal.getX()), CamData.ObjectY - (moveFaktor*Normal.getY()), CamData.ObjectZ - (moveFaktor*Normal.getZ()), CamData.ObjectRx, CamData.ObjectRy, CamData.ObjectRz]},
                    'tool': 'No Tool'
            }, "Stuetzpunkt");
 
            // Fahre Objektposition an
            move({
                    'Coord': 'CARTESIAN_BASIS',
                    'MoveType': 'LINEAR',
                    'PoseRelation': 'ABSOLUTE',
                    'anyconfiguration': false,
                    'speed.ratio': 0.5,
                    'target': {'xyz+euler': [CamData.ObjectX, CamData.ObjectY, CamData.ObjectZ, CamData.ObjectRx, CamData.ObjectRy, CamData.ObjectRz]},
                    'tool': 'No Tool'
            }, "Objektposition");  
             
            // Schliesse Greifer
            closeGripper();
 
            // Fahre Stuetzpunkt an
            move({
                    'Coord': 'CARTESIAN_BASIS',
                    'MoveType': 'JOINT',
                    'PoseRelation': 'ABSOLUTE',
                    'anyconfiguration': false,
                'blendradius.orient': 180.0,