00001 '''Control of the arm for action execution.'''
00002
00003
00004
00005
00006
00007
00008 import rospy
00009
00010
00011 import threading
00012
00013
00014 from std_srvs.srv import Empty, EmptyResponse
00015 from tf import TransformListener
00016
00017
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
00033
00034
00035
00036
00037 ARM_MOVEMENT_THRESHOLD = 0.04
00038
00039
00040
00041 MOVE_TO_JOINTS_SLEEP_INTERVAL = 0.01
00042
00043
00044
00045 TRAJECTORY_COMPLETE_SLEEP_INTERVAL = 0.01
00046
00047
00048
00049 GRIPPER_FINISH_SLEEP_INTERVAL = 0.01
00050
00051
00052
00053
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
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
00099
00100
00101 def update(self):
00102 '''Periodic update for the arm.'''
00103 self._arm.update()
00104
00105
00106
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
00133 return SetGripperStateResponse()
00134 else:
00135
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
00181
00182
00183
00184 if arm_state.ref_type == ArmState.OBJECT:
00185
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
00194 target_joints = self._arm.get_ik_for_ee(
00195 target_pose, arm_state.joint_pose)
00196
00197
00198 if target_joints is None:
00199
00200 rospy.loginfo('No IK for relative end-effector pose.')
00201 return solution, False
00202 else:
00203
00204
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
00211
00212 target_pose = arm_state.ee_pose
00213 target_pose.pose.position.z = target_pose.pose.position.z + \
00214 z_offset
00215
00216
00217 target_joints = self._arm.get_ik_for_ee(
00218 target_pose, arm_state.joint_pose)
00219 if target_joints is None:
00220
00221 rospy.logdebug('No IK for absolute end-effector pose.')
00222 return arm_state, False
00223 else:
00224
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
00232
00233 target_pose = arm_state.ee_pose
00234 target_pose.pose.position.z = target_pose.pose.position.z + \
00235 z_offset
00236
00237
00238 target_joints = self._arm.get_ik_for_ee(
00239 target_pose, arm_state.joint_pose)
00240 if target_joints is None:
00241
00242 rospy.logdebug('No IK for end-effector pose' +
00243 'relative to previous target.')
00244 return arm_state, False
00245 else:
00246
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
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
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
00365 arm_state = req.arm_state
00366
00367
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
00375 while self._arm.is_executing():
00376 rospy.sleep(MOVE_TO_JOINTS_SLEEP_INTERVAL)
00377
00378 rospy.loginfo('\tArms reached target.')
00379
00380
00381
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
00412 joints = []
00413
00414
00415
00416
00417
00418
00419
00420 for arm_state in req.arm_states:
00421 joints.append(arm_state.joint_pose)
00422
00423
00424
00425
00426
00427 suc = False
00428 self._status = ExecutionStatus.EXECUTING
00429 suc = self._arm.move_to_joints(
00430 joints, req.times)
00431
00432
00433 while self._arm.is_executing():
00434 rospy.sleep(MOVE_TO_JOINTS_SLEEP_INTERVAL)
00435
00436 rospy.loginfo('\tArms reached target.')
00437
00438
00439
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)