pick_and_place_action_server.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015, Jorge Santos
3  * All Rights Reserved
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * * Neither the name of Vanadium Labs LLC nor the names of its
14  * contributors may be used to endorse or promote products derived
15  * from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL VANADIUM LABS BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
23  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
25  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
26  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  *
28  * Author: Jorge Santos
29  */
30 
31 #include <ros/ros.h>
32 #include <tf/tf.h>
33 #include <tf/transform_listener.h>
34 
35 #include <std_srvs/Empty.h>
36 
37 // auxiliary libraries
40 
41 // action servers
43 #include <turtlebot_arm_object_manipulation/PickAndPlaceAction.h>
44 #include <turtlebot_arm_object_manipulation/MoveToTargetAction.h>
45 
46 // MoveIt!
47 #include <moveit_msgs/Grasp.h>
48 #include <moveit_msgs/PlaceLocation.h>
51 
52 
54 {
55 
57 {
58 private:
60  std::string action_name_;
61 
62  turtlebot_arm_object_manipulation::PickAndPlaceFeedback feedback_;
63  turtlebot_arm_object_manipulation::PickAndPlaceResult result_;
64  turtlebot_arm_object_manipulation::PickAndPlaceGoalConstPtr goal_;
65 
66  std_srvs::Empty empty_srv_;
69 
71 
72  // Move groups to control arm and gripper with MoveIt!
75 
76  // We use the planning scene to gather information of tabletop/attached objects
78 
79  // Pick and place parameters
80  std::string arm_link;
81  double gripper_open;
82  double attach_time;
83  double detach_time;
84  double z_backlash;
85 
86  const int PICK_ATTEMPTS = 5;
88 
89 public:
90  PickAndPlaceServer(const std::string name) :
91  as_(name, false), action_name_(name), arm_("arm"), gripper_("gripper")
92  {
93  ros::NodeHandle nh("~");
94 
95  // Read specific pick and place parameters
96  nh.param("grasp_attach_time", attach_time, 0.8);
97  nh.param("grasp_detach_time", detach_time, 0.6);
98  nh.param("vertical_backlash", z_backlash, 0.01);
99  nh.param("/gripper_controller/max_opening", gripper_open, 0.045);
100 
101  // Register the goal and feedback callbacks
102  as_.registerGoalCallback(boost::bind(&PickAndPlaceServer::goalCB, this));
104 
105  as_.start();
106 
107  // We will clear the octomap and retry whenever a pick/place fails
108  clear_octomap_srv_ = nh.serviceClient<std_srvs::Empty>("/clear_octomap");
109 
110  // We publish the pick and place poses for debugging purposes
111  target_pose_pub_ = nh.advertise<geometry_msgs::PoseStamped>("/target_pose", 1, true);
112  }
113 
115  {
116  as_.shutdown();
117  }
118 
119  void goalCB()
120  {
121  ROS_INFO("[pick and place] Received goal!");
122 
123  goal_ = as_.acceptNewGoal();
124  arm_link = goal_->frame;
125 
126  arm_.setPoseReferenceFrame(arm_link);
127  arm_.setSupportSurfaceName("table");
128 
129  // Allow some leeway in position (meters) and orientation (radians)
130  arm_.setGoalPositionTolerance(0.001);
131  arm_.setGoalOrientationTolerance(0.02);
132 
133  // Allow replanning to increase the odds of a solution
134  arm_.allowReplanning(true);
135 
136  geometry_msgs::PoseStamped pick_pose, place_pose;
137  pick_pose.header = goal_->header;
138  pick_pose.pose = goal_->pick_pose;
139  place_pose.header = goal_->header;
140  place_pose.pose = goal_->place_pose;
141 
142  pickAndPlace(goal_->obj_name, pick_pose, place_pose);
143  }
144 
145  void preemptCB()
146  {
147  ROS_WARN("[pick and place] %s: Preempted", action_name_.c_str());
148  gripper_.stop();
149  arm_.stop();
150 
151  // set the action state to preempted
152  as_.setPreempted();
153  }
154 
155  bool pickAndPlace(const std::string& obj_name, geometry_msgs::PoseStamped& pick_pose,
156  geometry_msgs::PoseStamped& place_pose)
157  {
158  if (pick(obj_name, pick_pose))
159  {
160  if (place(obj_name, place_pose, pick_pose.pose.position.z))
161  {
162  as_.setSucceeded(result_);
163  return true;
164  }
165  else
166  {
167  // Ensure we don't retain any object attached to the gripper
168  arm_.detachObject(obj_name);
169  setGripper(gripper_open, false);
170  }
171  }
172 
173  as_.setAborted(result_);
174  return false;
175  }
176 
177  bool pick(const std::string& obj_name, geometry_msgs::PoseStamped& pose)
178  {
179  // Look for obj_name in the list of available objects
180  std::map<std::string, moveit_msgs::CollisionObject> objects =
181  planning_scene_interface_.getObjects(std::vector<std::string>(1, obj_name));
182  if (objects.size() == 0)
183  {
184  // Maybe the object's interactive marker name is wrong?
185  ROS_ERROR("[pick and place] Tabletop collision object '%s' not found", obj_name.c_str());
186  return false;
187  }
188 
189  if (objects.size() > 1)
190  {
191  // This should not happen, as object detection tries to provide unique names to all objects...
192  ROS_WARN("[pick and place] More than one (%d) tabletop collision objects with name '%s' found!",
193  objects.size(), obj_name.c_str());
194  }
195 
196  // We need object's pose and size for picking
197  Eigen::Vector3d tco_size;
198  geometry_msgs::PoseStamped tco_pose;
199  const moveit_msgs::CollisionObject& tco = objects[obj_name];
200 
201  tco_pose.header = tco.header;
202 
203  // We get object's pose from the mesh/primitive poses; try first with the meshes
204  if (tco.mesh_poses.size() > 0)
205  {
206  tco_pose.pose = tco.mesh_poses[0];
207  if (tco.meshes.size() > 0)
208  {
209  tco_size = shapes::computeShapeExtents(tco.meshes[0]);
210 
211  // We assume meshes laying in the floor, so we bump its pose by half z-dimension to
212  // grasp the object at mid-height. TODO: we could try something more sophisticated...
213  tco_pose.pose.position.z += tco_size[2]/2.0;
214  }
215  else
216  {
217  ROS_ERROR("[pick and place] Tabletop collision object '%s' has no meshes", obj_name.c_str());
218  return false;
219  }
220  }
221  else if (tco.primitive_poses.size() > 0)
222  {
223  tco_pose.pose = tco.primitive_poses[0];
224  if (tco.primitives.size() > 0)
225  {
226  tco_size = shapes::computeShapeExtents(tco.primitives[0]);
227  }
228  else
229  {
230  ROS_ERROR("[pick and place] Tabletop collision object '%s' has no primitives", obj_name.c_str());
231  return false;
232  }
233  }
234  else
235  {
236  ROS_ERROR("[pick and place] Tabletop collision object '%s' has no mesh/primitive poses", obj_name.c_str());
237  return false;
238  }
239 
240  ROS_INFO("[pick and place] Picking object '%s' with size [%.3f, %.3f, %.3f] at location [%s]...",
241  obj_name.c_str(), tco_size[0], tco_size[1], tco_size[2], mtk::point2str2D(tco_pose.pose.position).c_str());
242 
243  // Try up to PICK_ATTEMPTS grasps with slightly different poses
244  for (int attempt = 0; attempt < PICK_ATTEMPTS; ++attempt)
245  {
246  geometry_msgs::PoseStamped p = tco_pose;
247  if (!validateTargetPose(p, true, attempt))
248  {
249  return false;
250  }
251 
252  ROS_DEBUG("[pick and place] Pick attempt %d at pose [%s]...", attempt, mtk::pose2str3D(p).c_str());
253 
254  moveit_msgs::Grasp g;
255  g.grasp_pose = p;
256 
257  g.pre_grasp_approach.direction.vector.x = 0.5;
258  g.pre_grasp_approach.direction.header.frame_id = arm_.getEndEffectorLink();
259  g.pre_grasp_approach.min_distance = 0.005;
260  g.pre_grasp_approach.desired_distance = 0.1;
261 
262  g.post_grasp_retreat.direction.header.frame_id = arm_.getEndEffectorLink();
263  g.post_grasp_retreat.direction.vector.x = -0.5;
264  g.post_grasp_retreat.min_distance = 0.005;
265  g.post_grasp_retreat.desired_distance = 0.1;
266 
267  g.pre_grasp_posture.joint_names.push_back("gripper_joint");
268  g.pre_grasp_posture.points.resize(1);
269  g.pre_grasp_posture.points[0].positions.push_back(gripper_open);
270 
271  // As we grasp the object "blindly", just in the center, we use the maximum possible value as the opened
272  // gripper position and the smallest dimension minus a small "tightening" epsilon as the closed position
273  g.grasp_posture.joint_names.push_back("gripper_joint");
274  g.grasp_posture.points.resize(1);
275  g.grasp_posture.points[0].positions.push_back(tco_size.minCoeff() - 0.002);
276 
277  g.allowed_touch_objects.push_back(obj_name);
278  g.allowed_touch_objects.push_back("table");
279 
280  g.id = attempt;
281 
282  std::vector<moveit_msgs::Grasp> grasps(1, g);
283 
284  moveit::planning_interface::MoveItErrorCode result = arm_.pick(obj_name, grasps);
285  if (result)
286  {
287  ROS_INFO("[pick and place] Pick successfully completed");
288  pose = p; // provide the used pose so we use the same z coordinate
289  return true;
290  }
291 
292  ROS_DEBUG("[pick and place] Pick attempt %d failed: %s", attempt, mec2str(result));
293 
294  if (attempt == 1)
295  clear_octomap_srv_.call(empty_srv_);
296  }
297 
298  ROS_ERROR("[pick and place] Pick failed after %d attempts", PICK_ATTEMPTS);
299  return false;
300  }
301 
302  bool place(const std::string& obj_name, const geometry_msgs::PoseStamped& pose, double pick_position_z)
303  {
304  // Look for obj_name in the list of attached objects
305  std::map<std::string, moveit_msgs::AttachedCollisionObject> objects =
306  planning_scene_interface_.getAttachedObjects(std::vector<std::string>(1, obj_name));
307 
308  if (objects.size() == 0)
309  {
310  // Maybe pick failed; we will not continue because place will surely fail without knowing the attaching pose
311  ROS_ERROR("[pick and place] Attached collision object '%s' not found", obj_name.c_str());
312  return false;
313  }
314 
315  if (objects.size() > 1)
316  {
317  // This should not happen... we grasped two objects with the same name???
318  ROS_WARN("[pick and place] More than one (%d) attached collision objects with name '%s' found!",
319  objects.size(), obj_name.c_str());
320  }
321 
322  // We just need object's pose so we can subtract its pose from the place location poses
323  geometry_msgs::Pose aco_pose; // No stamped; it's relative to attaching frame (gripper_link)
324  const moveit_msgs::AttachedCollisionObject& aco = objects[obj_name];
325 
326  if (aco.object.primitive_poses.size() > 0)
327  {
328  aco_pose = aco.object.primitive_poses[0];
329  }
330  else if (aco.object.mesh_poses.size() > 0)
331  {
332  aco_pose = aco.object.mesh_poses[0];
333  }
334  else
335  {
336  ROS_ERROR("[pick and place] Attached collision object '%s' has no pose!", obj_name.c_str());
337  return false;
338  }
339 
340  ROS_INFO("[pick and place] Placing object '%s' at pose [%s]...", obj_name.c_str(), mtk::pose2str3D(pose).c_str());
341 
342  // Try up to PLACE_ATTEMPTS place locations with slightly different poses
343  for (int attempt = 0; attempt < PLACE_ATTEMPTS; ++attempt)
344  {
345  geometry_msgs::PoseStamped p = pose;
346  if (!validateTargetPose(p, false, attempt))
347  {
348  return false;
349  }
350 
351  // MoveGroup::place will transform the provided place pose with the attached body pose, so the object retains
352  // the orientation it had when picked. However, with our 4-dofs arm this is infeasible (nor we care about the
353  // objects orientation!), so we cancel this transformation. It is applied here:
354  // https://github.com/ros-planning/moveit_ros/blob/jade-devel/manipulation/pick_place/src/place.cpp#L64
355  // More details on this issue: https://github.com/ros-planning/moveit_ros/issues/577
356  tf::Transform place_tf, aco_tf;
357  tf::poseMsgToTF(p.pose, place_tf);
358  tf::poseMsgToTF(aco_pose, aco_tf);
359  tf::poseTFToMsg(place_tf * aco_tf, p.pose);
360  p.pose.position.z = pick_position_z;
361 
362  ROS_DEBUG("Compensate place pose with the attached object pose [%s]. Results: [%s]",
363  mtk::pose2str3D(aco_pose).c_str(), mtk::pose2str3D(p.pose).c_str());
364 
365  ROS_DEBUG("[pick and place] Place attempt %d at pose [%s]...", attempt, mtk::pose2str3D(p).c_str());
366 
367  moveit_msgs::PlaceLocation l;
368  l.place_pose = p;
369 
370  l.pre_place_approach.direction.vector.x = 0.5;
371  l.pre_place_approach.direction.header.frame_id = arm_.getEndEffectorLink();
372  l.pre_place_approach.min_distance = 0.005;
373  l.pre_place_approach.desired_distance = 0.1;
374 
375  l.post_place_retreat.direction.vector.x = -0.5;
376  l.post_place_retreat.direction.header.frame_id = arm_.getEndEffectorLink();
377  l.post_place_retreat.min_distance = 0.005;
378  l.post_place_retreat.desired_distance = 0.1;
379 
380  l.post_place_posture.joint_names.push_back("gripper_joint");
381  l.post_place_posture.points.resize(1);
382  l.post_place_posture.points[0].positions.push_back(gripper_open);
383 
384  l.allowed_touch_objects.push_back(obj_name);
385  l.allowed_touch_objects.push_back("table");
386 
387  l.id = attempt;
388 
389  std::vector<moveit_msgs::PlaceLocation> locs(1, l);
390 
391  moveit::planning_interface::MoveItErrorCode result = arm_.place(obj_name, locs);
392  if (result)
393  {
394  ROS_INFO("[pick and place] Place successfully completed");
395  return true;
396  }
397 
398  ROS_DEBUG("[pick and place] Place attempt %d failed: %s", attempt, mec2str(result));
399 
400  if (attempt == 1)
401  clear_octomap_srv_.call(empty_srv_);
402  }
403 
404  ROS_ERROR("[pick and place] Place failed after %d attempts", PLACE_ATTEMPTS);
405  return false;
406  }
407 
408 private:
418  bool validateTargetPose(geometry_msgs::PoseStamped& target, bool compensate_backlash, int attempt = 0)
419  {
420  // We always work relative to the arm base, so roll/pitch/yaw angles calculation make sense
421  if (target.header.frame_id != arm_link)
422  {
423  transformPose(target.header.frame_id, arm_link, target, target);
424  }
425 
426  double x = target.pose.position.x;
427  double y = target.pose.position.y;
428  double z = target.pose.position.z;
429  double d = sqrt(x*x + y*y);
430  if (d > 0.3)
431  {
432  // Maximum reachable distance by the turtlebot arm is 30 cm, but above twenty something the arm makes
433  // strange and ugly contortions, and overcomes the reduced elbow lower limit we have to operate always
434  // with the same gripper orientation
435  // XXX solved constraining also both shoulder limits (180 deg. operation); we get back the 30 cm limit
436  ROS_ERROR("[pick and place] Target pose out of reach [%f > %f]", d, 0.3);
437  return false;
438  }
439 
440  // Pitch is 90 (vertical) at 10 cm from the arm base; the farther the target is, the closer to horizontal
441  // we point the gripper (0.22 = arm's max reach - vertical pitch distance + ε). We also try some random
442  // variations to increase the chances of successful planning. Yaw is the direction to the target, and so
443  // must be fixed. Roll is plainly ignored, as our arm lacks that dof.
444  double rp = M_PI_2 - std::asin((d - 0.1)/0.22) + ((attempt%2)*2 - 1)*(std::ceil(attempt/2.0)*0.05);
445  double ry = std::atan2(y, x);
446  double rr = 0.0;
447  target.pose.orientation = tf::createQuaternionMsgFromRollPitchYaw(rr, rp, ry);
448 
449  if (compensate_backlash)
450  {
451  // Slightly increase z proportionally to pitch to avoid hitting the table with the lower gripper corner and
452  // a bit extra to compensate the effect of the arm's backlash in the height of the gripper over the table
453  double z_delta1 = std::abs(std::cos(rp))/50.0;
454  double z_delta2 = z_backlash;
455  ROS_DEBUG("[pick and place] Z increase: %f + %f + %f", target.pose.position.z, z_delta1, z_delta2);
456  target.pose.position.z += z_delta1;
457  target.pose.position.z += z_delta2;
458  }
459 
460  ROS_DEBUG("[pick and place] Target pose [%s] [d: %.2f]", mtk::pose2str3D(target.pose).c_str(), d);
461  target_pose_pub_.publish(target);
462 
463  return true;
464  }
465 
472  bool setGripper(float opening, bool wait_for_complete = true)
473  {
474  ROS_DEBUG("[pick and place] Set gripper opening to %f", opening);
475  if (gripper_.setJointValueTarget("gripper_joint", opening) == false)
476  {
477  ROS_ERROR("[pick and place] Set gripper opening to %f failed", opening);
478  return false;
479  }
480 
482  wait_for_complete ? gripper_.move() : gripper_.asyncMove();
483  if (result == true)
484  {
485  return true;
486  }
487  else
488  {
489  ROS_ERROR("[pick and place] Set gripper opening failed (error %d)", result.val);
490  return false;
491  }
492  }
493 
494  float fRand(float min, float max)
495  {
496  return ((float(rand()) / float(RAND_MAX)) * (max - min)) + min;
497  }
498 
500  {
501  switch (mec.val)
502  {
503  case moveit::planning_interface::MoveItErrorCode::SUCCESS:
504  return "success";
505  case moveit::planning_interface::MoveItErrorCode::FAILURE:
506  return "failure";
507  case moveit::planning_interface::MoveItErrorCode::PLANNING_FAILED:
508  return "planning failed";
509  case moveit::planning_interface::MoveItErrorCode::INVALID_MOTION_PLAN:
510  return "invalid motion plan";
511  case moveit::planning_interface::MoveItErrorCode::MOTION_PLAN_INVALIDATED_BY_ENVIRONMENT_CHANGE:
512  return "motion plan invalidated by environment change";
513  case moveit::planning_interface::MoveItErrorCode::CONTROL_FAILED:
514  return "control failed";
515  case moveit::planning_interface::MoveItErrorCode::UNABLE_TO_AQUIRE_SENSOR_DATA:
516  return "unable to acquire sensor data";
517  case moveit::planning_interface::MoveItErrorCode::TIMED_OUT:
518  return "timed out";
519  case moveit::planning_interface::MoveItErrorCode::PREEMPTED:
520  return "preempted";
521  case moveit::planning_interface::MoveItErrorCode::START_STATE_IN_COLLISION:
522  return "start state in collision";
523  case moveit::planning_interface::MoveItErrorCode::START_STATE_VIOLATES_PATH_CONSTRAINTS:
524  return "start state violates path constraints";
525  case moveit::planning_interface::MoveItErrorCode::GOAL_IN_COLLISION:
526  return "goal in collision";
527  case moveit::planning_interface::MoveItErrorCode::GOAL_VIOLATES_PATH_CONSTRAINTS:
528  return "goal violates path constraints";
529  case moveit::planning_interface::MoveItErrorCode::GOAL_CONSTRAINTS_VIOLATED:
530  return "goal constraints violated";
531  case moveit::planning_interface::MoveItErrorCode::INVALID_GROUP_NAME:
532  return "invalid group name";
533  case moveit::planning_interface::MoveItErrorCode::INVALID_GOAL_CONSTRAINTS:
534  return "invalid goal constraints";
535  case moveit::planning_interface::MoveItErrorCode::INVALID_ROBOT_STATE:
536  return "invalid robot state";
537  case moveit::planning_interface::MoveItErrorCode::INVALID_LINK_NAME:
538  return "invalid link name";
539  case moveit::planning_interface::MoveItErrorCode::INVALID_OBJECT_NAME:
540  return "invalid object name";
541  case moveit::planning_interface::MoveItErrorCode::FRAME_TRANSFORM_FAILURE:
542  return "frame transform failure";
543  case moveit::planning_interface::MoveItErrorCode::COLLISION_CHECKING_UNAVAILABLE:
544  return "collision checking unavailable";
545  case moveit::planning_interface::MoveItErrorCode::ROBOT_STATE_STALE:
546  return "robot state stale";
547  case moveit::planning_interface::MoveItErrorCode::SENSOR_INFO_STALE:
548  return "sensor info stale";
549  case moveit::planning_interface::MoveItErrorCode::NO_IK_SOLUTION:
550  return "no ik solution";
551  default:
552  return "unrecognized error code";
553  }
554  }
555 
556 
557  bool transformPose(const std::string& in_frame, const std::string& out_frame,
558  const geometry_msgs::PoseStamped& in_pose, geometry_msgs::PoseStamped& out_pose)
559  {
560  try
561  {
562  tf_listener_.waitForTransform(in_frame, out_frame, ros::Time(0.0), ros::Duration(1.0));
563  tf_listener_.transformPose(out_frame, in_pose, out_pose);
564 
565  return true;
566  }
567  catch (tf::InvalidArgument& e)
568  {
569  ROS_ERROR("[pick and place] Transformed pose has invalid orientation: %s", e.what());
570  return false;
571  }
572  catch (tf::TransformException& e)
573  {
574  ROS_ERROR("[pick and place] Could not get sensor to arm transform: %s", e.what());
575  return false;
576  }
577  }
578 
579 };
580 
581 
589 {
590 private:
592  std::string action_name_;
593 
594  turtlebot_arm_object_manipulation::MoveToTargetFeedback feedback_;
595  turtlebot_arm_object_manipulation::MoveToTargetResult result_;
596  turtlebot_arm_object_manipulation::MoveToTargetGoalConstPtr goal_;
597 
599 
600  // Move groups to control arm and gripper with MoveIt!
603 
604 public:
605  MoveToTargetServer(const std::string name) :
606  as_(name, false), action_name_(name), arm_("arm"), gripper_("gripper")
607  {
608  // Register the goal and feedback callbacks
609  as_.registerGoalCallback(boost::bind(&MoveToTargetServer::goalCB, this));
611 
612  as_.start();
613 
614  ros::NodeHandle nh("~");
615  target_pose_pub_ = nh.advertise<geometry_msgs::PoseStamped>("/target_pose", 1, true);
616  }
617 
619  {
620  as_.shutdown();
621  }
622 
623  void goalCB()
624  {
625  ROS_INFO("[move to target] Received goal!");
626  goal_ = as_.acceptNewGoal();
627  bool result = false;
628 
629  switch (goal_->target_type)
630  {
631  case turtlebot_arm_object_manipulation::MoveToTargetGoal::NAMED_TARGET:
632  result = moveArmTo(goal_->named_target);
633  break;
634  case turtlebot_arm_object_manipulation::MoveToTargetGoal::JOINT_STATE:
635  case turtlebot_arm_object_manipulation::MoveToTargetGoal::POSE_TARGET:
636  default:
637  ROS_ERROR("[move to target] Move to target of type %d not implemented", goal_->target_type);
638  break;
639  }
640 
641  if (result)
642  {
643  as_.setSucceeded(result_);
644  }
645  else
646  {
647  as_.setAborted(result_);
648  }
649  }
650 
651  void preemptCB()
652  {
653  ROS_INFO("[move to target] %s: Preempted", action_name_.c_str());
654  gripper_.stop();
655  arm_.stop();
656 
657  // set the action state to preempted
658  as_.setPreempted();
659  }
660 
661 private:
667  bool moveArmTo(const std::string& target)
668  {
669  ROS_DEBUG("[move to target] Move arm to '%s' position", target.c_str());
670  if (arm_.setNamedTarget(target) == false)
671  {
672  ROS_ERROR("[move to target] Set named target '%s' failed", target.c_str());
673  return false;
674  }
675 
677  if (bool(result) == true)
678  {
679  ROS_INFO("[move to target] Move to target \"%s\" completed", target.c_str());
680  return true;
681  }
682  else
683  {
684  ROS_ERROR("[move to target] Move to target \"%s\" failed (error %d)", target.c_str(), result.val);
685  return false;
686  }
687  }
688 
695  bool moveArmTo(const geometry_msgs::PoseStamped& target)
696  {
697  int attempts = 0;
698  ROS_DEBUG("[move to target] Move arm to [%.2f, %.2f, %.2f, %.2f]",
699  target.pose.position.x, target.pose.position.y, target.pose.position.z,
700  tf::getYaw(target.pose.orientation));
701  while (attempts < 5)
702  {
703  geometry_msgs::PoseStamped modiff_target = target;
704 
705  double x = modiff_target.pose.position.x;
706  double y = modiff_target.pose.position.y;
707  double z = modiff_target.pose.position.z;
708  double d = sqrt(x*x + y*y);
709  if (d > 0.3)
710  {
711  // Maximum reachable distance by the turtlebot arm is 30 cm
712  ROS_ERROR("[move to target] Target pose out of reach [%f > %f]", d, 0.3);
713  return false;
714  }
715  // Pitch is 90 (vertical) at 10 cm from the arm base; the farther the target is, the closer to horizontal
716  // we point the gripper. Yaw is the direction to the target. We also try some random variations of both to
717  // increase the chances of successful planning.
718  double rp = M_PI_2 - std::asin((d - 0.1)/0.205); // 0.205 = arm's max reach - vertical pitch distance + ε
719  double ry = std::atan2(y, x);
720 
722  attempts*fRand(-0.05, +0.05) + rp,
723  attempts*fRand(-0.05, +0.05) + ry);
724  tf::quaternionTFToMsg(q, modiff_target.pose.orientation);
725 
726  // Slightly increase z proportionally to pitch to avoid hitting the table with the lower gripper corner
727  ROS_DEBUG("[move to target] Z increase: %f + %f", modiff_target.pose.position.z, std::abs(std::cos(rp))/50.0);
728  modiff_target.pose.position.z += std::abs(std::cos(rp))/50.0;
729 
730  ROS_DEBUG("[move to target] Set pose target [%.2f, %.2f, %.2f] [d: %.2f, p: %.2f, y: %.2f]", x, y, z, d, rp, ry);
731  target_pose_pub_.publish(modiff_target);
732 
733  if (arm_.setPoseTarget(modiff_target) == false)
734  {
735  ROS_ERROR("[move to target] Set pose target [%.2f, %.2f, %.2f, %.2f] failed",
736  modiff_target.pose.position.x, modiff_target.pose.position.y, modiff_target.pose.position.z,
737  tf::getYaw(modiff_target.pose.orientation));
738  return false;
739  }
740 
742  if (bool(result) == true)
743  {
744  ROS_INFO("[move to target] Move to target [%.2f, %.2f, %.2f, %.2f] completed",
745  modiff_target.pose.position.x, modiff_target.pose.position.y, modiff_target.pose.position.z,
746  tf::getYaw(modiff_target.pose.orientation));
747  return true;
748  }
749  else
750  {
751  ROS_ERROR("[move to target] Move to target failed (error %d) at attempt %d",
752  result.val, attempts + 1);
753  }
754  attempts++;
755  }
756 
757  ROS_ERROR("[move to target] Move to target failed after %d attempts", attempts);
758  return false;
759  }
760 
767  bool setGripper(float opening, bool wait_for_complete = true)
768  {
769  ROS_DEBUG("[move to target] Set gripper opening to %f", opening);
770  if (gripper_.setJointValueTarget("gripper_joint", opening) == false)
771  {
772  ROS_ERROR("[move to target] Set gripper opening to %f failed", opening);
773  return false;
774  }
775 
777  wait_for_complete ? gripper_.move() : gripper_.asyncMove();
778  if (result == true)
779  {
780  return true;
781  }
782  else
783  {
784  ROS_ERROR("[move to target] Set gripper opening failed (error %d)", result.val);
785  return false;
786  }
787  }
788 
789  float fRand(float min, float max)
790  {
791  return ((float(rand()) / float(RAND_MAX)) * (max - min)) + min;
792  }
793 };
794 
795 };
796 
797 
798 int main(int argc, char** argv)
799 {
800  ros::init(argc, argv, "object_pick_and_place_action_server");
801 
802  // Create pick_and_place and move_to_target action servers
803  turtlebot_arm_object_manipulation::PickAndPlaceServer pnp_server("pick_and_place");
804  turtlebot_arm_object_manipulation::MoveToTargetServer mtt_server("move_to_target");
805 
806  // Setup an asynchronous spinner as the move groups operations need continuous spinning
808  spinner.start();
809 
811  spinner.stop();
812 
813  return 0;
814 }
d
ServiceClient serviceClient(const std::string &service_name, bool persistent=false, const M_string &header_values=M_string())
boost::shared_ptr< const Goal > acceptNewGoal()
static tf::Quaternion createQuaternionFromRPY(double roll, double pitch, double yaw)
static void poseMsgToTF(const geometry_msgs::Pose &msg, Pose &bt)
bool detachObject(const std::string &name="")
bool validateTargetPose(geometry_msgs::PoseStamped &target, bool compensate_backlash, int attempt=0)
q
void publish(const boost::shared_ptr< M > &message) const
moveit::planning_interface::MoveGroupInterface arm_
bool setJointValueTarget(const std::vector< double > &group_variable_values)
MoveItErrorCode pick(const std::string &object, bool plan_only=false)
turtlebot_arm_object_manipulation::PickAndPlaceFeedback feedback_
static double getYaw(const Quaternion &bt_q)
ROSCPP_DECL void init(int &argc, char **argv, const std::string &name, uint32_t options=0)
bool call(MReq &req, MRes &res)
turtlebot_arm_object_manipulation::MoveToTargetFeedback feedback_
void setPoseReferenceFrame(const std::string &pose_reference_frame)
bool setGripper(float opening, bool wait_for_complete=true)
std::map< std::string, moveit_msgs::CollisionObject > getObjects(const std::vector< std::string > &object_ids=std::vector< std::string >())
bool moveArmTo(const geometry_msgs::PoseStamped &target)
bool setPoseTarget(const Eigen::Affine3d &end_effector_pose, const std::string &end_effector_link="")
bool place(const std::string &obj_name, const geometry_msgs::PoseStamped &pose, double pick_position_z)
std::string pose2str3D(const geometry_msgs::Pose &pose)
turtlebot_arm_object_manipulation::PickAndPlaceResult result_
bool pickAndPlace(const std::string &obj_name, geometry_msgs::PoseStamped &pick_pose, geometry_msgs::PoseStamped &place_pose)
#define ROS_WARN(...)
void setGoalOrientationTolerance(double tolerance)
void setSucceeded(const Result &result=Result(), const std::string &text=std::string(""))
double y
void spinner()
const std::string & getEndEffectorLink() const
bool waitForTransform(const std::string &target_frame, const std::string &source_frame, const ros::Time &time, const ros::Duration &timeout, const ros::Duration &polling_sleep_duration=ros::Duration(0.01), std::string *error_msg=NULL) const
moveit::planning_interface::MoveGroupInterface gripper_
#define M_PI_2
void setPreempted(const Result &result=Result(), const std::string &text=std::string(""))
int main(int argc, char **argv)
#define ROS_INFO(...)
bool param(const std::string &param_name, T &param_val, const T &default_val) const
const char * mec2str(const moveit::planning_interface::MoveItErrorCode &mec)
static void quaternionTFToMsg(const Quaternion &bt, geometry_msgs::Quaternion &msg)
bool transformPose(const std::string &in_frame, const std::string &out_frame, const geometry_msgs::PoseStamped &in_pose, geometry_msgs::PoseStamped &out_pose)
void registerPreemptCallback(boost::function< void()> cb)
double z
bool pick(const std::string &obj_name, geometry_msgs::PoseStamped &pose)
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
moveit::planning_interface::MoveGroupInterface gripper_
MoveItErrorCode place(const std::string &object, bool plan_only=false)
bool setGripper(float opening, bool wait_for_complete=true)
void setAborted(const Result &result=Result(), const std::string &text=std::string(""))
actionlib::SimpleActionServer< turtlebot_arm_object_manipulation::MoveToTargetAction > as_
actionlib::SimpleActionServer< turtlebot_arm_object_manipulation::PickAndPlaceAction > as_
std::map< std::string, moveit_msgs::AttachedCollisionObject > getAttachedObjects(const std::vector< std::string > &object_ids=std::vector< std::string >())
static void poseTFToMsg(const Pose &bt, geometry_msgs::Pose &msg)
void transformPose(const std::string &target_frame, const geometry_msgs::PoseStamped &stamped_in, geometry_msgs::PoseStamped &stamped_out) const
turtlebot_arm_object_manipulation::MoveToTargetResult result_
turtlebot_arm_object_manipulation::MoveToTargetGoalConstPtr goal_
static geometry_msgs::Quaternion createQuaternionMsgFromRollPitchYaw(double roll, double pitch, double yaw)
void setSupportSurfaceName(const std::string &name)
turtlebot_arm_object_manipulation::PickAndPlaceGoalConstPtr goal_
void registerGoalCallback(boost::function< void()> cb)
Eigen::Vector3d computeShapeExtents(const ShapeMsg &shape_msg)
double x
moveit::planning_interface::PlanningSceneInterface planning_scene_interface_
#define ROS_ERROR(...)
bool setNamedTarget(const std::string &name)
ROSCPP_DECL void waitForShutdown()
std::string point2str2D(const geometry_msgs::Point &point)
#define ROS_DEBUG(...)


turtlebot_arm_object_manipulation
Author(s): Jorge Santos
autogenerated on Fri Feb 7 2020 03:56:40