# Copyright 2015-2019 - RoboDK Inc. - https://robodk.com/
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ----------------------------------------------------
# This file is a POST PROCESSOR for Robot Offline Programming to generate programs
# for a Universal 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 text editor 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)
#
# ----------------------------------------------------
# More information about RoboDK Post Processors and Offline Programming here:
# https://robodk.com/help#PostProcessor
# https://robodk.com/doc/en/PythonAPI/postprocessor.html
# ----------------------------------------------------
DEFAULT_HEADER_SCRIPT = """
#--------------------------
# Add any default subprograms here
# For example, to drive a gripper as a program call:
# def Gripper_Open():
# ...
# end
#
# Example to drive a spray gun:
def SprayOn(value):
# use the value as an output:
DO_SPRAY = 5
if value == 0:
set_standard_digital_out(DO_SPRAY, False)
else:
set_standard_digital_out(DO_SPRAY, True)
end
end
# Example to drive an extruder:
def Extruder(value):
# use the value as an output:
if value < 0:
# stop extruder
else:
# start extruder
end
end
# Example to move an external axis
def MoveAxis(value):
# use the value as an output:
DO_AXIS_1 = 1
DI_AXIS_1 = 1
if value <= 0:
set_standard_digital_out(DO_AXIS_1, False)
# Wait for digital input to change state
#while (get_standard_digital_in(DI_AXIS_1) != False):
# sync()
#end
else:
set_standard_digital_out(DO_AXIS_1, True)
# Wait for digital input to change state
#while (get_standard_digital_in(DI_AXIS_1) != True):
# sync()
#end
end
end
#--------------------------
"""
#SCRIPT_URP = '''
#
#
#
#
#
#
#
#'''
#SCRIPT_URP = '''
#
#
#
#
#
#
#
#'''
#
SCRIPT_URP = '''
'''
def get_safe_name(progname):
"""Get a safe program name"""
for c in r'-[]/\;,><&*:%=+@!#^|?^':
progname = progname.replace(c,'')
if len(progname) <= 0:
progname = 'Program'
if progname[0].isdigit():
progname = 'P' + progname
return progname
# ----------------------------------------------------
# Import RoboDK tools
from robodk import *
# ----------------------------------------------------
import socket
import struct
# UR information for real time control and monitoring
# Byte shifts for the real time packet:
UR_GET_RUNTIME_MODE = 132*8-4
RUNTIME_CANCELLED = 0
RUNTIME_READY = 1
RUNTIME_BUSY = 2
RUNTIME_MODE_MSG = []
RUNTIME_MODE_MSG.append("Operation cancelled") #0
RUNTIME_MODE_MSG.append("Ready") #1
RUNTIME_MODE_MSG.append("Running") #2 # Running or Jogging
# Get packet size according to the byte array
def UR_packet_size(buf):
if len(buf) < 4:
return 0
return struct.unpack_from("!i", buf, 0)[0]
# Check if a packet is complete
def UR_packet_check(buf):
msg_sz = UR_packet_size(buf)
if len(buf) < msg_sz:
print("Incorrect packet size %i vs %i" % (msg_sz, len(buf)))
return False
return True
# Get specific information from a packet
def UR_packet_value(buf, offset, nval=6):
if len(buf) < offset+nval:
print("Not available offset (maybe older Polyscope version?): %i - %i" % (len(buf), offset))
return None
format = '!'
for i in range(nval):
format+='d'
return list(struct.unpack_from(format, buf, offset)) #return list(struct.unpack_from("!dddddd", buf, offset))
ROBOT_PROGRAM_ERROR = -1
ROBOT_NOT_CONNECTED = 0
ROBOT_OK = 1
def GetErrorMsg(rec_bytes):
idx_error = -1
try:
idx_error = rec_bytes.index(b'error')
except:
return None
if idx_error >= 0:
idx_error_end = min(idx_error + 20, len(rec_bytes))
try:
idx_error_end = rec_bytes.index(b'\0',idx_error)
except:
return "Unknown error"
return rec_bytes[idx_error:idx_error_end].decode("utf-8")
def UR_SendProgramRobot(robot_ip, data):
print("POPUP: Connecting to robot...")
sys.stdout.flush()
robot_socket = socket.create_connection((robot_ip, 30002))
print("POPUP: Sending program..")
sys.stdout.flush()
robot_socket.send(data)
print("POPUP: Sending program...")
sys.stdout.flush()
pause(1)
received = robot_socket.recv(4096)
robot_socket.close()
if received:
#print("POPUP: Program running")
#print(str(received))
sys.stdout.flush()
error_msg = GetErrorMsg(received)
if error_msg:
print("POPUP: Robot response: " + error_msg + "")
sys.stdout.flush()
pause(5)
return ROBOT_PROGRAM_ERROR
else:
print("POPUP: Program sent. The program should be running on the robot.")
sys.stdout.flush()
return ROBOT_OK
else:
print("POPUP: Robot connection problems...")
sys.stdout.flush()
pause(2)
return ROBOT_NOT_CONNECTED
# Monitor thread to retrieve information from the robot
def UR_Wait_Ready(robot_ip, percent_cmpl):
RUNTIME_MODE_LAST = -1
while True:
print("Connecting to robot %s:%i" % (robot_ip, 30003))
rt_socket = socket.create_connection((robot_ip, 30003))
print("Connected")
buf = b''
while True:
more = rt_socket.recv(4096)
if more:
buf = buf + more
if UR_packet_check(buf):
packet_len = UR_packet_size(buf)
packet, buf = buf[:packet_len], buf[packet_len:]
RUNTIME_MODE = round(UR_packet_value(packet, UR_GET_RUNTIME_MODE, 1)[0])
if RUNTIME_MODE_LAST != RUNTIME_MODE:
RUNTIME_MODE_LAST = RUNTIME_MODE
if RUNTIME_MODE < len(RUNTIME_MODE_MSG):
print("POPUP: Robot " + RUNTIME_MODE_MSG[RUNTIME_MODE] + " (transfer in progress, %.1f%% completed)" % percent_cmpl)
sys.stdout.flush()
else:
print("POPUP: Robot Status Unknown (%.i)" % RUNTIME_MODE + " (transfer %.1f%% completed)" % percent_cmpl)
sys.stdout.flush()
if RUNTIME_MODE == RUNTIME_READY:
rt_socket.close()
return True
rt_socket.close()
return False
def pose_2_ur(pose):
"""Calculate the p[x,y,z,rx,ry,rz] position for a pose target"""
NUMERIC_TOLERANCE = 1e-8;
def saturate_1(value):
return min(max(value,-1.0),1.0)
angle = acos( saturate_1((pose[0,0]+pose[1,1]+pose[2,2]-1)/2) )
rxyz = [pose[2,1]-pose[1,2], pose[0,2]-pose[2,0], pose[1,0]-pose[0,1]]
if angle < NUMERIC_TOLERANCE:
rxyz = [0,0,0]
else:
sin_angle = sin(angle)
if abs(sin_angle) < NUMERIC_TOLERANCE:
d3 = [pose[0,0],pose[1,1],pose[2,2]]
mx = max(d3)
mx_id = d3.index(mx)
if mx_id == 0:
rxyz = [pose[0,0]+1, pose[1,0] , pose[2,0] ]
elif mx_id == 1:
rxyz = [pose[0,1] , pose[1,1]+1, pose[2,1] ]
else:
rxyz = [pose[0,2] , pose[1,2] , pose[2,2]+1]
rxyz = mult3(rxyz, angle/(sqrt(max(0,2*(1+mx)))))
else:
rxyz = normalize3(rxyz)
rxyz = mult3(rxyz, angle)
return [pose[0,3], pose[1,3], pose[2,3], rxyz[0], rxyz[1], rxyz[2]]
def pose_2_str(pose):
"""Prints a pose target"""
[x,y,z,w,p,r] = pose_2_ur(pose)
MM_2_M = 0.001
return ('p[%.6f, %.6f, %.6f, %.6f, %.6f, %.6f]' % (x*MM_2_M,y*MM_2_M,z*MM_2_M,w,p,r))
def angles_2_str(angles):
"""Prints a joint target"""
njoints = len(angles)
d2r = pi/180.0
if njoints == 6:
return ('[%.6f, %.6f, %.6f, %.6f, %.6f, %.6f]' % (angles[0]*d2r, angles[1]*d2r, angles[2]*d2r, angles[3]*d2r, angles[4]*d2r, angles[5]*d2r))
else:
return 'this post only supports 6 joints'
def circle_radius(p0,p1,p2):
a = norm(subs3(p0,p1))
b = norm(subs3(p1,p2))
c = norm(subs3(p2,p0))
radius = a*b*c/sqrt(pow(a*a+b*b+c*c,2)-2*(pow(a,4)+pow(b,4)+pow(c,4)))
return radius
#def distance_p1_p02(p0,p1,p2):
# v01 = subs3(p1, p0)
# v02 = subs3(p2, p0)
# return dot(v02,v01)/dot(v02,v02)
from Universal_Robots import RobotPost as MainPost
# ----------------------------------------------------
# Object class that handles the robot instructions/syntax
class RobotPost(MainPost):
# Set to True to use MoveP, set to False to use MoveL
USE_MOVEP = True
# If True, it will attempt to upload using SFTP. It requires PYSFTP (pip install pysftp. Important: It requires Visual Studio Community C++ 10.0)
UPLOAD_SFTP = False
# default speed for linear moves in m/s
SPEED_MS = 0.250
# default speed for joint moves in rad/s
SPEED_RADS = 0.75
# default acceleration for lineaer moves in m/ss
ACCEL_MSS = 1.2
# default acceleration for joint moves in rad/ss
ACCEL_RADSS = 1.2
# default blend radius in meters (corners smoothing)
BLEND_RADIUS_M = 0.001
# 5000 # Maximum number of lines per program. If the number of lines is exceeded, the program will be executed step by step by RoboDK
MAX_LINES_X_PROG = 1e9
# minimum circle radius to output (in mm). It does not take into account the Blend radius
MOVEC_MIN_RADIUS = 1
# maximum circle radius to output (in mm). It does not take into account the Blend radius
MOVEC_MAX_RADIUS = 10000
# -------------------------------------------------
# ------------ 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('Universal Robotics', 'Generic UR robot')
robot.ProgStart("Program")
robot.RunMessage("Program generated by RoboDK", 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.setSpeed(100) # set speed to 100 mm/s
robot.setAcceleration(3000) # set speed to 3000 mm/ss
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.ProgFinish("Program")
# robot.ProgSave(".","Program",True)
for line in robot.PROG:
print(line)
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()