4. Post Processors¶
A Post Processor defines how robot programs must be generated for a specific robot controller. A Post Processor is a key component of simulation and offline programming of industrial robots.
- More information regarding post processors is available in the following two links:
Video introduction about post processors: https://www.robodk.com/help#PostProcessor
RoboDK documentation: https://robodk.com/doc/en/Post-Processors.html
RoboDK comes with many Post Processors available, providing support for many robot brands (such as ABB, Fanuc, KUKA, Motoman, UR, …). Post Processors are located in the folder: C:/RoboDK/Posts/
. Each Post Processor is a .py file. You must place the py
Post Processor file in C:/RoboDK/Posts/
to use a Post Processor that has been provided to you. To remove an existing Post Processor you can simply delete the corresponding py
file located in C:/RoboDK/Posts/
.
- For example, for KUKA robots the following Post Processors are available:
KUKA_KRC2: Generates SRC program files compatible with KUKA KRC2 controllers.
KUKA_KRC4: Generates SRC program files compatible with KUKA KRC4 controllers.
KUKA_IIWA: Generates JAVA programs using the KUKA.Sunrise platform, required by KUKA IIWA robots.
Python should be installed and working with RoboDK to properly use Post Processors (How to install).
- Follow these steps in RoboDK to link a robot to a specific Post Processor:
Open the robot panel by double clicking a robot.
Select
Parameters
at the top right.Select
Select Post Processor
.
Alternatively, you can right click a program, then select Select Post Processor
.

- To modify or create a Post Processor:
Select
Program-Add/Edit Post Processor
.Select an existing Post Processor or create a new one. A template like the example provided in this section will be provided if you decide to create a new Post Processor.
You can edit a Post Processor using a text editor or a Python editor.
A Python editor allows you to quickly test the Post Processor given a sample program at the end of the module. You can also execute a Post Processor file to see a sample of the output that it will generate (double click the py
file, or right click the py
file, then select Open
).
- It is also possible to manually edit a Post Processor without using RoboDK:
Go to the folder
C:/RoboDK/Posts/
and open the correspondingpy
file with a text editor such as Notepad or Python IDLE (right click thepy
file, then, selectEdit with IDLE
)Make any required modifications.
To get a preview of the result: Run the Post Processor by double clicking the
py
file (or right click, then, selectOpen
, as shown in the following image).
Alternatively, you can run and preview the result sing a Python editor such as Python IDLE.

4.1. Post Processor Methods¶
This section shows the procedures that every Post Processor should define to properly generate programs from RoboDK simulations. This step is transparent to the end user of RoboDK because the program is automatically generated once a Post Processor has been defined. You do not need to understand what is going on behind the scenes unless you are willing to create or modify a Post Processor.
Once a robot program is ready to be generated RoboDK creates a generic Python file (pre processed) that is linked to a Post Processor. The pre-processed file is executed using the selected Post Processor and the desired program is obtained.
- class
samplepost.
RobotPost
(robotpost=None, robotname=None, robot_axes=6, **kwargs)¶ Robot Post Processor object
MoveC
(pose1, joints1, pose2, joints2, conf_RLF_1=None, conf_RLF_2=None)¶Defines a circular movement.
Tip: MoveC is triggered by the RoboDK instruction Program->Move Circular Instruction.
- Parameters
pose1 (
robodk.Mat()
) – pose target of a point defining an arc (waypoint)pose2 (
robodk.Mat()
) – pose target of the end of the arc (final point)joints1 (float list) – robot joints of the waypoint
joints2 – robot joints of the final point
conf_RLF_1 – robot configuration of the waypoint
conf_RLF_2 – robot configuration of the final point
MoveJ
(pose, joints, conf_RLF=None)¶Defines a joint movement.
Tip: MoveJ is triggered by the RoboDK instruction Program->Move Joint Instruction.
- Parameters
pose (
robodk.Mat()
) – pose target of the tool with respect to the reference frame. Pose can be None if the target is defined as a joint target.joints (float list) – robot joints as a list
conf_RLF (int list) – robot configuration as a list of 3 ints: [REAR, LOWER-ARM, FLIP]. [0,0,0] means [front, upper arm and non-flip] configuration.
MoveL
(pose, joints, conf_RLF=None)¶Defines a linear movement.
Tip: MoveL is triggered by the RoboDK instruction Program->Move Linear Instruction.
- Parameters
pose (
robodk.Mat()
) – pose target of the tool with respect to the reference frame. Pose can be None if the target is defined as a joint target.joints (float list) – robot joints as a list
conf_RLF (int list) – robot configuration as a list of 3 ints: [REAR, LOWER-ARM, FLIP]. [0,0,0] means [front, upper arm and non-flip] configuration.
Pause
(time_ms)¶Defines a pause in a program (including movements). time_ms is negative if the pause must provoke the robot to stop until the user desires to continue the program.
Tip: Pause is triggered by the RoboDK instruction Program->Pause Instruction.
- Parameters
time_ms (float) – time of the pause, in milliseconds
ProgFinish
(progname)¶This method is executed to define the end of a program or procedure. One module may have more than one program. No other instructions will be executed before another
samplepost.RobotPost.ProgStart()
is executed. Tip: ProgFinish is triggered after all the instructions of the program.- Parameters
progname (str) – name of the program
ProgSave
(folder, progname, ask_user=False, show_result=False)¶Saves the program. This method is executed after all programs have been processed.
Tip: ProgSave is triggered after all the programs and instructions have been executed.
- Parameters
folder (str) – Folder hint to save the program
progname (str) – Program name as a hint to save the program
ask_user (bool, str) – True if the default settings in RoboDK are set to promt the user to select the folder
show_result (bool, str) – False if the default settings in RoboDK are set to not show the program once it has been saved. Otherwise, a string is provided with the path of the preferred text editor.
ProgStart
(progname)¶Start a new program given a name. Multiple programs can be generated at the same times.
Tip: ProgStart is triggered every time a new program must be generated.
- Parameters
progname (str) – name of the program
RunCode
(code, is_function_call=False)¶Adds code or a function call.
Tip: RunCode is triggered by the RoboDK instruction Program->Function call Instruction.
- Parameters
code (str) – code or procedure to call
is_function_call (bool) – True if the provided code is a specific function to call
RunMessage
(message, iscomment=False)¶Display a message in the robot controller screen (teach pendant)
Tip: RunMessage is triggered by the RoboDK instruction Program->Show Message Instruction.
- Parameters
message (str) – Message to display on the teach pendant or as a comment on the code
iscomment (bool) – True if the message does not have to be displayed on the teach pendant but as a comment on the code
addline
(newline)¶Add a new program line. This is a private method used only by the other methods.
- Parameters
newline (str) – new line to add.
addlog
(newline)¶Add a message to the log. This is a private method used only by the other methods. The log is displayed when the program is generated to show any issues when the robot program has been generated.
- Parameters
newline (str) – new line
setAcceleration
(accel_mmss)¶Changes the robot acceleration (in mm/s2)
Tip: setAcceleration is triggered by the RoboDK instruction Program->Set Speed Instruction. An acceleration value must be provided.
- Parameters
accel_mmss (float) – speed in
setAccelerationJoints
(accel_degss)¶Changes the robot joint acceleration (in deg/s2)
Tip: setAccelerationJoints is triggered by the RoboDK instruction Program->Set Speed Instruction. A joint acceleration value must be provided.
- Parameters
accel_degss (float) – speed in
setDO
(io_var, io_value)¶Sets a variable io_var (usually a digital output) to a given value. This method can also be used to set other variables.
Tip: setDO is triggered by the RoboDK instruction Program->Set or Wait I/O Instruction.
- Parameters
io_var (int, str) – variable to set, provided as a str or int
io_value (int, float, str) – value of the variable, provided as a str, float or int
setFrame
(pose, frame_id=None, frame_name=None)¶Defines a new reference frame with respect to the robot base frame. This reference frame is used for following pose targets used by movement instructions.
Tip: setFrame is triggered by the RoboDK instruction Program->Set Reference Frame Instruction.
- Parameters
pose (
robodk.Mat()
) – pose of the reference frame with respect to the robot base frameframe_id (int, None) – number of the reference frame (if available)
frame_name (str) – Name of the reference frame as defined in RoboDK
setSpeed
(speed_mms)¶Changes the robot speed (in mm/s)
Tip: setSpeed is triggered by the RoboDK instruction Program->Set Speed Instruction.
- Parameters
speed_mms (float) – speed in
setSpeedJoints
(speed_degs)¶Changes the robot joint speed (in deg/s)
Tip: setSpeedJoints is triggered by the RoboDK instruction Program->Set Speed Instruction. A joint speed value must be provided.
- Parameters
speed_degs (float) – speed in
setTool
(pose, tool_id=None, tool_name=None)¶Change the robot TCP (Tool Center Point) with respect to the robot flange. Any movement defined in Cartesian coordinates assumes that it is using the last reference frame and tool frame provided.
Tip: setTool is triggered by the RoboDK instruction Program->Set Tool Frame Instruction.
- Parameters
pose (
robodk.Mat()
) – pose of the TCP frame with respect to the robot base frametool_id (int, None) – number of the reference frame (if available)
tool_name (str) – Name of the reference frame as defined in RoboDK
setZoneData
(zone_mm)¶Changes the smoothing radius (also known as rounding, blending radius, CNT, APO or zone data). If this parameter is higher it helps making the movement smoother
Tip: setZoneData is triggered by the RoboDK instruction Program->Set Rounding Instruction.
- Parameters
zone_mm (float) – rounding radius in mm
waitDI
(io_var, io_value, timeout_ms=-1)¶Waits for a variable io_var (usually a digital input) to attain a given value io_value. This method can also be used to set other variables. Optionally, a timeout can be provided.
Tip: waitDI is triggered by the RoboDK instruction Program->Set or Wait I/O Instruction.
- Parameters
io_var (int, str) – variable to wait for, provided as a str or int
io_value (int, float, str) – value of the variable, provided as a str, float or int
timeout_ms (float, int) – maximum wait time
samplepost.
joints_2_str
(joints)¶Converts a robot joint target to a string according to the syntax/format of the controller.
- Parameters
pose – 4x4 pose matrix
- Returns
joint format as a J1-Jn string
- Return type
str
samplepost.
pose_2_str
(pose)¶Converts a robot pose target to a string according to the syntax/format of the controller.
- Parameters
pose – 4x4 pose matrix
- Returns
postion as a XYZWPR string
- Return type
str
samplepost.
test_post
()¶Test the post processor with a simple program
4.2. Post Processor Example¶
This section shows a sample post processor. This sample Post Processor is used as a template when a new Post Processor is created using RoboDK.
from robodk import *
# ----------------------------------------------------
def pose_2_str(pose):
"""Prints a pose target"""
[x,y,z,r,p,w] = pose_2_xyzrpw(pose)
return ('X%.3f Y%.3f Z%.3f R%.3f P%.3f W%.3f' % (x,y,z,r,p,w))
def joints_2_str(joints):
"""Prints a joint target"""
str = ''
data = ['A','B','C','D','E','F','G','H','I','J','K','L']
for i in range(len(joints)):
str = str + ('%s%.6f ' % (data[i], joints[i]))
str = str[:-1]
return str
# ----------------------------------------------------
# Object class that handles the robot instructions/syntax
class RobotPost(object):
"""Robot post object"""
PROG_EXT = 'txt' # set the program extension
# other variables
ROBOT_POST = ''
ROBOT_NAME = ''
PROG_FILES = []
PROG = ''
LOG = ''
nAxes = 6
def __init__(self, robotpost=None, robotname=None, robot_axes = 6, **kwargs):
self.ROBOT_POST = robotpost
self.ROBOT_NAME = robotname
self.PROG = ''
self.LOG = ''
self.nAxes = robot_axes
def ProgStart(self, progname):
self.addline('PROC %s()' % progname)
def ProgFinish(self, progname):
self.addline('ENDPROC')
def ProgSave(self, folder, progname, ask_user=False, show_result=False):
progname = progname + '.' + self.PROG_EXT
if ask_user or not DirExists(folder):
filesave = getSaveFile(folder, progname, 'Save program as...')
if filesave is not None:
filesave = filesave.name
else:
return
else:
filesave = folder + '/' + progname
fid = open(filesave, "w")
fid.write(self.PROG)
fid.close()
print('SAVED: %s\n' % filesave)
#---------------------- show result
if show_result:
if type(show_result) is str:
# Open file with provided application
import subprocess
p = subprocess.Popen([show_result, filesave])
else:
# open file with default application
import os
os.startfile(filesave)
if len(self.LOG) > 0:
mbox('Program generation LOG:\n\n' + self.LOG)
def ProgSendRobot(self, robot_ip, remote_path, ftp_user, ftp_pass):
"""Send a program to the robot using the provided parameters. This method is executed right after ProgSave if we selected the option "Send Program to Robot".
The connection parameters must be provided in the robot connection menu of RoboDK"""
UploadFTP(self.PROG_FILES, robot_ip, remote_path, ftp_user, ftp_pass)
def MoveJ(self, pose, joints, conf_RLF=None):
"""Add a joint movement"""
self.addline('MOVJ ' + joints_2_str(joints))
def MoveL(self, pose, joints, conf_RLF=None):
"""Add a linear movement"""
self.addline('MOVL ' + pose_2_str(pose))
def MoveC(self, pose1, joints1, pose2, joints2, conf_RLF_1=None, conf_RLF_2=None):
"""Add a circular movement"""
self.addline('MOVC ' + pose_2_str(pose1) + ' ' + pose_2_str(pose2))
def setFrame(self, pose, frame_id=None, frame_name=None):
"""Change the robot reference frame"""
self.addline('BASE_FRAME ' + pose_2_str(pose))
def setTool(self, pose, tool_id=None, tool_name=None):
"""Change the robot TCP"""
self.addline('TOOL_FRAME ' + pose_2_str(pose))
def Pause(self, time_ms):
"""Pause the robot program"""
if time_ms < 0:
self.addline('PAUSE')
else:
self.addline('WAIT %.3f' % (time_ms*0.001))
def setSpeed(self, speed_mms):
"""Changes the robot speed (in mm/s)"""
self.addlog('setSpeed not defined (%.2f mms)' % speed_mms)
def setAcceleration(self, accel_mmss):
"""Changes the robot acceleration (in mm/s2)"""
self.addlog('setAcceleration not defined')
def setSpeedJoints(self, speed_degs):
"""Changes the robot joint speed (in deg/s)"""
self.addlog('setSpeedJoints not defined')
def setAccelerationJoints(self, accel_degss):
"""Changes the robot joint acceleration (in deg/s2)"""
self.addlog('setAccelerationJoints not defined')
def setZoneData(self, zone_mm):
"""Changes the rounding radius (aka CNT, APO or zone data) to make the movement smoother"""
self.addlog('setZoneData not defined (%.1f mm)' % zone_mm)
def setDO(self, io_var, io_value):
"""Sets a variable (digital output) to a given value"""
if type(io_var) != str: # set default variable name if io_var is a number
io_var = 'OUT[%s]' % str(io_var)
if type(io_value) != str: # set default variable value if io_value is a number
if io_value > 0:
io_value = 'TRUE'
else:
io_value = 'FALSE'
# at this point, io_var and io_value must be string values
self.addline('%s=%s' % (io_var, io_value))
def waitDI(self, io_var, io_value, timeout_ms=-1):
"""Waits for a variable (digital input) io_var to attain a given value io_value. Optionally, a timeout can be provided."""
if type(io_var) != str: # set default variable name if io_var is a number
io_var = 'IN[%s]' % str(io_var)
if type(io_value) != str: # set default variable value if io_value is a number
if io_value > 0:
io_value = 'TRUE'
else:
io_value = 'FALSE'
# at this point, io_var and io_value must be string values
if timeout_ms < 0:
self.addline('WAIT FOR %s==%s' % (io_var, io_value))
else:
self.addline('WAIT FOR %s==%s TIMEOUT=%.1f' % (io_var, io_value, timeout_ms))
def RunCode(self, code, is_function_call = False):
"""Adds code or a function call"""
if is_function_call:
code.replace(' ','_')
if not code.endswith(')'):
code = code + '()'
self.addline(code)
else:
self.addline(code)
def RunMessage(self, message, iscomment = False):
"""Display a message in the robot controller screen (teach pendant)"""
if iscomment:
self.addline('% ' + message)
else:
self.addlog('Show message on teach pendant not implemented (%s)' % message)
# ------------------ private ----------------------
def addline(self, newline):
"""Add a program line"""
self.PROG = self.PROG + newline + '\n'
def addlog(self, newline):
"""Add a log message"""
self.LOG = self.LOG + newline + '\n'
# -------------------------------------------------
# ------------ For testing purposes ---------------
def Pose(xyzrpw):
[x,y,z,r,p,w] = xyzrpw
a = r*math.pi/180
b = p*math.pi/180
c = w*math.pi/180
ca = math.cos(a)
sa = math.sin(a)
cb = math.cos(b)
sb = math.sin(b)
cc = math.cos(c)
sc = math.sin(c)
return Mat([[cb*ca, ca*sc*sb - cc*sa, sc*sa + cc*ca*sb, x],[cb*sa, cc*ca + sc*sb*sa, cc*sb*sa - ca*sc, y],[-sb, cb*sc, cc*cb, z],[0,0,0,1]])
def test_post():
"""Test the post with a basic program"""
robot = RobotPost()
robot.ProgStart("Program")
robot.RunMessage("Program generated by RoboDK using a custom post processor", True)
robot.setFrame(Pose([807.766544, -963.699898, 41.478944, 0, 0, 0]))
robot.setTool(Pose([62.5, -108.253175, 100, -60, 90, 0]))
robot.MoveJ(Pose([200, 200, 500, 180, 0, 180]), [-46.18419, -6.77518, -20.54925, 71.38674, 49.58727, -302.54752] )
robot.MoveL(Pose([200, 250, 348.734575, 180, 0, -150]), [-41.62707, -8.89064, -30.01809, 60.62329, 49.66749, -258.98418] )
robot.MoveL(Pose([200, 200, 262.132034, 180, 0, -150]), [-43.73892, -3.91728, -35.77935, 58.57566, 54.11615, -253.81122] )
robot.RunMessage("Setting air valve 1 on")
robot.RunCode("TCP_On", True)
robot.Pause(1000)
robot.MoveL(Pose([200, 250, 348.734575, 180, 0, -150]), [-41.62707, -8.89064, -30.01809, 60.62329, 49.66749, -258.98418] )
robot.MoveL(Pose([250, 300, 278.023897, 180, 0, -150]), [-37.52588, -6.32628, -34.59693, 53.52525, 49.24426, -251.44677] )
robot.MoveL(Pose([250, 250, 191.421356, 180, 0, -150]), [-39.75778, -1.04537, -40.37883, 52.09118, 54.15317, -246.94403] )
robot.RunMessage("Setting air valve off")
robot.RunCode("TCP_Off", True)
robot.Pause(1000)
robot.MoveL(Pose([250, 300, 278.023897, 180, 0, -150]), [-37.52588, -6.32628, -34.59693, 53.52525, 49.24426, -251.44677] )
robot.MoveL(Pose([250, 200, 278.023897, 180, 0, -150]), [-41.85389, -1.95619, -34.89154, 57.43912, 52.34162, -253.73403] )
robot.MoveL(Pose([250, 150, 191.421356, 180, 0, -150]), [-43.82111, 3.29703, -40.29493, 56.02402, 56.61169, -249.23532] )
robot.MoveJ(None, [-46.18419, -6.77518, -20.54925, 71.38674, 49.58727, -302.54752] )
robot.ProgFinish("Program")
# robot.ProgSave(".","Program",True)
print(robot.PROG)
if len(robot.LOG) > 0:
mbox('Program generation LOG:\n\n' + robot.LOG)
input("Press Enter to close...")
if __name__ == "__main__":
"""Function to call when the module is executed by itself: test"""
test_post()
4.3. Post Processor Example Output¶
Once a program has been generated using a Post Processor, an output such as the following will be obtained. In this example, the result is meant to be used with an ABB robot using an IRC5 controller.
MODULE MOD_Weld_Hexagon
PERS wobjdata rdkWObj := [FALSE, TRUE, "", [[0,0,0],[1,0,0,0]],[[0,0,0],[1,0,0,0]];
PERS tooldata rdkTool := [TRUE,[[0,0,0],[1,0,0,0]],[3,[0,0,200],[1,0,0,0],0,0,0.005]];
VAR speeddata rdkSpeed := [500,500,500,500];
VAR extjoint rdkExtax := [9E9,9E9,9E9,9E9,9E9,9E9];
PROC Weld_Hexagon()
!Program generated by RoboDK for ABB IRB 1600ID-4/1.5 on 29/11/2014 17:42:31
ConfJ \On;
ConfL \On;
rdkWObj.oframe:=[0,0,0],[1,0,0,0];
rdkWObj.uframe:=[0,0,0],[1,0,0,0];
rdkTool.tframe:=[-4,0,371.3],[0.92387953,0,0.38268343,0];
MoveAbsJ [[-0,-19.143793,-7.978668,0,49.189506,-0]],rdkExtax], rdkSpeed, rdkZone, rdkTool, \WObj:=rdkWObj;
MoveJ [[1010.634,-114.491,662.29],[0,0,1,0],[-1,0,-1,0],rdkExtax], rdkSpeed, rdkZone, rdkTool, \WObj:=rdkWObj;
WeldStart;
MoveL [[810.634,-114.491,662.29],[0,0,1,0],[-1,0,-1,0],rdkExtax], rdkSpeed, rdkZone, rdkTool, \WObj:=rdkWObj;
MoveL [[910.634,58.715,662.29],[0,0,1,0],[0,-1,0,0],rdkExtax], rdkSpeed, rdkZone, rdkTool, \WObj:=rdkWObj;
MoveL [[1110.634,58.715,662.29],[0,0,1,0],[0,-1,0,0],rdkExtax], rdkSpeed, rdkZone, rdkTool, \WObj:=rdkWObj;
MoveL [[1210.634,-114.491,662.29],[0,0,1,0],[-1,0,-1,0],rdkExtax], rdkSpeed, rdkZone, rdkTool, \WObj:=rdkWObj;
MoveL [[1110.634,-287.696,662.29],[0,0,1,0],[-1,0,-1,0],rdkExtax], rdkSpeed, rdkZone, rdkTool, \WObj:=rdkWObj;
MoveL [[910.634,-287.696,662.29],[0,0,1,0],[-1,0,-1,0],rdkExtax], rdkSpeed, rdkZone, rdkTool, \WObj:=rdkWObj;
MoveL [[810.634,-114.491,662.29],[0,0,1,0],[-1,0,-1,0],rdkExtax], rdkSpeed, rdkZone, rdkTool, \WObj:=rdkWObj;
WeldStop;
MoveL [[1010.634,-114.491,662.29],[0,0,1,0],[-1,0,-1,0],rdkExtax], rdkSpeed, rdkZone, rdkTool, \WObj:=rdkWObj;
MoveAbsJ [[-0,-19.143793,-7.978668,0,49.189506,-0]],rdkExtax], rdkSpeed, rdkZone, rdkTool, \WObj:=rdkWObj;
ConfJ \On;
ConfL \On;
ENDPROC
ENDMODULE