00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
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
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
00100 self.sendupdate_lock = threading.Lock()
00101
00102 self.joint_states_lock = threading.Lock()
00103
00104
00105 self.topic_ending = ""
00106
00107
00108
00109 self.activate_etherCAT_hand()
00110
00111
00112
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
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
00154 self.dict_ethercat_joints[joint_data.joint_name]=joint_data
00155
00156
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
00162
00163
00164
00165 self.isReady = True
00166
00167
00168
00169
00170
00171
00172
00173
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()