object_tracker.py
Go to the documentation of this file.
00001 #! /usr/bin/env python
00002 
00003 import roslib; roslib.load_manifest('simulation_object_tracker')
00004 import rospy
00005 import actionlib
00006 
00007 import sys, traceback
00008 import copy
00009 import math
00010 from hardcoded_facts.geometry_pose_loader import read_pose_stamped_file
00011 from visualization_msgs import msg as visualization
00012 from geometry_msgs import msg as geometry
00013 from tidyup_msgs import srv
00014 from tidyup_msgs import msg
00015 from arm_navigation_msgs.msg import CollisionObject, Shape, OrderedCollisionOperations, CollisionOperation, ArmNavigationErrorCodes
00016 from pr2_python.world_interface import WorldInterface
00017 from pr2_python.arm_mover import ArmMover
00018 from pr2_python.arm_planner import ArmPlanner
00019 from pr2_python.planning_scene_interface import get_planning_scene_interface
00020 from pr2_python import transform_listener
00021 from pr2_python.exceptions import ArmNavError
00022 from pr2_python.arm_mover import ArmMover
00023 import tf
00024 
00025 class SimulatedDoor():
00026   def __init__(self, name):
00027     self.open = False
00028     self._poses = dict()
00029     self.name = name
00030   def current_pose(self):
00031     return self._poses[self.open]
00032   def add_pose(self, open, pose):
00033     self._poses[open] = pose
00034 
00035 class ObjectTracker():
00036 
00037   def __init__(self, simulate_moving):
00038     self.simulate_movable_objects = simulate_moving
00039     # world interface
00040     #self._tasks = Tasks()
00041     self._wi = WorldInterface()
00042     self._wi.reset()
00043     self._psi = get_planning_scene_interface()
00044     self._psi.reset()
00045     self._transformListener = transform_listener.get_transform_listener()
00046     self._arm_mover = ArmMover()
00047     self._right_arm_planner = self._arm_mover._planners['right_arm']
00048     self._left_arm_planner = self._arm_mover._planners['left_arm']
00049     # object_name -> SimulatedDoor
00050     self._doors = dict()
00051     # object_name -> geometry_msgs/PoseStamped
00052     self._movable_objects = dict()
00053     # actions and services
00054     if self.simulate_movable_objects:
00055         # detect objects
00056         self._detectGraspableObjectsService = rospy.Service('/tidyup/detect_objects', srv.DetectGraspableObjects, self._detect_objects_callback)
00057         # graspability
00058         #self._requestObjectGraspabilityService = rospy.Service('/tidyup/request_graspability', srv.RequestObjectsGraspability, self._request_graspability_callback)
00059         # pickup
00060         #self._pickUpActionServer = actionlib.SimpleActionServer('/tidyup/grasping_action', msg.GraspObjectAction, self._pick_up_execute, False)
00061         #self._pickUpActionServer.start() 
00062         # door
00063         self._detectDoorService = rospy.Service('/tidyup/detect_door_state', srv.DetectDoorState, self._detect_door_state_callback)
00064         self._openDoorActionServer = actionlib.SimpleActionServer('/tidyup/open_door', msg.OpenDoorAction, self._open_door_execute, False)
00065         self._openDoorActionServer.start() 
00066 
00067     rospy.loginfo('[{0}]initialized.'.format(rospy.get_name()))
00068 
00069   def _detect_objects_callback(self, request):
00070     self._psi.reset()
00071     rospy.loginfo('request: detect objects.')
00072     # add objects not yet in the in the planning scene
00073     visibles = [self._create_tidyup_object(name, pose_stamped) for name, pose_stamped in self._movable_objects.items() if self._pose_in_view(pose_stamped)]
00074     for object in visibles:
00075       # once added to the planning scene, remove them from our list
00076       self._movable_objects.pop(object.name) 
00077       if object.name.startswith('plate'):
00078         rospy.loginfo('added plate {0} to environment.'.format(object.name))
00079         object.pose.pose.position.z += 0.015 # place above table
00080         self._wi.add_collision_box(object.pose, [0.25, 0.25, 0.03], object.name)
00081       elif object.name.startswith('bowl'):
00082         rospy.loginfo('added bowl {0} to environment.'.format(object.name))
00083         object.pose.pose.position.z += 0.035 # place above table
00084         self._wi.add_collision_box(object.pose, [0.17, 0.17, 0.07], object.name)
00085       elif object.name.startswith('cup'):
00086         rospy.loginfo('added cup {0} to environment.'.format(object.name))
00087         object.pose.pose.position.z += 0.075 # place above table
00088         self._wi.add_collision_box(object.pose, [0.07, 0.07, 0.15], object.name)
00089     # find object already in the planning scene
00090     ps_visibles = [self._convert_collision_object_to_tidyup_object(object) for object in self._psi.collision_objects() if self._object_in_view(object)]
00091     visibles.extend(ps_visibles)
00092     self._psi.reset()
00093     return { 'objects': visibles}
00094 
00095   def _request_graspability_callback(self, request):
00096     rospy.loginfo('request: object graspability.')
00097     for object in request.objects:
00098       object.reachable_right_arm = self._object_in_reach(object, self._right_arm_planner) 
00099       object.reachable_left_arm = self._object_in_reach(object, self._left_arm_planner) 
00100     return { 'objects': request.objects}
00101 
00102   def _pick_up_execute(self, goal):
00103     self._psi.reset()
00104     arm_name = 'no_arm'
00105     if goal.right_arm:
00106       arm_name = 'right_arm'
00107     elif goal.left_arm:
00108       arm_name = 'left_arm'
00109     target = self._wi.collision_object(goal.target.name)
00110     if not target:
00111       self._pickUpActionServer.set_aborted(text='pick-up: specified object {0} does not exist.'.format(goal.target.name))
00112       return
00113 
00114     rospy.loginfo('executing: pick up of {0} with {1}.'.format(target.id, arm_name))
00115     # allow collisions between object and anything
00116     collisions = OrderedCollisionOperations()
00117     collision = CollisionOperation()
00118     collision.object1 = goal.target.name
00119     collision.object2 = collision.COLLISION_SET_ALL # arm_planner.joint_names[6]
00120     collision.operation = collision.DISABLE
00121     collisions.collision_operations.append(collision)
00122     gripper_pose = geometry.PoseStamped()
00123     gripper_pose.pose = transform_listener.transform_pose('/torso_lift_link', '/map', goal.target.pose.pose)
00124     gripper_pose.header.frame_id = '/torso_lift_link'
00125     gripper_pose.pose.position.z += 0.2
00126     gripper_pose.pose.orientation.x = 0.7071
00127     gripper_pose.pose.orientation.y = 0.0
00128     gripper_pose.pose.orientation.z = -0.7071
00129     gripper_pose.pose.orientation.w = 0.0
00130     reached_goal = self._arm_mover.move_to_goal(arm_name, gripper_pose, ordered_collisions=collisions)
00131     self._psi.reset()
00132     if reached_goal:
00133       self._wi.attach_object_to_gripper(arm_name, target.id)
00134       self._psi.reset()
00135       self._pickUpActionServer.set_succeeded(text='pickup successful')
00136       return
00137     self._pickUpActionServer.set_aborted(text='execution failed')
00138 
00139   def _create_tidyup_object(self, name, pose_stamped):
00140     tidyup_object = msg.GraspableObject()
00141     tidyup_object.name = name
00142     tidyup_object.pose = pose_stamped
00143     return tidyup_object
00144 
00145   def _convert_collision_object_to_tidyup_object(self, object):
00146     tidyup_object = msg.GraspableObject()
00147     tidyup_object.name = object.id
00148     tidyup_object.pose = geometry.PoseStamped()
00149     tidyup_object.pose.pose = object.poses[0]
00150     tidyup_object.pose.header.frame_id = object.header.frame_id
00151     return tidyup_object
00152 
00153   def _detect_door_state_callback(self, request):
00154     rospy.loginfo('request: detect door state.')
00155     for door in self._doors.values():
00156       if self._pose_in_view(door.current_pose()):
00157         return { 'door_open': door.open}
00158     # no door in view
00159     rospy.loginfo('detect door: no door in view. assuming door is open.')
00160     return { 'door_open': True}
00161 
00162   def _open_door_execute(self, goal):
00163     self._psi.reset()
00164     arm_name = 'no_arm'
00165     if goal.right_arm:
00166       arm_name = 'right_arm'
00167     elif goal.left_arm:
00168       arm_name = 'left_arm'
00169     rospy.loginfo('executing: open door with {0}.'.format(arm_name))
00170     # find door
00171     for door in self._doors.values():
00172       if self._pose_in_view(door.current_pose()):
00173         # open door
00174         door.open = True
00175         # set new coordinates
00176         self._update_door_pose(door)
00177         self._psi.reset()
00178         self._openDoorActionServer.set_succeeded(text='succeeded: door is open')
00179         return
00180     rospy.logerr('request failed: no door in view')
00181     self._openDoorActionServer.set_aborted(text='failed: not in front of door')
00182 
00183   def _pose_in_view(self, pose_stamped):
00184     # transform to head frame
00185     object_pose = transform_listener.transform_pose('/head_pan_link', pose_stamped.header.frame_id, pose_stamped.pose)
00186     # check angle and distance
00187     yawAngle = math.atan2(object_pose.position.y, object_pose.position.x)
00188     maxYawAngle = math.pi/4.0
00189     minX = 0.2
00190     maxX = 2.0
00191     if math.fabs(yawAngle) < maxYawAngle and minX < object_pose.position.x < maxX: 
00192       return True
00193     return False
00194 
00195   def _object_in_view(self, object):
00196     # ignore doors and tables
00197     if object.id.startswith('table') or object.id.startswith('door'): 
00198       return False
00199 
00200     # transform to head frame
00201     object_pose = transform_listener.transform_pose('/head_pan_link', object.header.frame_id, object.poses[0])
00202     # check angle and distance
00203     yawAngle = math.atan2(object_pose.position.y, object_pose.position.x)
00204     maxYawAngle = math.pi/4.0
00205     minX = 0.2
00206     maxX = 2.0
00207     if math.fabs(yawAngle) < maxYawAngle and minX < object_pose.position.x < maxX: 
00208       #print '{0} is in view: coords={1}|{2} yaw={3}'.format(object.id, object_pose.position.x, object_pose.position.y, yawAngle)
00209       return True
00210     #print '{0} is NOT in view: coords={1}|{2} yaw={3}'.format(object.id, object_pose.position.x, object_pose.position.y, yawAngle)
00211     return False
00212 
00213   def _object_in_reach(self, object, arm_planner):
00214     # transform to head frame
00215     self._psi.reset()
00216     # allow collisions between gripper and object
00217     collisions = OrderedCollisionOperations()
00218     collision = CollisionOperation()
00219     collision.object1 = object.name
00220     collision.object2 = collision.COLLISION_SET_ALL # arm_planner.joint_names[6]
00221     collision.operation = collision.DISABLE
00222     collisions.collision_operations.append(collision)
00223     gripper_pose = geometry.PoseStamped()
00224     gripper_pose.pose = transform_listener.transform_pose('/torso_lift_link', '/map', object.pose.pose)
00225     gripper_pose.header.frame_id = '/torso_lift_link'
00226     gripper_pose.pose.position.z += 0.2
00227     gripper_pose.pose.orientation.x = 0.7071
00228     gripper_pose.pose.orientation.y = 0.0
00229     gripper_pose.pose.orientation.z = -0.7071
00230     gripper_pose.pose.orientation.w = 0.0
00231     response = arm_planner.get_ik(gripper_pose, ordered_collisions=collisions)
00232     self._psi.reset()
00233     return response.error_code.val == ArmNavigationErrorCodes.SUCCESS
00234 
00235   def poseStampToStr(self, pose):
00236       (_, _, yaw) = tf.transformations.euler_from_quaternion([pose.pose.orientation.x, pose.pose.orientation.y, pose.pose.orientation.z, pose.pose.orientation.w])
00237       ps = "position: (%.2f, %.2f, %.2f) - quat: (%.2f, %.2f, %.2f, %.2f) - yaw: %.2f deg" % \
00238               (pose.pose.position.x, pose.pose.position.y, pose.pose.position.z, \
00239               pose.pose.orientation.x, pose.pose.orientation.y, pose.pose.orientation.z, pose.pose.orientation.w, 180.0/math.pi*yaw)
00240       return ps
00241 
00242   def create_objects(self, dataFileName):
00243     # read from file
00244     poses = read_pose_stamped_file(dataFileName)
00245     rospy.loginfo("Creating virtual objects:")
00246     for name, pose in poses.items():
00247       if name.startswith('table'):
00248         rospy.loginfo("Adding table: " + name + " at " + self.poseStampToStr(pose))
00249         self.add_table(pose, name)
00250       elif name.startswith('door'):
00251         rospy.loginfo("Adding door: " + name + " at " + self.poseStampToStr(pose))
00252         self.add_door(pose, name)
00253       elif self.simulate_movable_objects:
00254         rospy.loginfo("Adding movable object: " + name + " at " + self.poseStampToStr(pose))
00255         self._movable_objects[name] = pose
00256     self._psi.reset()
00257 
00258   def add_door(self, pose, pose_name):
00259     if pose_name.endswith('_closed'):
00260       open_key = False
00261       name = pose_name[:-len('_closed')]
00262     elif pose_name.endswith('_open'):
00263       name = pose_name[:-len('_open')]
00264       open_key = True
00265     if self._doors.has_key(name):
00266       door = self._doors[name]
00267     else:
00268       door = SimulatedDoor(name)
00269       self._doors[name] = door
00270     door.add_pose(open_key, pose)
00271     if door.open == open_key:
00272       self._update_door_pose(door)
00273 
00274   def _update_door_pose(self, door):
00275     self._wi.add_collision_box(door.current_pose(), [0.05, 1.0, 2.0], door.name)
00276 
00277   def add_table(self, pose, id):
00278 #    co = CollisionObject()
00279 #    co.header.frame_id = pose.header.frame_id
00280 #    co.padding = -1
00281 #    co.id = id
00282 #    
00283 #    xmin = -0.35
00284 #    xmax = 0.35
00285 #    ymin = -0.5
00286 #    ymax = 0.5
00287 #    bbox = Shape()
00288 #    bbox.type = bbox.BOX
00289 #    bbox.dimensions = [abs(xmax - xmin), abs(ymax - ymin), 0.05]
00290 #    co.shapes.append(bbox)
00291 #    co.poses.append(copy.deepcopy(pose.pose))
00292 #    self._wi.add_object(co)
00293     self._wi.add_collision_box(pose, [0.7, 1.0, 0.05], id)
00294 #    return co  
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Properties Friends


simulation_object_tracker
Author(s): andreas
autogenerated on Wed Dec 26 2012 15:51:57