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:

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 SelectPostProcessor.

Alternatively, you can right click a program, then select SelectPostProcessor.

To modify or create a Post Processor:

Select Program-Add/EditPostProcessor.

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 corresponding py file with a text editor such as Notepad or Python IDLE (right click the py file, then, select EditwithIDLE)

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, select Open, as shown in the following image).

Alternatively, you can run and preview the result sing a Python editor such as Python IDLE.

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.

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.

# This file is a sample POST PROCESSOR script to generate programs for a generic robot with RoboDK## To edit/test this POST PROCESSOR script file:# Select "Program"->"Add/Edit Post Processor", then select your post or create a new one.# You can edit this file using any text editor or Python editor. Using a Python editor allows to quickly evaluate a sample program at the end of this file.# Python should be automatically installed with RoboDK## You can also edit the POST PROCESSOR manually:# 1- Open the *.py file with Python IDLE (right click -> Edit with IDLE)# 2- Make the necessary changes# 3- Run the file to open Python Shell: Run -> Run module (F5 by default)# 4- The "test_post()" function is called automatically# Alternatively, you can edit this file using a Notepad and run it with Python## To use a POST PROCESSOR file you must place the *.py file in "C:/RoboDK/Posts/"# To select one POST PROCESSOR for your robot in RoboDK you must follow these steps:# 1- Open the robot panel (double click a robot)# 2- Select "Parameters"# 3- Select "Unlock advanced options"# 4- Select your post as the file name in the "Robot brand" box## To delete an existing POST PROCESSOR script, simply delete this file (.py file)# ----------------------------------------------------# Import RoboDK toolsfromrobodkimport*# ----------------------------------------------------defpose_2_str(pose):"""Converts a robot pose target to a string according to the syntax/format of the controller. :param pose: 4x4 pose matrix :return: postion as a XYZWPR string :rtype: str"""[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))defjoints_2_str(joints):"""Converts a robot joint target to a string according to the syntax/format of the controller. :param pose: 4x4 pose matrix :return: joint format as a J1-Jn string :rtype: str"""str=''data=['A1','A2','A3','A4','A5','A6','E1','E2','E3','E4','E5','E6']foriinrange(len(joints)):str=str+('%s%.6f '%(data[i],joints[i]))str=str[:-1]returnstr# ---------------------------------------------------- # Object class that handles the robot instructions/syntaxclassRobotPost(object):"""Robot Post Processor object"""PROG_EXT='txt'# set the program extension# other variablesROBOT_POST=''ROBOT_NAME=''PROG=''LOG=''nAxes=6def__init__(self,robotpost=None,robotname=None,robot_axes=6,**kwargs):"""Create a new post processor. :param robotpost: name of the post processor :param robotname: name of the robot :param robot_axes: number of axes of the robot :type robotpost: str :type robotname: str :type robot_axes: int """self.ROBOT_POST=robotpostself.ROBOT_NAME=robotnameself.PROG=''self.LOG=''self.nAxes=robot_axesdefProgStart(self,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. :param progname: name of the program :type progname: str """self.addline('PROC %s()'%progname)defProgFinish(self,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 :meth:`samplepost.RobotPost.ProgStart` is executed. **Tip**: ProgFinish is triggered after all the instructions of the program. :param progname: name of the program :type progname: str """self.addline('ENDPROC')defProgSave(self,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. :param folder: Folder hint to save the program :param progname: Program name as a hint to save the program :param ask_user: True if the default settings in RoboDK are set to promt the user to select the folder :param show_result: 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. :type folder: str :type progname: str :type ask_user: bool, str :type show_result: bool, str """progname=progname+'.'+self.PROG_EXTifask_userornotDirExists(folder):filesave=getSaveFile(folder,progname,'Save program as...')iffilesaveisnotNone:filesave=filesave.nameelse:returnelse:filesave=folder+'/'+prognamefid=open(filesave,"w")fid.write(self.PROG)fid.close()print('SAVED: %s\n'%filesave)#---------------------- show resultifshow_result:iftype(show_result)isstr:# Open file with provided applicationimportsubprocessp=subprocess.Popen([show_result,filesave])else:# open file with default applicationimportosos.startfile(filesave)iflen(self.LOG)>0:mbox('Program generation LOG:\n\n'+self.LOG)defMoveJ(self,pose,joints,conf_RLF=None):"""Defines a joint movement. **Tip**: MoveJ is triggered by the RoboDK instruction Program->Move Joint Instruction. :param pose: pose target of the tool with respect to the reference frame. Pose can be None if the target is defined as a joint target. :param joints: robot joints as a list :param conf_RLF: robot configuration as a list of 3 ints: [REAR, LOWER-ARM, FLIP]. [0,0,0] means [front, upper arm and non-flip] configuration. :type pose: :meth:`robodk.Mat` :type joints: float list :type conf_RLF: int list """self.addline('MOVJ '+joints_2_str(joints))defMoveL(self,pose,joints,conf_RLF=None):"""Defines a linear movement. **Tip**: MoveL is triggered by the RoboDK instruction Program->Move Linear Instruction. :param pose: pose target of the tool with respect to the reference frame. Pose can be None if the target is defined as a joint target. :param joints: robot joints as a list :param conf_RLF: robot configuration as a list of 3 ints: [REAR, LOWER-ARM, FLIP]. [0,0,0] means [front, upper arm and non-flip] configuration. :type pose: :meth:`robodk.Mat` :type joints: float list :type conf_RLF: int list """self.addline('MOVL '+pose_2_str(pose))defMoveC(self,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. :param pose1: pose target of a point defining an arc (waypoint) :param pose2: pose target of the end of the arc (final point) :param joints1: robot joints of the waypoint :param joints2: robot joints of the final point :param conf_RLF_1: robot configuration of the waypoint :param conf_RLF_2: robot configuration of the final point :type pose1: :meth:`robodk.Mat` :type pose2: :meth:`robodk.Mat` :type joints1: float list :type joints1: float list :type conf_RLF1: int list :type conf_RLF2: int list """self.addline('MOVC '+pose_2_str(pose1)+' '+pose_2_str(pose2))defsetFrame(self,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. :param pose: pose of the reference frame with respect to the robot base frame :param frame_id: number of the reference frame (if available) :param frame_name: Name of the reference frame as defined in RoboDK :type pose: :meth:`robodk.Mat` :type frame_id: int, None :type frame_name: str """self.addline('BASE_FRAME '+pose_2_str(pose))defsetTool(self,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. :param pose: pose of the TCP frame with respect to the robot base frame :param tool_id: number of the reference frame (if available) :param tool_name: Name of the reference frame as defined in RoboDK :type pose: :meth:`robodk.Mat` :type tool_id: int, None :type tool_name: str """self.addline('TOOL_FRAME '+pose_2_str(pose))defPause(self,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. :param time_ms: time of the pause, in milliseconds :type time_ms: float"""iftime_ms<0:self.addline('PAUSE')else:self.addline('WAIT %.3f'%(time_ms*0.001))defsetSpeed(self,speed_mms):"""Changes the robot speed (in mm/s) **Tip**: setSpeed is triggered by the RoboDK instruction Program->Set Speed Instruction. :param speed_mms: speed in :math:`mm/s` :type speed_mms: float"""self.addlog('setSpeed not defined (%.2f mms)'%speed_mms)defsetAcceleration(self,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. :param accel_mmss: speed in :math:`mm/s^2` :type accel_mmss: float"""self.addlog('setAcceleration not defined')defsetSpeedJoints(self,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. :param speed_degs: speed in :math:`deg/s` :type speed_degs: float"""self.addlog('setSpeedJoints not defined')defsetAccelerationJoints(self,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. :param accel_degss: speed in :math:`deg/s^2` :type accel_degss: float"""self.addlog('setAccelerationJoints not defined')defsetZoneData(self,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. :param zone_mm: rounding radius in mm :type zone_mm: float"""self.addlog('setZoneData not defined (%.1f mm)'%zone_mm)defsetDO(self,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. :param io_var: variable to set, provided as a str or int :type io_var: int, str :param io_value: value of the variable, provided as a str, float or int :type io_value: int, float, str"""iftype(io_var)!=str:# set default variable name if io_var is a numberio_var='OUT[%s]'%str(io_var)iftype(io_value)!=str:# set default variable value if io_value is a numberifio_value>0:io_value='TRUE'else:io_value='FALSE'# at this point, io_var and io_value must be string valuesself.addline('%s=%s'%(io_var,io_value))defwaitDI(self,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. :param io_var: variable to wait for, provided as a str or int :type io_var: int, str :param io_value: value of the variable, provided as a str, float or int :type io_value: int, float, str :param timeout_ms: maximum wait time :type timeout_ms: float, int"""iftype(io_var)!=str:# set default variable name if io_var is a numberio_var='IN[%s]'%str(io_var)iftype(io_value)!=str:# set default variable value if io_value is a numberifio_value>0:io_value='TRUE'else:io_value='FALSE'# at this point, io_var and io_value must be string valuesiftimeout_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))defRunCode(self,code,is_function_call=False):"""Adds code or a function call. **Tip**: RunCode is triggered by the RoboDK instruction Program->Function call Instruction. :param code: code or procedure to call :param is_function_call: True if the provided code is a specific function to call :type code: str :type is_function_call: bool"""ifis_function_call:code.replace(' ','_')ifnotcode.endswith(')'):code=code+'()'self.addline(code)else:self.addline(code)defRunMessage(self,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. :param message: Message to display on the teach pendant or as a comment on the code :type message: str :param iscomment: True if the message does not have to be displayed on the teach pendant but as a comment on the code :type iscomment: bool"""ifiscomment:self.addline('% '+message)else:self.addlog('Show message on teach pendant not implemented (%s)'%message)# ------------------ private ---------------------- defaddline(self,newline):"""Add a new program line. This is a private method used only by the other methods. :param newline: new line to add. :type newline: str"""self.PROG=self.PROG+newline+'\n'defaddlog(self,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. :param newline: new line :type newline: str"""self.LOG=self.LOG+newline+'\n'# -------------------------------------------------# ------------ For testing purposes --------------- defPose(xyzrpw):[x,y,z,r,p,w]=xyzrpwa=r*math.pi/180b=p*math.pi/180c=w*math.pi/180ca=math.cos(a)sa=math.sin(a)cb=math.cos(b)sb=math.sin(b)cc=math.cos(c)sc=math.sin(c)returnMat([[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]])deftest_post():"""Test the post processor with a simple 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)iflen(robot.LOG)>0:mbox('Program generation LOG:\n\n'+robot.LOG)input("Press Enter to close...")if__name__=="__main__":"""Procedure to call when the module is executed by itself: test_post()"""test_post()