arm_control.py
Go to the documentation of this file.
00001 '''Control of the arm for action execution.'''
00002 
00003 # ######################################################################
00004 # Imports
00005 # ######################################################################
00006 
00007 # Core ROS imports come first.
00008 import rospy
00009 
00010 # System builtins
00011 import threading
00012 
00013 # ROS builtins
00014 from std_srvs.srv import Empty, EmptyResponse
00015 from tf import TransformListener
00016 
00017 # Local
00018 from fetch_arm_control import Arm
00019 from fetch_arm_control.msg import GripperState
00020 from fetch_pbd_interaction.msg import ArmState, ExecutionStatus
00021 from fetch_pbd_interaction.srv import MoveArm, MoveArmResponse, \
00022                                       GetEEPose, GetEEPoseResponse, \
00023                                       GetGripperState, \
00024                                       GetGripperStateResponse, \
00025                                       SetGripperState, \
00026                                       SetGripperStateResponse, \
00027                                       GetJointStates, GetJointStatesResponse, \
00028                                       GetArmMovement, GetArmMovementResponse, \
00029                                       MoveArmTraj, MoveArmTrajResponse
00030 
00031 # ######################################################################
00032 # Module level constants
00033 # ######################################################################
00034 
00035 # The minimum arm movement that 'counts' as moved.
00036 # TODO(mbforbes): This is duplicated in arm.py. Use theirs.
00037 ARM_MOVEMENT_THRESHOLD = 0.04
00038 
00039 # How long to sleep between checking whether arms have successfully
00040 # completed their movement.
00041 MOVE_TO_JOINTS_SLEEP_INTERVAL = 0.01  # seconds
00042 
00043 # How long to sleep between checking whether arms have successfully
00044 # completed their trajectory.
00045 TRAJECTORY_COMPLETE_SLEEP_INTERVAL = 0.01  # seconds
00046 
00047 # How long to sleep between checking whether grippers have finished
00048 # opening/closing.
00049 GRIPPER_FINISH_SLEEP_INTERVAL = 0.01  # seconds
00050 
00051 
00052 # ######################################################################
00053 # Classes
00054 # ######################################################################
00055 
00056 class ArmControl:
00057     '''Higher-level interface that exposes capabilities from
00058     fetch_arm_control/Arm.py class as services.
00059     '''
00060 
00061     def __init__(self):
00062 
00063         # Initialize arm state.
00064         self._tf_listener = TransformListener()
00065         self._arm = Arm(self._tf_listener)
00066         self._arm.close_gripper()
00067         self._status = ExecutionStatus.NOT_EXECUTING
00068 
00069         rospy.Service('/fetch_pbd/move_arm_to_joints_plan', MoveArm,
00070                       self._move_to_joints_plan)
00071         rospy.Service('/fetch_pbd/move_arm_to_joints', MoveArmTraj, self._move_to_joints)
00072 
00073         rospy.Service('/fetch_pbd/move_arm_to_pose', MoveArm, self._move_to_pose)
00074         rospy.Service('/fetch_pbd/start_move_arm_to_pose', MoveArm,
00075                       self._start_move_to_pose)
00076 
00077         rospy.Service('/fetch_pbd/is_reachable', MoveArm, self._is_reachable)
00078         rospy.Service('/fetch_pbd/is_arm_moving', GetArmMovement, self._is_arm_moving)
00079 
00080 
00081         rospy.Service('/fetch_pbd/relax_arm', Empty, self._relax_arm)
00082 
00083         rospy.Service('/fetch_pbd/reset_arm_movement_history', Empty,
00084                       self._reset_movement_history)
00085 
00086         rospy.Service('/fetch_pbd/get_gripper_state', GetGripperState,
00087                       self._get_gripper_state)
00088         rospy.Service('/fetch_pbd/get_ee_pose', GetEEPose, self._get_ee_pose)
00089         rospy.Service('/fetch_pbd/get_joint_states', GetJointStates,
00090                       self._get_joint_states)
00091 
00092         rospy.Service('/fetch_pbd/set_gripper_state', SetGripperState,
00093                       self._set_gripper_state)
00094 
00095         rospy.loginfo('Arm initialized.')
00096 
00097     # ##################################################################
00098     # Instance methods: Public (API)
00099     # ##################################################################
00100 
00101     def update(self):
00102         '''Periodic update for the arm.'''
00103         self._arm.update()
00104 
00105     # ##################################################################
00106     # Instance methods: Internal ("private")
00107     # ##################################################################
00108 
00109     def _reset_movement_history(self, req):
00110         '''
00111         Args:
00112             req (EmptyRequest)
00113         Returns:
00114             EmptyResponse
00115         '''
00116 
00117         self._arm.reset_movement_history()
00118         return EmptyResponse()
00119 
00120     def _set_gripper_state(self, req):
00121         '''Set gripper to gripper_state
00122         (open or closed).
00123 
00124         Args:
00125             req (SetGripperStateRequest)
00126 
00127         Returns:
00128             SetGripperStateResponse
00129         '''
00130         gripper_state = req.gripper_state
00131         if gripper_state == self._arm.get_gripper_state():
00132             # Already in that mode; do nothing and return False.
00133             return SetGripperStateResponse()
00134         else:
00135             # Change gripper mode.
00136             if gripper_state == GripperState.OPEN:
00137                 self._arm.open_gripper()
00138             else:
00139                 self._arm.close_gripper()
00140             return SetGripperStateResponse()
00141 
00142     def _is_reachable(self, req):
00143         '''.
00144 
00145         Args:
00146             req (MoveArmRequest): The arm's state
00147 
00148         Returns:
00149             MoveArmResponse
00150         '''
00151         arm_state = req.arm_state
00152         solution, reachable = self._solve_ik_for_arm(arm_state)
00153 
00154         return MoveArmResponse(reachable)
00155 
00156     def _solve_ik_for_arm(self, arm_state, z_offset=0.0):
00157         '''Finds an  IK solution for a particular arm pose.
00158 
00159         Args:
00160             arm_state (ArmState): The arm's state,
00161             z_offset (float, optional): Offset to add to z-values of
00162                 pose positions. Defaults to 0.0.
00163 
00164         Returns:
00165             (ArmState, bool): Tuple of
00166 
00167                 the ArmState, which will be an updated ArmState object
00168                 with IK solved if an IK solution was found. If IK was
00169                 not found, what is returned will depend on whether the
00170                 reference frame is relative (ArmState.OBJECT) or
00171                 absolute (ArmState.ROBOT_BASE). If the reference frame
00172                 is relative and no IK was found, an empty ArmState is
00173                 returned. If the reference frame is absolute and no IK
00174                 was found, then the original passed arm_state is
00175                 returned;
00176 
00177                 the bool, which is whether an IK solution was
00178                 successfully found.
00179         '''
00180         # Ideally we need to find IK only if the frame is relative to an
00181         # object, but users can edit absolute poses in the GUI to make
00182         # them unreachable, so we try IK for absolute poses too.
00183 
00184         if arm_state.ref_type == ArmState.OBJECT:
00185             # Arm is relative.
00186             solution = ArmState()
00187 
00188             target_pose = self._tf_listener.transformPose(
00189                 'base_link', arm_state.ee_pose)
00190             target_pose.pose.position.z = target_pose.pose.position.z + \
00191                                             z_offset
00192 
00193             # Try solving IK.
00194             target_joints = self._arm.get_ik_for_ee(
00195                 target_pose, arm_state.joint_pose)
00196 
00197             # Check whether solution found.
00198             if target_joints is None:
00199                 # No solution: RETURN EMPTY ArmState.
00200                 rospy.loginfo('No IK for relative end-effector pose.')
00201                 return solution, False
00202             else:
00203                 # Found a solution; update the solution arm_state and
00204                 # return.
00205                 solution.ref_type = ArmState.ROBOT_BASE
00206                 solution.ee_pose = target_pose
00207                 solution.joint_pose = target_joints
00208                 return solution, True
00209         elif arm_state.ref_type == ArmState.ROBOT_BASE:
00210             # Arm is absolute.
00211 
00212             target_pose = arm_state.ee_pose
00213             target_pose.pose.position.z = target_pose.pose.position.z + \
00214                                             z_offset
00215 
00216             # Try solving IK.
00217             target_joints = self._arm.get_ik_for_ee(
00218                 target_pose, arm_state.joint_pose)
00219             if target_joints is None:
00220                 # No IK found; return the original.
00221                 rospy.logdebug('No IK for absolute end-effector pose.')
00222                 return arm_state, False
00223             else:
00224                 # IK found; fill in solution ArmState and return.
00225                 solution = ArmState()
00226                 solution.ref_type = ArmState.ROBOT_BASE
00227                 solution.ee_pose = target_pose
00228                 solution.joint_pose = target_joints
00229                 return solution, True
00230         elif arm_state.ref_type == ArmState.PREVIOUS_TARGET:
00231             # Arm is relative to previous primitive
00232 
00233             target_pose = arm_state.ee_pose
00234             target_pose.pose.position.z = target_pose.pose.position.z + \
00235                                             z_offset
00236 
00237             # Try solving IK.
00238             target_joints = self._arm.get_ik_for_ee(
00239                 target_pose, arm_state.joint_pose)
00240             if target_joints is None:
00241                 # No IK found; return the original.
00242                 rospy.logdebug('No IK for end-effector pose' +  
00243                                 'relative to previous target.')
00244                 return arm_state, False
00245             else:
00246                 # IK found; fill in solution ArmState and return.
00247                 solution = ArmState()
00248                 solution.ref_type = ArmState.PREVIOUS_TARGET
00249                 solution.ee_pose = target_pose
00250                 solution.joint_pose = target_joints
00251                 return solution, True
00252         else:
00253             return arm_state, True
00254 
00255     def _get_joint_states(self, req):
00256         '''Get joint positions.
00257 
00258         Args:
00259             req (GetJointStatesRequest)
00260 
00261         Returns:
00262             GetJointStatesResponse
00263         '''
00264 
00265         return GetJointStatesResponse(self._arm.get_joint_state())
00266 
00267     def _get_gripper_state(self, req):
00268         ''' Get gripper status on the indicated side.
00269 
00270         Args:
00271             req (GetGripperStateRequest)
00272 
00273         Returns:
00274             GetGripperStateResponse
00275         '''
00276         return GetGripperStateResponse(self._arm.get_gripper_state())
00277 
00278 
00279     def _get_ee_pose(self, req):
00280         ''' Get current pose of the arm's end-effector on the indicated
00281         side.
00282 
00283         Args:
00284             req (GetEEPoseRequest)
00285 
00286         Returns:
00287             GetEEPoseResponse
00288         '''
00289         return GetEEPoseResponse(self._arm.get_ee_state())
00290 
00291     def _relax_arm(self, req):
00292         ''' Turns on gravity comp controller and turns off other controllers
00293 
00294         Args:
00295             req (EmptyRequest): Unused
00296 
00297         Returns:
00298             EmptyResponse
00299         '''
00300 
00301         self._arm.relax_arm()
00302 
00303         return EmptyResponse()
00304 
00305     def _start_move_to_pose(self, req):
00306         '''Creates a thread for moving to a target pose.
00307 
00308         Args:
00309             req (MoveArmRequest): Arm state that contains the pose to
00310                 move to.
00311         '''
00312         thread = threading.Thread(
00313             group=None,
00314             target=self._move_to_pose,
00315             args=(req,),
00316             name='move_to_arm_state_thread'
00317         )
00318         thread.start()
00319 
00320         # Log
00321         rospy.loginfo('Started thread to move arm.')
00322 
00323         return MoveArmResponse(True)
00324 
00325     def _move_to_pose(self, req):
00326         '''The thread function that makes the arm move to the
00327         target end-effector pose (within arm_state).
00328 
00329         Args:
00330             req (MoveArmRequest): Arm state that contains the pose to
00331                 move to.
00332         '''
00333         arm_state = req.arm_state
00334         rospy.loginfo("Moving to pose: {}".format(arm_state.ee_pose))
00335         self._status = ExecutionStatus.EXECUTING
00336 
00337         # Should we transform pose to base_link?
00338         if self._arm.move_to_pose(arm_state.ee_pose):
00339             self._status = ExecutionStatus.SUCCEEDED
00340             success = True
00341         else:
00342             self._status = ExecutionStatus.NO_IK
00343             success = False
00344 
00345         self._arm.relax_arm()
00346         return MoveArmResponse(success)
00347 
00348     def _move_to_joints_plan(self, req):
00349         '''
00350         Makes the arms move to the joint positions contained in the
00351         passed arm states.
00352 
00353         This assumes that the joint positions are valid (i.e. IK has
00354         been called previously to set each ArmState's joints to good
00355         values).
00356 
00357         Args:
00358             req (MoveArmRequest)
00359 
00360         Returns:
00361             MoveArmResponse: Whether the arms successfully moved to the passed
00362                 joint positions.
00363         '''
00364         # Estimate times to get to both poses.
00365         arm_state = req.arm_state
00366 
00367         # Move arms to target.
00368 
00369         self._status = ExecutionStatus.EXECUTING
00370         suc = self._arm.move_to_joints_plan(arm_state.joint_pose,
00371                                             arm_state.velocities)
00372 
00373 
00374         # Wait until both arms complete the trajectory.
00375         while self._arm.is_executing():
00376             rospy.sleep(MOVE_TO_JOINTS_SLEEP_INTERVAL)
00377 
00378         rospy.loginfo('\tArms reached target.')
00379 
00380         # Verify that both arms succeeded
00381         # DEBUG: remove
00382 
00383         if not suc:
00384             self._status = ExecutionStatus.NO_IK
00385             success = False
00386         else:
00387             self._status = ExecutionStatus.SUCCEEDED
00388             success = True
00389 
00390         self._arm.relax_arm()
00391         return MoveArmResponse(success)
00392 
00393     def _move_to_joints(self, req):
00394         '''
00395         Makes the arms move to the joint positions contained in the
00396         passed arm states.
00397 
00398         Note: This moves directly to the joint positions using interpolation
00399 
00400         This assumes that the joint positions are valid (i.e. IK has
00401         been called previously to set each ArmState's joints to good
00402         values).
00403 
00404         Args:
00405             req (MoveArmRequest)
00406 
00407         Returns:
00408             MoveArmResponse: Whether the arms successfully moved to the passed
00409                 joint positions.
00410         '''
00411         # Estimate times to get to both poses.
00412         joints = []
00413         # times_to_poses = []
00414         # arm_state = req.arm_state[i]
00415 
00416         # time_to_pose = None
00417 
00418         # if arm_state is not None:
00419         #     time_to_pose = self._arm.get_time_to_pose(arm_state.ee_pose)
00420         for arm_state in req.arm_states:
00421             joints.append(arm_state.joint_pose)
00422 
00423         # If both arms are moving, adjust velocities and find most
00424         # moving arm. Look at it.
00425 
00426         # Move arms to target.
00427         suc = False
00428         self._status = ExecutionStatus.EXECUTING
00429         suc = self._arm.move_to_joints(
00430             joints, req.times)
00431 
00432         # Wait until both arms complete the trajectory.
00433         while self._arm.is_executing():
00434             rospy.sleep(MOVE_TO_JOINTS_SLEEP_INTERVAL)
00435 
00436         rospy.loginfo('\tArms reached target.')
00437 
00438         # Verify that both arms succeeded
00439         # DEBUG: remove
00440 
00441         if not suc:
00442             self._status = ExecutionStatus.NO_IK
00443             success = False
00444         else:
00445             self._status = ExecutionStatus.SUCCEEDED
00446             success = True
00447 
00448         self._arm.relax_arm()
00449         return MoveArmTrajResponse(success)
00450 
00451 
00452     def _is_arm_moving(self, req):
00453         '''
00454         Returns true is arm is moving
00455 
00456         Args:
00457             req (IsArmMovingRequest)
00458 
00459         Returns:
00460             IsArmMovingResponse: Whether the arm is currently moving
00461         '''
00462 
00463         if self._arm.get_movement() < ARM_MOVEMENT_THRESHOLD:
00464             return GetArmMovementResponse(False)
00465         else:
00466             return GetArmMovementResponse(True)


fetch_pbd_interaction
Author(s): Sarah Elliott
autogenerated on Thu Jun 6 2019 18:27:21