Contents

UDP Server for KUKA RSI - Python Server with GUI for Axis Jogging from Master Computer | ROS Connectivity to KUKA Robot via Robot Sensor Interface

   Feb 2, 2023     21 min read

KUKA Robot controller has inbuilt Ethernet port called KLI (KUKA LINE INTERFACE, Static IP) which is used for UDP/TCP IP connections from external system.

KUKA RSI Server

Real Time Axis/Cartesian Position corrections (Delta) can be achieved via KUKA Optional Technology Package called RSI.(Robot Sensor Interface)

Now, Here RSI installed on Controller will behave as UDP Client and External Computer/System should be Server.

Step 1: Use RSIVisual (Graphical XML/RSI Editor) supplied by KUKA to generate .XML and .RSI files.

Step 2: Copy all these (.RSI, .Diagram, .XML, Config.XML) files inside KUKA Controller under path: C:/KRC/Roboter/Config/User/Common/SensorInterface

Step 3: Start ServerApp.py on External Computer. (ServerApp.py Attached)

kuka rsi gui

Step 4: Run RSI_Ethernet.src file on KUKA Controller. (This src is supplied by KUKA RSI — Example Folder)

Step 5: Post Connection is established with Controller, Run GUI.py to Control Axis 1 to Axis 6 in Real Time.

Enjoy!

Thanks.

   Project Source Code - https://github.com/pawankumardev/kukarsiserver

Source 1: Example Code to demonstrate working principle of KUKA RSI IPOC Reflection using PY:

  # if the customer is creating his own server with someother software then python then provide him the Port no of the Cotroller which will 
# be coming on "socket_of_krc" line no 38 


import socket

UDP_IP = "192.168.1.100" # ip address of your computer

UDP_PORT = 59152 #port enabled in KRC NAT port configuration
    
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # Create a UDP socket
sock.bind((UDP_IP, UDP_PORT))
xml_file = open("rsi.xml", "r") #the rsi.xml files contains the data that is to be send 
default_command = xml_file.read()

# The function below is used to reflect the IPOC value received from the controller data. if the IPOC does not reply for the given timeout set in Ethernet 
#block of the kuka RSIX file the controller shows an error of RSI bad Timeout. 

def mirroripoc(received_data, data_to_send):

    ipoc_begin_index = int (received_data.index("<IPOC>"))
    ipoc_end_index = int(received_data.index("</IPOC>"))
    received_ipoc = int (received_data[ipoc_begin_index + 6: ipoc_end_index])

    old_ipoc_begin_index = data_to_send.index("<IPOC>")
    old_ipoc_end_index = data_to_send.index("</IPOC>")
    old_ipoc = data_to_send[old_ipoc_begin_index + 6: old_ipoc_end_index]

    oldipocrsi =  f"<IPOC>{old_ipoc}</IPOC>" 
   
    newipocrsi = f"<IPOC>{received_ipoc}</IPOC>"

   
    return data_to_send.replace(oldipocrsi,newipocrsi)
   


while True:

    received_data, socket_of_krc = sock.recvfrom(1024)  # buffer size is 1024 bytes

    received_data =  received_data.decode('utf-8')

    data_to_send = default_command

    data_to_send = mirroripoc(received_data, data_to_send)


    bytesToSend = str.encode(data_to_send)
    sock.sendto(bytesToSend, socket_of_krc)

    xml_file.close()

Source 2: Server with GUI for RSI Jogging of KUKA Robot:

ServerApp.py : Connects to KUKA Controller. Read Write ActualPos.xml

import socket
import os
import time;
from bs4 import BeautifulSoup
import xml.etree.cElementTree as ET
import sys

localIP     = "192.168.100.111" ## Address of your Computer -- Same must be entered in RSI Config File

localPort   = 9508  ## Port on your computer

bufferSize  = 1024 ## No need to change

 


 

# Create a datagram socket

UDPServerSocket = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)

 

# Bind to address and ip

UDPServerSocket.bind((localIP, localPort))

 

print("UDP server up and listening for KUKA RSI !  - Set your Server Computer IP to 192.168.100.111 , Port : 9508 ")

 

# Listen for incoming datagrams

while(True):
    
    

    bytesAddressPair = UDPServerSocket.recvfrom(bufferSize)

    message = bytesAddressPair[0]

    address = bytesAddressPair[1]

    clientMsg = format(message)
    
    clientIP  = "Client IP Address:{}".format(address)
    

    #print(clientMsg)
    
    
    receivedData = BeautifulSoup(message, 'html.parser')
    
    #print(receivedData)
    
    axis1_act = receivedData.a1.get_text()
    axis2_act = receivedData.a2.get_text()
    axis3_act = receivedData.a3.get_text()
    axis4_act = receivedData.a4.get_text()
    axis5_act = receivedData.a5.get_text()
    axis6_act = receivedData.a6.get_text()
    

    ## Write to XML Actual Position
    root = ET.Element("root")
   

    ET.SubElement(root, "Axis1").text = axis1_act
    ET.SubElement(root, "Axis2").text = axis2_act 
    ET.SubElement(root, "Axis3").text = axis3_act
    ET.SubElement(root, "Axis4").text = axis4_act
    ET.SubElement(root, "Axis5").text = axis5_act
    ET.SubElement(root, "Axis6").text = axis6_act
    
    tree = ET.ElementTree(root)
    tree.write("actualpos.xml")
    
 
##Corrections Read from XML axiscorr and apply the same
   
    
    
    
    tree1 = ET.parse('axiscorr.xml')
             
    axis1_corr = tree1.find('Axis1').text
    axis2_corr = tree1.find('Axis2').text
    axis3_corr = tree1.find('Axis3').text
    axis4_corr = tree1.find('Axis4').text
    axis5_corr = tree1.find('Axis5').text
    axis6_corr = tree1.find('Axis6').text
    direction = tree1.find('Direction').text

   

    
    
    if(direction=="0"):
                    axis1_corr = "-"+axis1_corr
                    axis2_corr = "-"+axis2_corr
                    axis3_corr = "-"+axis3_corr
                    axis4_corr = "-"+axis4_corr
                    axis5_corr = "-"+axis5_corr
                    axis6_corr = "-"+axis6_corr
                
                 
    
    
    
    ## Correction
    
    
    
    
    
    ##Sending XML to RSI Client
    
    reflectIPOC = '<Sen Type="ImFree"><EStr>Message from RSI Control Server - PPG !</EStr><AxisCorr A1="'+axis1_corr+'" A2="'+axis2_corr+'" A3="'+axis3_corr+'" A4="'+axis4_corr+'" A5="'+axis5_corr+'" A6="'+axis6_corr+'"><RKorr X="0.00001" Y="0.0000" Z="0.0000" A="0.0000" B="0.0000" C="0.0000" /><IPOC>'+receivedData.ipoc.getText()+'</IPOC></Sen>\n'
    print("Commandin with <IPOC>:"+receivedData.ipoc.getText()+", Axis Actual Pos: " + (axis1_act+","+axis2_act+","+axis3_act+","+axis4_act+","+axis5_act+","+axis6_act))
   
    bytesToSend = str.encode(reflectIPOC)
    UDPServerSocket.sendto(bytesToSend, address)

GUI.py - Writes Corrections to AxisCorr.xml


from tkinter import *
import xml.etree.cElementTree as ET



window = Tk()

window.title("PawankumarG.com/KUKARSIServer")

window.geometry('600x600')

delta = "0.01" # Jogging speed


axis1 = Label(window, text="Axis 1")
axis2 = Label(window, text="Axis 2")
axis3 = Label(window, text="Axis 3")
axis4 = Label(window, text="Axis 4")
axis5 = Label(window, text="Axis 5")
axis6 = Label(window, text="Axis 6")


axis1.grid(column=0, row=0)
axis2.grid(column=0, row=1)
axis3.grid(column=0, row=2)
axis4.grid(column=0, row=3)
axis5.grid(column=0, row=4)
axis6.grid(column=0, row=5)



def clicked1p():
    
    print("axis 1 plus")
    delta = txtfld.get()
    root = ET.Element("root")
    ET.SubElement(root, "Axis1").text = delta
    ET.SubElement(root, "Axis2").text = "0.0"
    ET.SubElement(root, "Axis3").text = "0.0"
    ET.SubElement(root, "Axis4").text = "0.0"
    ET.SubElement(root, "Axis5").text = "0.0"
    ET.SubElement(root, "Axis6").text = "0.0"
    ET.SubElement(root, "Direction").text = "1"
    tree = ET.ElementTree(root)
    tree.write("axiscorr.xml")
    
def clicked2p():

    print("axis 2 plus")
    delta =  txtfld.get()
    root = ET.Element("root")
    ET.SubElement(root, "Axis1").text = "0.0"
    ET.SubElement(root, "Axis2").text = delta
    ET.SubElement(root, "Axis3").text = "0.0"
    ET.SubElement(root, "Axis4").text = "0.0"
    ET.SubElement(root, "Axis5").text = "0.0"
    ET.SubElement(root, "Axis6").text = "0.0"
    ET.SubElement(root, "Direction").text = "1"
    tree = ET.ElementTree(root)
    tree.write("axiscorr.xml")
def clicked3p():

    print("axis 3 plus")
    delta =  txtfld.get()
    root = ET.Element("root")
    ET.SubElement(root, "Axis1").text = "0.0"
    ET.SubElement(root, "Axis2").text = "0.0"
    ET.SubElement(root, "Axis3").text = delta
    ET.SubElement(root, "Axis4").text = "0.0"
    ET.SubElement(root, "Axis5").text = "0.0"
    ET.SubElement(root, "Axis6").text = "0.0"
    ET.SubElement(root, "Direction").text = "1"
    tree = ET.ElementTree(root)
    tree.write("axiscorr.xml")
    
def clicked4p():

    print("axis 4 plus")
    delta =  txtfld.get()
    root = ET.Element("root")
    ET.SubElement(root, "Axis1").text = "0.0"
    ET.SubElement(root, "Axis2").text = "0.0"
    ET.SubElement(root, "Axis3").text = "0.0"
    ET.SubElement(root, "Axis4").text = delta
    ET.SubElement(root, "Axis5").text = "0.0"
    ET.SubElement(root, "Axis6").text = "0.0"
    ET.SubElement(root, "Direction").text = "1"
    tree = ET.ElementTree(root)
    tree.write("axiscorr.xml")    
    
def clicked5p():

    print("axis 5 plus")
    delta =  txtfld.get()
    root = ET.Element("root")
    ET.SubElement(root, "Axis1").text = "0.0"
    ET.SubElement(root, "Axis2").text = "0.0"
    ET.SubElement(root, "Axis3").text = "0.0"
    ET.SubElement(root, "Axis4").text = "0.0"
    ET.SubElement(root, "Axis5").text = delta
    ET.SubElement(root, "Axis6").text = "0.0"
    ET.SubElement(root, "Direction").text = "1"
    tree = ET.ElementTree(root)
    tree.write("axiscorr.xml")    
    
def clicked6p():

    print("axis 6 plus")
    delta =  txtfld.get()
    root = ET.Element("root")
    ET.SubElement(root, "Axis1").text = "0.0"
    ET.SubElement(root, "Axis2").text = "0.0"
    ET.SubElement(root, "Axis3").text = "0.0"
    ET.SubElement(root, "Axis4").text = "0.0"
    ET.SubElement(root, "Axis5").text = "0.0"
    ET.SubElement(root, "Axis6").text = delta
    ET.SubElement(root, "Direction").text = "1"
    tree = ET.ElementTree(root)
    tree.write("axiscorr.xml")    
###    
def clicked1n():

    print("axis 1 minus")
    delta =  txtfld.get()
    root = ET.Element("root")
    ET.SubElement(root, "Axis1").text = delta
    ET.SubElement(root, "Axis2").text = "0.0"
    ET.SubElement(root, "Axis3").text = "0.0"
    ET.SubElement(root, "Axis4").text = "0.0"
    ET.SubElement(root, "Axis5").text = "0.0"
    ET.SubElement(root, "Axis6").text = "0.0"
    ET.SubElement(root, "Direction").text = "0"
    tree = ET.ElementTree(root)
    tree.write("axiscorr.xml")
    
def clicked2n():

    print("axis 2 minus")
    delta =  txtfld.get()
    root = ET.Element("root")
    ET.SubElement(root, "Axis1").text = "0.0"
    ET.SubElement(root, "Axis2").text = delta
    ET.SubElement(root, "Axis3").text = "0.0"
    ET.SubElement(root, "Axis4").text = "0.0"
    ET.SubElement(root, "Axis5").text = "0.0"
    ET.SubElement(root, "Axis6").text = "0.0"
    ET.SubElement(root, "Direction").text = "0"
    tree = ET.ElementTree(root)
    tree.write("axiscorr.xml")
def clicked3n():

    print("axis 3 minus")
    delta =  txtfld.get()
    root = ET.Element("root")
    ET.SubElement(root, "Axis1").text = "0.0"
    ET.SubElement(root, "Axis2").text = "0.0"
    ET.SubElement(root, "Axis3").text = delta
    ET.SubElement(root, "Axis4").text = "0.0"
    ET.SubElement(root, "Axis5").text = "0.0"
    ET.SubElement(root, "Axis6").text = "0.0"
    ET.SubElement(root, "Direction").text = "0"
    tree = ET.ElementTree(root)
    tree.write("axiscorr.xml")
    
def clicked4n():

    print("axis 4 minus")
    delta =  txtfld.get()
    root = ET.Element("root")
    ET.SubElement(root, "Axis1").text = "0.0"
    ET.SubElement(root, "Axis2").text = "0.0"
    ET.SubElement(root, "Axis3").text = "0.0"
    ET.SubElement(root, "Axis4").text = delta
    ET.SubElement(root, "Axis5").text = "0.0"
    ET.SubElement(root, "Axis6").text = "0.0"
    ET.SubElement(root, "Direction").text = "0"
    tree = ET.ElementTree(root)
    tree.write("axiscorr.xml")    
    
def clicked5n():

    print("axis 5 minus")
    delta =  txtfld.get()
    root = ET.Element("root")
    ET.SubElement(root, "Axis1").text = "0.0"
    ET.SubElement(root, "Axis2").text = "0.0"
    ET.SubElement(root, "Axis3").text = "0.0"
    ET.SubElement(root, "Axis4").text = "0.0"
    ET.SubElement(root, "Axis5").text = delta
    ET.SubElement(root, "Axis6").text = "0.0"
    ET.SubElement(root, "Direction").text = "0"
    tree = ET.ElementTree(root)
    tree.write("axiscorr.xml")    
    
def clicked6n():

    print("axis 6 minus")
    delta =  txtfld.get()
    root = ET.Element("root")
    ET.SubElement(root, "Axis1").text = "0.0"
    ET.SubElement(root, "Axis2").text = "0.0"
    ET.SubElement(root, "Axis3").text = "0.0"
    ET.SubElement(root, "Axis4").text = "0.0"
    ET.SubElement(root, "Axis5").text = "0.0"
    ET.SubElement(root, "Axis6").text = "0.0"
    ET.SubElement(root, "Direction").text = "0"
    tree = ET.ElementTree(root)
    tree.write("axiscorr.xml")     

def ClickstartRobot():
    print("Stoping Robot")
    root = ET.Element("root")
    ET.SubElement(root, "Axis1").text = "0.0"
    ET.SubElement(root, "Axis2").text = "0.0"
    ET.SubElement(root, "Axis3").text = "0.0"
    ET.SubElement(root, "Axis4").text = "0.0"
    ET.SubElement(root, "Axis5").text = "0.0"
    ET.SubElement(root, "Axis6").text = "0.0"
    ET.SubElement(root, "Direction").text = "0"
    tree = ET.ElementTree(root)
    tree.write("axiscorr.xml") 
    
    
    

    
    
    
    
startRobot = Button(window, text="Stop Robot Movement", command=ClickstartRobot)  
inc = Label(window, text="Stop Robot Movement")         

axis1p = Button(window, text="Positive", command=clicked1p)
axis2p = Button(window, text="Positive", command=clicked2p)
axis3p = Button(window, text="Positive", command=clicked3p)
axis4p = Button(window, text="Positive", command=clicked4p)
axis5p = Button(window, text="Positive", command=clicked5p)
axis6p = Button(window, text="Positive", command=clicked6p)

axis1n = Button(window, text="Negative", command=clicked1n)
axis2n = Button(window, text="Negative", command=clicked2n)
axis3n = Button(window, text="Negative", command=clicked3n)
axis4n = Button(window, text="Negative", command=clicked4n)
axis5n = Button(window, text="Negative", command=clicked5n)
axis6n = Button(window, text="Negative", command=clicked6n)

txtfld=Entry(window, text="This is Entry Widget", bd=5)
txtfld.insert(END, '0.001')
startRobot.grid(column=5, row=3)
txtfld.grid(column=6, row=3)
axis1p.grid(column=1, row=0)
axis2p.grid(column=1, row=1)
axis3p.grid(column=1, row=2)
axis4p.grid(column=1, row=3)
axis5p.grid(column=1, row=4)
axis6p.grid(column=1, row=5)
axis1n.grid(column=2, row=0)
axis2n.grid(column=2, row=1)
axis3n.grid(column=2, row=2)
axis4n.grid(column=2, row=3)
axis5n.grid(column=2, row=4)
axis6n.grid(column=2, row=5)





window.mainloop()

Q&A

RSI (Robot Sensor Interface) is an KUKA Optional package (Add-on on KUKA Software) which has following functions: KUKA Controller is connected to your Computer (ROS is here) with Ethernet LAN Cable. As we know ROS needs to send and receive axis position in Real Time. RSI installation enables Real Time connection between KUKA Controller and Computer with data transfer rate at 4 ms. RSI enable Soft Real Time i.e. 4 ms of data polling from ROS. Without RSI 4 ms data rate is NOT possible, as System Software has 12ms of Interpolation Time. RSI enable UDP data exchange with handshaking, Real Time Cartesian and Joint motion Correction. RSI Visual Shell allows you to create RSI Objects in Graphical Interface which is finally exported to XML files. Refer link to know how RSI interface is going to help for ROS: https://bit.ly/kukarsi

KRL files are programs on KUKA Controller (On SmartHMI), mandatory to Start during initializing robot motion. Once KRL (KUKA Roboter Language) program started on controller then ROS can command robot for motion control. Point no. 2 is mandatory due to safety reasons. (BCO Run, Field Safety). KRL file create Ethernet node on KUKA Side where external ROS node will connect. KUKA RSI-Ethernet is always Client, External ROS will be Server. On running KRL file we are creating client socket on KUKA side and ROS will be Server.