Right after the object has been picked up, the manipulator will proceed with the place action. MoveIt! provides the /place action server, so the first step consists of creating an action client to send a place goal in the desired location, in order to place the object picked up:
# Create move group 'place' action client: self._place_ac = SimpleActionClient('/place', PlaceAction) if not self._place_ac.wait_for_server(rospy.Duration(5.0)): rospy.logerr('Place action client not available!') rospy.signal_shutdown('Place action client not available!') return
Then, we will try to place the object until we finally manage to do it:
# Place Coke can object on another place on the support surface
(table):while not self._place(self._arm_group, self._grasp_object_name,
self._pose_place): rospy.logwarn('Place failed! Retrying ...') rospy.sleep(1.0)
The _place method uses the following code:
def _place(self, group, target, place): # Obtain possible places: places = self._generate_places(place) # Create and send Place goal: goal = self._create_place_goal(group, target, places) state = self._place_ac.send_goal_and_wait(goal) if state != GoalStatus.SUCCEEDED: rospy.logerr('Place goal failed!: ' %
self._place_ac.get_goal_status_text()) return None result = self._place_ac.get_result() # Check for error: err = result.error_code.val if err != MoveItErrorCodes.SUCCESS: rospy.logwarn('Group %s cannot place target %s!: %s' % (group,
target, str(moveit_error_dict[err])))
return False return True
The method generates multiple possible places to leave the object, creates the place goal, and sends it. Then, it checks the result to verify whether the object has been placed or not. To place an object, we can use a single place pose, but it is generally better to provide several options. In this case, we have the _generate_places method, which generates places with different angles at the position given.
When the places are generated, they are also published as PoseArray so we can see them with blue arrows, as shown in the following screenshot:
Once the places are obtained, the _create_place_goal method creates a place goal as follows:
def _create_place_goal(self, group, target, places): # Create goal: goal = PlaceGoal() goal.group_name = group goal.attached_object_name = target goal.place_locations.extend(places) # Configure goal planning options: goal.allowed_planning_time = 5.0 goal.planning_options.planning_scene_diff.is_diff = True goal.planning_options.planning_scene_diff.robot_state.is_diff =
True goal.planning_options.plan_only = False goal.planning_options.replan = True goal.planning_options.replan_attempts = 10 return goal
In brief, the place goal has the group (arm in this case) and the target object (coke_can in this case), which are attached to the gripper and the place or places (poses). Additionally, several planning options are provided, along with the allowed planning time, which can be increased if needed. When the object is placed, we will see the box representing it in green again, and on top of the table. The arm will be up again, as shown in the following screenshot: