00001 '''Robot class that provides an interface to arm and head (maybe base later)'''
00002
00003
00004
00005
00006
00007
00008 import rospy
00009 import roslib
00010
00011
00012 import os
00013
00014
00015 from sound_play.libsoundplay import SoundClient
00016 from std_srvs.srv import Empty
00017 from actionlib import SimpleActionClient
00018 from actionlib_msgs.msg import GoalStatus
00019
00020
00021 from fetch_pbd_interaction.srv import MoveArm, GetGripperState, \
00022 SetGripperState, GetArmMovement, \
00023 GetEEPose, GetJointStates, GetGazeGoal, \
00024 GetNearestObject, MoveArmTraj
00025 from fetch_social_gaze.msg import GazeGoal, GazeAction
00026 from fetch_pbd_interaction.msg import ArmState, Landmark
00027 from fetch_arm_control.msg import GripperState
00028
00029
00030
00031
00032
00033
00034 BASE_LINK = 'base_link'
00035
00036 PKG = 'fetch_pbd_interaction'
00037
00038
00039 SOUNDS_DIR = os.path.join(roslib.packages.get_pkg_dir(PKG), 'sounds', '')
00040
00041
00042 SOUND_FILEFORMAT = '.wav'
00043
00044
00045
00046
00047
00048 class Robot:
00049 '''Robot class that provides an interface to arm
00050 and head (maybe base later)'''
00051
00052 def __init__(self, tf_listener, play_sound=True, social_gaze=True):
00053 '''
00054 Args:
00055 tf_listener (TransformListener)
00056 '''
00057 self._social_gaze = social_gaze
00058 self._play_sound = play_sound
00059 self._tf_listener = tf_listener
00060
00061
00062 self.move_arm_to_joints_plan_srv = rospy.ServiceProxy(
00063 '/fetch_pbd/move_arm_to_joints_plan',
00064 MoveArm)
00065 rospy.wait_for_service('/fetch_pbd/move_arm_to_joints_plan')
00066
00067 self.move_arm_to_joints_srv = rospy.ServiceProxy(
00068 '/fetch_pbd/move_arm_to_joints',
00069 MoveArmTraj)
00070 rospy.wait_for_service('/fetch_pbd/move_arm_to_joints')
00071
00072 self.move_arm_to_pose_srv = rospy.ServiceProxy(
00073 '/fetch_pbd/move_arm_to_pose',
00074 MoveArm)
00075 rospy.wait_for_service('/fetch_pbd/move_arm_to_pose')
00076
00077 self.start_move_arm_to_pose_srv = rospy.ServiceProxy(
00078 '/fetch_pbd/start_move_arm_to_pose',
00079 MoveArm)
00080 rospy.wait_for_service('/fetch_pbd/start_move_arm_to_pose')
00081
00082 self.is_reachable_srv = rospy.ServiceProxy('/fetch_pbd/is_reachable', MoveArm)
00083 rospy.wait_for_service('/fetch_pbd/is_reachable')
00084
00085 self.is_arm_moving_srv = rospy.ServiceProxy(
00086 '/fetch_pbd/is_arm_moving', GetArmMovement)
00087 rospy.wait_for_service('/fetch_pbd/is_arm_moving')
00088
00089 self.relax_arm_srv = rospy.ServiceProxy('/fetch_pbd/relax_arm', Empty)
00090 rospy.wait_for_service('/fetch_pbd/relax_arm')
00091
00092 self.reset_arm_movement_history_srv = rospy.ServiceProxy(
00093 '/fetch_pbd/reset_arm_movement_history',
00094 Empty)
00095 rospy.wait_for_service('/fetch_pbd/reset_arm_movement_history')
00096
00097 self.get_gripper_state_srv = rospy.ServiceProxy(
00098 '/fetch_pbd/get_gripper_state',
00099 GetGripperState)
00100 rospy.wait_for_service('/fetch_pbd/get_gripper_state')
00101
00102 self.get_joint_states_srv = rospy.ServiceProxy(
00103 '/fetch_pbd/get_joint_states',
00104 GetJointStates)
00105 rospy.wait_for_service('/fetch_pbd/get_joint_states')
00106
00107 self.get_ee_pose_srv = rospy.ServiceProxy('/fetch_pbd/get_ee_pose',
00108 GetEEPose)
00109 rospy.wait_for_service('/fetch_pbd/get_ee_pose')
00110
00111 self.set_gripper_state_srv = rospy.ServiceProxy(
00112 '/fetch_pbd/set_gripper_state',
00113 SetGripperState)
00114 rospy.wait_for_service('/fetch_pbd/set_gripper_state')
00115
00116 rospy.loginfo("Got all arm services")
00117
00118
00119
00120 self.gaze_client = SimpleActionClient('/fetch_pbd/gaze_action',
00121 GazeAction)
00122
00123 self.gaze_client.wait_for_server(rospy.Duration(5))
00124
00125 self.current_gaze_goal_srv = rospy.ServiceProxy(
00126 '/fetch_pbd/get_current_gaze_goal',
00127 GetGazeGoal)
00128 rospy.wait_for_service('/fetch_pbd/get_current_gaze_goal')
00129
00130 rospy.loginfo("Got all head services")
00131
00132
00133
00134 self._get_nearest_object_srv = rospy.ServiceProxy(
00135 '/fetch_pbd/get_nearest_object',
00136 GetNearestObject)
00137 rospy.wait_for_service('/fetch_pbd/get_nearest_object')
00138
00139 rospy.loginfo("Got all world services")
00140
00141
00142
00143 self._sound_client = SoundClient()
00144
00145
00146
00147 def move_arm_to_pose(self, arm_state):
00148 '''Move robot's arm to pose
00149
00150 Args:
00151 arm_state (ArmState) : contains pose info
00152 Return:
00153 bool : success
00154 '''
00155 try:
00156 result = self.move_arm_to_pose_srv(arm_state).success
00157 except Exception, e:
00158 result = False
00159 return result
00160
00161 def start_move_arm_to_pose(self, arm_state):
00162 '''Start thread to move robot's arm to pose
00163
00164 Args:
00165 arm_state (ArmState) : contains pose info
00166 Return:
00167 bool : success
00168 '''
00169
00170 try:
00171 result = self.start_move_arm_to_pose_srv(arm_state).success
00172 except Exception, e:
00173 result = False
00174 return result
00175
00176 def move_arm_to_joints_plan(self, arm_state):
00177 '''Move robot's arm to joints positions from arm_state
00178 using Moveit to plan
00179
00180 Args:
00181 arm_state (ArmState) : contains joint position info
00182 Return:
00183 bool : success
00184 '''
00185 try:
00186 result = self.move_arm_to_joints_plan_srv(arm_state).success
00187 except Exception, e:
00188 result = False
00189 return result
00190 def move_arm_to_joints(self, arm_states, times):
00191 '''Move robot's arm to joints positions from arm_state
00192 without planning, just joint interpolation
00193
00194 Args:
00195 arm_states ([ArmState]) : contains joint position info
00196 Return:
00197 bool : success
00198 '''
00199 try:
00200 result = self.move_arm_to_joints_srv(arm_states, times).success
00201 except Exception, e:
00202 result = False
00203 return result
00204
00205 def can_reach(self, arm_state):
00206 '''Returns whether arm can reach pose
00207
00208 Args:
00209 arm_state (ArmState) : contains pose info
00210 Return:
00211 bool : success
00212 '''
00213 try:
00214 result = self.is_reachable_srv(arm_state).success
00215 except Exception, e:
00216 result = False
00217 return result
00218
00219 def reset_arm_movement_history(self):
00220 '''Resets movement history of arm'''
00221 try:
00222 self.reset_arm_movement_history_srv()
00223 except Exception, e:
00224 pass
00225 return
00226
00227 def get_gripper_state(self):
00228 '''Returns state of gripper
00229
00230 Returns:
00231 GripperState.OPEN|GripperState.CLOSED
00232 '''
00233 try:
00234 result = self.get_gripper_state_srv().gripper_state
00235 except Exception, e:
00236
00237 result = GripperState.CLOSED
00238 return result
00239
00240 def set_gripper_state(self, gripper_state):
00241 '''Sets state of gripper. Assumed to succeed
00242
00243 Args:
00244 gripper_state (GripperState.OPEN|GripperState.CLOSED)
00245 '''
00246 try:
00247 self.set_gripper_state_srv(gripper_state)
00248 except Exception, e:
00249 pass
00250 return
00251
00252 def get_arm_state(self):
00253 '''Returns current state of arm
00254
00255 Returns:
00256 ArmState
00257 '''
00258 abs_ee_pose = self.get_ee_pose_srv().ee_pose
00259 joint_pose = self.get_joint_states_srv().joints
00260
00261 state = None
00262 rel_ee_pose = None
00263 try:
00264 resp = self._get_nearest_object_srv(
00265 abs_ee_pose)
00266 has_nearest = resp.has_nearest
00267 except Exception, e:
00268 rospy.logwarn(str(e))
00269 has_nearest = False
00270
00271 if not has_nearest:
00272
00273 state = ArmState(
00274 ArmState.ROBOT_BASE,
00275 abs_ee_pose,
00276 joint_pose,
00277 [],
00278 Landmark()
00279 )
00280 else:
00281 nearest_obj = resp.nearest_object
00282
00283
00284
00285 rel_ee_pose = self._tf_listener.transformPose(
00286 nearest_obj.name, abs_ee_pose)
00287
00288 state = ArmState(
00289 ArmState.OBJECT,
00290 rel_ee_pose,
00291 joint_pose,
00292 [],
00293 nearest_obj
00294 )
00295
00296 return state
00297
00298 def relax_arm(self):
00299 '''Make sure gravity compensation controller is on and other
00300 controllers are off
00301 '''
00302 try:
00303 self.relax_arm_srv()
00304 except Exception, e:
00305 pass
00306 return
00307
00308 def is_arm_moving(self):
00309 '''Check if arm is currently moving
00310
00311 Returns:
00312 bool : True if arm is moving, else False
00313 '''
00314 try:
00315 result = self.is_arm_moving_srv().moving
00316 except Exception, e:
00317 result = False
00318 return result
00319
00320
00321
00322 def shake_head(self, num=5):
00323 '''Shakes robot's head
00324
00325 Args:
00326 num (int) : number of times to perform action
00327 '''
00328 rospy.loginfo("Head shake requested")
00329 if self._social_gaze:
00330 try:
00331 goal = GazeGoal()
00332 goal.action = GazeGoal.SHAKE
00333 goal.repeat = num
00334 current_goal = self.current_gaze_goal_srv().gaze_goal
00335 if goal.action != current_goal:
00336 self.gaze_client.send_goal(goal)
00337 self.gaze_client.wait_for_result()
00338 rospy.loginfo("Gaze result: {}".format(self.gaze_client.get_result()))
00339 else:
00340 rospy.loginfo("Gaze goal is same as previous")
00341 except Exception, e:
00342 rospy.logwarn("Fetch social gaze exception: {}".format(str(e)))
00343
00344 def nod_head(self, num=5):
00345 '''Nods robot's head
00346
00347 Args:
00348 num (int) : number of times to perform action
00349 '''
00350 rospy.loginfo("Head nod requested")
00351 if self._social_gaze:
00352 try:
00353 goal = GazeGoal()
00354 goal.action = GazeGoal.NOD
00355 goal.repeat = num
00356 current_goal = self.current_gaze_goal_srv().gaze_goal
00357 if goal.action != current_goal:
00358 self.gaze_client.send_goal(goal)
00359 self.gaze_client.wait_for_result()
00360 rospy.loginfo("Gaze result: {}".format(self.gaze_client.get_result()))
00361 else:
00362 rospy.loginfo("Gaze goal is same as previous")
00363 except Exception, e:
00364 rospy.logwarn("Fetch social gaze exception: {}".format(str(e)))
00365
00366 def look_at_point(self, point):
00367 '''Points robot's head at point
00368
00369 Args:
00370 point (Point)
00371 '''
00372 if self._social_gaze:
00373 try:
00374 goal = GazeGoal()
00375 goal.action = GazeGoal.LOOK_AT_POINT
00376 goal.point = point
00377 current_goal = self.current_gaze_goal_srv().gaze_goal
00378 if goal.action != current_goal:
00379 self.gaze_client.send_goal(goal)
00380 except Exception, e:
00381 pass
00382
00383 def look_at_ee(self, follow=True):
00384 '''Makes head look at (or follow) end effector position
00385
00386 Args:
00387 follow (bool, optional) : If True, follow end effector,
00388 else, just glance at end effector
00389 '''
00390
00391 if self._social_gaze:
00392 try:
00393 goal = GazeGoal()
00394 if follow:
00395 goal.action = GazeGoal.FOLLOW_EE
00396 else:
00397 goal.action = GazeGoal.GLANCE_EE
00398 current_goal = self.current_gaze_goal_srv().gaze_goal
00399 if goal.action != current_goal:
00400 self.gaze_client.send_goal(goal)
00401 except Exception, e:
00402 pass
00403
00404 def look_forward(self):
00405 '''Point head forward'''
00406 if self._social_gaze:
00407 try:
00408 goal = GazeGoal()
00409 goal.action = GazeGoal.LOOK_FORWARD
00410 current_goal = self.current_gaze_goal_srv().gaze_goal
00411 if goal.action != current_goal:
00412 self.gaze_client.send_goal(goal)
00413 except Exception, e:
00414 pass
00415
00416 def look_down(self):
00417 '''Point head down at table'''
00418
00419
00420 try:
00421 rospy.loginfo("Requesting to look down")
00422 goal = GazeGoal()
00423 goal.action = GazeGoal.LOOK_DOWN
00424 current_goal = self.current_gaze_goal_srv().gaze_goal
00425 if goal.action != current_goal:
00426 self.gaze_client.send_goal(goal)
00427 while (self.gaze_client.get_state() == GoalStatus.PENDING or
00428 self.gaze_client.get_state() == GoalStatus.ACTIVE):
00429 rospy.sleep(0.2)
00430 rospy.sleep(0.5)
00431 except Exception, e:
00432 rospy.logwarn("Gaze error: {}".format(e))
00433
00434
00435
00436 def play_sound(self, requested_sound):
00437 '''Play sound that is requested
00438
00439 Args:
00440 requested_sound (RobotSound.ERROR|etc...) : see RobotSound.msg
00441 '''
00442 try:
00443 if self._play_sound:
00444 self._sound_client.playWave(
00445 os.path.join(SOUNDS_DIR, requested_sound + SOUND_FILEFORMAT))
00446 except Exception, e:
00447 rospy.loginfo(e)