shadowhand_ros.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 #
00003 # Copyright 2011 Shadow Robot Company Ltd.
00004 #
00005 # This program is free software: you can redistribute it and/or modify it
00006 # under the terms of the GNU General Public License as published by the Free
00007 # Software Foundation, either version 2 of the License, or (at your option)
00008 # any later version.
00009 #
00010 # This program is distributed in the hope that it will be useful, but WITHOUT
00011 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00012 # FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
00013 # more details.
00014 #
00015 # You should have received a copy of the GNU General Public License along
00016 # with this program.  If not, see <http://www.gnu.org/licenses/>.
00017 #
00018 import time
00019 import os
00020 import math
00021 import rospy
00022 import rospkg
00023 import threading
00024 import rosgraph.masterapi
00025 import control_msgs.msg
00026 from sr_robot_msgs.msg import sendupdate, joint, joints_data, JointControllerState
00027 from sensor_msgs.msg import JointState
00028 from std_msgs.msg import Float64
00029 from sr_hand.grasps_parser import GraspParser
00030 from sr_hand.grasps_interpoler import GraspInterpoler
00031 from sr_hand.tactile_receiver import TactileReceiver
00032 # this is only used to detect if the hand is a Gazebo hand, not to actually reconfigure anything
00033 from dynamic_reconfigure.msg import Config
00034 
00035 
00036 class Joint(object):
00037     def __init__(self, name="", motor="", j_min=0, j_max=90):
00038         self.name = name
00039         self.motor = motor
00040         self.min = j_min
00041         self.max = j_max
00042 
00043 
00044 class ShadowHand_ROS(object):
00045     """
00046     This is a python library used to easily access the shadow hand ROS interface.
00047     """
00048     def __init__(self):
00049         """
00050         Builds the library, creates the communication node in ROS and
00051         initializes the hand publisher and subscriber to the default
00052         values of shadowhand_data and sendupdate
00053         """
00054         self.allJoints = [Joint("THJ1", "smart_motor_th1", 0, 90),
00055                           Joint("THJ2", "smart_motor_th2", -40, 40),
00056                           Joint("THJ3", "smart_motor_th3", -15, 15),
00057                           Joint("THJ4", "smart_motor_th4", 0, 75),
00058                           Joint("THJ5", "smart_motor_th5", -60, 60),
00059                           Joint("FFJ0", "smart_motor_ff2", 0, 180),
00060                           Joint("FFJ3", "smart_motor_ff3"),
00061                           Joint("FFJ4", "smart_motor_ff4", -20, 20),
00062                           Joint("MFJ0", "smart_motor_mf2", 0, 180),
00063                           Joint("MFJ3", "smart_motor_mf3"),
00064                           Joint("MFJ4", "smart_motor_mf4", -20, 20),
00065                           Joint("RFJ0", "smart_motor_rf2", 0, 180),
00066                           Joint("RFJ3", "smart_motor_rf3"),
00067                           Joint("RFJ4", "smart_motor_rf4", -20, 20),
00068                           Joint("LFJ0", "smart_motor_lf2", 0, 180),
00069                           Joint("LFJ3", "smart_motor_lf3"),
00070                           Joint("LFJ4", "smart_motor_lf4", -20, 20),
00071                           Joint("LFJ5", "smart_motor_lf5", 0, 45),
00072                           Joint("WRJ1", "smart_motor_wr1", -45, 30),
00073                           Joint("WRJ2", "smart_motor_wr2", -30, 10),
00074                            ]
00075         self.handJoints = []
00076         self.armJoints = [Joint("ShoulderJRotate", "", -45, 60),
00077                           Joint("ShoulderJSwing", "", 0, 80),
00078                           Joint("ElbowJSwing", "", 0, 120),
00079                           Joint("ElbowJRotate", "", -80, 80)
00080                          ]
00081         self.lastMsg = joints_data()
00082         self.lastArmMsg = joints_data()
00083         self.cyberglove_pub = 0
00084         self.cyberglove_sub = 0
00085         self.cybergrasp_pub = 0
00086         self.cyberglove_sub = 0
00087         self.isFirstMessage = True
00088         self.isFirstMessageArm = True
00089         self.isReady = False
00090         self.liste = 0
00091         self.hasarm = 0
00092         self.dict_pos = {}
00093         self.dict_tar = {}
00094         self.dict_arm_pos = {}
00095         self.dict_arm_tar = {}
00096         self.dict_ethercat_joints = {}
00097         self.eth_publishers = {}
00098         self.eth_subscribers = {}
00099         #rospy.init_node('python_hand_library')
00100         self.sendupdate_lock = threading.Lock()
00101 
00102         self.joint_states_lock = threading.Lock()
00103 
00104         #contains the ending for the topic depending on which controllers are loaded
00105         self.topic_ending = ""
00106 
00107 
00108         ##EtherCAT hand
00109         self.activate_etherCAT_hand()
00110 
00111         ###
00112         # Grasps
00113         self.grasp_parser = GraspParser()
00114 
00115         self.rootPath = rospkg.RosPack().get_path('sr_hand')
00116         self.grasp_parser.parse_tree(self.rootPath+"/scripts/sr_hand/grasps.xml")
00117 
00118         self.grasp_interpoler = 0
00119         self.pub = rospy.Publisher('srh/sendupdate', sendupdate, queue_size=1, latch=True)
00120         self.pub_arm = rospy.Publisher('sr_arm/sendupdate', sendupdate, queue_size=1, latch=True)
00121 
00122         self.sub_arm = rospy.Subscriber('sr_arm/shadowhand_data', joints_data,self.callback_arm)
00123         self.sub = rospy.Subscriber('srh/shadowhand_data', joints_data ,self.callback)
00124 
00125         self.hand_type = self.check_hand_type()
00126 
00127         self.hand_velocity = {}
00128         self.hand_effort = {}
00129 
00130         if (self.hand_type == "etherCAT") or (self.hand_type == "gazebo"):
00131             self.joint_states_listener = rospy.Subscriber("joint_states", JointState, self.joint_states_callback)
00132             # Initialize the command publishers here, to avoid the delay caused when initializing them in the sendupdate
00133             for jnt in self.allJoints:
00134                 if jnt.name not in self.eth_publishers:
00135                     topic = "sh_"+ jnt.name.lower() + self.topic_ending+"/command"
00136                     self.eth_publishers[jnt.name] = rospy.Publisher(topic, Float64, queue_size=1, latch=True)
00137         self.tactile_receiver = TactileReceiver()
00138 
00139         threading.Thread(None, rospy.spin)
00140 
00141     def create_grasp_interpoler(self, current_step, next_step):
00142         self.grasp_interpoler = GraspInterpoler(current_step, next_step)
00143 
00144     def callback_ethercat_states(self, data, jointName):
00145         """
00146         @param data: The ROS message received which called the callback
00147         If the message is the first received, initializes the dictionnaries
00148         Else, it updates the lastMsg
00149         @param joint: a Joint object that contains the name of the joint that we receive data from
00150         """
00151         joint_data = joint(joint_name=jointName, joint_target=math.degrees(float(data.set_point)), joint_position=math.degrees(float(data.process_value)))
00152 
00153         # update the dictionary of joints
00154         self.dict_ethercat_joints[joint_data.joint_name]=joint_data
00155 
00156         # Build a CAN hand style lastMsg with the latest info in self.dict_ethercat_joints
00157         self.lastMsg = joints_data()
00158 
00159         for joint_name in self.dict_ethercat_joints.keys() :
00160             self.lastMsg.joints_list.append(self.dict_ethercat_joints[joint_name])
00161             #self.lastMsg.joints_list_length++
00162 
00163         #TODO This is not the right way to do it. We should check that messages from all the joints have been
00164         #  received (but we don't know if we have all the joints, e.g three finger hand)
00165         self.isReady = True
00166 
00167         # if self.isFirstMessage :
00168         #     self.init_actual_joints()
00169         #     for joint in self.lastMsg.joints_list :
00170         #         self.dict_pos[joint.joint_name]=joint.joint_position
00171         #         self.dict_tar[joint.joint_name]=joint.joint_target
00172         #     self.isFirstMessage = False
00173         #     self.isReady = True
00174 
00175     def callback(self, data):
00176         """
00177         @param data: The ROS message received which called the callback
00178         If the message is the first received, initializes the dictionnaries
00179         Else, it updates the lastMsg
00180         """
00181         self.lastMsg = data;
00182         if self.isFirstMessage :
00183             self.init_actual_joints()
00184             for jnt in self.lastMsg.joints_list :
00185                 self.dict_pos[jnt.joint_name]=jnt.joint_position
00186                 self.dict_tar[jnt.joint_name]=jnt.joint_target
00187             self.isFirstMessage = False
00188             self.isReady = True
00189 
00190     def callback_arm(self, data):
00191         """
00192         @param data: The ROS message received which called the callback
00193         If the message is the first received, initializes the dictionnaries
00194         Else, it updates the lastMsg
00195         """
00196         self.lastArmMsg = data
00197         if self.isFirstMessageArm :
00198             for jnt in self.lastArmMsg.joints_list :
00199                 self.dict_arm_pos[jnt.joint_name]=jnt.joint_position
00200                 self.dict_arm_tar[jnt.joint_name]=jnt.joint_target
00201 
00202     def init_actual_joints(self):
00203         """
00204         Initializes the library with just the fingers actually connected
00205         """
00206         for joint_all in self.allJoints :
00207             for joint_msg in self.lastMsg.joints_list :
00208                 if joint_msg.joint_name == joint_all.name:
00209                     self.handJoints.append(joint_all)
00210 
00211     def check_hand_type(self):
00212         """
00213         @return : true if some hand is detected
00214         """
00215         if self.check_gazebo_hand_presence():
00216             return "gazebo"
00217         elif self.check_etherCAT_hand_presence():
00218             return "etherCAT"
00219         elif self.check_CAN_hand_presence():
00220             return "CANhand"
00221         return None
00222 
00223     def check_CAN_hand_presence(self):
00224         """
00225         @return : true if the CAN hand is detected
00226         """
00227         t = 0.0
00228         while not self.isReady:
00229             time.sleep(1.0)
00230             t = t+1.0
00231             rospy.loginfo("Waiting for service since "+str(t)+" seconds...")
00232             if t >= 5.0:
00233                 rospy.logerr("No hand found. Are you sure the ROS hand is running ?")
00234                 return False
00235         return True
00236 
00237     def set_shadowhand_data_topic(self, topic):
00238         """
00239         @param topic: The new topic to be set as the hand publishing topic
00240         Set the library to listen to a new topic
00241         """
00242         print 'Changing subscriber to ' + topic
00243         self.sub = rospy.Subscriber(topic, joints_data ,self.callback)
00244 
00245     def set_sendupdate_topic(self, topic):
00246         """
00247         @param topic: The new topic to be set as the hand subscribing topic
00248         Set the library to publish to a new topic
00249         """
00250         print 'Changing publisher to ' + topic
00251         self.pub = rospy.Publisher(topic,sendupdate, latch=True)
00252 
00253     def sendupdate_from_dict(self, dicti):
00254         """
00255         @param dicti: Dictionnary containing all the targets to send, mapping the name of the joint to the value of its target
00256         Sends new targets to the hand from a dictionnary
00257         """
00258         self.sendupdate_lock.acquire()
00259 
00260         if (self.hand_type == "etherCAT") or (self.hand_type == "gazebo"):
00261             for join in dicti.keys():
00262 
00263                 if join not in self.eth_publishers:
00264                     topic = "sh_"+ join.lower() + self.topic_ending+"/command"
00265                     self.eth_publishers[join] = rospy.Publisher(topic, Float64, latch=True)
00266 
00267                 msg_to_send = Float64()
00268                 msg_to_send.data = math.radians( float( dicti[join] ) )
00269                 self.eth_publishers[join].publish(msg_to_send)
00270         elif self.hand_type == "CANhand":
00271             message = []
00272             for join in dicti.keys():
00273                 message.append(joint(joint_name=join, joint_target=dicti[join]))
00274             self.pub.publish(sendupdate(len(message), message))
00275 
00276         self.sendupdate_lock.release()
00277 
00278     def sendupdate(self, jointName, angle=0):
00279         """
00280         @param jointName: Name of the joint to update
00281         @param angle: Target of the joint, 0 if not set
00282         Sends a new target for the specified joint
00283         """
00284         self.sendupdate_lock.acquire()
00285 
00286         if (self.hand_type == "etherCAT") or (self.hand_type == "gazebo"):
00287             if jointName not in self.eth_publishers:
00288                 topic = "sh_"+ jointName.lower() + self.topic_ending + "/command"
00289                 self.eth_publishers[jointName] = rospy.Publisher(topic, Float64, latch=True)
00290 
00291             msg_to_send = Float64()
00292             msg_to_send.data = math.radians( float( angle ) )
00293             self.eth_publishers[jointName].publish(msg_to_send)
00294         elif self.hand_type == "CANhand":
00295             message = [joint(joint_name=jointName, joint_target=angle)]
00296             self.pub.publish(sendupdate(len(message), message))
00297 
00298         self.sendupdate_lock.release()
00299 
00300     def sendupdate_arm_from_dict(self, dicti):
00301         """
00302         @param dicti: Dictionnary containing all the targets to send, mapping the name of the joint to the value of its target
00303         Sends new targets to the hand from a dictionnary
00304         """
00305         message = []
00306         for join in dicti.keys():
00307             message.append(joint(joint_name=join, joint_target=dicti[join]))
00308         self.pub_arm.publish(sendupdate(len(message), message))
00309 
00310     def sendupdate_arm(self, jointName, angle=0):
00311         """
00312         @param jointName: Name of the joint to update
00313         @param angle: Target of the joint, 0 if not set
00314         Sends a new target for the specified joint
00315         """
00316         message = [joint(joint_name=jointName, joint_target=angle)]
00317         self.pub_arm.publish(sendupdate(len(message), message))
00318 
00319     def valueof(self, jointName):
00320         """
00321         @param jointName: Name of the joint to read the value
00322         @return: 'NaN' if the value is not correct, the actual position of the joint else
00323         """
00324         for jnt in self.lastMsg.joints_list:
00325             if jnt.joint_name == jointName:
00326                 return float(jnt.joint_position)
00327         for jnt in self.lastArmMsg.joints_list:
00328             if jnt.joint_name == jointName:
00329                 return float(jnt.joint_position)
00330         return 'NaN'
00331 
00332     def has_arm(self):
00333         """
00334         @return : True if an arm is detected on the roscore
00335         """
00336         if not self.hasarm == 0:
00337             return self.hasarm
00338         self.hasarm = False
00339         if self.liste == 0:
00340             master = rosgraph.masterapi.Master('/rostopic')
00341             self.liste = master.getPublishedTopics('/')
00342         for topic_typ in self.liste :
00343             for topic in topic_typ:
00344                 if 'sr_arm/shadowhand_data' in topic :
00345                     self.hasarm = True
00346         return self.hasarm
00347 
00348     def record_step_to_file(self, filename, grasp_as_xml):
00349         """
00350         @param filename: name (or path) of the file to save to
00351         @param grasp_as_xml: xml-formatted grasp
00352         Write the grasp at the end of the file, creates the file if does not exist
00353         """
00354         if os.path.exists(filename) :
00355             obj = open(filename, 'r')
00356             text = obj.readlines()
00357             obj.close()
00358             objWrite = open(filename,'w')
00359             for index in range(0,len(text)-1):
00360                 objWrite.write(text[index])
00361             objWrite.write(grasp_as_xml)
00362             objWrite.write('</root>')
00363             objWrite.close()
00364         else :
00365             objWrite = open(filename,'w')
00366             objWrite.write('<root>\n')
00367             objWrite.write(grasp_as_xml)
00368             objWrite.write('</root>')
00369             objWrite.close()
00370 
00371     def save_hand_position_to_file(self, filename):
00372         objFile=open(filename,'w')
00373         for key, value in self.dict_pos:
00374             objFile.write(key + ' ' + value + '\n')
00375         objFile.close()
00376 
00377     def read_all_current_positions(self):
00378         """
00379         @return: dictionnary mapping joint names to actual positions
00380         Read all the positions in the lastMsg
00381         """
00382         if not self.isReady:
00383             return
00384         for jnt in self.lastMsg.joints_list:
00385             self.dict_pos[jnt.joint_name] = jnt.joint_position
00386         return self.dict_pos
00387 
00388     def read_all_current_targets(self):
00389         """
00390         @return: dictionnary mapping joint names to current targets
00391         Read all the targets in the lastMsg
00392         """
00393         for jnt in self.lastMsg.joints_list:
00394             self.dict_tar[jnt.joint_name] = jnt.joint_target
00395         return self.dict_tar
00396 
00397     def read_all_current_velocities(self):
00398         """
00399         @return: dictionary mapping joint names to current velocities
00400         """
00401         with self.joint_states_lock:
00402             return self.hand_velocity
00403 
00404     def read_all_current_efforts(self):
00405         """
00406         @return: dictionary mapping joint names to current efforts
00407         """
00408         with self.joint_states_lock:
00409             return self.hand_effort
00410 
00411     def read_all_current_arm_positions(self):
00412         """
00413         @return: dictionnary mapping joint names to actual positions
00414         Read all the positions in the lastMsg
00415         """
00416         for jnt in self.lastArmMsg.joints_list:
00417             self.dict_arm_pos[jnt.joint_name] = jnt.joint_position
00418         return self.dict_arm_pos
00419 
00420     def read_all_current_arm_targets(self):
00421         """
00422         @return: dictionnary mapping joint names to actual targets
00423         Read all the targets in the lastMsg
00424         """
00425         for jnt in self.lastArmMsg.joints_list:
00426             self.dict_arm_tar[jnt.joint_name] = jnt.joint_target
00427         return self.dict_arm_tar
00428 
00429     def resend_targets(self):
00430         """
00431         Resend the targets read in the lastMsg to the hand
00432         """
00433         for key, value in self.dict_tar.items():
00434             self.sendupdate(jointName=key, angle=value)
00435 
00436     def callVisualisationService(self, callList=0, reset = 0):
00437         """
00438         @param callList: dictionnary mapping joint names to information that should be displayed
00439         @param reset: flag used to tell if the parameters should be replaced by the new ones or just added to the previous ones
00440         Calls a ROS service to display various information in Rviz
00441         """
00442         if reset == 0:
00443             print 'no reset'
00444 
00445         if reset == 1:
00446             print 'reset'
00447 
00448     def check_etherCAT_hand_presence(self):
00449         """
00450         Only used to check if a real etherCAT hand is detected in the system
00451         check if something is being published to this topic, otherwise
00452         return false
00453         Bear in mind that the gazebo hand also publishes the joint_states topic,
00454         so we need to check for the gazebo hand first
00455         """
00456 
00457         try:
00458             rospy.wait_for_message("joint_states", JointState, timeout = 0.2)
00459         except rospy.ROSException:
00460             rospy.logwarn("no message received from joint_states")
00461             return False
00462 
00463         return True
00464 
00465     def check_gazebo_hand_presence(self):
00466         """
00467         Only used to check if a Gazebo simulated (etherCAT protocol) hand is detected in the system
00468         check if something is being published to this topic, otherwise
00469         return false
00470         """
00471 
00472         try:
00473             rospy.wait_for_message("gazebo/parameter_updates", Config, timeout = 0.2)
00474         except rospy.ROSException:
00475             return False
00476 
00477         return True
00478 
00479     def activate_etherCAT_hand(self):
00480         """
00481         At the moment we just try to use the mixed position velocity controllers
00482         """
00483         success = True
00484         for joint_all in self.allJoints :
00485             self.topic_ending = "_position_controller"
00486             topic = "sh_"+ joint_all.name.lower() + self.topic_ending + "/state"
00487             success = True
00488             try:
00489                 rospy.wait_for_message(topic, control_msgs.msg.JointControllerState, timeout = 0.2)
00490             except:
00491                 try:
00492                     self.topic_ending = "_mixed_position_velocity_controller"
00493                     topic = "sh_"+ joint_all.name.lower() + self.topic_ending + "/state"
00494                     rospy.wait_for_message(topic, JointControllerState, timeout = 0.2)
00495                 except rospy.ROSException:
00496                     success = False
00497 
00498             if success:
00499                 if self.topic_ending == "_mixed_position_velocity_controller":
00500                     self.eth_subscribers[joint_all.name] = rospy.Subscriber(topic, JointControllerState, self.callback_ethercat_states, joint_all.name)
00501                 else:
00502                     self.eth_subscribers[joint_all.name] = rospy.Subscriber(topic,
00503                                                                             control_msgs.msg.JointControllerState,
00504                                                                             self.callback_ethercat_states,
00505                                                                             joint_all.name)
00506 
00507         if len(self.eth_subscribers) > 0:
00508             return True
00509 
00510         return False
00511 
00512     def joint_states_callback(self, joint_state):
00513         """
00514         The callback function for the topic joint_states.
00515         It will store the received joint velocity and effort information in two dictionaries
00516         Velocity will be converted to degrees/s.
00517         Effort units are kept as they are (currently ADC units, as no calibration is performed on the strain gauges)
00518 
00519         @param joint_state: the message containing the joints data.
00520         """
00521         with self.joint_states_lock:
00522             self.hand_velocity = {n:math.degrees(v) for n,v in zip(joint_state.name, joint_state.velocity)}
00523             self.hand_effort = {n:e for n,e in zip(joint_state.name, joint_state.effort)}
00524 
00525             for finger in ['FF', 'MF', 'RF', 'LF']:
00526                 for dic in [self.hand_velocity, self.hand_effort]:
00527                     if (finger + 'J1') in dic and (finger + 'J2') in dic:
00528                         dic[finger + 'J0'] = dic[finger + 'J1'] + dic[finger + 'J2']
00529                         del dic[finger + 'J1']
00530                         del dic[finger + 'J2']
00531 
00532     def get_tactile_type(self):
00533         return self.tactile_receiver.get_tactile_type()
00534 
00535     def get_tactile_state(self):
00536         return self.tactile_receiver.get_tactile_state()


sr_hand
Author(s): Ugo Cupcic
autogenerated on Fri Aug 21 2015 12:24:55