UDP Server for KUKA RSI - Python Server with GUI for Axis Jogging from Master Computer | ROS Connectivity to KUKA Robot via Robot Sensor Interface
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.
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)
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.