driver.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 import roslib; roslib.load_manifest('ur_driver')
00003 import time, sys, threading, math
00004 import copy
00005 import datetime
00006 import socket, select
00007 import struct
00008 import traceback, code
00009 import optparse
00010 import SocketServer
00011 
00012 import rospy
00013 import actionlib
00014 from sensor_msgs.msg import JointState
00015 from control_msgs.msg import FollowJointTrajectoryAction
00016 from trajectory_msgs.msg import JointTrajectory, JointTrajectoryPoint
00017 from geometry_msgs.msg import WrenchStamped
00018 
00019 from dynamic_reconfigure.server import Server
00020 from ur_driver.cfg import URDriverConfig
00021 
00022 from ur_driver.deserialize import RobotState, RobotMode
00023 from ur_driver.deserializeRT import RobotStateRT
00024 
00025 from ur_msgs.srv import SetPayload, SetIO
00026 from ur_msgs.msg import *
00027 
00028 # renaming classes
00029 DigitalIn = Digital
00030 DigitalOut = Digital
00031 Flag  = Digital
00032 
00033 prevent_programming = False
00034 
00035 # Joint offsets, pulled from calibration information stored in the URDF
00036 #
00037 # { "joint_name" : offset }
00038 #
00039 # q_actual = q_from_driver + offset
00040 joint_offsets = {}
00041 
00042 PORT=30002       # 10 Hz, RobotState 
00043 RT_PORT=30003    #125 Hz, RobotStateRT
00044 DEFAULT_REVERSE_PORT = 50001     #125 Hz, custom data (from prog)
00045 
00046 MSG_OUT = 1
00047 MSG_QUIT = 2
00048 MSG_JOINT_STATES = 3
00049 MSG_MOVEJ = 4
00050 MSG_WAYPOINT_FINISHED = 5
00051 MSG_STOPJ = 6
00052 MSG_SERVOJ = 7
00053 MSG_SET_PAYLOAD = 8
00054 MSG_WRENCH = 9
00055 MSG_SET_DIGITAL_OUT = 10
00056 MSG_GET_IO = 11
00057 MSG_SET_FLAG = 12
00058 MSG_SET_TOOL_VOLTAGE = 13
00059 MSG_SET_ANALOG_OUT = 14
00060 MULT_payload = 1000.0
00061 MULT_wrench = 10000.0
00062 MULT_jointstate = 10000.0
00063 MULT_time = 1000000.0
00064 MULT_blend = 1000.0
00065 MULT_analog = 1000000.0
00066 MULT_analog_robotstate = 0.1
00067 
00068 #Max Velocity accepted by ur_driver
00069 MAX_VELOCITY = 10.0
00070 #Using a very high value in order to not limit execution of trajectories being sent from MoveIt!
00071 
00072 #Bounds for SetPayload service
00073 MIN_PAYLOAD = 0.0
00074 MAX_PAYLOAD = 1.0
00075 #Using a very conservative value as it should be set throught the parameter server
00076 
00077 
00078 IO_SLEEP_TIME = 0.05
00079 
00080 JOINT_NAMES = ['shoulder_pan_joint', 'shoulder_lift_joint', 'elbow_joint',
00081                'wrist_1_joint', 'wrist_2_joint', 'wrist_3_joint']
00082 
00083 Q1 = [2.2,0,-1.57,0,0,0]
00084 Q2 = [1.5,0,-1.57,0,0,0]
00085 Q3 = [1.5,-0.2,-1.57,0,0,0]
00086   
00087 
00088 connected_robot = None
00089 connected_robot_lock = threading.Lock()
00090 connected_robot_cond = threading.Condition(connected_robot_lock)
00091 last_joint_states = None
00092 last_joint_states_lock = threading.Lock()
00093 pub_joint_states = rospy.Publisher('joint_states', JointState, queue_size=1)
00094 pub_wrench = rospy.Publisher('wrench', WrenchStamped, queue_size=1)
00095 pub_io_states = rospy.Publisher('io_states', IOStates, queue_size=1)
00096 #dump_state = open('dump_state', 'wb')
00097 
00098 class EOF(Exception): pass
00099 
00100 def dumpstacks():
00101     id2name = dict([(th.ident, th.name) for th in threading.enumerate()])
00102     code = []
00103     for threadId, stack in sys._current_frames().items():
00104         code.append("\n# Thread: %s(%d)" % (id2name.get(threadId,""), threadId))
00105         for filename, lineno, name, line in traceback.extract_stack(stack):
00106             code.append('File: "%s", line %d, in %s' % (filename, lineno, name))
00107             if line:
00108                 code.append("  %s" % (line.strip()))
00109     print "\n".join(code)
00110 
00111 def log(s):
00112     print "[%s] %s" % (datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f'), s)
00113 
00114 
00115 RESET_PROGRAM = '''def resetProg():
00116   sleep(0.0)
00117 end
00118 '''
00119 #RESET_PROGRAM = ''
00120     
00121 class URConnection(object):
00122     TIMEOUT = 1.0
00123     
00124     DISCONNECTED = 0
00125     CONNECTED = 1
00126     READY_TO_PROGRAM = 2
00127     EXECUTING = 3
00128     
00129     def __init__(self, hostname, port, program):
00130         self.__thread = None
00131         self.__sock = None
00132         self.robot_state = self.DISCONNECTED
00133         self.hostname = hostname
00134         self.port = port
00135         self.program = program
00136         self.last_state = None
00137 
00138     def connect(self):
00139         if self.__sock:
00140             self.disconnect()
00141         self.__buf = ""
00142         self.robot_state = self.CONNECTED
00143         self.__sock = socket.create_connection((self.hostname, self.port))
00144         self.__keep_running = True
00145         self.__thread = threading.Thread(name="URConnection", target=self.__run)
00146         self.__thread.daemon = True
00147         self.__thread.start()
00148 
00149     def send_program(self):
00150         global prevent_programming
00151         if prevent_programming:
00152             rospy.loginfo("Programming is currently prevented")
00153             return
00154         assert self.robot_state in [self.READY_TO_PROGRAM, self.EXECUTING]
00155         rospy.loginfo("Programming the robot at %s" % self.hostname)
00156         self.__sock.sendall(self.program)
00157         self.robot_state = self.EXECUTING
00158 
00159     def send_reset_program(self):
00160         self.__sock.sendall(RESET_PROGRAM)
00161         self.robot_state = self.READY_TO_PROGRAM
00162         
00163     def disconnect(self):
00164         if self.__thread:
00165             self.__keep_running = False
00166             self.__thread.join()
00167             self.__thread = None
00168         if self.__sock:
00169             self.__sock.close()
00170             self.__sock = None
00171         self.last_state = None
00172         self.robot_state = self.DISCONNECTED
00173 
00174     def ready_to_program(self):
00175         return self.robot_state in [self.READY_TO_PROGRAM, self.EXECUTING]
00176 
00177     def __trigger_disconnected(self):
00178         log("Robot disconnected")
00179         self.robot_state = self.DISCONNECTED
00180     def __trigger_ready_to_program(self):
00181         rospy.loginfo("Robot ready to program")
00182     def __trigger_halted(self):
00183         log("Halted")
00184 
00185     def __on_packet(self, buf):
00186         state = RobotState.unpack(buf)
00187         self.last_state = state
00188         #import deserialize; deserialize.pstate(self.last_state)
00189 
00190         #log("Packet.  Mode=%s" % state.robot_mode_data.robot_mode)
00191 
00192         if not state.robot_mode_data.real_robot_enabled:
00193             rospy.logfatal("Real robot is no longer enabled.  Driver is fuxored")
00194             time.sleep(2)
00195             sys.exit(1)
00196         
00197         ###
00198         # IO-Support is EXPERIMENTAL
00199         # 
00200         # Notes: 
00201         # - Where are the flags coming from? Do we need flags? No, as 'prog' does not use them and other scripts are not running!
00202         # - analog_input2 and analog_input3 are within ToolData
00203         # - What to do with the different analog_input/output_range/domain?
00204         # - Shall we have appropriate ur_msgs definitions in order to reflect MasterboardData, ToolData,...?
00205         ###
00206         
00207         # Use information from the robot state packet to publish IOStates        
00208         msg = IOStates()
00209         #gets digital in states
00210         for i in range(0, 10):
00211             msg.digital_in_states.append(DigitalIn(i, (state.masterboard_data.digital_input_bits & (1<<i))>>i))
00212         #gets digital out states
00213         for i in range(0, 10):
00214             msg.digital_out_states.append(DigitalOut(i, (state.masterboard_data.digital_output_bits & (1<<i))>>i))
00215         #gets analog_in[0] state
00216         inp = state.masterboard_data.analog_input0 / MULT_analog_robotstate
00217         msg.analog_in_states.append(Analog(0, inp))
00218         #gets analog_in[1] state
00219         inp = state.masterboard_data.analog_input1 / MULT_analog_robotstate
00220         msg.analog_in_states.append(Analog(1, inp))      
00221         #gets analog_out[0] state
00222         inp = state.masterboard_data.analog_output0 / MULT_analog_robotstate
00223         msg.analog_out_states.append(Analog(0, inp))     
00224         #gets analog_out[1] state
00225         inp = state.masterboard_data.analog_output1 / MULT_analog_robotstate
00226         msg.analog_out_states.append(Analog(1, inp))     
00227         #print "Publish IO-Data from robot state data"
00228         pub_io_states.publish(msg)
00229         
00230 
00231         # Updates the state machine that determines whether we can program the robot.
00232         can_execute = (state.robot_mode_data.robot_mode in [RobotMode.READY, RobotMode.RUNNING])
00233         if self.robot_state == self.CONNECTED:
00234             if can_execute:
00235                 self.__trigger_ready_to_program()
00236                 self.robot_state = self.READY_TO_PROGRAM
00237         elif self.robot_state == self.READY_TO_PROGRAM:
00238             if not can_execute:
00239                 self.robot_state = self.CONNECTED
00240         elif self.robot_state == self.EXECUTING:
00241             if not can_execute:
00242                 self.__trigger_halted()
00243                 self.robot_state = self.CONNECTED
00244 
00245         # Report on any unknown packet types that were received
00246         if len(state.unknown_ptypes) > 0:
00247             state.unknown_ptypes.sort()
00248             s_unknown_ptypes = [str(ptype) for ptype in state.unknown_ptypes]
00249             self.throttle_warn_unknown(1.0, "Ignoring unknown pkt type(s): %s. "
00250                           "Please report." % ", ".join(s_unknown_ptypes))
00251 
00252     def throttle_warn_unknown(self, period, msg):
00253         self.__dict__.setdefault('_last_hit', 0.0)
00254         # this only works for a single caller
00255         if (self._last_hit + period) <= rospy.get_time():
00256             self._last_hit = rospy.get_time()
00257             rospy.logwarn(msg)
00258 
00259     def __run(self):
00260         while self.__keep_running:
00261             r, _, _ = select.select([self.__sock], [], [], self.TIMEOUT)
00262             if r:
00263                 more = self.__sock.recv(4096)
00264                 if more:
00265                     self.__buf = self.__buf + more
00266 
00267                     #unpack_from requires a buffer of at least 48 bytes
00268                     while len(self.__buf) >= 48:
00269                         # Attempts to extract a packet
00270                         packet_length, ptype = struct.unpack_from("!IB", self.__buf)
00271                         #print("PacketLength: ", packet_length, "; BufferSize: ", len(self.__buf))
00272                         if len(self.__buf) >= packet_length:
00273                             packet, self.__buf = self.__buf[:packet_length], self.__buf[packet_length:]
00274                             self.__on_packet(packet)
00275                         else:
00276                             break
00277 
00278                 else:
00279                     self.__trigger_disconnected()
00280                     self.__keep_running = False
00281                     
00282             else:
00283                 self.__trigger_disconnected()
00284                 self.__keep_running = False
00285 
00286 
00287 class URConnectionRT(object):
00288     TIMEOUT = 1.0
00289     
00290     DISCONNECTED = 0
00291     CONNECTED = 1
00292     
00293     def __init__(self, hostname, port):
00294         self.__thread = None
00295         self.__sock = None
00296         self.robot_state = self.DISCONNECTED
00297         self.hostname = hostname
00298         self.port = port
00299         self.last_stateRT = None
00300 
00301     def connect(self):
00302         if self.__sock:
00303             self.disconnect()
00304         self.__buf = ""
00305         self.robot_state = self.CONNECTED
00306         self.__sock = socket.create_connection((self.hostname, self.port))
00307         self.__keep_running = True
00308         self.__thread = threading.Thread(name="URConnectionRT", target=self.__run)
00309         self.__thread.daemon = True
00310         self.__thread.start()
00311         
00312     def disconnect(self):
00313         if self.__thread:
00314             self.__keep_running = False
00315             self.__thread.join()
00316             self.__thread = None
00317         if self.__sock:
00318             self.__sock.close()
00319             self.__sock = None
00320         self.last_state = None
00321         self.robot_state = self.DISCONNECTED
00322 
00323     def __trigger_disconnected(self):
00324         log("Robot disconnected")
00325         self.robot_state = self.DISCONNECTED
00326 
00327     def __on_packet(self, buf):
00328         global last_joint_states, last_joint_states_lock
00329         now = rospy.get_rostime()
00330         stateRT = RobotStateRT.unpack(buf)
00331         self.last_stateRT = stateRT
00332         
00333         msg = JointState()
00334         msg.header.stamp = now
00335         msg.header.frame_id = "From real-time state data"
00336         msg.name = joint_names
00337         msg.position = [0.0] * 6
00338         for i, q in enumerate(stateRT.q_actual):
00339             msg.position[i] = q + joint_offsets.get(joint_names[i], 0.0)
00340         msg.velocity = stateRT.qd_actual
00341         msg.effort = [0]*6
00342         pub_joint_states.publish(msg)
00343         with last_joint_states_lock:
00344             last_joint_states = msg
00345         
00346         wrench_msg = WrenchStamped()
00347         wrench_msg.header.stamp = now
00348         wrench_msg.wrench.force.x = stateRT.tcp_force[0]
00349         wrench_msg.wrench.force.y = stateRT.tcp_force[1]
00350         wrench_msg.wrench.force.z = stateRT.tcp_force[2]
00351         wrench_msg.wrench.torque.x = stateRT.tcp_force[3]
00352         wrench_msg.wrench.torque.y = stateRT.tcp_force[4]
00353         wrench_msg.wrench.torque.z = stateRT.tcp_force[5]
00354         pub_wrench.publish(wrench_msg)
00355         
00356 
00357     def __run(self):
00358         while self.__keep_running:
00359             r, _, _ = select.select([self.__sock], [], [], self.TIMEOUT)
00360             if r:
00361                 more = self.__sock.recv(4096)
00362                 if more:
00363                     self.__buf = self.__buf + more
00364                     
00365                     #unpack_from requires a buffer of at least 48 bytes
00366                     while len(self.__buf) >= 48:
00367                         # Attempts to extract a packet
00368                         packet_length = struct.unpack_from("!i", self.__buf)[0]
00369                         #print("PacketLength: ", packet_length, "; BufferSize: ", len(self.__buf))
00370                         if len(self.__buf) >= packet_length:
00371                             packet, self.__buf = self.__buf[:packet_length], self.__buf[packet_length:]
00372                             self.__on_packet(packet)
00373                         else:
00374                             break
00375                 else:
00376                     self.__trigger_disconnected()
00377                     self.__keep_running = False
00378                     
00379             else:
00380                 self.__trigger_disconnected()
00381                 self.__keep_running = False
00382 
00383 
00384 def setConnectedRobot(r):
00385     global connected_robot, connected_robot_lock
00386     with connected_robot_lock:
00387         connected_robot = r
00388         connected_robot_cond.notify()
00389 
00390 def getConnectedRobot(wait=False, timeout=-1):
00391     started = time.time()
00392     with connected_robot_lock:
00393         if wait:
00394             while not connected_robot:
00395                 if timeout >= 0 and time.time() > started + timeout:
00396                     break
00397                 connected_robot_cond.wait(0.2)
00398         return connected_robot
00399 
00400 # Receives messages from the robot over the socket
00401 class CommanderTCPHandler(SocketServer.BaseRequestHandler):
00402 
00403     def recv_more(self):
00404         global last_joint_states, last_joint_states_lock
00405         while True:
00406             r, _, _ = select.select([self.request], [], [], 0.2)
00407             if r:
00408                 more = self.request.recv(4096)
00409                 if not more:
00410                     raise EOF("EOF on recv")
00411                 return more
00412             else:
00413                 now = rospy.get_rostime()
00414                 if last_joint_states and \
00415                         last_joint_states.header.stamp < now - rospy.Duration(1.0):
00416                     rospy.logerr("Stopped hearing from robot (last heard %.3f sec ago).  Disconnected" % \
00417                                      (now - last_joint_states.header.stamp).to_sec())
00418                     raise EOF()
00419 
00420     def handle(self):
00421         self.__socket_lock = threading.Lock()
00422         setConnectedRobot(self)
00423         print "Handling a request"
00424         try:
00425             buf = self.recv_more()
00426             if not buf: return
00427 
00428             while True:
00429                 #print "Buf:", [ord(b) for b in buf]
00430 
00431                 # Unpacks the message type
00432                 mtype = struct.unpack_from("!i", buf, 0)[0]
00433                 buf = buf[4:]
00434                 #print "Message type:", mtype
00435 
00436                 if mtype == MSG_OUT:
00437                     # Unpacks string message, terminated by tilde
00438                     i = buf.find("~")
00439                     while i < 0:
00440                         buf = buf + self.recv_more()
00441                         i = buf.find("~")
00442                         if len(buf) > 2000:
00443                             raise Exception("Probably forgot to terminate a string: %s..." % buf[:150])
00444                     s, buf = buf[:i], buf[i+1:]
00445                     log("Out: %s" % s)
00446                 
00447                 elif mtype == MSG_QUIT:
00448                     print "Quitting"
00449                     raise EOF("Received quit")
00450                 elif mtype == MSG_WAYPOINT_FINISHED:
00451                     while len(buf) < 4:
00452                         buf = buf + self.recv_more()
00453                     waypoint_id = struct.unpack_from("!i", buf, 0)[0]
00454                     buf = buf[4:]
00455                     print "Waypoint finished (not handled)"
00456                 else:
00457                     raise Exception("Unknown message type: %i" % mtype)
00458 
00459                 if not buf:
00460                     buf = buf + self.recv_more()
00461         except EOF, ex:
00462             print "Connection closed (command):", ex
00463             setConnectedRobot(None)
00464 
00465     def __send_message(self, data):
00466         """
00467         Send a message to the robot.
00468 
00469         The message is given as a list of integers that will be packed
00470         as 4 bytes each in network byte order (big endian).
00471 
00472         A lock is acquired before sending the message to prevent race conditions.
00473 
00474         :param data: list of int, the data to send
00475         """
00476         buf = struct.pack("!%ii" % len(data), *data)
00477         with self.__socket_lock:
00478             self.request.send(buf)
00479 
00480     def send_quit(self):
00481         self.__send_message([MSG_QUIT])
00482 
00483     def send_servoj(self, waypoint_id, q_actual, t):
00484         assert(len(q_actual) == 6)
00485         q_robot = [0.0] * 6
00486         for i, q in enumerate(q_actual):
00487             q_robot[i] = q - joint_offsets.get(joint_names[i], 0.0)
00488         params = [MSG_SERVOJ, waypoint_id] + \
00489                  [MULT_jointstate * qq for qq in q_robot] + \
00490                  [MULT_time * t]
00491         self.__send_message(params)
00492 
00493     #Experimental set_payload implementation
00494     def send_payload(self,payload):
00495         self.__send_message([MSG_SET_PAYLOAD, payload * MULT_payload])
00496 
00497     #Experimental set_digital_output implementation
00498     def set_digital_out(self, pinnum, value):
00499         self.__send_message([MSG_SET_DIGITAL_OUT, pinnum, value])
00500         time.sleep(IO_SLEEP_TIME)
00501 
00502     def set_analog_out(self, pinnum, value):
00503         self.__send_message([MSG_SET_ANALOG_OUT, pinnum, value * MULT_analog])
00504         time.sleep(IO_SLEEP_TIME)
00505 
00506     def set_tool_voltage(self, value):
00507         self.__send_message([MSG_SET_TOOL_VOLTAGE, value, 0])
00508         time.sleep(IO_SLEEP_TIME)
00509 
00510     def set_flag(self, pin, val):
00511         self.__send_message([MSG_SET_FLAG, pin, val])
00512         #set_flag will fail if called too closely together--added delay
00513         time.sleep(IO_SLEEP_TIME)
00514 
00515     def send_stopj(self):
00516         self.__send_message([MSG_STOPJ])
00517 
00518     def set_waypoint_finished_cb(self, cb):
00519         self.waypoint_finished_cb = cb
00520 
00521     # Returns the last JointState message sent out
00522     def get_joint_states(self):
00523         global last_joint_states, last_joint_states_lock
00524         return last_joint_states
00525     
00526 
00527 class TCPServer(SocketServer.TCPServer):
00528     allow_reuse_address = True  # Allows the program to restart gracefully on crash
00529     timeout = 5
00530 
00531 
00532 # Waits until all threads have completed.  Allows KeyboardInterrupt to occur
00533 def joinAll(threads):
00534     while any(t.isAlive() for t in threads):
00535         for t in threads:
00536             t.join(0.2)
00537 
00538 # Returns the duration between moving from point (index-1) to point
00539 # index in the given JointTrajectory
00540 def get_segment_duration(traj, index):
00541     if index == 0:
00542         return traj.points[0].time_from_start.to_sec()
00543     return (traj.points[index].time_from_start - traj.points[index-1].time_from_start).to_sec()
00544 
00545 # Reorders the JointTrajectory traj according to the order in
00546 # joint_names.  Destructive.
00547 def reorder_traj_joints(traj, joint_names):
00548     order = [traj.joint_names.index(j) for j in joint_names]
00549 
00550     new_points = []
00551     for p in traj.points:
00552         new_points.append(JointTrajectoryPoint(
00553             positions = [p.positions[i] for i in order],
00554             velocities = [p.velocities[i] for i in order] if p.velocities else [],
00555             accelerations = [p.accelerations[i] for i in order] if p.accelerations else [],
00556             time_from_start = p.time_from_start))
00557     traj.joint_names = joint_names
00558     traj.points = new_points
00559 
00560 def interp_cubic(p0, p1, t_abs):
00561     T = (p1.time_from_start - p0.time_from_start).to_sec()
00562     t = t_abs - p0.time_from_start.to_sec()
00563     q = [0] * 6
00564     qdot = [0] * 6
00565     qddot = [0] * 6
00566     for i in range(len(p0.positions)):
00567         a = p0.positions[i]
00568         b = p0.velocities[i]
00569         c = (-3*p0.positions[i] + 3*p1.positions[i] - 2*T*p0.velocities[i] - T*p1.velocities[i]) / T**2
00570         d = (2*p0.positions[i] - 2*p1.positions[i] + T*p0.velocities[i] + T*p1.velocities[i]) / T**3
00571 
00572         q[i] = a + b*t + c*t**2 + d*t**3
00573         qdot[i] = b + 2*c*t + 3*d*t**2
00574         qddot[i] = 2*c + 6*d*t
00575     return JointTrajectoryPoint(positions=q, velocities=qdot, accelerations=qddot, time_from_start=rospy.Duration(t_abs))
00576 
00577 # Returns (q, qdot, qddot) for sampling the JointTrajectory at time t.
00578 # The time t is the time since the trajectory was started.
00579 def sample_traj(traj, t):
00580     # First point
00581     if t <= 0.0:
00582         return copy.deepcopy(traj.points[0])
00583     # Last point
00584     if t >= traj.points[-1].time_from_start.to_sec():
00585         return copy.deepcopy(traj.points[-1])
00586     
00587     # Finds the (middle) segment containing t
00588     i = 0
00589     while traj.points[i+1].time_from_start.to_sec() < t:
00590         i += 1
00591     return interp_cubic(traj.points[i], traj.points[i+1], t)
00592 
00593 def traj_is_finite(traj):
00594     for pt in traj.points:
00595         for p in pt.positions:
00596             if math.isinf(p) or math.isnan(p):
00597                 return False
00598         for v in pt.velocities:
00599             if math.isinf(v) or math.isnan(v):
00600                 return False
00601     return True
00602         
00603 def has_limited_velocities(traj):
00604     for p in traj.points:
00605         for v in p.velocities:
00606             if math.fabs(v) > max_velocity:
00607                 return False
00608     return True
00609 
00610 def has_velocities(traj):
00611     for p in traj.points:
00612         if len(p.velocities) != len(p.positions):
00613             return False
00614     return True
00615 
00616 def within_tolerance(a_vec, b_vec, tol_vec):
00617     for a, b, tol in zip(a_vec, b_vec, tol_vec):
00618         if abs(a - b) > tol:
00619             return False
00620     return True
00621 
00622 class URServiceProvider(object):
00623     def __init__(self, robot):
00624         self.robot = robot
00625         rospy.Service('ur_driver/setPayload', SetPayload, self.setPayload)
00626 
00627     def set_robot(self, robot):
00628         self.robot = robot
00629 
00630     def setPayload(self, req):
00631         if req.payload < min_payload or req.payload > max_payload:
00632             print 'ERROR: Payload ' + str(req.payload) + ' out of bounds (' + str(min_payload) + ', ' + str(max_payload) + ')'
00633             return False
00634         
00635         if self.robot:
00636             self.robot.send_payload(req.payload)
00637         else:
00638             return False
00639         return True
00640 
00641 class URTrajectoryFollower(object):
00642     RATE = 0.02
00643     def __init__(self, robot, goal_time_tolerance=None):
00644         self.goal_time_tolerance = goal_time_tolerance or rospy.Duration(0.0)
00645         self.joint_goal_tolerances = [0.05, 0.05, 0.05, 0.05, 0.05, 0.05]
00646         self.following_lock = threading.Lock()
00647         self.T0 = time.time()
00648         self.robot = robot
00649         self.server = actionlib.ActionServer("follow_joint_trajectory",
00650                                              FollowJointTrajectoryAction,
00651                                              self.on_goal, self.on_cancel, auto_start=False)
00652 
00653         self.goal_handle = None
00654         self.traj = None
00655         self.traj_t0 = 0.0
00656         self.first_waypoint_id = 10
00657         self.tracking_i = 0
00658         self.pending_i = 0
00659         self.last_point_sent = True
00660 
00661         self.update_timer = rospy.Timer(rospy.Duration(self.RATE), self._update)
00662 
00663     def set_robot(self, robot):
00664         # Cancels any goals in progress
00665         if self.goal_handle:
00666             self.goal_handle.set_canceled()
00667             self.goal_handle = None
00668         self.traj = None
00669         self.robot = robot
00670         if self.robot:
00671             self.init_traj_from_robot()
00672 
00673     # Sets the trajectory to remain stationary at the current position
00674     # of the robot.
00675     def init_traj_from_robot(self):
00676         if not self.robot: raise Exception("No robot connected")
00677         # Busy wait (avoids another mutex)
00678         state = self.robot.get_joint_states()
00679         while not state:
00680             time.sleep(0.1)
00681             state = self.robot.get_joint_states()
00682         self.traj_t0 = time.time()
00683         self.traj = JointTrajectory()
00684         self.traj.joint_names = joint_names
00685         self.traj.points = [JointTrajectoryPoint(
00686             positions = state.position,
00687             velocities = [0] * 6,
00688             accelerations = [0] * 6,
00689             time_from_start = rospy.Duration(0.0))]
00690 
00691     def start(self):
00692         self.init_traj_from_robot()
00693         self.server.start()
00694         print "The action server for this driver has been started"
00695 
00696     def on_goal(self, goal_handle):
00697         log("on_goal")
00698 
00699         # Checks that the robot is connected
00700         if not self.robot:
00701             rospy.logerr("Received a goal, but the robot is not connected")
00702             goal_handle.set_rejected()
00703             return
00704 
00705         # Checks if the joints are just incorrect
00706         if set(goal_handle.get_goal().trajectory.joint_names) != set(joint_names):
00707             rospy.logerr("Received a goal with incorrect joint names: (%s)" % \
00708                          ', '.join(goal_handle.get_goal().trajectory.joint_names))
00709             goal_handle.set_rejected()
00710             return
00711 
00712         if not traj_is_finite(goal_handle.get_goal().trajectory):
00713             rospy.logerr("Received a goal with infinites or NaNs")
00714             goal_handle.set_rejected(text="Received a goal with infinites or NaNs")
00715             return
00716         
00717         # Checks that the trajectory has velocities
00718         if not has_velocities(goal_handle.get_goal().trajectory):
00719             rospy.logerr("Received a goal without velocities")
00720             goal_handle.set_rejected(text="Received a goal without velocities")
00721             return
00722 
00723         # Checks that the velocities are withing the specified limits
00724         if not has_limited_velocities(goal_handle.get_goal().trajectory):
00725             message = "Received a goal with velocities that are higher than %f" % max_velocity
00726             rospy.logerr(message)
00727             goal_handle.set_rejected(text=message)
00728             return
00729 
00730         # Orders the joints of the trajectory according to joint_names
00731         reorder_traj_joints(goal_handle.get_goal().trajectory, joint_names)
00732                 
00733         with self.following_lock:
00734             if self.goal_handle:
00735                 # Cancels the existing goal
00736                 self.goal_handle.set_canceled()
00737                 self.first_waypoint_id += len(self.goal_handle.get_goal().trajectory.points)
00738                 self.goal_handle = None
00739 
00740             # Inserts the current setpoint at the head of the trajectory
00741             now = time.time()
00742             point0 = sample_traj(self.traj, now - self.traj_t0)
00743             point0.time_from_start = rospy.Duration(0.0)
00744             goal_handle.get_goal().trajectory.points.insert(0, point0)
00745             self.traj_t0 = now
00746 
00747             # Replaces the goal
00748             self.goal_handle = goal_handle
00749             self.traj = goal_handle.get_goal().trajectory
00750             self.goal_handle.set_accepted()
00751 
00752     def on_cancel(self, goal_handle):
00753         log("on_cancel")
00754         if goal_handle == self.goal_handle:
00755             with self.following_lock:
00756                 # Uses the next little bit of trajectory to slow to a stop
00757                 STOP_DURATION = 0.5
00758                 now = time.time()
00759                 point0 = sample_traj(self.traj, now - self.traj_t0)
00760                 point0.time_from_start = rospy.Duration(0.0)
00761                 point1 = sample_traj(self.traj, now - self.traj_t0 + STOP_DURATION)
00762                 point1.velocities = [0] * 6
00763                 point1.accelerations = [0] * 6
00764                 point1.time_from_start = rospy.Duration(STOP_DURATION)
00765                 self.traj_t0 = now
00766                 self.traj = JointTrajectory()
00767                 self.traj.joint_names = joint_names
00768                 self.traj.points = [point0, point1]
00769                 
00770                 self.goal_handle.set_canceled()
00771                 self.goal_handle = None
00772         else:
00773             goal_handle.set_canceled()
00774 
00775     last_now = time.time()
00776     def _update(self, event):
00777         if self.robot and self.traj:
00778             now = time.time()
00779             if (now - self.traj_t0) <= self.traj.points[-1].time_from_start.to_sec():
00780                 self.last_point_sent = False #sending intermediate points
00781                 setpoint = sample_traj(self.traj, now - self.traj_t0)
00782                 try:
00783                     self.robot.send_servoj(999, setpoint.positions, 4 * self.RATE)
00784                 except socket.error:
00785                     pass
00786                     
00787             elif not self.last_point_sent:
00788                 # All intermediate points sent, sending last point to make sure we
00789                 # reach the goal.
00790                 # This should solve an issue where the robot does not reach the final
00791                 # position and errors out due to not reaching the goal point.
00792                 last_point = self.traj.points[-1]
00793                 state = self.robot.get_joint_states()
00794                 position_in_tol = within_tolerance(state.position, last_point.positions, self.joint_goal_tolerances)
00795                 # Performing this check to try and catch our error condition.  We will always
00796                 # send the last point just in case.
00797                 if not position_in_tol:
00798                     rospy.logwarn("Trajectory time exceeded and current robot state not at goal, last point required")
00799                     rospy.logwarn("Current trajectory time: %s, last point time: %s" % \
00800                                 (now - self.traj_t0, self.traj.points[-1].time_from_start.to_sec()))
00801                     rospy.logwarn("Desired: %s\nactual: %s\nvelocity: %s" % \
00802                                           (last_point.positions, state.position, state.velocity))
00803                 setpoint = sample_traj(self.traj, self.traj.points[-1].time_from_start.to_sec())
00804 
00805                 try:
00806                     self.robot.send_servoj(999, setpoint.positions, 4 * self.RATE)
00807                     self.last_point_sent = True
00808                 except socket.error:
00809                     pass
00810                     
00811             else:  # Off the end
00812                 if self.goal_handle:
00813                     last_point = self.traj.points[-1]
00814                     state = self.robot.get_joint_states()
00815                     position_in_tol = within_tolerance(state.position, last_point.positions, [0.1]*6)
00816                     velocity_in_tol = within_tolerance(state.velocity, last_point.velocities, [0.05]*6)
00817                     if position_in_tol and velocity_in_tol:
00818                         # The arm reached the goal (and isn't moving).  Succeeding
00819                         self.goal_handle.set_succeeded()
00820                         self.goal_handle = None
00821                     #elif now - (self.traj_t0 + last_point.time_from_start.to_sec()) > self.goal_time_tolerance.to_sec():
00822                     #    # Took too long to reach the goal.  Aborting
00823                     #    rospy.logwarn("Took too long to reach the goal.\nDesired: %s\nactual: %s\nvelocity: %s" % \
00824                     #                      (last_point.positions, state.position, state.velocity))
00825                     #    self.goal_handle.set_aborted(text="Took too long to reach the goal")
00826                     #    self.goal_handle = None
00827 
00828 
00829 # joint_names: list of joints
00830 #
00831 # returns: { "joint_name" : joint_offset }
00832 def load_joint_offsets(joint_names):
00833     from lxml import etree
00834     robot_description = rospy.get_param("robot_description")
00835     doc = etree.fromstring(robot_description)
00836 
00837     # select only 'calibration_offset' elements whose parent is a joint
00838     # element with a specific value for the name attribute
00839     expr = "/robot/joint[@name=$name]/calibration_offset"
00840     result = {}
00841     for joint in joint_names:
00842         joint_elt = doc.xpath(expr, name=joint)
00843         if len(joint_elt) == 1:
00844             calibration_offset = float(joint_elt[0].get("value"))
00845             result[joint] = calibration_offset
00846             rospy.loginfo("Found calibration offset for joint \"%s\": %.4f" % (joint, calibration_offset))
00847         elif len(joint_elt) > 1:
00848             rospy.logerr("Too many joints matched on \"%s\". Please report to package maintainer(s)." % joint)
00849         else:
00850             rospy.logwarn("No calibration offset for joint \"%s\"" % joint)
00851     return result
00852 
00853 def get_my_ip(robot_ip, port):
00854     s = socket.create_connection((robot_ip, port))
00855     tmp = s.getsockname()[0]
00856     s.close()
00857     return tmp
00858 
00859 def handle_set_io(req):
00860     r = getConnectedRobot(wait=False)
00861     if r:
00862         if req.fun == req.FUN_SET_DIGITAL_OUT:
00863             r.set_digital_out(req.pin, req.state)
00864             return True
00865         elif req.fun == req.FUN_SET_FLAG:
00866             r.set_flag(req.pin, req.state)
00867             return True
00868         elif req.fun == req.FUN_SET_ANALOG_OUT:
00869             r.set_analog_out(req.pin, req.state)
00870             return True
00871         elif req.fun == req.FUN_SET_TOOL_VOLTAGE:
00872             r.set_tool_voltage(req.pin)
00873             return True
00874     else:
00875         raise ROSServiceException("Robot not connected")
00876 
00877 def set_io_server():
00878     s= rospy.Service('set_io', SetIO, handle_set_io)
00879 
00880 def reconfigure_callback(config, level):
00881     global prevent_programming
00882     prevent_programming = config.prevent_programming
00883     ## What about updating the value on the parameter server?
00884     return config
00885 
00886 def main():
00887     rospy.init_node('ur_driver', disable_signals=True)
00888     if rospy.get_param("use_sim_time", False):
00889         rospy.logwarn("use_sim_time is set!!!")
00890     
00891     global prevent_programming
00892     reconfigure_srv = Server(URDriverConfig, reconfigure_callback)
00893     
00894     prefix = rospy.get_param("~prefix", "")
00895     print "Setting prefix to %s" % prefix
00896     global joint_names
00897     joint_names = [prefix + name for name in JOINT_NAMES]
00898 
00899     # Parses command line arguments
00900     parser = optparse.OptionParser(usage="usage: %prog robot_hostname [reverse_port]")
00901     (options, args) = parser.parse_args(rospy.myargv()[1:])
00902     if len(args) < 1:
00903         parser.error("You must specify the robot hostname")
00904     elif len(args) == 1:
00905         robot_hostname = args[0]
00906         reverse_port = DEFAULT_REVERSE_PORT
00907     elif len(args) == 2:
00908         robot_hostname = args[0]
00909         reverse_port = int(args[1])
00910         if not (0 <= reverse_port <= 65535):
00911                 parser.error("You entered an invalid port number")
00912     else:
00913         parser.error("Wrong number of parameters")
00914 
00915     # Reads the calibrated joint offsets from the URDF
00916     global joint_offsets
00917     joint_offsets = load_joint_offsets(joint_names)
00918     if len(joint_offsets) > 0:
00919         rospy.loginfo("Loaded calibration offsets from urdf: %s" % joint_offsets)
00920     else:
00921         rospy.loginfo("No calibration offsets loaded from urdf")
00922 
00923     # Reads the maximum velocity
00924     # The max_velocity parameter is only used for debugging in the ur_driver. It's not related to actual velocity limits
00925     global max_velocity
00926     max_velocity = rospy.get_param("~max_velocity", MAX_VELOCITY) # [rad/s] 
00927     rospy.loginfo("Max velocity accepted by ur_driver: %s [rad/s]" % max_velocity)
00928     
00929     # Reads the minimum payload
00930     global min_payload
00931     min_payload = rospy.get_param("~min_payload", MIN_PAYLOAD)
00932     # Reads the maximum payload
00933     global max_payload
00934     max_payload = rospy.get_param("~max_payload", MAX_PAYLOAD)
00935     rospy.loginfo("Bounds for Payload: [%s, %s]" % (min_payload, max_payload))
00936     
00937 
00938     # Sets up the server for the robot to connect to
00939     server = TCPServer(("", reverse_port), CommanderTCPHandler)
00940     thread_commander = threading.Thread(name="CommanderHandler", target=server.serve_forever)
00941     thread_commander.daemon = True
00942     thread_commander.start()
00943 
00944     with open(roslib.packages.get_pkg_dir('ur_driver') + '/prog') as fin:
00945         program = fin.read() % {"driver_hostname": get_my_ip(robot_hostname, PORT), "driver_reverseport": reverse_port}
00946     connection = URConnection(robot_hostname, PORT, program)
00947     connection.connect()
00948     connection.send_reset_program()
00949     
00950     connectionRT = URConnectionRT(robot_hostname, RT_PORT)
00951     connectionRT.connect()
00952     
00953     set_io_server()
00954     
00955     service_provider = None
00956     action_server = None
00957     try:
00958         while not rospy.is_shutdown():
00959             # Checks for disconnect
00960             if getConnectedRobot(wait=False):
00961                 time.sleep(0.2)
00962                 try:
00963                     prevent_programming = rospy.get_param("~prevent_programming")
00964                     update = {'prevent_programming': prevent_programming}
00965                     reconfigure_srv.update_configuration(update)
00966                 except KeyError, ex:
00967                     print "Parameter 'prevent_programming' not set. Value: " + str(prevent_programming)
00968                     pass
00969                 if prevent_programming:
00970                     print "Programming now prevented"
00971                     connection.send_reset_program()
00972             else:
00973                 print "Disconnected.  Reconnecting"
00974                 if action_server:
00975                     action_server.set_robot(None)
00976 
00977                 rospy.loginfo("Programming the robot")
00978                 while True:
00979                     # Sends the program to the robot
00980                     while not connection.ready_to_program():
00981                         print "Waiting to program"
00982                         time.sleep(1.0)
00983                     try:
00984                         prevent_programming = rospy.get_param("~prevent_programming")
00985                         update = {'prevent_programming': prevent_programming}
00986                         reconfigure_srv.update_configuration(update)
00987                     except KeyError, ex:
00988                         print "Parameter 'prevent_programming' not set. Value: " + str(prevent_programming)
00989                         pass
00990                     connection.send_program()
00991 
00992                     r = getConnectedRobot(wait=True, timeout=1.0)
00993                     if r:
00994                         break
00995                 rospy.loginfo("Robot connected")
00996 
00997                 #provider for service calls
00998                 if service_provider:
00999                     service_provider.set_robot(r)
01000                 else:
01001                     service_provider = URServiceProvider(r)
01002                 
01003                 if action_server:
01004                     action_server.set_robot(r)
01005                 else:
01006                     action_server = URTrajectoryFollower(r, rospy.Duration(1.0))
01007                     action_server.start()
01008 
01009     except KeyboardInterrupt:
01010         try:
01011             r = getConnectedRobot(wait=False)
01012             rospy.signal_shutdown("KeyboardInterrupt")
01013             if r: r.send_quit()
01014         except:
01015             pass
01016         raise
01017 
01018 if __name__ == '__main__': main()


ur_driver
Author(s): Stuart Glaser, Shaun Edwards , Felix Messmer
autogenerated on Thu Jun 6 2019 18:26:26