00001 '''Defines behaviour for ArmTarget primitive. This is for primitives
00002 where the arm moves to a single pose.
00003 '''
00004
00005
00006
00007
00008
00009 import math
00010
00011
00012 import rospy
00013
00014
00015 import tf
00016 from std_msgs.msg import ColorRGBA, String
00017 from geometry_msgs.msg import Vector3, Point, Pose, Quaternion, PoseStamped
00018 from visualization_msgs.msg import Marker, InteractiveMarker
00019 from visualization_msgs.msg import InteractiveMarkerControl
00020 from visualization_msgs.msg import InteractiveMarkerFeedback
00021 from interactive_markers.menu_handler import MenuHandler
00022
00023
00024 from fetch_pbd_interaction.primitive import Primitive
00025 from fetch_arm_control.msg import GripperState
00026 from fetch_pbd_interaction.msg import ArmState, Landmark
00027 from fetch_pbd_interaction.srv import GetObjectList, \
00028 GetObjectFromName, GetMostSimilarObject
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038 COLOR_MESH_REACHABLE = ColorRGBA(1.0, 0.5, 0.0, 0.6)
00039 COLOR_MESH_REACHABLE_SELECTED = ColorRGBA(0.0, 1.0, 0.0, 1.0)
00040
00041 COLOR_MESH_UNREACHABLE = ColorRGBA(0.5, 0.5, 0.5, 0.6)
00042 COLOR_MESH_UNREACHABLE_SELECTED = ColorRGBA(0.5, 0.9, 0.5, 0.4)
00043
00044
00045 SCALE_TRAJ_STEP_SPHERES = Vector3(0.02, 0.02, 0.02)
00046 SCALE_OBJ_REF_ARROW = Vector3(0.02, 0.03, 0.04)
00047
00048
00049 STR_MESH_GRIPPER_FOLDER = 'package://fetch_description/meshes/'
00050 STR_GRIPPER_PALM_FILE = STR_MESH_GRIPPER_FOLDER + 'gripper_link.STL'
00051 STR_L_GRIPPER_FINGER_FILE = STR_MESH_GRIPPER_FOLDER + \
00052 'l_gripper_finger_link.STL'
00053 STR_R_GRIPPER_FINGER_FILE = STR_MESH_GRIPPER_FOLDER + \
00054 'r_gripper_finger_link.STL'
00055 DEFAULT_OFFSET = 0.085
00056
00057
00058 MENU_OPTIONS = {
00059 'ref': 'Change target:',
00060 'move_here': 'Move arm here',
00061 'move_current': 'Move to current arm pose',
00062 'del': 'Delete'
00063
00064 }
00065
00066
00067
00068
00069
00070 ID_OFFSET_REF_ARROW = 1000
00071 ID_OFFSET_TRAJ_FIRST = 2000
00072 ID_OFFSET_TRAJ_LAST = 3000
00073
00074
00075 TRAJ_MARKER_LIFETIME = rospy.Duration()
00076 TEXT_Z_OFFSET = 0.1
00077 INT_MARKER_SCALE = 0.2
00078 TRAJ_ENDPOINT_SCALE = 0.05
00079
00080
00081
00082
00083 TOPIC_IM_SERVER = 'programmed_actions'
00084
00085
00086
00087 BASE_LINK = 'base_link'
00088 PREVIOUS_PRIMITIVE = "previous primitive"
00089
00090
00091
00092
00093
00094 class ArmTarget(Primitive):
00095 '''Defines behaviour for ArmTarget primitive. This is for primitives
00096 where the arm moves to a single pose.
00097 '''
00098
00099 _offset = DEFAULT_OFFSET
00100
00101 def __init__(self, robot, tf_listener, im_server, arm_state=ArmState(),
00102 gripper_state=None, number=None):
00103 '''
00104 Args:
00105 robot (Robot) : interface to lower level robot functionality
00106 tf_listener (TransformListener)
00107 im_server (InteractiveMarkerSerever)
00108 arm_state (ArmState, optional)
00109 gripper_state (GripperState.OPEN|GripperState.CLOSED, optional)
00110 number (int, optional): The number of this primitive in the
00111 action sequence
00112 '''
00113 self._name = ""
00114 self._im_server = im_server
00115 self._robot = robot
00116 self._arm_state = arm_state
00117 self._gripper_state = gripper_state
00118 self._number = number
00119 self._is_control_visible = False
00120 self._selected = False
00121 self._tf_listener = tf_listener
00122 self._marker_visible = False
00123 self._color_mesh_reachable = COLOR_MESH_REACHABLE
00124 self._color_mesh_unreachable = COLOR_MESH_UNREACHABLE
00125 self._reachable = True
00126 self._landmark_found = False
00127
00128 self._menu_handler = MenuHandler()
00129
00130 self._sub_ref_entries = None
00131 self._marker_click_cb = None
00132 self._marker_delete_cb = None
00133 self._pose_change_cb = None
00134 self._action_change_cb = None
00135
00136 self._get_object_from_name_srv = rospy.ServiceProxy(
00137 '/fetch_pbd/get_object_from_name',
00138 GetObjectFromName)
00139 self._get_most_similar_obj_srv = rospy.ServiceProxy(
00140 '/fetch_pbd/get_most_similar_object',
00141 GetMostSimilarObject)
00142 self._get_object_list_srv = rospy.ServiceProxy(
00143 '/fetch_pbd/get_object_list',
00144 GetObjectList)
00145
00146
00147
00148
00149
00150 def get_json(self):
00151 '''Returns json representation of primitive'''
00152
00153 json = {}
00154
00155 json['name'] = self._name
00156 json['number'] = self._number
00157 json['arm_state'] = ArmTarget._get_json_from_arm_state(self._arm_state)
00158 json['gripper_state'] = self._gripper_state
00159
00160 return {'arm_target': json}
00161
00162 def build_from_json(self, json):
00163 '''Fills out ArmTarget using json from db'''
00164
00165 self._name = json['name']
00166 self._number = json['number']
00167 arm_state_json = json['arm_state']
00168
00169
00170 self._arm_state = ArmState()
00171 self._arm_state.ref_type = arm_state_json['ref_type']
00172 self._arm_state.joint_pose = arm_state_json['joint_pose']
00173 pose = ArmTarget._get_pose_stamped_from_json(arm_state_json['ee_pose'])
00174 self._arm_state.ee_pose = pose
00175
00176 self._arm_state.ref_landmark = Landmark()
00177 landmark_name = arm_state_json['ref_landmark']['name']
00178 self._arm_state.ref_landmark.name = landmark_name
00179
00180 landmark_pose_json = arm_state_json['ref_landmark']['pose']
00181 landmark_pose = ArmTarget._get_pose_from_json(landmark_pose_json)
00182 self._arm_state.ref_landmark.pose = landmark_pose
00183
00184 landmark_dimensions = Vector3()
00185 x_dim = arm_state_json['ref_landmark']['dimensions']['x']
00186 y_dim = arm_state_json['ref_landmark']['dimensions']['y']
00187 z_dim = arm_state_json['ref_landmark']['dimensions']['z']
00188 landmark_dimensions.x = x_dim
00189 landmark_dimensions.y = y_dim
00190 landmark_dimensions.z = z_dim
00191
00192
00193 self._gripper_state = json['gripper_state']
00194
00195 self._arm_state.ref_landmark.dimensions = landmark_dimensions
00196
00197 def check_pre_condition(self):
00198 ''' Currently just a placeholder
00199 Meant to return conditions that need to be met before a
00200 primitive can be executed. This could be something like
00201 "There should only be one object" or something.
00202
00203 Returns:
00204 None
00205 '''
00206 if self._arm_state.ref_type == ArmState.OBJECT:
00207 if not self._landmark_found:
00208 return False, "No matching object found" + \
00209 " for primitive: {}".format(self.get_name())
00210 else:
00211 return True, None
00212 else:
00213 return True, None
00214
00215 def check_post_condition(self):
00216 ''' Currently just a placeholder
00217 Meant to return conditions that need to be met after a
00218 primitive is executed in order for execution to be a success.
00219 This could be something like "object must be 0.1m above surface"
00220
00221 Returns:
00222 None
00223 '''
00224
00225 return True, None
00226
00227 def add_marker_callbacks(self, click_cb, delete_cb, pose_change_cb,
00228 action_change_cb):
00229 '''Adds marker to world'''
00230
00231
00232 self._marker_click_cb = click_cb
00233 self._marker_delete_cb = delete_cb
00234 self._pose_change_cb = pose_change_cb
00235 self._action_change_cb = action_change_cb
00236
00237
00238 def show_marker(self):
00239 '''Adds marker for primitive'''
00240 self._marker_visible = False
00241 if self.update_ref_frames():
00242 try:
00243 self._update_menu()
00244 self._update_viz_core()
00245 self._menu_handler.apply(self._im_server, self.get_name())
00246 self._im_server.applyChanges()
00247 self._marker_visible = True
00248 except Exception, e:
00249 rospy.logwarn("Show marker error: {}".format(e))
00250
00251 return self._marker_visible
00252
00253 def hide_marker(self):
00254 '''Removes marker from the world.'''
00255
00256 rospy.loginfo("Deleting marker for: {}".format(self.get_name()))
00257 self._im_server.erase(self.get_name())
00258 self._im_server.applyChanges()
00259 self._marker_visible = False
00260
00261 def marker_visible(self):
00262 '''Return whether or not marker is visible
00263
00264 Returns:
00265 bool
00266 '''
00267 return self._marker_visible
00268
00269 def update_ref_frames(self):
00270 '''Updates and re-assigns coordinate frames when the world changes.'''
00271
00272 arm_pose = self._arm_state
00273 if arm_pose.ref_type == ArmState.OBJECT:
00274 prev_ref_obj = arm_pose.ref_landmark
00275
00276 resp = self._get_most_similar_obj_srv(prev_ref_obj)
00277 if resp.has_similar:
00278 self._arm_state.ref_landmark = resp.similar_object
00279 self._arm_state.ee_pose.header.frame_id = resp.similar_object.name
00280 self._landmark_found = True
00281 return True
00282 else:
00283 self._landmark_found = False
00284 return False
00285 else:
00286 return True
00287
00288 def change_ref_frame(self, ref_type, landmark):
00289 '''Sets new reference frame for primitive
00290
00291 Args:
00292
00293 '''
00294 self._arm_state.ref_type = ref_type
00295
00296
00297 self._convert_ref_frame(landmark)
00298 rospy.loginfo(
00299 'Switching reference frame for primitive ' +
00300 self.get_name())
00301 self._menu_handler.reApply(self._im_server)
00302 self._im_server.applyChanges()
00303 self.update_viz(False)
00304 self._action_change_cb()
00305
00306 def get_ref_frame_name(self):
00307 '''Returns the name string for the reference frame object of the
00308 primitive.
00309
00310 Returns:
00311 str|None: Under all normal circumstances, returns the str
00312 reference frame name. Returns None in error.
00313 '''
00314
00315 ref_type = self._arm_state.ref_type
00316 ref_name = self._arm_state.ref_landmark.name
00317
00318
00319
00320 if ref_type == ArmState.ROBOT_BASE:
00321 ref_name = BASE_LINK
00322 elif ref_type == ArmState.PREVIOUS_TARGET:
00323 ref_name = "primitive_" + str(self._number - 1)
00324 elif ref_name == '':
00325 ref_name = BASE_LINK
00326 rospy.loginfo("Empty frame: {}".format(self._number))
00327
00328 return ref_name
00329
00330 def select(self, is_selected):
00331 '''Set whether primitive is selected or not
00332
00333 Args:
00334 is_selected (bool)
00335 '''
00336 self._selected = is_selected
00337 self.set_control_visible(is_selected)
00338 if is_selected:
00339 self._color_mesh_reachable = COLOR_MESH_REACHABLE_SELECTED
00340 self._color_mesh_unreachable = COLOR_MESH_UNREACHABLE_SELECTED
00341 else:
00342 self._color_mesh_reachable = COLOR_MESH_REACHABLE
00343 self._color_mesh_unreachable = COLOR_MESH_UNREACHABLE
00344
00345
00346
00347 def is_selected(self):
00348 '''Return whether or not primitive is selected
00349
00350 Returns
00351 bool
00352 '''
00353 return self._selected
00354
00355 def is_control_visible(self):
00356 '''Check if the marker control is visible
00357
00358 Returns
00359 bool
00360 '''
00361 return self._is_control_visible
00362
00363 def set_control_visible(self, visible=True):
00364 '''Set visibility of marker controls
00365
00366 Args:
00367 visible (bool, optional)
00368 '''
00369 self._is_control_visible = visible
00370
00371 def update_viz(self, check_reachable=True):
00372 '''Updates visualization fully.
00373 Args:
00374 check_reachable (bool) : whether to evaluate reachability
00375 before drawing marker
00376 '''
00377 rospy.loginfo("Updating viz for: {}".format(self.get_name()))
00378 draw_markers = True
00379 if self._arm_state.ref_type == ArmState.OBJECT:
00380 landmark_name = self._arm_state.ref_landmark.name
00381 resp = self._get_object_from_name_srv(landmark_name)
00382 if not resp.has_object:
00383 draw_markers = False
00384
00385 if draw_markers and self._marker_visible:
00386 try:
00387 self._update_menu()
00388 if self._update_viz_core(check_reachable):
00389 self._menu_handler.apply(self._im_server, self.get_name())
00390 self._im_server.applyChanges()
00391 except Exception, e:
00392 rospy.logwarn("Error when updating primitive viz")
00393 rospy.logwarn(e)
00394
00395 def get_primitive_number(self):
00396 '''Returns what number this primitive is in the sequence
00397
00398 Returns:
00399 int
00400 '''
00401 return self._number
00402
00403 def set_primitive_number(self, number):
00404 '''Sets what number this primitive is in the sequence
00405
00406 Args:
00407 num (int)
00408 '''
00409 self._number = number
00410
00411 def is_object_required(self):
00412 '''Check if this primitive requires an object to be present
00413
00414 Returns:
00415 bool
00416 '''
00417
00418 is_required = False
00419 ref = self._arm_state.ref_type
00420
00421 if ref == ArmState.OBJECT:
00422 is_required = True
00423
00424 return is_required
00425
00426 def execute(self):
00427 '''Execute this primitive
00428
00429 Returns
00430 bool : Success of execution
00431 '''
00432 if not self._robot.move_arm_to_pose(self._arm_state):
00433 return False, "Problem finding IK solution"
00434 if not self._gripper_state == self._robot.get_gripper_state():
00435 self._robot.set_gripper_state(self._gripper_state)
00436 return True, None
00437
00438 def head_busy(self):
00439 '''Return true if head busy
00440
00441 Returns:
00442 bool
00443 '''
00444 return False
00445
00446 def is_reachable(self):
00447 '''Check if robot can physically reach target'''
00448 self._reachable = self._robot.can_reach(self._arm_state)
00449 return self._reachable
00450
00451 def get_relative_pose(self, use_final=True):
00452 '''Returns the relative pose of the primitive.
00453 Args:
00454 use_final (bool, optional) : Unused
00455 Returns:
00456 PoseStamped
00457 '''
00458 return self._arm_state.ee_pose
00459
00460
00461 def get_absolute_pose(self):
00462 '''Returns the absolute pose of the primitive.
00463
00464 Args:
00465 None
00466
00467 Returns:
00468 PoseStamped
00469 '''
00470 try:
00471 abs_pose = self._tf_listener.transformPose('base_link',
00472 self._arm_state.ee_pose)
00473 return abs_pose
00474 except:
00475 frame_id = self._arm_state.ee_pose.header.frame_id
00476 rospy.logwarn("Frame: {} does not exist".format(frame_id))
00477 return None
00478
00479 def get_absolute_marker_pose(self, use_final=True):
00480 '''Returns the absolute pose of the primitive marker.
00481
00482 Args:
00483 use_final (bool, optional). Unused
00484
00485 Returns:
00486 PoseStamped
00487 '''
00488 try:
00489 abs_pose = self._tf_listener.transformPose('base_link',
00490 self._arm_state.ee_pose)
00491 return ArmTarget._offset_pose(abs_pose)
00492 except:
00493 frame_id = self._arm_state.ee_pose.header.frame_id
00494
00495 return None
00496
00497 def get_absolute_marker_position(self, use_final=True):
00498 '''Returns the absolute position of the primitive marker.
00499
00500 Args:
00501 use_final (bool, optional) : Unused
00502 True.
00503
00504 Returns:
00505 Point
00506 '''
00507 abs_pose = self.get_absolute_marker_pose()
00508 if not abs_pose is None:
00509 return abs_pose.pose.position
00510 else:
00511 return None
00512
00513 def decrease_id(self):
00514 '''Reduces the number of the primitive.'''
00515 self._number -= 1
00516
00517 def set_name(self, name):
00518 '''Sets the display name for the primitive.
00519
00520 Args:
00521 name (str) : A human-readable unique name for the primitive.
00522 '''
00523 self._name = name
00524
00525 def get_name(self):
00526 '''Returns the display name for the primitive.
00527
00528 Returns:
00529 str: A human-readable unique name for the primitive.
00530 '''
00531 return self._name
00532
00533 def get_number(self):
00534 '''Returns number of primitive
00535
00536 Returns:
00537 int
00538 '''
00539 return self._number
00540
00541 def set_pose(self, new_pose):
00542 '''Changes the pose of the primitive to new_pose.
00543
00544 Args:
00545 new_pose (PoseStamped)
00546 '''
00547 self._arm_state.ee_pose = new_pose
00548 self.update_viz()
00549 self._action_change_cb()
00550
00551 def pose_editable(self):
00552 '''Return whether pose of primitive is editable
00553
00554 Returns:
00555 bool : True
00556 '''
00557 return True
00558
00559 def get_ref_type(self):
00560 '''Return reference type of primitive
00561
00562 Returns:
00563 ArmState.ROBOT_BASE, etc
00564 '''
00565 return self._arm_state.ref_type
00566
00567
00568
00569
00570
00571 @staticmethod
00572 def _get_json_from_arm_state(arm_state):
00573 '''Return json containing data of arm_state
00574
00575 Args:
00576 arm_state (ArmState)
00577 Returns:
00578 json (dict)
00579 '''
00580 json = {}
00581 json['ref_type'] = arm_state.ref_type
00582 json['joint_pose'] = arm_state.joint_pose
00583 pose = ArmTarget._get_json_from_pose_stamped(arm_state.ee_pose)
00584 json['ee_pose'] = pose
00585 landmark = arm_state.ref_landmark
00586 json['ref_landmark'] = ArmTarget._get_json_from_landmark(landmark)
00587 return json
00588
00589 @staticmethod
00590 def _get_json_from_pose_stamped(pose_stamped):
00591 '''Return json containing data of pose_stamped
00592
00593 Args:
00594 pose_stamped (PoseStamped)
00595 Returns:
00596 json (dict)
00597 '''
00598 json = {}
00599 json['pose'] = ArmTarget._get_json_from_pose(pose_stamped.pose)
00600 json['frame_id'] = pose_stamped.header.frame_id
00601
00602 return json
00603
00604 @staticmethod
00605 def _get_json_from_pose(pose):
00606 '''Return json containing data of pose
00607
00608 Args:
00609 pose (Pose)
00610 Returns:
00611 json (dict)
00612 '''
00613 json = {}
00614 json['position'] = {}
00615 json['position']['x'] = pose.position.x
00616 json['position']['y'] = pose.position.y
00617 json['position']['z'] = pose.position.z
00618
00619 json['orientation'] = {}
00620 json['orientation']['x'] = pose.orientation.x
00621 json['orientation']['y'] = pose.orientation.y
00622 json['orientation']['z'] = pose.orientation.z
00623 json['orientation']['w'] = pose.orientation.w
00624
00625 return json
00626
00627 @staticmethod
00628 def _get_json_from_landmark(landmark):
00629 '''Return json containing data of landmark
00630
00631 Args:
00632 landmark (Landmark)
00633 Returns:
00634 json (dict)
00635 '''
00636 json = {}
00637
00638 json['name'] = landmark.name
00639 json['pose'] = ArmTarget._get_json_from_pose(landmark.pose)
00640 json['dimensions'] = {}
00641 json['dimensions']['x'] = landmark.dimensions.x
00642 json['dimensions']['y'] = landmark.dimensions.y
00643 json['dimensions']['z'] = landmark.dimensions.z
00644
00645 return json
00646
00647 @staticmethod
00648 def _get_pose_stamped_from_json(json):
00649 '''Return PoseStamped msg from json
00650
00651 Args:
00652 json (dict)
00653 Returns:
00654 PoseStamped
00655 '''
00656 pose_stamped = PoseStamped()
00657 pose = ArmTarget._get_pose_from_json(json['pose'])
00658 pose_stamped.pose = pose
00659 pose_stamped.header.frame_id = json['frame_id']
00660
00661 return pose_stamped
00662
00663 @staticmethod
00664 def _get_pose_from_json(json):
00665 '''Return Pose msg from json
00666
00667 Args:
00668 json (dict)
00669 Returns:
00670 Pose
00671 '''
00672 pose = Pose()
00673
00674 pose.position.x = json['position']['x']
00675 pose.position.y = json['position']['y']
00676 pose.position.z = json['position']['z']
00677
00678 pose.orientation.x = json['orientation']['x']
00679 pose.orientation.y = json['orientation']['y']
00680 pose.orientation.z = json['orientation']['z']
00681 pose.orientation.w = json['orientation']['w']
00682
00683 return pose
00684
00685 @staticmethod
00686 def _diff(float1, float2, thresh):
00687 '''Return true if floats are different by a threshold
00688
00689 Args:
00690 float1 (float)
00691 float2 (float)
00692 thresh (float)
00693 '''
00694 if math.fabs(float1 - float2) < thresh:
00695 return False
00696 else:
00697 return True
00698
00699 @staticmethod
00700 def _offset_pose(pose, constant=1):
00701 '''Offsets the world pose for visualization.
00702
00703 Args:
00704 pose (PoseStamped): The pose to offset.
00705 constant (int, optional): How much to scale the set offset
00706 by (scales ArmTarget._offset). Defaults to 1.
00707
00708 Returns:
00709 PoseStamped: The offset pose.
00710 '''
00711 transform = ArmTarget._get_matrix_from_pose(pose)
00712 offset_array = [constant * ArmTarget._offset, 0, 0]
00713 offset_transform = tf.transformations.translation_matrix(offset_array)
00714 hand_transform = tf.transformations.concatenate_matrices(
00715 transform, offset_transform)
00716
00717 new_pose = PoseStamped()
00718 new_pose.header.frame_id = pose.header.frame_id
00719 new_pose.pose = ArmTarget._get_pose_from_transform(hand_transform)
00720 return new_pose
00721
00722 @staticmethod
00723 def _get_matrix_from_pose(pose):
00724 '''Returns the transformation matrix for given pose.
00725
00726 Args:
00727 pose (PoseStamped)
00728
00729 Returns:
00730 Matrix3x3
00731 '''
00732 position, orientation = pose.pose.position, pose.pose.orientation
00733 rot_list = [orientation.x, orientation.y, orientation.z, orientation.w]
00734 transformation = tf.transformations.quaternion_matrix(rot_list)
00735 pos_list = [position.x, position.y, position.z]
00736 transformation[:3, 3] = pos_list
00737 return transformation
00738
00739 @staticmethod
00740 def _get_pose_from_transform(transform):
00741 '''Returns pose for transformation matrix.
00742
00743 Args:
00744 transform (Matrix3x3): (I think this is the correct type.
00745 See ArmTarget as a reference for how to use.)
00746
00747 Returns:
00748 Pose
00749 '''
00750 pos = transform[:3, 3].copy()
00751 rot = tf.transformations.quaternion_from_matrix(transform)
00752 return Pose(
00753 Point(pos[0], pos[1], pos[2]),
00754 Quaternion(rot[0], rot[1], rot[2], rot[3])
00755 )
00756
00757 @staticmethod
00758 def _make_mesh_marker(color):
00759 '''Creates and returns a mesh marker.
00760
00761 Returns:
00762 Marker
00763 '''
00764 mesh = Marker()
00765 mesh.mesh_use_embedded_materials = False
00766 mesh.type = Marker.MESH_RESOURCE
00767 mesh.scale.x = 1.0
00768 mesh.scale.y = 1.0
00769 mesh.scale.z = 1.0
00770 mesh.color = color
00771 return mesh
00772
00773
00774
00775
00776
00777 def _update_menu(self):
00778 '''Recreates the menu when something has changed.'''
00779 rospy.loginfo("Making new menu")
00780 self._menu_handler = MenuHandler()
00781
00782
00783 self._sub_ref_entries = []
00784 frame_entry = self._menu_handler.insert(MENU_OPTIONS['ref'])
00785 object_list = self._get_object_list_srv().object_list
00786 refs = [obj.name for obj in object_list]
00787 if self._number > 0:
00788 refs.append(PREVIOUS_PRIMITIVE)
00789 if len(refs) > 0:
00790 refs.append(BASE_LINK)
00791 for ref in refs:
00792 subent = self._menu_handler.insert(
00793 ref, parent=frame_entry, callback=self._change_ref_cb)
00794 self._sub_ref_entries += [subent]
00795
00796
00797
00798 for subent in self._sub_ref_entries:
00799 self._menu_handler.setCheckState(subent, MenuHandler.UNCHECKED)
00800
00801
00802 menu_id = self._get_menu_ref_id(self._get_menu_ref())
00803 if not menu_id is None:
00804
00805 self._menu_handler.setCheckState(menu_id, MenuHandler.CHECKED)
00806
00807
00808
00809
00810
00811
00812
00813
00814
00815
00816
00817
00818
00819
00820
00821
00822
00823
00824
00825
00826
00827
00828
00829 self._menu_handler.insert(
00830 MENU_OPTIONS['move_here'], callback=self._move_to_cb)
00831 self._menu_handler.insert(
00832 MENU_OPTIONS['move_current'], callback=self._move_pose_to_cb)
00833 self._menu_handler.insert(
00834 MENU_OPTIONS['del'], callback=self._delete_primitive_cb)
00835
00836
00837
00838 def _get_menu_ref_id(self, ref_name):
00839 '''Returns the unique menu id from its name or None if the
00840 object is not found.
00841
00842 Args:
00843 ref_name (str)
00844 Returns:
00845 int (?)|None
00846 '''
00847 object_list = self._get_object_list_srv().object_list
00848 refs = [obj.name for obj in object_list]
00849 if self._number > 0:
00850 refs.append(PREVIOUS_PRIMITIVE)
00851 refs.append(BASE_LINK)
00852 if ref_name in refs:
00853 index = refs.index(ref_name)
00854 if index < len(self._sub_ref_entries):
00855 return self._sub_ref_entries[index]
00856 else:
00857 return None
00858 else:
00859 return None
00860
00861
00862
00863
00864
00865
00866
00867
00868
00869
00870
00871
00872
00873
00874
00875
00876
00877
00878
00879
00880
00881
00882 def _get_menu_ref_name(self, menu_id):
00883 '''Returns the menu name from its unique menu id.
00884
00885 Args:
00886 menu_id (int)
00887 Returns:
00888 str
00889 '''
00890 index = self._sub_ref_entries.index(menu_id)
00891 object_list = self._get_object_list_srv().object_list
00892 refs = [obj.name for obj in object_list]
00893 if self._number > 0:
00894 refs.append(PREVIOUS_PRIMITIVE)
00895 refs.append(BASE_LINK)
00896 return refs[index]
00897
00898
00899
00900
00901
00902
00903
00904
00905
00906
00907
00908
00909
00910
00911
00912 def _set_target(self, new_ref):
00913 '''Changes the reference frame of the primitive to
00914 new_ref_name.
00915
00916 Args:
00917 new_ref_name
00918 '''
00919
00920 self._arm_state.ref_type = ArmState.OBJECT
00921 new_ref_obj = self._get_object_from_name_srv(new_ref).obj
00922 rospy.loginfo("Setting reference of primitive" +
00923 "{} to object".format(self._number))
00924 self._arm_state.ref_landmark = new_ref_obj
00925 self._landmark_found = True
00926 self._arm_state.ee_pose.header.frame_id = new_ref_obj.name
00927
00928 def _set_ref(self, new_ref):
00929 '''Changes the reference frame of the primitive to
00930 new_ref_name.
00931
00932 Args:
00933 new_ref_name
00934 '''
00935
00936 new_ref_obj = Landmark()
00937 if new_ref == PREVIOUS_PRIMITIVE:
00938 self._arm_state.ref_type = ArmState.PREVIOUS_TARGET
00939 rospy.loginfo("PREVIOUS!!!")
00940 elif new_ref == BASE_LINK:
00941 self._arm_state.ref_type = ArmState.ROBOT_BASE
00942 else:
00943 self._arm_state.ref_type = ArmState.OBJECT
00944 new_ref_obj = self._get_object_from_name_srv(new_ref).obj
00945 rospy.loginfo("OBJECT!!!")
00946
00947 self._convert_ref_frame(new_ref_obj)
00948
00949 def _convert_ref_frame(self, new_landmark):
00950 '''Convert arm_state to be in a different reference frame
00951
00952 Args:
00953 new_landmark (Landmark)
00954 Returns:
00955 ArmState
00956 '''
00957 if self._arm_state.ref_type == ArmState.OBJECT:
00958 rospy.loginfo("Relative to object")
00959 if self._arm_state.ref_landmark.name != new_landmark.name:
00960 ee_pose = self._tf_listener.transformPose(
00961 new_landmark.name,
00962 self._arm_state.ee_pose
00963 )
00964 self._arm_state.ref_landmark = new_landmark
00965 self._landmark_found = True
00966 self._arm_state.ee_pose = ee_pose
00967 elif self._arm_state.ref_type == ArmState.ROBOT_BASE:
00968 ee_pose = self._tf_listener.transformPose(
00969 BASE_LINK,
00970 self._arm_state.ee_pose
00971 )
00972 self._arm_state.ee_pose = ee_pose
00973 self._arm_state.ref_landmark = Landmark()
00974 self._landmark_found = False
00975 elif self._arm_state.ref_type == ArmState.PREVIOUS_TARGET:
00976 prev_frame_name = 'primitive_' + str(self._number - 1)
00977 rospy.loginfo("Original pose: {}".format(self._arm_state.ee_pose))
00978 ee_pose = self._tf_listener.transformPose(
00979 prev_frame_name,
00980 self._arm_state.ee_pose
00981 )
00982 rospy.loginfo("New pose: {}".format(ee_pose))
00983
00984 self._arm_state.ee_pose = ee_pose
00985 self._arm_state.ref_landmark = Landmark()
00986 self._landmark_found = False
00987
00988 def _get_marker_pose(self):
00989 '''Returns the pose of the primitive.
00990
00991 Returns:
00992 Pose
00993 '''
00994 rospy.loginfo("Pose frame is: {}".format(self.get_ref_frame_name()))
00995
00996 try:
00997 rospy.loginfo("Pose: {}".format(self._arm_state.ee_pose))
00998 self._tf_listener.waitForTransform(BASE_LINK,
00999 self._arm_state.ee_pose.header.frame_id,
01000 rospy.Time.now(),
01001 rospy.Duration(4.0))
01002 intermediate_pose = self._tf_listener.transformPose(
01003 BASE_LINK,
01004 self._arm_state.ee_pose)
01005 offset_pose = ArmTarget._offset_pose(intermediate_pose)
01006
01007
01008 return offset_pose
01009 except Exception, e:
01010 rospy.logwarn(e)
01011 rospy.logwarn("Frame not available yet: {}".format(self.get_ref_frame_name()))
01012 return None
01013
01014 def _update_viz_core(self, check_reachable=True):
01015 '''Updates visualization after a change.
01016
01017 Args:
01018 check_reachable (bool) : Check reachability of pose before drawing marker
01019 '''
01020
01021 menu_control = InteractiveMarkerControl()
01022 menu_control.interaction_mode = InteractiveMarkerControl.BUTTON
01023 menu_control.always_visible = True
01024 frame_id = BASE_LINK
01025 pose = self._get_marker_pose()
01026 if pose is None:
01027 return
01028
01029 if check_reachable:
01030 if not self.is_reachable():
01031 rospy.logwarn("Marker pose not reachable")
01032 else:
01033 rospy.loginfo("Marker pose is reachable")
01034
01035 menu_control = self._make_gripper_marker(
01036 menu_control, self._gripper_state)
01037
01038
01039 int_marker = InteractiveMarker()
01040 int_marker.name = self.get_name()
01041 int_marker.header.frame_id = frame_id
01042 int_marker.pose = pose.pose
01043 int_marker.scale = INT_MARKER_SCALE
01044 self._add_6dof_marker(int_marker, True)
01045 int_marker.controls.append(menu_control)
01046 prev_marker = self._im_server.get(self.get_name())
01047 prev_color = None
01048 if not prev_marker is None:
01049 if len(prev_marker.controls) > 0:
01050 if len(prev_marker.controls[-1].markers) > 0:
01051 prev_color = prev_marker.controls[-1].markers[-1].color
01052 new_color = None
01053 if len(int_marker.controls) > 0:
01054 if len(int_marker.controls[-1].markers) > 0:
01055 new_color = int_marker.controls[-1].markers[-1].color
01056
01057 if not prev_marker:
01058 self._im_server.insert(
01059 int_marker, self._marker_feedback_cb)
01060 rospy.logwarn("Adding marker for primitive {}".format(self.get_number()))
01061 return True
01062 elif (prev_marker.pose != int_marker.pose) or (prev_color != new_color):
01063 rospy.loginfo("Updating marker")
01064 self._im_server.insert(
01065 int_marker, self._marker_feedback_cb)
01066 return True
01067
01068 rospy.logwarn("Not updating marker for primitive {}".format(self.get_number()))
01069 return False
01070
01071 def _add_6dof_marker(self, int_marker, is_fixed):
01072 '''Adds a 6 DoF control marker to the interactive marker.
01073
01074 Args:
01075 int_marker (InteractiveMarker)
01076 is_fixed (bool): Looks like whether position is fixed (?).
01077 Currently only passed as True.
01078 '''
01079
01080 options = [
01081 ('rotate_x', Quaternion(1, 0, 0, 1), False),
01082 ('move_x', Quaternion(1, 0, 0, 1), True),
01083 ('rotate_z', Quaternion(0, 1, 0, 1), False),
01084 ('move_z', Quaternion(0, 1, 0, 1), True),
01085 ('rotate_y', Quaternion(0, 0, 1, 1), False),
01086 ('move_y', Quaternion(0, 0, 1, 1), True),
01087 ]
01088 for opt in options:
01089 name, orient, is_move = opt
01090 control = self._make_6dof_control(name, orient, is_move, is_fixed)
01091 int_marker.controls.append(control)
01092
01093 def _make_6dof_control(self, name, orientation, is_move, is_fixed):
01094 '''Creates and returns one component of the 6dof controller.
01095
01096 Args:
01097 name (str): Name for hte control
01098 orientation (Quaternion): How the control should be
01099 oriented.
01100 is_move (bool): Looks like whether the marker moves the
01101 object (?). Currently passed as True for moving markers,
01102 False for rotating ones.
01103 is_fixed (bool): Looks like whether position is fixed (?).
01104 Currently always passed as True.
01105
01106 Returns:
01107 InteractiveMarkerControl
01108 '''
01109 control = InteractiveMarkerControl()
01110 control.name = name
01111 control.orientation = orientation
01112 control.always_visible = False
01113 if self._is_control_visible:
01114 if is_move:
01115 control.interaction_mode = InteractiveMarkerControl.MOVE_AXIS
01116 else:
01117 control.interaction_mode = InteractiveMarkerControl.ROTATE_AXIS
01118 else:
01119 control.interaction_mode = InteractiveMarkerControl.NONE
01120 if is_fixed:
01121 control.orientation_mode = InteractiveMarkerControl.FIXED
01122 return control
01123
01124 def _set_new_pose(self, new_pose, frame_id):
01125 '''Changes the pose of the primitive to new_pose.
01126
01127 Args:
01128 new_pose (Pose)
01129 '''
01130 rospy.loginfo("Setting new ee_pose!!!")
01131 pose_stamped = PoseStamped()
01132 pose_stamped.header.frame_id = frame_id
01133 pose_stamped.pose = new_pose
01134 pose_stamped_transformed = self._tf_listener.transformPose(
01135 self.get_ref_frame_name(),
01136 pose_stamped)
01137 self._arm_state.ee_pose = ArmTarget._offset_pose(
01138 pose_stamped_transformed,
01139 -1)
01140 self.update_viz()
01141
01142 def _pose_changed(self, new_pose, frame_id):
01143 '''Check if marker pose has changed
01144
01145 Args:
01146 pose (Pose)
01147 '''
01148 pose_stamped = PoseStamped()
01149 pose_stamped.header.frame_id = frame_id
01150 pose_stamped.pose = new_pose
01151 pose_stamped_transformed = self._tf_listener.transformPose(
01152 self.get_ref_frame_name(),
01153 pose_stamped)
01154 offset_pose = ArmTarget._offset_pose(pose_stamped_transformed,
01155 -1)
01156 if ArmTarget._diff(offset_pose.pose.position.x,
01157 self._arm_state.ee_pose.pose.position.x,
01158 0.001) \
01159 or ArmTarget._diff(offset_pose.pose.position.y,
01160 self._arm_state.ee_pose.pose.position.y,
01161 0.001) \
01162 or ArmTarget._diff(offset_pose.pose.position.z,
01163 self._arm_state.ee_pose.pose.position.z,
01164 0.001) \
01165 or ArmTarget._diff(offset_pose.pose.orientation.x,
01166 self._arm_state.ee_pose.pose.orientation.x,
01167 0.01) \
01168 or ArmTarget._diff(offset_pose.pose.orientation.y,
01169 self._arm_state.ee_pose.pose.orientation.y,
01170 0.01) \
01171 or ArmTarget._diff(offset_pose.pose.orientation.z,
01172 self._arm_state.ee_pose.pose.orientation.z,
01173 0.01) \
01174 or ArmTarget._diff(offset_pose.pose.orientation.w,
01175 self._arm_state.ee_pose.pose.orientation.w,
01176 0.01):
01177 rospy.loginfo("New pose: {}".format(new_pose))
01178 rospy.loginfo("New pose transformed: {}".format(offset_pose))
01179 rospy.loginfo("Old pose: {}".format(self._arm_state.ee_pose))
01180 return True
01181 else:
01182 return False
01183
01184 def _get_mesh_marker_color(self):
01185 '''Gets the color for the mesh marker (thing that looks like a
01186 gripper) for this primitive.
01187
01188 A simple implementation of this will return one color for
01189 reachable poses, another for unreachable ones. Future
01190 implementations may provide further visual cues.
01191
01192 Returns:
01193 ColorRGBA: The color for the gripper mesh for this arm target.
01194 '''
01195 if self._reachable:
01196 return self._color_mesh_reachable
01197 else:
01198 return self._color_mesh_unreachable
01199
01200 def _make_gripper_marker(self, control, gripper_state=GripperState.CLOSED):
01201 '''Makes a gripper marker, adds it to control, returns control.
01202
01203 Args:
01204 control (InteractiveMarkerControl): IM Control we're using.
01205 is_hand_open (bool, optional): Whether the gripper is open.
01206 Defaults to False (closed).
01207
01208 Returns:
01209 InteractiveMarkerControl: The passed control.
01210 '''
01211
01212 mesh_color = self._get_mesh_marker_color()
01213
01214
01215
01216 mesh1 = ArmTarget._make_mesh_marker(mesh_color)
01217 mesh1.mesh_resource = STR_GRIPPER_PALM_FILE
01218 mesh1.pose.position.x = ArmTarget._offset
01219 mesh1.pose.orientation.w = 1
01220
01221
01222
01223 if gripper_state == GripperState.OPEN:
01224 mesh2 = ArmTarget._make_mesh_marker(mesh_color)
01225 mesh2.mesh_resource = STR_L_GRIPPER_FINGER_FILE
01226 mesh2.pose.position.x = 0.08
01227 mesh2.pose.position.y = -0.165
01228 mesh2.pose.orientation.w = 1
01229
01230
01231 mesh3 = ArmTarget._make_mesh_marker(mesh_color)
01232 mesh3.mesh_resource = STR_R_GRIPPER_FINGER_FILE
01233 mesh3.pose.position.x = 0.08
01234 mesh3.pose.position.y = 0.165
01235 mesh3.pose.orientation.w = 1
01236
01237 else:
01238 mesh2 = ArmTarget._make_mesh_marker(mesh_color)
01239 mesh2.mesh_resource = STR_L_GRIPPER_FINGER_FILE
01240 mesh2.pose.position.x = 0.08
01241 mesh2.pose.position.y = -0.116
01242 mesh2.pose.orientation.w = 1
01243
01244
01245 mesh3 = ArmTarget._make_mesh_marker(mesh_color)
01246 mesh3.mesh_resource = STR_R_GRIPPER_FINGER_FILE
01247 mesh3.pose.position.x = 0.08
01248 mesh3.pose.position.y = 0.116
01249 mesh3.pose.orientation.w = 1
01250
01251
01252
01253 control.markers.append(mesh1)
01254 control.markers.append(mesh2)
01255 control.markers.append(mesh3)
01256
01257
01258
01259 return control
01260
01261 def _delete_primitive_cb(self, feedback):
01262 '''Callback for when delete is requested.
01263
01264 Args:
01265 feedback (InteractiveMarkerFeedback): Unused
01266 '''
01267 self._marker_delete_cb(self._number)
01268
01269 def _move_to_cb(self, feedback):
01270 '''Callback for when moving to a pose is requested.
01271
01272 Args:
01273 feedback (InteractiveMarkerFeedback): Unused
01274 '''
01275 self._robot.move_arm_to_pose(self._arm_state)
01276
01277 def _move_pose_to_cb(self, feedback):
01278 '''Callback for when a pose change to current is requested.
01279
01280 Args:
01281 feedback (InteractiveMarkerFeedback): Unused
01282
01283 '''
01284 self._arm_state = self._robot.get_arm_state()
01285 self._gripper_state = self._robot.get_gripper_state()
01286 self.update_ref_frames()
01287 self.update_viz()
01288 self._pose_change_cb()
01289
01290 def _change_target_cb(self, feedback):
01291 '''Callback for when a reference frame change is requested.
01292
01293 Args:
01294 feedback (InteractiveMarkerFeedback (?))
01295 '''
01296 self._menu_handler.setCheckState(
01297 self._get_menu_target_id(self._get_menu_ref(target=True)), MenuHandler.UNCHECKED)
01298 self._menu_handler.setCheckState(
01299 feedback.menu_entry_id, MenuHandler.CHECKED)
01300 new_ref = self._get_menu_target_name(feedback.menu_entry_id)
01301 self._set_target(new_ref)
01302 rospy.loginfo(
01303 'Switching reference frame to ' + new_ref + ' for primitive ' +
01304 self.get_name())
01305 self._menu_handler.reApply(self._im_server)
01306 self._im_server.applyChanges()
01307 self.update_viz(False)
01308 self._action_change_cb()
01309
01310 def _change_ref_cb(self, feedback):
01311 '''Callback for when a reference frame change is requested.
01312
01313 Args:
01314 feedback (InteractiveMarkerFeedback (?))
01315 '''
01316 self._menu_handler.setCheckState(
01317 self._get_menu_ref_id(self._get_menu_ref()), MenuHandler.UNCHECKED)
01318 self._menu_handler.setCheckState(
01319 feedback.menu_entry_id, MenuHandler.CHECKED)
01320 new_ref = self._get_menu_ref_name(feedback.menu_entry_id)
01321 self._set_ref(new_ref)
01322 rospy.loginfo(
01323 'Switching reference frame to ' + new_ref + ' for primitive ' +
01324 self.get_name())
01325 self._menu_handler.reApply(self._im_server)
01326 self._im_server.applyChanges()
01327 self.update_viz(False)
01328 self._action_change_cb()
01329
01330 def _marker_feedback_cb(self, feedback):
01331 '''Callback for when an event occurs on the marker.
01332
01333 Args:
01334 feedback (InteractiveMarkerFeedback)
01335 '''
01336 if feedback.event_type == InteractiveMarkerFeedback.MOUSE_UP:
01337
01338
01339
01340
01341 if self._pose_changed(feedback.pose, feedback.header.frame_id):
01342 rospy.loginfo('Pose change.')
01343 self._set_new_pose(feedback.pose, feedback.header.frame_id)
01344 self._pose_change_cb()
01345 self._action_change_cb()
01346 else:
01347 rospy.loginfo('Changing visibility of the pose controls.')
01348 self._is_control_visible = not self._is_control_visible
01349 self._marker_click_cb(
01350 self._number, self._is_control_visible)
01351 self._set_new_pose(feedback.pose, feedback.header.frame_id)
01352 self._pose_change_cb()
01353 self._action_change_cb()
01354 else:
01355
01356
01357
01358 rospy.logdebug('Unknown event: ' + str(feedback.event_type))
01359
01360 def _get_menu_ref(self, target=False):
01361 '''Returns the name string for the reference frame object of the
01362 primitive.
01363
01364 Returns:
01365 str|None: Under all normal circumstances, returns the str
01366 reference frame name. Returns None in error.
01367 '''
01368
01369 ref_type = self._arm_state.ref_type
01370 ref_name = self._arm_state.ref_landmark.name
01371
01372 if not target:
01373
01374 if ref_type == ArmState.ROBOT_BASE:
01375 ref_name = BASE_LINK
01376 elif ref_type == ArmState.PREVIOUS_TARGET:
01377 ref_name = PREVIOUS_PRIMITIVE
01378 elif ref_name == '':
01379 ref_name = BASE_LINK
01380 rospy.loginfo("Empty frame: {}".format(self._number))
01381 else:
01382 ref_name = ref_name + " "
01383
01384 return ref_name
01385
01386
01387