Optonic Mikado Find

Mikado Find ist ein Gesamtpaket aus Kamera und Software zur Erkennung von 3D-Objekten.

1. Einleitung

Die Punktwolke einer beliebigen Ensenso- 3D-Kamera wird nach dem CAD-Modell durchsucht. �ber die integrierte OPC-UA Schnittstelle werden Position, Orientierung und Matchingscore der gefundenen Teile bereitgestellt. Das intuitive Kalibrierverfahren macht das Einrichten an der Anlage schnell und einfach m�glich

2. Versionen

Dieser Artikel gilt f�r die Mikado Find Version > 1.1

Dieser Artikel gilt f�r eine horstFX Version > 2021.07

Dieser Artikel gilt f�r eine Python Version > 3.7

3. Einrichten des Systems

Videos sowie Dokumentationen zum Einrichten des Systems finden sich unter https://www.optonic.com/produkte/mikado/find/

4. Kommunikation

Mikado Find kommuniziert mittels OPC-UA Schnittstelle. Dazu wird ein Python OPC-UA Client genutzt. Die Installation sowie Funktionsweise ist in folgender Dokumentation n�her beschrieben OPC UA. Zum Testen der Kommunikation empfiehlt es sich, das OPC-UA GUI herunterzuladen https://github.com/FreeOpcUa/opcua-client-gui.

Das Python Skript kann direkt auf dem Schaltschrank ausgef�hrt werden. Wie es �ber horstFX automatisch aufgerufen wird, ist in Ausf�hren externer Python Skripte beschrieben.

5.  Beispielprogramm

Folgendes Programm dient als komplettes Programmierbeispiel. Das Python Skript zur Kommunikation mittels OPC UA wird direkt aus dem horstFX Programm aufgerufen. Die relevanten Befehle zur Verbindung mit der Kamera, zum Laden der Kamerakonfiguration sowie zum Senden des Triggerbefehls werden von horstFX mittels TCP IP Socket an das Python Skript geschickt. Anschlie�end sendet dieses mittels OPC UA die Informationen weiter an die Kamera. Die Objektkoordinaten der gefundenen Objekte werden anschlie�end von der Kamera an das Python Skript �bermittelt. Dieses sendet dieses Daten wieder mittels TCP IP an horstFX. 

In horstFX findet anschlie�end eine Arbeitsraumkontrolle statt. D.h. es wird �berpr�ft, ob der Roboter beim Anfahren eines Greifpunktes sich innerhalb eines gew�nschten Bereichs befindet und nicht z.B. mit der Zellwand kollidiert. Ist der Arbeitsraum g�ltig, wird das Objekt gegriffen und in einer Palette abgelegt. Sobald die Palette voll ist, werden f�r ein Endlosablauf die Objekte aus der Palette per Zufallsposition zur�ck in die Box geworfen.

5.1. Python Skript

Das Python Skript wurde so aufgebaut, dass es in der Regel auch f�r andere Programmabl�ufe nicht modifiziert werden muss. Es muss lediglich auf die Synchronisation zwischen horstFX und diesem Skript geachtet werden. 

#!/usr/bin/python3

import sys
import time
import socket
sys.path.insert(0, "..")

from opcua import Client
from opcua import ua
from numpy import uint32

# definition of function to execute commands via OPC UA
def executeCommand(cmd):
    while outBusyBitNode.get_value():                               # wait for busy bit = false
        time.sleep(0.001)

    inCommandNoNode.set_value(cmd, ua.VariantType.Int32)            # set Trigger 

    while not outBusyBitNode.get_value():                           # wait for busy bit = true
        time.sleep(0.001)
    inCommandNoNode.set_value(0, ua.VariantType.Int32)              # reset trigger
                    
    while outBusyBitNode.get_value():                               # wait for busy bit = false
        time.sleep(0.001)

        
if __name__ == "__main__":

    # Connection to Camera via OPC-UA
    client = Client("opc.tcp://192.168.0.50:4840")      # connect using an IP Adress

    # Socket Connection
    HOST = "0.0.0.0"                                    # IP HORST 
    PORT = 3000                                         # Port HORST 

    try:        
        # connect to camera via OPC-UA
        print("connect to camera")
        client.connect()

        # connect to horstFX va Socket
        s = socket.socket()                             # Erstelle Socket Objekt
        s.bind((HOST, PORT))                            # Binde Port
        print("socket listen for client")
        s.listen(5)                                     # Warte auf client connection
        c, addr = s.accept()                            # Stelle Verbidnung mit client her
        print("socket accepted by client")
         
        doStop = False

        # get Objects node, this is where we should put our nodes
        root = client.get_root_node()

        # populating address space
        outBusyBitNode = root.get_child(["0:Objects", "2:Outputs", "2:BusyBit"])
        outResultNoNode = root.get_child(["0:Objects", "2:Outputs", "2:ResultNo"])
        outResultsNode = root.get_child(["0:Objects", "2:Outputs", "2:Results.50"])
        outErrorNoNode = root.get_child(["0:Objects", "2:Outputs", "2:ErrorNo"])

        inCameraNameNode = root.get_child(["0:Objects", "2:Inputs", "2:CameraName"])
        inRotationModeNode = root.get_child(["0:Objects", "2:Inputs", "2:RotationMode"])
        inCommandNoNode = root.get_child(["0:Objects", "2:Inputs", "2:CommandNo"])
        inConfigNameNode = root.get_child(["0:Objects", "2:Inputs", "2:ConfigName"])
        inSTLNameNode = root.get_child(["0:Objects", "2:Inputs", "2:STLName"])


        # open camera
        print("open camera")
        cameraName = "204929"                           # name of the camera
        inCameraNameNode.set_value(cameraName)          # set camaera name 
        executeCommand(6)                               # call execute function to execute the command

        if outErrorNoNode.get_value() == 0:
            print("connected to camera")
            c.sendall("Verbindung zur Kamera erfolgreich".encode('utf-8'))

        else :
            print("connection to camera could not be established")
            c.sendall("Es konnte keine Verindung zur Kamera hergestellt werden".encode('utf-8'))
                        
        # Wann ist Kamera vollst�ndig ge�ffnet? Gibt es ein Kammando dazu von der Kamera?
        #time.sleep(8)
        
        # load camera configuration
        configName = c.recv(1024).decode()              # Lese HORST Nachricht �ber Socket
        print("load configuration:", configName);
        inConfigNameNode.set_value(configName)          # set config file name
        executeCommand(1)                               # call execute function to execute the command

        if outErrorNoNode.get_value() == 0:
            print("configuration has been loaded")
            c.sendall("Konfiguration erfolgreich geladen".encode('utf-8'))

        else :
            print("configuration could not be loaded")
            c.sendall("Fehler beim Laden der Konfiguration".encode('utf-8'))

        #time.sleep(5)
        
        inRotationModeNode.set_value("sxyz")            # set Rotation Mode
        
        while (not doStop):

            try:
                msg = c.recv(1024)                      # Lese HORST Nachricht �ber Socket

#************************************************************************************************trigger camera*******************************************************************************************
                
                if msg == b'Trigger':                   # check message of HORST

                    executeCommand(4)                   # call execute function to execute the command


#******************************************************************************'***************get camera results****************************************************************************************'
                        
                    # print("Error Number: ", outErrorNoNode.get_value())
                    
                    if outErrorNoNode.get_value() == 0:                                         # check camera for errors

                        print("Result Number: ", outResultNoNode.get_value())               

                        if outResultNoNode.get_value() != 0:                                    # check if camera has detected objects
                      
                            #cam_values_str = str(outResultsNode.get_value())
                            
                            cam_values = outResultsNode.get_value()
                            
                            # sort the detected objects regarding the object score
                            cam_values.sort(key=lambda x: float(x[0]), reverse=True)
                            print("Camera Object Values:\n", cam_values)

                            cam_values_str = str(cam_values)
                            c.sendall(cam_values_str.encode('utf-8'))                           # send complete camera value message
                            
                            #c.sendall(str(outResultsNode.get_value()[0][0]).encode('utf-8'))   # send just one camera value

                        else:
                            c.sendall("Kein Objekt gefunden".encode('utf-8'))
                    else :
                        c.sendall("Fehler".encode('utf-8'))

                        if outErrorNoNode.get_value() == 1:
                            ErrorMsg = "Unbekannter Systemfehler"

                        elif outErrorNoNode.get_value() == 2:
                            ErrorMsg = "Kamera kann nicht geoeffnet werden"
                            
                        elif outErrorNoNode.get_value() == 3:
                            ErrorMsg = "Keine Stereokamera angeschlossen"

                        elif outErrorNoNode.get_value() == 4:
                            ErrorMsg = "Kamera-Fehler"

                        elif outErrorNoNode.get_value() == 5:
                            ErrorMsg = "Suche fehlgeschlagen"

                        elif outErrorNoNode.get_value() == 6:
                            ErrorMsg = "Suche hat keine zuegehoeriges Modell"
                            
                        elif outErrorNoNode.get_value() == 7:
                            ErrorMsg = "Modell generieren fehlgeschlagen"

                        elif outErrorNoNode.get_value() == 8:
                            ErrorMsg = "3D-Referenzpunkt setzen fehlgeschlagen"

                        elif outErrorNoNode.get_value() == 9:
                            ErrorMsg = "Konfiguration konnte nicht geladen werden"

                        elif outErrorNoNode.get_value() == 10:
                            ErrorMsg = "Konfiguration konnte nicht gespeichert werden"
                            
                        elif outErrorNoNode.get_value() == 11:
                            ErrorMsg = "Befehl nicht implementiert"

                        elif outErrorNoNode.get_value() == 12:
                            ErrorMsg = "Falscher Index zum Abrufen der Ergebnisse"

                        elif outErrorNoNode.get_value() == 13:
                            ErrorMsg = "STL-Datei nicht gefunden"

                        c.sendall(ErrorMsg.encode('utf-8'))
                          
                else:
                    print("Program interrupted by HORST")
                    doStop = True

            except KeyboardInterrupt:
                print("Unterbrochen!")
                doStop = True
        
    finally:
        client.disconnect()
        c.close()

5.2. horstFX Skript

Aus dem horstFX Beispiel k�nnen f�r unterschiedliche Anwendungen einige der Funktionen direkt genutzt werden. In einigen F�llen reicht es aus, lediglich die Wegpunkte anzupassen.

6. Downloads

horstFX Programm Mikado Find

OPC UA Client Mikad Find