import urx import math3d as m3d from scipy.optimize import fsolve import math import numpy as np import time import os #import logging import yaml import sys from util import fprint from pyModbusTCP.client import ModbusClient from multiprocessing import Queue import subprocess from util import win32 class Rob(): robot = None #offset_x, offset_y, offset_z = (0, 0, 0.14) # Tool offset # def __init__(self, config): self.config = config armc = config["arm"] self.ip = armc["ip"] tool = armc["tool"] limbs = armc["limbs"] self.offset_x, self.offset_y, self.offset_z = (tool["offset_x"], tool["offset_y"], tool["offset_z"]) self.limb_base = limbs["limb_base"] self.limb1 = limbs["limb1"] self.limb2 = limbs["limb2"] self.limb3 = limbs["limb3"] self.limb_wrist = limbs["limb_wrist"] #self.init_arm() def ping(host): #Returns True if host (str) responds to a ping request. # Option for the number of packets as a function of if win32: param1 = '-n' param2 = '-w' param3 = '250' else: param1 = '-c' param2 = '-W' param3 = '0.25' # Building the command. Ex: "ping -c 1 google.com" command = ['ping', param1, '1', param2, param3, host] return subprocess.call(command, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT) == 0 def powerup_arm(robot): #sys.stdout = Logger() fprint("Starting UR5 power up...") # power up robot here via PCB # # wait for power up (this function runs async) count = 0 while not ping(robot.ip) and count == 10: time.sleep(0.5) count += 1 # trigger auto-initialize fprint("Arm online. Waiting for calibration.") # wait for auto-initialize def connect(robot): if robot.robot is None: newrobot = Rob(robot.config) robot = newrobot ip = robot.ip fprint("Connecting to arm at " + ip) trying = True count = 0 while trying and count < 10: count += 1 try: robot.robot = urx.Robot(ip) robot.robot.set_tcp((robot.offset_x, robot.offset_y, robot.offset_z, 0, 0, 0)) # Set weight robot.robot.set_payload(2, (0, 0, 0.1)) trying = False except: time.sleep(0.5) # Sets robot arm endpoint offset (x,y,z,rx,ry,rz) return robot def init_arm(robot): robot = connect(robot) # init urx #rob.set_payload(2, (0, 0, 0.1)) time.sleep(0.2) fprint("UR5 ready.") #return robot.robot # setup - in case of fail. open gripper, move up, then go home. rob = robot.robot open_gripper() curr_pos = rob.getl() new_pos = curr_pos new_pos[2] += 0.025 rob.movel(new_pos, vel=0.05, acc=1) move_to_home(robot, speed=0.5) return True def set_pos_abs(robot, x, y, z, xb, yb, zb, threshold=None): rob = robot.robot new_orientation = m3d.Transform() new_orientation.orient.rotate_xb(xb) # Replace rx with the desired rotation around X-axis new_orientation.orient.rotate_yb(yb) # Replace ry with the desired rotation around Y-axis new_orientation.orient.rotate_zb(zb) # Replace rz with the desired rotation around Z-axis # Get the current pose trans = rob.getl() # Apply the new orientation while keeping the current position new_trans = m3d.Transform(new_orientation.orient, m3d.Vector(trans[0:3])) new_trans.pos.x = x new_trans.pos.y = y new_trans.pos.z = z #rob.speedj(0.2, 0.5, 99999) rob.set_pose(new_trans, acc=2, vel=2, command="movej", threshold=threshold) # apply the new pose def set_pos_rel_rot_abs(robot, x, y, z, xb, yb, zb): rob = robot.robot new_orientation = m3d.Transform() new_orientation.orient.rotate_xb(xb) # Replace rx with the desired rotation around X-axis new_orientation.orient.rotate_yb(yb) # Replace ry with the desired rotation around Y-axis new_orientation.orient.rotate_zb(zb) # Replace rz with the desired rotation around Z-axis # Get the current pose trans = rob.getl() # Apply the new orientation while keeping the current position new_trans = m3d.Transform(new_orientation.orient, m3d.Vector(trans[0:3])) new_trans.pos.x += x new_trans.pos.y += y new_trans.pos.z += z #rob.speedj(0.2, 0.5, 99999) rob.set_pose(new_trans, acc=0.1, vel=0.4, command="movej") # apply the new pose def set_pos_abs_rot_rel(robot, x, y, z, xb, yb, zb): rob = robot.robot new_orientation = m3d.Transform() new_orientation.orient.rotate_xb(xb) # Replace rx with the desired rotation around X-axis new_orientation.orient.rotate_yb(yb) # Replace ry with the desired rotation around Y-axis new_orientation.orient.rotate_zb(zb) # Replace rz with the desired rotation around Z-axis # Get the current pose trans = rob.getl() # Apply the new orientation while keeping the current position new_trans = m3d.Transform(new_orientation.orient, m3d.Vector(trans[0:3])) new_trans.pos.x = x new_trans.pos.y = y new_trans.pos.z = z #rob.speedj(0.2, 0.5, 99999) rob.set_pose(new_trans, acc=0.1, vel=0.4, command="movej") # apply the new pose def is_safe_move(start_pose, end_pose, r=0.25): start_x, start_y = (start_pose[0], start_pose[1]) end_x, end_y = (end_pose[0], end_pose[1]) try: m = (end_y-start_y)/(end_x-start_x) b = start_y - m*start_x # print('m = y/x =', m) # print('b =', b) except: m = (end_x-start_x)/(end_y-start_y) b = start_x - m*start_y # print('m = x/y =', m) # print('b =', b) return r**2 - b**2 + m**2 * r**2 < 0 def cartesian_to_polar(x, y): r = np.sqrt(x**2 + y**2) theta = np.arctan2(y, x) return r, theta def polar_to_cartesian(r, theta): x = r * np.cos(theta) y = r * np.sin(theta) return x, y def move_to_polar(robot, start_pos, end_pos): rob = robot.robot # Convert to polar coordinates start_r, start_theta = cartesian_to_polar(start_pos[0], start_pos[1]) end_r, end_theta = cartesian_to_polar(end_pos[0], end_pos[1]) # Interpolate for xy (spiral arc) n_points = 30 r_intermediate = np.linspace(start_r, end_r, n_points) theta_intermediate = np.linspace(start_theta, end_theta, n_points) # Interpolate for z (height) start_z = start_pos[2] end_z = end_pos[2] z_intermediate = np.linspace(start_z, end_z, n_points) # Interpolate for rz (keep tool rotation fixed relative to robot) curr_rot = rob.getl() theta_delta = theta_intermediate[1]-theta_intermediate[0] rx_intermediate = [curr_rot[5] + theta_delta*i for i in range(n_points)] # curr_rot = rob.getj() # start_rz = curr_rot[5] # rot = end_theta - start_theta # end_base_joint = curr_rot[0]-start_theta + rot # end_rz = curr_rot[0] + rot # # rob.movel([*polar_to_cartesian(end_r, end_theta), *rob.getl()[2:]], acc=2, vel=2) # print('start_theta = ', math.degrees(start_theta)) # print('end_theta = ', math.degrees(curr_rot[0]-start_theta+rot)) # print('start_rz =', math.degrees(start_rz)) # print('rot =', math.degrees(rot)) # print('end_rz =', math.degrees(end_rz)) # rz_intermediate = np.linspace(start_rz, end_rz, n_points) # Convert back to cartesian coordinates curr_pos = rob.getl() intermediate_points = [[*polar_to_cartesian(r, theta), z, *curr_pos[3:]] for r, theta, z, rx in zip(r_intermediate, theta_intermediate, z_intermediate, rx_intermediate)] # Move robot rob.movels(intermediate_points, acc=2, vel=2, radius=0.1) return rx_intermediate def move_to_home(robot, keep_flip=False, speed=2): rob = robot.robot if is_flipped(robot) and not keep_flip: flip(robot) # Move robot to home position rob.movej(offset_gripper_angle(robot, *(-0.18, -0.108, 0.35), flip=is_flipped(robot)), vel=2, acc=2) # Move to safe position return True def move_to_packup(robot, speed=0.25): rob = robot.robot # known good starting point to reach store position goto_holder_index(robot, 12, 0.3, flip=False, use_closest_path=False) # Home position in degrees store_pos = [-1.5708, -1.3, 2.362, 0.7056, -1.425, 1.5708] # Move robot rob.movej(store_pos, acc=0.1, vel=speed) return True def normalize_degree(theta): # Normalizes degree theta from -1.5pi to 1.5pi multiplier = 1 normalized_theta = theta % (math.pi * multiplier) # Maintain the negative sign if the original angle is negative if theta < 0: normalized_theta -= math.pi * multiplier # Return angle return normalized_theta def get_joints_from_xyz_rel(robot, x, y, z, rx=0, ry=-math.pi/2, rz=0, initial_guess = (math.pi/2, math.pi/2, 0), l3offset=0): # Get limbs and offsets #l3=0.15 l_bs, l1, l2, l3, l_wt = (robot.limb_base, robot.limb1, robot.limb2, robot.limb3, robot.limb_wrist) # Limb lengths l3 += l3offset # add wrist offset, used for gripper angle calculations offset_x = robot.offset_x offset_y = robot.offset_y offset_z = robot.offset_z # Calculate base angle and r relative to shoulder joint def calculate_theta(x, y, a): # Calculate if we need the + or - in our equations if (x>-a and y>=0) or (x>a and y<0): flip = 1 elif (x<-a and y>=0) or (x 137: print("CRASH! Shoulder at", joints[1] * 180/math.pi) #else: #print("Shoulder at", joints[1] * 180/math.pi) # Get adjusted joint positions adjusted_joints = [o+j*i for j, o, i in zip(joints, offsets, inverse)] curr_joints = rob.getj() def get_complimentary_angle(joint_angle): if joint_angle<0: new_angle = joint_angle + 2*math.pi else: new_angle = joint_angle - 2*math.pi if abs(new_angle) > math.radians(350): return joint_angle else: return new_angle # Use closest path (potentially going beyond 180 degrees) if use_closest_path: if abs(get_complimentary_angle(adjusted_joints[0])-curr_joints[0]) < abs(adjusted_joints[0]-curr_joints[0]): adjusted_joints[0] = get_complimentary_angle(adjusted_joints[0]) # final_joint_positions = [] # for curr_joint, adjusted_joint in zip(curr_joints, adjusted_joints): # if abs(curr_joint - adjusted_joint) < abs(curr_joint - get_complimentary_angle(adjusted_joint)): # final_joint_positions.append(adjusted_joint) # else: # final_joint_positions.append(get_complimentary_angle(adjusted_joint)) # return final_joint_positions return adjusted_joints def move_arc(robot, x, y, z, rx=0, ry=-math.pi/2, rz=math.pi/2): rob = robot.robot start_joints = rob.getj() end_joint = get_joints_from_xyz_abs(robot, x, y, z, rx, ry, rz) n_points = 50 intermediate_joints = [] for i in range(0, 6): intermediate_joints.append(np.linspace(start_joints[i], end_joint[i], n_points)) joints = [joint_position for joint_position in zip(*intermediate_joints)] rob.movejs(joints, acc=2, vel=2, radius=0.1) def offset_gripper_angle(robot, x, y, z, gripperangle=30, gripperlength=0.20+0.018, flip=False, use_closest_path=True, rzoffset=0): # gripper angle: from vertical # gripper length: from joint to start of grip # to flip, you can use flip=True or make gripper angle negative limb3 = robot.limb3 # Determine tool rotation depending on gripper angle if gripperangle < 0: rz = - math.pi / 2 else: rz = math.pi / 2 if flip: gripperangle = -math.radians(gripperangle) grippery = gripperlength - math.cos(gripperangle) * gripperlength grippery += math.sin(gripperangle) * limb3 gripperx = math.sin(gripperangle) * gripperlength + limb3 * 2 gripperx -= (1-math.cos(gripperangle)) * limb3 rz = math.pi / 2 # flip the whole wrist return get_joints_from_xyz_abs(robot, x, y, z-grippery, rx=gripperangle + math.radians(180), l3offset=-gripperx, ry=-3*math.pi/2, rz=rz + rzoffset, use_closest_path=use_closest_path) else: gripperangle = math.radians(gripperangle) grippery = gripperlength - math.cos(gripperangle) * gripperlength grippery -= math.sin(gripperangle) * limb3 gripperx = math.sin(gripperangle) * gripperlength gripperx += (1-math.cos(gripperangle)) * limb3 return get_joints_from_xyz_abs(robot, x, y, z-grippery, rx=gripperangle, l3offset=-gripperx, rz=rz, use_closest_path=use_closest_path) def goto_holder_index(robot, idx, z=0.05, gripperangle=30, flip=False, use_closest_path=True, verbose=False): joint = robot.config["position_map"][idx] if verbose: print("Going to cable holder index", joint["index"], "at position", joint["pos"]) safe_move(robot, joint["pos"][0]/1000, joint["pos"][1]/1000, z, use_closest_path=use_closest_path) #angles = offset_gripper_angle(joint["pos"][1]/1000, joint["pos"][0]/1000, z, gripperangle=gripperangle, flip=flip) #rob.movej(angles, acc=2, vel=2) #return angles #angles = get_joints_from_xyz_abs(joint["pos"][1]/1000, joint["pos"][0]/1000, 0.05, ) return True def is_flipped(robot): rob = robot.robot wrist1 = rob.getj()[3] if wrist1 > 0: return True else: return False def flip(robot): rob = robot.robot # A list of safe positions to flip safe_positions = [(-0.18, -0.108, 0.35), (0.18, -0.108, 0.35)] # Find the closest safe position curr_pos = rob.getl()[:3] def dist_from_robot(pos): x, y, z = pos rx, ry, rz = curr_pos return math.sqrt((rx-x)**2+(ry-y)**2+(rz-z)**2) pos_dist_pairs = zip(safe_positions, [dist_from_robot(pos) for pos in safe_positions]) safe_pos = min(pos_dist_pairs, key=lambda x:x[1])[0] # Flip at safe position rob.movej(offset_gripper_angle(robot, *safe_pos, flip=is_flipped(robot)), vel=2, acc=2) # Move to safe position rob.movej(offset_gripper_angle(robot, *safe_pos, flip=(not is_flipped(robot))), vel=2, acc=2) # Flip gripper # print('flip?: ', is_flipped(robot)) return True def safe_move(robot, x, y, z, use_closest_path=True): rob = robot.robot flip_radius = 0.22 # Min radius on which to flip r = math.sqrt(x**2 + y**2) # Get position radius # Flip gripper if needed if (r <= flip_radius and is_flipped(robot)) or (r > flip_radius and not is_flipped(robot)): flip(robot) rob.movej(offset_gripper_angle(robot, x, y, z, flip=is_flipped(robot), use_closest_path=use_closest_path), vel=2, acc=2) return True def holder_routine(robot, pos_updates, holder_index, pick_up, verbose=False): robot = connect(robot) rob = robot.robot # Don't attempt to place a tube in the camera slot if holder_index == 49: return if verbose: fprint('Pickup routine for index' + str(holder_index)) # Go to the correct holder if pick_up: goto_holder_index(robot, holder_index, 0.05, use_closest_path=False) else: goto_holder_index(robot, holder_index, 0.2, use_closest_path=False) pos_updates.put(1) fprint("Triggering LED interface") if pick_up: open_gripper() # Move down curr_pos = rob.getl() new_pos = curr_pos new_pos[2] = 0.005 rob.movel(new_pos, vel=0.1, acc=1) # Pick up or drop off if pick_up: close_gripper() else: open_gripper() # Move up new_pos[2] = 0.2 rob.movel(new_pos, vel=2, acc=1) was_flipped = is_flipped(robot) pos_updates.put(2) fprint("Triggering LED interface") # goto_holder_index(robot, 25, z=0.2) def pick_up_holder(robot, pos_updates, holder_index, verbose=False): holder_routine(robot, pos_updates, holder_index, True, verbose=verbose) def drop_off_holder(robot, pos_updates, holder_index, verbose=False): holder_routine(robot, pos_updates, holder_index, False, verbose=verbose) def tray_routine(robot, slot=0, pick_up=True): robot = connect(robot) rob = robot.robot # Default to 0 if invalid value if slot not in [0, 1, 2, 3]: slot = 0 slot_prepositions = [(-9.93, -112.67, 144.02, -116.69, -54.13, -10.29), (-12.35, -124.95, 148.61, -107.27, -54.36, -13.26), (-16.45, -96.97, 137.85, 58.39, -305.08, 161.75), (-16.66, -97.28, 138.16, 58.54, -305.05, 161.50)] # Initial position depending on slot and robot orientation if slot in [0, 1]: if is_flipped(robot): flip(robot) else: move_to_home(robot, keep_flip=True) else: goto_holder_index(robot, 25, z=0.3) # Align robot to the slot if slot in [2,3]: angles = [(-2.77, -99.64, 131.02, 67.67, 70.04-360, 153.03), slot_prepositions[slot]] else: angles = [(-58, -114.45, 100.52, -45.24, -96.95, 120), (-39.98, -124.92, 132.28, -61.56, -55.60, -50.77), slot_prepositions[slot]] angles = [[x*math.pi/180 for x in move] for move in angles] rob.movejs(angles,vel=2,acc=1) # Positions for each slot slot_distance = .052 slot_height = -.015-.0095 first_slot = -0.3084+0.02 slot_position = [ [first_slot, -0.3426, slot_height, 1.5899, 1.5526, -0.9411], [first_slot+slot_distance, -0.3426, slot_height, 1.5899, 1.5526, -0.9411], [first_slot+2*slot_distance, -0.3426, slot_height, 1.5899, 1.5526, -0.9411], [first_slot+3*slot_distance, -0.3426, slot_height, 1.5899, 1.5526, -0.9411], ] rob.movel(slot_position[slot],vel=0.2, acc=1) # Place/Grab the tube if pick_up: close_gripper() else: open_gripper() # Move back tilt = 0.3 curr_pos = rob.getl() new_pos = curr_pos if slot==3: new_pos[0] -= 0.05 #x new_pos[1] += 0.15 #y new_pos[2] = 0.09 #z new_pos[3] += tilt new_pos[4] += tilt new_pos[5] += tilt rob.movel(new_pos, vel=0.2, acc=1) # Go home to safe position move_to_home(robot, speed=1, keep_flip=True) def pick_up_tray(robot, slot=0): tray_routine(robot, slot, True) def drop_off_tray(robot, slot=0): tray_routine(robot, slot, False) def return_routine(robot, slot, holder_index=None, verbose=False): # OLD UNUSED robot = connect(robot) rob = robot.robot open_gripper() was_flipped = is_flipped(robot) if slot is None: rob.movej(offset_gripper_angle(robot, -0.15, -0.15, 0.3, flip=was_flipped, use_closest_path=False), vel=4, acc=3) rob.movej(get_joints_from_xyz_abs(robot, -0.35, -0.15, 0.0, math.pi/2, 0.1), vel=4, acc=3) close_gripper() else: xoffset = 0.051 * slot rob.movej(offset_gripper_angle(robot, -0.15, -0.15, 0.3, flip=was_flipped, use_closest_path=False), vel=4, acc=3) rob.movej(get_joints_from_xyz_abs(robot, -0.35+xoffset, -0.15, 0.0, math.pi/2, 0.1), vel=4, acc=3) close_gripper() if holder_index is not None: goto_holder_index(robot, holder_index, 0.2, use_closest_path=False) curr_pos = rob.getl() new_pos = curr_pos new_pos[2] = 0.015 rob.movel(new_pos, vel=0.1, acc=1) open_gripper() new_pos[2] = 0.1 rob.movel(new_pos, vel=2, acc=1) return True else: # go to camera rob.movej(offset_gripper_angle(robot, 0.35, -0.35, 0.3, flip=was_flipped, use_closest_path=False), vel=2, acc=2) return True def goto_camera(robot, pos_updates): robot = connect(robot) goto_holder_index(robot, 49, 0.2) def tray_to_camera(robot, pos_updates, slot): pick_up_tray(robot, slot) goto_camera(robot, pos_updates) def holder_to_tray(robot, pos_updates, holder_index, slot): pick_up_holder(robot, pos_updates, holder_index) drop_off_tray(robot, slot) def holder_to_camera(robot, pos_updates, holder_index, verbose=False): robot = connect(robot) fprint("Bringing tube at " + str(holder_index) + " to camera") rob = robot.robot pick_up_holder(robot, pos_updates, holder_index) goto_camera(robot, pos_updates) def camera_to_holder(robot, pos_updates, holder_index, verbose=False): robot = connect(robot) rob = robot.robot drop_off_holder(robot, pos_updates, holder_index) def open_gripper(): fprint("Opening gripper") c = ModbusClient(host="192.168.1.21", port=502, auto_open=True, auto_close=False) c.write_single_register(112, 0b0) c.write_single_register(435, 0b10000000) c.write_single_register(112, 0b0) c.write_single_register(435, 0b10000000) time.sleep(0.5) c.close() #c.close() def close_gripper(): fprint("Closing gripper") c = ModbusClient(host="192.168.1.21", port=502, auto_open=True, auto_close=False) c.write_single_register(435, 0b00000000) c.write_single_register(112, 0b1) c.write_single_register(435, 0b00000000) c.write_single_register(112, 0b1) time.sleep(0.5) c.close() # def get_position_thread(robot, pos_updates): robot = connect(robot) rob = robot.robot while True: if pos_updates.qsize() < 2: vals = rob.getl() pos_updates.put(tuple(vals)) #print("Adding position to queue") time.sleep(0.01) if __name__ == "__main__": with open('config.yml', 'r') as fileread: #global config config = yaml.safe_load(fileread) robot = Rob(config) # robot of type Rob is the custom class above #powerup_arm(robot) robot = connect(robot) init_arm(robot) rob = robot.robot # rob is robot.robot is the urx robot class, what we've been using previously #move_to_packup(robot) # pick_up_holder(robot, 2) # drop_off_tray(robot, 0) # pick_up_tray(robot, 1) # drop_off_holder(robot, 5) # pick_up_holder(robot, 26) # drop_off_tray(robot, 3) for i in range(44,45): pick_up_holder(robot, i) print('Drop off', i+1) drop_off_holder(robot, i+1) input() # holder_to_camera(robot, 0) # camera_to_holder(robot, 0) print("Current tool pose is: ", rob.getl()) curr_pos = rob.getl() config = None rob.stop() os.kill(os.getpid(), 9) # dirty kill of self sys.exit(0)