00001
00002
00003 import roslib; roslib.load_manifest('tidyup_arm_services')
00004 import rospy
00005 import actionlib
00006
00007 import sys, traceback, math, copy
00008 import std_srvs
00009 from tidyup_msgs import msg
00010 from tidyup_msgs import srv
00011 from pr2_tasks.arm_tasks import ArmTasks
00012 from arm_navigation_msgs.msg import ArmNavigationErrorCodes
00013 from geometry_msgs.msg import PoseStamped, Quaternion, Point, Pose
00014 from arm_navigation_msgs.msg import CollisionObject, AttachedCollisionObject, CollisionOperation
00015 from pr2_python import exceptions
00016 import pr2_python.geometry_tools as gt
00017 from pr2_python.hand_description import HandDescription
00018 from tidyup_arm_services.find_free_space import free_spots_on_table
00019 from pr2_python.exceptions import ManipulationError, AllYourBasePosesAreBelongToUs, ActionFailedError
00020 from pr2_python.planning_scene_interface import PlanningSceneInterface, get_planning_scene_interface
00021 from pr2_python.arm_planner import ArmPlanner
00022 from pr2_tasks.pickplace import PickPlace, get_wrist_pose_from_grasp, PickupResult
00023 from pr2_tasks.pickplace_definitions import PlaceGoal, PickupGoal
00024 from pr2_tasks.tableware_detection import TablewareDetection, TablewareDetectionResult
00025 from pr2_tasks.exceptions import DetectionError
00026 from tidyup_arm_services.planning_scene_sync import PlanningSceneSync
00027 from pr2_python import trajectory_tools as tt
00028 from pr2_python import visualization_tools as vt
00029 from pr2_python import head
00030 from visualization_msgs.msg import Marker, MarkerArray
00031 from pr2_python.world_interface import WorldInterface
00032 from hardcoded_facts.geometry_pose_loader import read_table_file
00033 import tf
00034 import numpy as np
00035 import object_manipulation_msgs
00036 import tabletop_object_detector
00037
00038 DEFAULT_CARRY_JOINT_TRAJECTORY = {'left_arm': [[0.968, 0.129, 0.554, -1.891, 1.786, -1.127, 0.501],
00039 [2.135, -0.02, 1.64, -2.07, 1.64, -1.68, 1.398],
00040 [2.110, 1.230, 2.06, -1.69, -0.3, -1.32, 1.57]],
00041 'right_arm': [[-0.968, 0.129, -0.554, -1.891, -1.786, -1.127, 0.501],
00042 [-2.135, -0.02, -1.64, -2.07, -1.64, -1.68, 1.398],
00043 [-2.110, 1.230, -2.06, -1.69, 0.3, -1.32, 1.57]]}
00044
00045 class SimpleArmTasks():
00046 def __init__(self):
00047 self._detector = TablewareDetection()
00048 self._tasks = ArmTasks()
00049 self._psi = get_planning_scene_interface()
00050 self._wi = self._detector._wi
00051
00052 self._reset_world_interface()
00053
00054 self._pick_place = PickPlace()
00055 self._known_objects = dict()
00056 self._known_wipe_goals = dict()
00057 self._head = head.Head()
00058
00059
00060 self._side_joint_trajectory = self._tasks.side_joint_trajectory
00061 self._toSideActionServer = actionlib.SimpleActionServer('/tidyup/side_position_action', msg.ArmToSideAction, self._to_side_execute, False)
00062 self._toSideActionServer.start()
00063
00064 self._carry_joint_trajectory = DEFAULT_CARRY_JOINT_TRAJECTORY
00065
00066
00067 self._toCarryActionServer = actionlib.SimpleActionServer('/tidyup/post_grasp_position_action', msg.PostGraspPositionAction, self._to_carry_execute, False)
00068 self._toCarryActionServer.start()
00069
00070 self._detectGraspableObjectsService = rospy.Service('/tidyup/detect_objects', srv.DetectGraspableObjects, self._detect_objects_callback)
00071
00072 self._requestObjectGraspabilityService = rospy.Service('/tidyup/request_graspability', srv.RequestObjectsGraspability, self._request_graspability_callback)
00073
00074 self._pickUpActionServer = actionlib.SimpleActionServer('/tidyup/grasping_action', msg.GraspObjectAction, self._pick_up_execute, False)
00075 self._pickUpActionServer.start()
00076
00077 self._find_putdown_pose_service = rospy.Service('/tidyup/request_putdown_pose', srv.GetPutdownPose, self._find_putdown_pose_callback)
00078
00079 self._putDownActionServer = actionlib.SimpleActionServer('/tidyup/placing_action', msg.PlaceObjectAction, self._put_down_execute, False)
00080 self._putDownActionServer.start()
00081
00082 self._resetWorldInterface = rospy.Service('/tidyup/reset_world_interface', std_srvs.srv.Empty, self._reset_world_interface)
00083 self._attachSponge = rospy.Service('/tidyup/attach_sponge', std_srvs.srv.Empty, self._add_sponge_to_right_gripper)
00084 self._detachSponge = rospy.Service('/tidyup/detach_sponge', std_srvs.srv.Empty, self._add_sponge_to_base)
00085
00086 self._visualization_publisher = rospy.Publisher('/tidyup/visualization', MarkerArray)
00087
00088 rospy.loginfo('tidyup_arm_services: initialized.')
00089
00090
00091 def _to_carry_execute(self, goal):
00092 self._psi.reset()
00093 arm_name = 'no_arm'
00094 if goal.right_arm:
00095 arm_name = 'right_arm'
00096 elif goal.left_arm:
00097 arm_name = 'left_arm'
00098 rospy.loginfo('executing: move {0} to carry.'.format(arm_name))
00099 self._tasks.side_joint_trajectory = self._carry_joint_trajectory
00100 self._tasks.move_arm_to_side(arm_name)
00101 self._psi.reset()
00102 self._toCarryActionServer.set_succeeded()
00103
00104 def _to_side_execute(self, goal):
00105 self._psi.reset()
00106 arm_name = 'no_arm'
00107 if goal.right_arm:
00108 arm_name = 'right_arm'
00109 elif goal.left_arm:
00110 arm_name = 'left_arm'
00111 rospy.loginfo('executing: move {0} to side.'.format(arm_name))
00112 self._tasks.side_joint_trajectory = self._side_joint_trajectory
00113 self._tasks.move_arm_to_side(arm_name)
00114 self._psi.reset()
00115 self._toSideActionServer.set_succeeded()
00116
00117 def _reset_world_interface(self, request=None):
00118 rospy.loginfo("Resetting world interface")
00119 self._wi.reset(True)
00120 self._known_objects = dict()
00121 self._known_wipe_goals = dict()
00122 self._add_tables_to_wi()
00123 self._add_sponge_to_base()
00124 self._psi.reset()
00125 return {}
00126
00127 def _pose_to_str(self, pose):
00128 if pose is None:
00129 return "None pose"
00130 (_, _, yaw) = tf.transformations.euler_from_quaternion([pose.orientation.x, pose.orientation.y, pose.orientation.z, pose.orientation.w])
00131 ps = "position: (%.2f, %.2f, %.2f) - quat: (%.2f, %.2f, %.2f, %.2f) - yaw: %.2f deg" % \
00132 (pose.position.x, pose.position.y, pose.position.z, \
00133 pose.orientation.x, pose.orientation.y, pose.orientation.z, pose.orientation.w, 180.0/math.pi*yaw)
00134 return ps
00135
00136 def _point_to_str(self, pt):
00137 ps = "position: (%.2f, %.2f, %.2f)" % \
00138 (pt.x, pt.y, pt.z)
00139 return ps
00140
00141 def _dump_planning_scene_objects(self, planning_scene=None):
00142 """ Output a condensed printable form of the planning scene.
00143 If the planning_scene is None, the current world scene is retrieved
00144 from the world interface."""
00145 if planning_scene is None:
00146 planning_scene = self._wi.get_planning_scene().planning_scene
00147
00148 s = "Collision Objects:"
00149 if not planning_scene.collision_objects:
00150 s += "\nNone"
00151 for co in planning_scene.collision_objects:
00152 s += "\nid: %s op: %d frame: %s pose: %s" % (co.id, co.operation.operation, co.header.frame_id, \
00153 self._pose_to_str(co.poses[0]))
00154 s += "\nAttached Collision Objects:"
00155 if not planning_scene.attached_collision_objects:
00156 s += "\nNone"
00157 for aco in planning_scene.attached_collision_objects:
00158 s += "\nid: %s attach_link: %s op: %d frame: %s pose: %s" % (aco.object.id, aco.link_name, \
00159 aco.object.operation.operation, \
00160 aco.object.header.frame_id, self._pose_to_str(aco.object.poses[0]))
00161 rospy.loginfo("Planning Scene Objects:\n%s" % s)
00162
00163 def _pickup_goal_to_str(self, pg):
00164 if pg is None:
00165 return "Pickup Goal is None"
00166 s = "Pickup Goal for %s (collision_object) on collision_support %s using arm \"%s\".\n" % \
00167 (pg.collision_object_name, pg.collision_support_surface_name, pg.arm_name)
00168 s += "label: %s graspable_object collision name: %s object pose: %s %s\n" % \
00169 (pg.label, pg.target.collision_name, pg.object_pose_stamped.header.frame_id, self._pose_to_str(pg.object_pose_stamped.pose))
00170 if hasattr(pg, 'collision_object_header') and hasattr(pg, 'collision_object_poses'):
00171 s += "collision_object_pose: %s %s" % (pg.collision_object_header.frame_id, self._pose_to_str(pg.collision_object_poses[0]))
00172 else:
00173 s += "no collision_object_pose"
00174
00175 return s
00176
00177 def _place_goal_to_str(self, pg, print_locations=True):
00178 if pg is None:
00179 return "Place Goal is None"
00180 s = "Place Goal for %s (collision_object) on collision_support %s using arm \"%s\"." % \
00181 (pg.collision_object_name, pg.collision_support_surface_name, pg.arm_name)
00182 if print_locations:
00183 s += "\nPlace Locations:"
00184 for loc in pg.place_locations:
00185 s += "\nframe: %s pose: %s" % (loc.header.frame_id, self._pose_to_str(loc.pose))
00186 return s
00187
00188 def _dump_known_objects(self):
00189 s = ""
00190 if not self._known_objects:
00191 s += " None"
00192 for so, known in self._known_objects.iteritems():
00193 s += "\nOn Static Object %s:" % so
00194 for pg in known.pickup_goals:
00195 s += "\n" + self._pickup_goal_to_str(pg)
00196 rospy.loginfo("Known Objects:%s" % s)
00197
00198 def _wipe_goal_to_str(self, wg, valid):
00199 if wg is None:
00200 return "Wipe Goal is None"
00201 validity = "validity unknown"
00202 if valid is not None:
00203 if valid:
00204 validity = "valid"
00205 else:
00206 validity = "invalid"
00207 s = "Wipe Goal %s (box_size %s) at %s, %s (%s)." % \
00208 (wg.name, wg.box_size, wg.spot.header.frame_id, self._point_to_str(wg.spot.point), validity)
00209 return s
00210
00211 def _dump_known_wipe_goals(self):
00212 s = ""
00213 if not self._known_wipe_goals:
00214 s += " None"
00215 for so, wgs in self._known_wipe_goals.iteritems():
00216 s += "\nOn Static Object %s:" % so
00217 for (wg, valid) in wgs:
00218 s += "\n" + self._wipe_goal_to_str(wg, valid)
00219 rospy.loginfo("Known Wipe Goals:%s" % s)
00220
00221 def _remove_known_objects(self, known):
00222 """ Remove known objects from the world."""
00223 if known:
00224 for pg in known.pickup_goals:
00225 self._wi.remove_collision_object(pg.collision_object_name)
00226
00227 def _remove_objects_in_fov(self, cur_collision_objects):
00228 """ Remove objects from the world that are in the FOV.
00229 The cur_collision_objects contain the collision objects as they were in
00230 the world right before calling this function."""
00231
00232 rospy.loginfo("Removing collision objects in FOV for re-detection")
00233
00234
00235 fov_point = PoseStamped()
00236 fov_point.header.frame_id = "base_link"
00237 fov_point.header.stamp = rospy.Time.now()
00238 fov_point.pose.position.x = 0.7
00239 fov_point.pose.position.y = 0.0
00240 fov_point.pose.position.z = 0.7
00241 fov_point.pose.orientation.x = 0.0
00242 fov_point.pose.orientation.y = 0.0
00243 fov_point.pose.orientation.z = 0.0
00244 fov_point.pose.orientation.w = 1.0
00245 map_fov_point = self._psi.transform_pose_stamped("map", fov_point)
00246 clearing_radius = 1.0
00247 for co in cur_collision_objects:
00248 if co.id.startswith("table"):
00249 continue
00250 distance = gt.euclidean_distance(map_fov_point.pose.position, co.poses[0].position)
00251 if distance < clearing_radius:
00252 self._wi.remove_collision_object(co.id)
00253
00254 def _compute_redetected_objects(self, known, det, cur_collision_objects):
00255 """ Compute which objects have been redetected, which are newly detected, and
00256 which known objects have not been redetected.
00257 known and det are detection results of the previsouly known objects (known)
00258 and the current detection (det).
00259 The cur_collision_objects contain the collision objects as they were in
00260 the world right before calling this function.
00261 Returns a tuple (known_not_redetected, newly_detected, redetected) that
00262 contains sets of the respective pickup goals. Redetected contains a set
00263 of tuples (known_pg, detected_pg) that are redetected matches."""
00264
00265 rospy.loginfo("Updating/merging world interface/known objects.")
00266
00267 known_not_redetected = set()
00268 redetected = set()
00269 if known:
00270 for known_pg in known.pickup_goals:
00271
00272 min_distance = 0.1
00273 closest_new_pg = None
00274 for det_pg in det.pickup_goals:
00275 det_co = None
00276 for co in cur_collision_objects:
00277 if co.id == det_pg.collision_object_name:
00278 det_co = co
00279 break
00280 if det_co is None:
00281 rospy.error("Could not find collision object for pickup goal %s" % det_pg.collision_object_name)
00282 assert det_co.header.frame_id == known_pg.collision_object_header.frame_id
00283 distance = gt.euclidean_distance(known_pg.collision_object_poses[0].position, det_co.poses[0].position)
00284 if distance < min_distance:
00285 min_distance = distance
00286 closest_new_pg = det_pg
00287 if closest_new_pg is None:
00288 known_not_redetected.add(known_pg)
00289 else:
00290
00291
00292 known_type = known_pg.collision_object_name.split("_")[0]
00293 new_detected_type = closest_new_pg.collision_object_name.split("_")[0]
00294 if known_type != new_detected_type:
00295 rospy.logwarn('Possible type redetection mismatch: known: %s new_detected: %s' % \
00296 (self._pickup_goal_to_str(known_pg), self._pickup_goal_to_str(closest_new_pg)))
00297 rospy.logwarn('Treating as non-match/new detection')
00298 known_not_redetected.add(known_pg)
00299 else:
00300 redetected.add((known_pg, closest_new_pg))
00301
00302
00303 newly_detected = set()
00304 redetected_only_new = [new_pg for (known_pg, new_pg) in redetected]
00305
00306
00307 for det_pg in det.pickup_goals:
00308 if det_pg not in redetected_only_new:
00309 newly_detected.add(det_pg)
00310
00311
00312 for pg in known_not_redetected:
00313 rospy.logwarn('not able to re-detect known object {0}.'.format(self._pickup_goal_to_str(pg)))
00314 for pg in newly_detected:
00315 rospy.loginfo('Newly detected object: %s' % self._pickup_goal_to_str(pg))
00316 for (known_pg, detected_pg) in redetected:
00317 rospy.loginfo('known object {0} re-detected as {1}.'.format(
00318 self._pickup_goal_to_str(known_pg), self._pickup_goal_to_str(detected_pg)))
00319
00320 return (known_not_redetected, newly_detected, redetected)
00321
00322 def _compute_detection_name_mappings(self, det_names, world_names, newly_detected, redetected):
00323 """ Determine name mappings in the form newly_detected_name -> old/known_name.
00324 The name mappings should include a mapping for every detected object and
00325 thus might include trivial ones like: cup_1 -> cup_1.
00326 Args:
00327 det_names - set of names in the new detection
00328 world_names - set of names that exist in the world (from cur_collision_objects)
00329 newly_detected - pickup goals for objects that have been newly detected
00330 redetected - set of pickup goal tuples of redetected objects: (known_pg, redetected_pg)
00331 """
00332
00333 name_mappings = dict()
00334 new_names = set()
00335
00336 for (known_pg, detected_pg) in redetected:
00337 name_mappings[detected_pg.collision_object_name] = known_pg.collision_object_name
00338 if detected_pg.collision_object_name != known_pg.collision_object_name:
00339 new_names.add(known_pg.collision_object_name)
00340
00341
00342
00343
00344
00345
00346 world_other = world_names - det_names
00347 world_conflicts = world_other & new_names
00348 for wc_name in world_conflicts:
00349 rospy.logerror('Naming conflict: Found %s in world, but should be reassigned from known' % wc_name)
00350
00351
00352
00353 for new_pg in newly_detected:
00354 if new_pg.collision_object_name in new_names:
00355
00356 type_name_parts = new_pg.collision_object_name.split("_")
00357 type_name = type_name_parts[0]
00358 if(len(type_name_parts) > 1):
00359 type_name = '_'.join(type_name_parts[:-1])
00360
00361 cand = ""
00362 for cand_id in range(1, 100000):
00363 cand = type_name + "_" + str(cand_id)
00364 if cand not in world_other and cand not in new_names:
00365 break
00366 if not cand:
00367 rospy.logerror('Could not find new name for %s - this is bad.' % new_pg.collision_object_name)
00368 else:
00369 name_mappings[new_pg.collision_object_name] = cand
00370 new_names.add(cand)
00371 else:
00372 name_mappings[new_pg.collision_object_name] = new_pg.collision_object_name
00373 new_names.add(new_pg.collision_object_name)
00374
00375 rospy.loginfo("Determined the following renamings: %s" % str(name_mappings))
00376 return name_mappings
00377
00378 def _remove_objects_for_renaming(self, name_mappings):
00379 """ Remove all objects from the world that should be renamed. """
00380
00381 rospy.loginfo('Removing old objects for renaming')
00382 for old_name, new_name in name_mappings.iteritems():
00383 if old_name != new_name:
00384 self._wi.remove_collision_object(old_name)
00385
00386 def _readd_objects_for_renaming(self, name_mappings, cur_collision_objects):
00387 """ Readd all objects from the world (with a new name) that were
00388 previously removed for renaming.
00389 The cur_collision_objects are the object as they were in the world
00390 before _remove_objects_for_renaming was called!"""
00391
00392 rospy.loginfo('Add new objects for renaming')
00393 for old_name, new_name in name_mappings.iteritems():
00394 if old_name != new_name:
00395 match_co = None
00396 for co in cur_collision_objects:
00397 if co.id == old_name:
00398 match_co = co
00399 break
00400 if match_co is None:
00401 rospy.logerror('_readd_objects_for_renaming: Could not find collision object for %s in current collision objects.' % old_name)
00402 else:
00403 match_co.id = new_name
00404 self._wi.add_object(match_co)
00405
00406 def _rename_and_update_pickup_goals(self, name_mappings, cur_collision_objects, pickup_goals):
00407 """ Rename the pickup goals according to name mappings and also update/assign a
00408 collision_object_header/collision_object_poses to them.
00409 The cur_collision_objects contain the collision objects as they were in
00410 the world right before calling this function."""
00411
00412 rospy.loginfo('Renaming known pickup goals')
00413 for pg in pickup_goals:
00414 pg.target.collision_name = name_mappings[pg.collision_object_name]
00415 pg.collision_object_name = name_mappings[pg.collision_object_name]
00416
00417 rospy.loginfo("Adding collision_object_poses to pickup goals")
00418 for pg in pickup_goals:
00419 match_co = None
00420 for co in cur_collision_objects:
00421 if co.id == pg.collision_object_name:
00422 match_co = co
00423 break
00424 if match_co is None:
00425 rospy.logerror('_rename_and_update_pickup_goals: Could not find collision object for %s in current collision objects.' % pg.collision_object_name)
00426 else:
00427 pg.collision_object_header = match_co.header
00428 pg.collision_object_poses = match_co.poses
00429 assert pg.collision_object_poses
00430
00431 def _compute_new_wipe_goals(self, pickup_goals):
00432 """ Compute tentative wiping goals from pickup_goals (using their obj locations)
00433 pickup_goals is a list of PickupGoal with collision_object_poses + names
00434 Returns: list of (msg.WipeGoal, valid) - Here valid is None = unknown."""
00435 wgs = []
00436 for pg in pickup_goals:
00437 wg = msg.WipeGoal()
00438 wg.name = "wipe_goal_" + pg.collision_object_name
00439 wg.box_size = 0.3
00440 wg.spot.header = pg.collision_object_header
00441 wg.spot.point = pg.collision_object_poses[0].position
00442 wgs.append((wg, None))
00443 return wgs
00444
00445 def _filter_wipe_goals(self, new_wgs, known_wgs):
00446 """ Filter wiping goals for this location.
00447 new_wg is a list of wiping goals computed for this location.
00448 known_wgs is a list of the known wiping goals already at this location.
00449 Returns a filtered list of WipeGoals where all new and known wipe goals
00450 are contained, unless a new WipeGoal is too close to an already known WipeGoal."""
00451
00452
00453
00454
00455 def wg_distance(wg1, wg2):
00456 return gt.euclidean_distance(wg1.spot.point, wg2.spot.point)
00457
00458
00459
00460 all_known_wipe_goal_names = set()
00461 for so, wgs in self._known_wipe_goals.iteritems():
00462 for (wg, valid) in wgs:
00463 all_known_wipe_goal_names.add(wg.name)
00464
00465
00466
00467 filtered_wgs = []
00468
00469 if known_wgs:
00470 for (wg, valid) in known_wgs:
00471 filtered_wgs.append((wg, valid))
00472
00473 for (wg, _) in new_wgs:
00474
00475 if wg.name in all_known_wipe_goal_names:
00476 continue
00477
00478
00479 valid = True
00480 if known_wgs:
00481 for (known_wg, known_wg_valid) in known_wgs:
00482 if not known_wg_valid:
00483 continue
00484 if wg_distance(wg, known_wg) <= 0.1:
00485 valid = False
00486 break
00487 filtered_wgs.append((wg, valid))
00488 return filtered_wgs
00489
00490 def _create_detected_visible_objects(self, pickup_goals):
00491 """ Create a reply of visible_objects for an object detection request from the pickup_goals. """
00492
00493 self._psi.reset()
00494
00495
00496 visible_objects = list()
00497 for pg in pickup_goals:
00498 object = msg.GraspableObject()
00499 object.name = pg.collision_object_name
00500 object.pose = pg.object_pose_stamped
00501
00502
00503 if hasattr(pg, 'collision_object_header') and hasattr(pg, 'collision_object_poses'):
00504 object.pose = PoseStamped()
00505 object.pose.header = pg.collision_object_header
00506 object.pose.pose = pg.collision_object_poses[0]
00507 else:
00508 rospy.logwarn("Pickup goal for %s did not have collision_object_header/poses" % pg.collision_object_name)
00509 object.reachable_right_arm = True
00510 object.reachable_left_arm = True
00511
00512
00513
00514
00515
00516
00517
00518
00519
00520
00521
00522 visible_objects.append(object)
00523
00524 return visible_objects
00525
00526 def _detect_objects_callback(self, request):
00527 """ Call the object detection and based on that:
00528 update the world interface so the newly detected objects are now in the world and
00529 already known objects are updated. The set of objects that we know to be on this
00530 static_object should be exaclty the set of detected objects, i.e. not re-detected objects are removed,
00531 new ones added, re-detected ones updated.
00532 Objects in the world not on this static_object should never be touched.
00533
00534 In addition update our known_objects on this static_object with this information.
00535 We will also forget about any other known_objects on this location
00536 and the planner state should do the same.
00537 Any picked up object should also be removed."""
00538
00539 rospy.loginfo('request: detect objects at %s.' % request.static_object)
00540 self._psi.reset()
00541
00542 self._head.look_at_relative_point(0.8, 0.0, 0.7)
00543
00544 self._dump_planning_scene_objects()
00545 self._dump_known_objects()
00546
00547 rospy.loginfo("Removing known objects at %s from world interface for re-detection." % request.static_object)
00548
00549 cur_collision_objects = self._wi.collision_objects()
00550
00551 known = self._known_objects.get(request.static_object)
00552 self._remove_known_objects(known)
00553
00554 self._dump_planning_scene_objects()
00555
00556 self._remove_objects_in_fov(cur_collision_objects)
00557
00558 self._dump_planning_scene_objects()
00559 self._dump_known_objects()
00560
00561 rospy.loginfo("Calling object detection.")
00562
00563 try:
00564
00565 det = self._detector.detect_objects(add_objects_to_map='recognized', add_table_to_map=False, reset_collision_objects=False, table_name=request.static_object)
00566 except DetectionError, last_ex:
00567 rospy.logerr('object detection FAILED')
00568 return False
00569
00570 self._dump_planning_scene_objects()
00571 self._dump_known_objects()
00572
00573
00574 recognized_pgs = list()
00575 for pg in det.pickup_goals:
00576 if pg.label != 'graspable':
00577 recognized_pgs.append(pg)
00578 det.pickup_goals = recognized_pgs
00579 rospy.loginfo("Recognized pickup goals from detection:")
00580 for pg in det.pickup_goals:
00581 rospy.loginfo(self._pickup_goal_to_str(pg))
00582
00583 cur_collision_objects = self._wi.collision_objects()
00584 (known_not_redetected, newly_detected, redetected) = self._compute_redetected_objects(known, det, cur_collision_objects)
00585
00586
00587
00588 new_known = det
00589
00590
00591
00592
00593 det_names = set([pg.collision_object_name for pg in det.pickup_goals])
00594 world_names = set([co.id for co in cur_collision_objects])
00595
00596 name_mappings = self._compute_detection_name_mappings(det_names, world_names, newly_detected, redetected)
00597
00598
00599
00600 self._dump_planning_scene_objects()
00601 self._remove_objects_for_renaming(name_mappings)
00602 self._dump_planning_scene_objects()
00603 self._readd_objects_for_renaming(name_mappings, cur_collision_objects)
00604 self._dump_planning_scene_objects()
00605
00606
00607 cur_collision_objects = self._wi.collision_objects()
00608 self._rename_and_update_pickup_goals(name_mappings, cur_collision_objects, new_known.pickup_goals)
00609
00610 self._known_objects[request.static_object] = new_known
00611
00612 rospy.loginfo("Final world scene/known objects")
00613 self._dump_planning_scene_objects()
00614 self._dump_known_objects()
00615
00616 rospy.loginfo("Update/Merge done.")
00617
00618 rospy.loginfo("Computing Wipe Goals")
00619 self._dump_known_wipe_goals()
00620
00621
00622 new_wgs = self._compute_new_wipe_goals(self._known_objects[request.static_object].pickup_goals)
00623 rospy.loginfo("Tentative new wipe goals at %s" % request.static_object)
00624 for (wg, valid) in new_wgs:
00625 rospy.loginfo(self._wipe_goal_to_str(wg, valid))
00626
00627 filtered_wgs = self._filter_wipe_goals(new_wgs, self._known_wipe_goals.get(request.static_object))
00628 rospy.loginfo("Filtered wipe goals at %s" % request.static_object)
00629 for (wg, valid) in filtered_wgs:
00630 rospy.loginfo(self._wipe_goal_to_str(wg, valid))
00631
00632 self._known_wipe_goals[request.static_object] = filtered_wgs
00633
00634 self._dump_known_wipe_goals()
00635
00636
00637 visible_objects = self._create_detected_visible_objects(self._known_objects[request.static_object].pickup_goals)
00638 valid_wipe_goals = [wg for (wg, valid) in filtered_wgs if valid]
00639
00640 obj_names = ", ".join([obj.name for obj in visible_objects])
00641 wg_names = ", ".join([wg.name for wg in valid_wipe_goals])
00642 rospy.loginfo('detect objects finished. sending {0} objects ({1})'.format(len(visible_objects), obj_names))
00643 rospy.loginfo('determined %d wipe goals (%s)' % (len(valid_wipe_goals), wg_names))
00644 return { 'objects': visible_objects, 'wipe_goals': valid_wipe_goals}
00645
00646 def _request_graspability_callback(self, request):
00647 rospy.loginfo('DEPRECATED: request object graspability.')
00648 return { 'objects': request.objects}
00649
00650 def _pick_up_execute(self, goal):
00651 rospy.loginfo('pick up execute: pickup {0} on {1} with {2}'.format(goal.pickup_object, goal.static_object, goal.arm))
00652 self._psi.reset()
00653 action_result = msg.GraspObjectResult()
00654 action_result.grasp.pose.orientation.w = 1
00655
00656 self._dump_known_objects()
00657
00658
00659 known = self._known_objects.get(goal.static_object)
00660 if not known:
00661 rospy.logerr('pickup_execute: FAILED: detection data not available for this static_object {0}'.format(goal.static_object))
00662 action_result.error_code.val = action_result.error_code.SENSOR_INFO_STALE
00663 self._pickUpActionServer.set_aborted(result=action_result, text='execution failed: detection data not available for this static_object {0}'.format(goal.static_object))
00664 return
00665 pickup_goal = None
00666 for pg in known.pickup_goals:
00667 if pg.collision_object_name == goal.pickup_object:
00668 pickup_goal = pg
00669 break
00670 if pickup_goal == None:
00671 rospy.logerr('pickup_execute: FAILED: detection data not available for this pickup_object {0}'.format(goal.pickup_object))
00672 action_result.error_code.val = action_result.error_code.SENSOR_INFO_STALE
00673 self._pickUpActionServer.set_aborted(result=action_result, text='execution failed: detection data not available for this pickup_object {0}'.format(goal.pickup_object))
00674 return
00675
00676
00677
00678 pickup_goal.arm_name = goal.arm
00679
00680 try:
00681 pickup_result = self._pick_place.pickup(pickup_goal)
00682 except ManipulationError, last_ex:
00683 rospy.logerr('pickup_execute: FAILED: {0}'.format(last_ex))
00684
00685 self._pickUpActionServer.set_aborted(result=action_result, text='execution failed: {0}'.format(last_ex))
00686 return
00687
00688
00689
00690 known.pickup_goals.remove(pickup_goal)
00691
00692 self._dump_known_objects()
00693
00694 action_result.error_code.val = action_result.error_code.SUCCESS
00695
00696
00697 rospy.loginfo('pickup_execute: succeded')
00698 action_result.grasp.header.frame_id = self._tasks._arm_planners[goal.arm].hand.attach_link
00699 self._pickUpActionServer.set_succeeded(result=action_result, text='pickup successful')
00700
00701 def _find_putdown_pose_callback(self, request):
00702 rospy.loginfo('find putdown pose request: place {0} held in {2} on {1}'.format(request.putdown_object, request.static_object, request.arm))
00703 psi = self._psi
00704 psi.current_diff.planning_scene_diff = request.planning_scene
00705 psi.send_diff()
00706
00707
00708 place_goal, grasp = self._create_place_goal(request)
00709 response = srv.GetPutdownPoseResponse()
00710 if not place_goal:
00711 rospy.logerr('find putdown pose FAILED: no object attached to gripper.')
00712 response.error_code.val = ArmNavigationErrorCodes.INCOMPLETE_ROBOT_STATE
00713 return response
00714 place_pose = self._check_trajectory_to_place_goal(place_goal)
00715 if not place_pose:
00716 rospy.logerr('find putdown pose FAILED: no valid pose found.')
00717 response.error_code.val = ArmNavigationErrorCodes.PLANNING_FAILED
00718 return response
00719 response.error_code.val = ArmNavigationErrorCodes.SUCCESS
00720 response.putdown_pose = place_pose
00721 rospy.loginfo('found putdown pose: {0}'.format(response.putdown_pose))
00722 return response
00723
00724 def _put_down_execute(self, goal):
00725 rospy.loginfo('find putdown pose request: place {0} held in {2} on {1}'.format(goal.putdown_object, goal.static_object, goal.arm))
00726 self._psi.reset()
00727
00728 place_goal, grasp = self._create_place_goal(goal)
00729 rospy.loginfo(self._place_goal_to_str(place_goal))
00730
00731 self._pick_place._place_client.send_goal_and_wait(place_goal)
00732 result = self._pick_place._place_client.get_result()
00733 if result.manipulation_result.value == result.manipulation_result.SUCCESS:
00734 rospy.loginfo('planner selected pose: {0}'.format(result.place_location))
00735
00736 self._dump_known_objects()
00737
00738 rospy.loginfo('Inserting new pickup goal for %s at %s' % (goal.putdown_object, goal.static_object))
00739
00740
00741
00742
00743 known = self._known_objects.get(goal.static_object)
00744 putdown_pg = None
00745
00746
00747 if known:
00748 for pg in known.pickup_goals:
00749 if pg.collision_object_name == goal.putdown_object:
00750 putdown_pg = pg
00751 rospy.logwarn('Found old pickup goal (%s) for putdown object already at this location' % _pickup_goal_to_str(putdown_pg))
00752 break
00753 else:
00754 table = tabletop_object_detector.msg.Table()
00755 known = TablewareDetectionResult([], table, goal.static_object)
00756 self._known_objects[goal.static_object] = known
00757
00758
00759 place_object_location = self.get_object_pose_from_grasp(result.place_location, grasp)
00760
00761
00762 if putdown_pg is None:
00763 grasp_target = object_manipulation_msgs.msg.GraspableObject()
00764 grasp_target.collision_name = goal.putdown_object
00765
00766 putdown_pg = PickupGoal("", grasp_target, object_pose_stamped=place_object_location, collision_object_name=goal.putdown_object)
00767 known.pickup_goals.append(putdown_pg)
00768 else:
00769 putdown_pg.target.collision_name = goal.putdown_object
00770 putdown_pg.object_pose_stamped = place_object_location
00771
00772 putdown_pg.collision_object_header = place_object_location.header
00773 putdown_pg.collision_object_poses = [place_object_location.pose]
00774 putdown_pg.collision_support_surface_name = goal.static_object
00775
00776 self._dump_known_objects()
00777
00778
00779 self._psi.reset()
00780 action_result = msg.PlaceObjectResult()
00781 action_result.putdown_pose = place_object_location
00782 action_result.error_code.val = result.manipulation_result.value
00783 self._putDownActionServer.set_succeeded(text='putdown successful', result=action_result)
00784 rospy.loginfo("putdown successful")
00785 else:
00786 rospy.logerr('pickplace.place pose FAILED: error code: {0}'.format(result.manipulation_result.value))
00787 rospy.logerr('Attempted Locations:')
00788 for ps in result.attempted_locations:
00789 rospy.logerr("frame: %s pose: %s" % (ps.header.frame_id, self._pose_to_str(ps.pose)))
00790 rospy.logerr('PlaceLocationResults:')
00791 for plr in result.attempted_location_results:
00792 rospy.logerr("Result Code: %d" % plr.result_code)
00793 self._putDownActionServer.set_aborted(text='find putdown pose FAILED: no valid pose.')
00794
00795
00796 def get_object_pose_from_grasp(self, wrist_pose_stamped, frame_free_grasp_pose):
00797 '''
00798 Function for finding the end effector pose given a grasp and a desired place for the object.
00799
00800 **Args:**
00801
00802 **wrist_pose_stamped (geometry_msgs.msg.PoseStamped):** The pose as a geometry_msgs.msg.PoseStamped
00803 of the end effector to place the object at place_pose_stamped.
00804 For the PR2 this is the wrist pose.
00805
00806 **frame_free_grasp (pickplace_definitions.FrameFreeGrasp):** Grasp with which the object is held
00807
00808 **Returns:**
00809 place_pose_stamped (geometry_msgs.msg.PoseStamped): The Place for an object
00810 '''
00811
00812 origin = Pose()
00813 origin.orientation.w = 1.0
00814 pose_stamped = PoseStamped()
00815 pose_stamped.header.frame_id = wrist_pose_stamped.header.frame_id
00816 pose_stamped.header.stamp = wrist_pose_stamped.header.stamp
00817 pose_stamped.pose = gt.transform_pose(gt.transform_pose(origin, frame_free_grasp_pose),
00818 wrist_pose_stamped.pose)
00819 return pose_stamped
00820
00821 def _create_place_goal(self, request):
00822 rospy.loginfo('create_place_goal')
00823
00824 psi = self._psi
00825 scene = psi.current_planning_scene()
00826
00827
00828 putdown_object = None
00829 for attached in scene.attached_collision_objects:
00830 if attached.object.id == request.putdown_object and attached.link_name[0] == request.arm[0]:
00831 putdown_object = attached
00832
00833 if not putdown_object:
00834 rospy.logerr('no attached object.')
00835 return None, None
00836 rospy.loginfo('attached object: {0}'.format(putdown_object.object.id))
00837
00838
00839 table = None
00840 for co in scene.collision_objects:
00841 if co.id == request.static_object:
00842 table = co
00843
00844 collision_objects = []
00845 for object in scene.collision_objects:
00846 if object.id != request.static_object and object.id != request.putdown_object:
00847 collision_objects.append(object)
00848
00849
00850 orientation = scene.robot_state.multi_dof_joint_state.poses[0].orientation
00851 object_poses = free_spots_on_table(table, putdown_object.object, orientation, collision_objects, orientation_count=8)
00852 rospy.loginfo('finished: free_spots_on_table ({1}). first pose: {0}'.format(object_poses[0], len(object_poses)))
00853
00854
00855 preferred_putdown_point = Point()
00856 preferred_putdown_point.x = 0.7
00857 preferred_putdown_point.y = 0.0
00858 preferred_putdown_point.z = 0
00859 if request.arm == 'right_arm':
00860 preferred_putdown_point.y *= -1;
00861 preferred_putdown_point = psi.transform_point('/map', '/torso_lift_link', preferred_putdown_point)
00862 object_poses.sort(key=lambda object_pose: math.sqrt((preferred_putdown_point.x - object_pose.pose.position.x)**2 + (preferred_putdown_point.y - object_pose.pose.position.y)**2))
00863 shoulder_point = Point()
00864 shoulder_point.x = 0
00865 shoulder_point.y = 0.3
00866 shoulder_point.z = 0
00867 if request.arm == 'right_arm':
00868 shoulder_point.y *= -1;
00869 shoulder_point = psi.transform_point('/map', '/torso_lift_link', shoulder_point)
00870 rospy.loginfo('should point at: {0}'.format(shoulder_point))
00871 object_poses = filter(lambda object_pose: math.sqrt((shoulder_point.x - object_pose.pose.position.x)**2 + (shoulder_point.y - object_pose.pose.position.y)**2) <= 1.0, object_poses)
00872 if object_poses:
00873 rospy.loginfo('finished: prioritizing/filtering ({1}). first pose: {0}'.format(object_poses[0], len(object_poses)))
00874 else:
00875 rospy.logwarn('finished: prioritizing/filtering: 0 poses.')
00876
00877
00878
00879
00880 grasp = psi.transform_pose(\
00881 psi.hands[request.arm].hand_frame, \
00882 putdown_object.object.header.frame_id, \
00883 putdown_object.object.poses[0])
00884 rospy.loginfo('grasp: {0}'.format(grasp))
00885
00886
00887 wrist_poses = []
00888 for object_pose in object_poses:
00889 wrist_poses.append(get_wrist_pose_from_grasp(object_pose, grasp))
00890
00891
00892 if object_poses:
00893 rospy.loginfo('finished creating place goal. firts pose: {0}'.format(wrist_poses[0]))
00894 else:
00895 rospy.logwarn('place goal does not have any poses')
00896 return PlaceGoal(request.arm, wrist_poses, collision_object_name=putdown_object.object.id, collision_support_surface_name=table.id), grasp
00897
00898 def _check_trajectory_to_place_goal(self, place_goal):
00899 rospy.loginfo('check putdown for {0} with {1}'.format(place_goal.collision_object_name, place_goal.arm_name))
00900
00901
00902 collision = CollisionOperation()
00903 collision.object1 = place_goal.collision_object_name
00904 collision.object2 = collision.COLLISION_SET_ALL
00905 collision.operation = collision.DISABLE
00906 place_goal.additional_collision_operations.collision_operations.append(collision)
00907 self._psi.add_ordered_collisions(place_goal.additional_collision_operations)
00908 marker_ns = place_goal.collision_object_name+"_"+place_goal.arm_name+'_putdown'
00909 result = self._check_trajectory_to_wrist_poses_list(place_goal.place_locations, place_goal.arm_name, marker_ns=marker_ns)
00910 self._psi.remove_ordered_collisions(place_goal.additional_collision_operations)
00911
00912
00913
00914 return result
00915
00916 def _check_trajectory_to_pickup_goal(self, pickup_goal):
00917 rospy.loginfo('check pickup for {0} with {1}'.format(pickup_goal.collision_object_name, pickup_goal.arm_name))
00918
00919 grasps = self._pick_place.get_grasps(pickup_goal)
00920
00921
00922 collision = CollisionOperation()
00923 collision.object1 = pickup_goal.collision_object_name
00924 collision.object2 = collision.COLLISION_SET_ALL
00925 collision.operation = collision.DISABLE
00926 pickup_goal.additional_collision_operations.collision_operations.append(collision)
00927 self._psi.add_ordered_collisions(pickup_goal.additional_collision_operations)
00928 grasp_markers = MarkerArray()
00929 marker_ns = pickup_goal.collision_object_name+"_"+pickup_goal.arm_name+"_pickup"
00930
00931 rospy.loginfo('checking grasps: {0}'.format(len(grasps)))
00932 wrist_poses = list()
00933 for grasp in grasps:
00934
00935 gripper_pose = PoseStamped()
00936 gripper_pose.pose = grasp.grasp_pose
00937 gripper_pose.header.frame_id = '/base_link'
00938 wrist_poses.append(self._psi.transform_pose_stamped('/map', gripper_pose))
00939 result = self._check_trajectory_to_wrist_poses_list(wrist_poses, pickup_goal.arm_name, marker_ns)
00940 self._psi.remove_ordered_collisions(pickup_goal.additional_collision_operations)
00941 pickup_goal.additional_collision_operations.collision_operations.remove(collision)
00942 return result
00943
00944 def _check_trajectory_to_wrist_poses_list(self, wrist_poses, arm_name, marker_ns=None):
00945 if not marker_ns:
00946 marker_ns='wrist_'+arm_name
00947 rospy.loginfo('check to wrist poses {0} for {1}'.format(len(wrist_poses), arm_name))
00948
00949 arm_planner = self._tasks._arm_planners[arm_name]
00950 robot_state = self._psi.get_robot_state()
00951 wrist_markers = MarkerArray()
00952 found_trajectory = False
00953 trajectory_pose = None
00954 for wrist_pose in wrist_poses:
00955
00956
00957
00958 wrist_pose.pose.position.z += 0.1
00959
00960
00961
00962
00963 ik_result = arm_planner.get_ik(wrist_pose, starting_state=robot_state, handle_collision_operations=False)
00964 if ik_result.error_code.val == ik_result.error_code.SUCCESS:
00965 wrist_markers.markers.append(vt.marker_at(pose_stamped=wrist_pose, ns=marker_ns+'_'+str(ik_result.error_code.val), mid=len(wrist_markers.markers), mtype=Marker.ARROW, r=0.1, g=0.3, b=0.8, a=1.0))
00966 if not found_trajectory:
00967 rospy.loginfo('found ik solution, checking trajectory')
00968 try:
00969
00970 trajectory = arm_planner.plan_collision_free(wrist_pose, starting_state=robot_state)
00971 found_trajectory = True
00972 trajectory_pose = wrist_pose
00973 except exceptions.ArmNavError, e:
00974 wrist_markers.markers.append(vt.marker_at(pose_stamped=wrist_pose, ns=marker_ns+'_'+str(ik_result.error_code.val), mid=len(wrist_markers.markers), mtype=Marker.ARROW, r=0.1, g=0.8, b=0.8, a=1.0))
00975 else:
00976 wrist_markers.markers.append(vt.marker_at(pose_stamped=wrist_pose, ns=marker_ns+'_'+str(ik_result.error_code.val), mid=len(wrist_markers.markers), mtype=Marker.ARROW, r=0.8, g=0.3, b=0.1, a=1.0))
00977 if found_trajectory:
00978
00979 final_arm_state = tt.last_state_on_joint_trajectory(trajectory)
00980 final_robot_state = tt.set_joint_state_in_robot_state(final_arm_state, copy.deepcopy(robot_state))
00981 robot_marker = vt.robot_marker(robot_state=final_robot_state, ns=marker_ns+'_succeeded', r=0.2, g=0.5, b=0.9, a=0.5)
00982 self._visualization_publisher.publish(robot_marker)
00983 self._visualization_publisher.publish(wrist_markers)
00984 rospy.loginfo('trajectory ok, wrist pose reachable')
00985 return wrist_pose
00986 else:
00987
00988 robot_marker = vt.robot_marker(robot_state=robot_state, ns=marker_ns+'_failed', r=0.8, g=0.3, b=0.1, a=0.5)
00989 self._visualization_publisher.publish(robot_marker)
00990 self._visualization_publisher.publish(wrist_markers)
00991 rospy.loginfo('trajectory check FAILED')
00992 return None
00993
00994 def _add_tables_to_wi(self):
00995 table_file = rospy.get_param('~tables')
00996 tables = read_table_file(table_file)
00997 for table in tables:
00998 self._wi.add_collision_box(table[0], table[1], table[2])
00999
01000 def _add_sponge_to_base(self, req=None):
01001 link_name = 'base_link'
01002 object_name = 'sponge'
01003 touch_links = ['base_link']
01004 stamped = PoseStamped()
01005 pose = Pose()
01006 pose.position.x = 0.25
01007 pose.position.y = 0.0
01008 pose.position.z = 0.33
01009 pose.orientation.x = 0
01010 pose.orientation.y = 0
01011 pose.orientation.z = 0
01012 pose.orientation.w = 1
01013 stamped.pose = pose
01014 stamped.header.frame_id = link_name
01015 self._attach_object(object_name, link_name, touch_links, stamped)
01016 return {}
01017
01018 def _add_sponge_to_right_gripper(self, req=None):
01019 wi = self._wi
01020 hand = arm_planner = self._tasks._arm_planners['right_arm'].hand
01021 link_name = hand.attach_link
01022 object_name = 'sponge'
01023 touch_links = hand.touch_links
01024 stamped = PoseStamped()
01025 pose = Pose()
01026 pose.position.x = 0.025
01027 pose.position.y = 0.025
01028 pose.position.z = 0
01029 quat_list = tf.transformations.quaternion_from_euler(np.pi/2.0, 0, -np.pi/2.0)
01030 pose.orientation.x = quat_list[0]
01031 pose.orientation.y = quat_list[1]
01032 pose.orientation.z = quat_list[2]
01033 pose.orientation.w = quat_list[3]
01034 stamped.pose = pose
01035 stamped.header.frame_id = hand.attach_link
01036 self._attach_object(object_name, link_name, touch_links, stamped)
01037 return {}
01038
01039 def _attach_object(self, object_name, link_name, touch_links, stamped):
01040 wi = self._wi
01041
01042 attached = wi.attached_collision_object(object_name)
01043 if attached:
01044 attached.object.operation.operation = attached.object.operation.DETACH_AND_ADD_AS_OBJECT
01045 wi._publish(attached, wi._attached_object_pub)
01046
01047 object = wi.collision_object(object_name)
01048
01049 if not object:
01050 rospy.loginfo('creating sponge')
01051 wi.add_collision_box(stamped, [0.10, 0.15, 0.05], object_name)
01052 object = wi.collision_object(object_name)
01053
01054
01055 object.header.frame_id = stamped.header.frame_id
01056 object.poses[0] = stamped.pose
01057 object.operation.operation = object.operation.ADD
01058 wi.add_object(object)
01059
01060
01061 attached = AttachedCollisionObject()
01062 attached.object = wi.collision_object(object_name)
01063 attached.link_name = link_name
01064 attached.touch_links = touch_links
01065 attached.object.operation.operation = attached.object.operation.ATTACH_AND_REMOVE_AS_OBJECT
01066 wi._publish(attached, wi._attached_object_pub)
01067
01068 self._psi.reset()
01069
01070
01071