move_base_to_manip.cpp
Go to the documentation of this file.
1 
2 # include "move_base_to_manip.h"
3 
5 // The algorithm:
7 
8 // Put the end-effector at the height & orientation of the desired pose.
9 
10 // Calculate the X,Y vector from the ee's current pose to the desired pose.
11 
12 // Plan a Cartesian move to the grasp pose. What % completes? If 100%, we're done!
13 
14 // Based on the completed Cartesian %, how far along the (X,Y) vector must the base move?
15 // Move the base to that position.
16 
17 // Should be able to reach it now. Run the program again to complete the motion or make minor
18 // corrections as needed.
19 
21 
22 int main(int argc, char **argv)
23 {
24  ros::init(argc, argv, "move_base_to_manip");
25 
26  // Start an action server
27  base_planner commander;
28 
29  // Wait for an action goal to trigger the CB
30  ros::spin();
31 
32  return 0;
33 }
34 
35 
37 // Constructor
40  as_(nh_, "move_base_to_manip", boost::bind(&base_planner::do_motion_CB, this, _1), false)
41 {
42  // Set node parameters, if they aren't defined in a launch file
44 
45  // Action server
46  as_.start();
47 
48 }
49 
50 
52 // Once a goal is received, start planning and moving
54 void base_planner::do_motion_CB( const move_base_to_manip::desired_poseGoalConstPtr& goal )
55 {
56  ROS_INFO("[move_base_to_manip] Received a new goal. Moving.");
57 
58  // Initialize MoveGroup for the arm
59  std::string move_group_name;
60  nh_.getParam("/move_base_to_manip/move_group_name", move_group_name);
62 
63  // get the current EE pose
64  geometry_msgs::PoseStamped start_pose = move_group.getCurrentPose();
65 
66  // make sure the goal is in the right frame
67  std::string base_frame_name;
68  nh_.getParam("/move_base_to_manip/base_frame_name", base_frame_name);
69  move_base_to_manip::desired_poseResult result;
70  if (goal->desired_pose.header.frame_id != base_frame_name)
71  {
72  result.success = false;
73  as_.setSucceeded( result );
74  ROS_WARN_STREAM("The target pose should be given in " << base_frame_name);
75  return;
76  }
77 
78  // We don't want to move in (X,Y), initially
79  geometry_msgs::PoseStamped desired_height_orient = goal->desired_pose;
80  desired_height_orient.pose.position.x = start_pose.pose.position.x;
81  desired_height_orient.pose.position.y = start_pose.pose.position.y;
82 
84  // Put the EE at the height & orientation we desire
86  ROS_INFO_STREAM("[move_base_to_manip] Moving to the desired height and orientation.");
87  move_group.setPoseTarget( desired_height_orient );
88 
89  // Get the orientation as RPY so we can manipulate it
90  tf::Quaternion gripper_quat;
91  tf::quaternionMsgToTF( desired_height_orient.pose.orientation, gripper_quat );
92  double object_roll, object_pitch, object_yaw;
93  tf::Matrix3x3(gripper_quat).getRPY(object_roll, object_pitch, object_yaw);
94 
96 
97 PLAN_AGAIN:
98  bool ok_to_flip;
99  nh_.getParam("/move_base_to_manip/ok_to_flip", ok_to_flip);
100  if ( !move_group.plan(move_plan) && ok_to_flip ) // If it fails, try spinning the gripper 180deg
101  {
102  geometry_msgs::Quaternion gripper_quat_msg = tf::createQuaternionMsgFromRollPitchYaw( 0., 0., object_yaw +3.14159);
103  desired_height_orient.pose.orientation = gripper_quat_msg;
104  move_group.setPoseTarget( desired_height_orient );
105  if ( !move_group.plan(move_plan) ) // If it fails again, try spinning the gripper -180deg from the original attempt
106  {
107  gripper_quat_msg = tf::createQuaternionMsgFromRollPitchYaw( 0., 0., object_yaw -3.14159);
108  desired_height_orient.pose.orientation = gripper_quat_msg;
109  move_group.setPoseTarget( desired_height_orient );
110  if( !move_group.plan(move_plan) ) // One last attempt
111  {
112  ROS_ERROR("Failed to reach the desired height and orientation.");
113  ROS_ERROR("Try starting from an arm position with better manipulability.");
114  ros::shutdown();
115  return;
116  }
117  }
118  }
119 
120  bool prompt_before_motion;
121  nh_.getParam("/move_base_to_manip/prompt_before_motion", prompt_before_motion);
122  if ( prompt_before_motion )
123  {
124  char character;
125  ROS_INFO_STREAM("Enter 'c' to continue, otherwise re-plan.");
126  std::cin.clear();
127  std::cin.get(character);
128  if ( character != 'c' )
129  goto PLAN_AGAIN;
130  }
131 
132  move_group.execute(move_plan);
133 
134 
136  // Calculate the X,Y vector from the EE's current pose to the desired pose
138  geometry_msgs::Vector3 vec_from_cur_pose_to_goal;
139  vec_from_cur_pose_to_goal.x = goal->desired_pose.pose.position.x - start_pose.pose.position.x;
140  vec_from_cur_pose_to_goal.y = goal->desired_pose.pose.position.y - start_pose.pose.position.y;
141  vec_from_cur_pose_to_goal.z = 0.;
142 
144  // Plan a Cartesian move to the grasp pose. What % completes?
145  // If 100% complete, we're done!
147  ROS_INFO_STREAM("Planning a Cartesian motion to the desired pose.");
148 
149  std::vector<geometry_msgs::Pose> waypoints;
150  geometry_msgs::Pose cartesian_target_pose; // Cartesian motion requires a Pose (not PoseStamped)
151  cartesian_target_pose.position = goal->desired_pose.pose.position;
152  cartesian_target_pose.orientation = goal->desired_pose.pose.orientation;
153  waypoints.push_back(cartesian_target_pose);
154 
155  moveit_msgs::RobotTrajectory trajectory;
156 
157 PLAN_CARTESIAN_AGAIN:
158  double fraction = cartesian_motion(waypoints, trajectory, move_group);
159  ROS_INFO("Cartesian path: %.2f%% achieved", fraction * 100.);
160 
161  if ( prompt_before_motion )
162  {
163  char character;
164  ROS_INFO_STREAM("Enter 'c' to continue, otherwise re-plan.");
165  std::cin.ignore (INT_MAX, '\n'); // Make sure the buffer is empty.
166  std::cin.get(character);
167  if ( character != 'c' )
168  goto PLAN_CARTESIAN_AGAIN;
169  }
170 
171 
172  if ( ( 0.999 < fraction) && (fraction < 1.001) ) // We're there! Move then quit.
173  {
174  ROS_INFO_STREAM("Making the final move.");
175 
176  bool move_cartesian;
177  nh_.getParam("/move_base_to_manip/move_cartesian", move_cartesian);
178  if ( move_cartesian ) // Use a Cartesian motion, i.e. keep the end-effector orientation constant as it moves
179  move_group.move();
180  else // Execute a regular motion
181  {
182  move_group.setPoseTarget( goal->desired_pose );
183  move_group.plan(move_plan);
184  move_group.execute(move_plan);
185  }
186 
187  // Return "success" to the action server
188  result.success = true;
189  as_.setSucceeded( result );
190  return;
191  }
192 
194  // Based on the completed Cartesian %, how far along the (X,Y) vector must the base move?
195  // Move the base to that position
197 
198  // Tell the action client that we want to spin a thread by default
199  MoveBaseClient ac("move_base", true);
200 
201  while(!ac.waitForServer(ros::Duration(5.0))){
202  ROS_INFO("Waiting for the move_base action server to come up");
203  }
204 
205  move_base_msgs::MoveBaseGoal goal;
206  goal.target_pose.header.frame_id = base_frame_name;
207  goal.target_pose.header.stamp = ros::Time::now();
208 
209  // Goal position = current base position + calculated change
210  // The current base position is (0,0) in /base_link by default, so we don't need to add anything
211  geometry_msgs::PoseStamped currentPose = move_group.getCurrentPose();
212  // motion_buffer: make the base move just a bit farther than the minimum req'd distance
213  // fraction: fraction of the motion that the arm alone could complete
214  double motion_buffer;
215  nh_.getParam("/move_base_to_manip/motion_buffer", motion_buffer);
216  goal.target_pose.pose.position.x = (1-motion_buffer*fraction)*vec_from_cur_pose_to_goal.x;
217  goal.target_pose.pose.position.y = (1-motion_buffer*fraction)*vec_from_cur_pose_to_goal.y;
218  goal.target_pose.pose.position.z = 0.; // Stay in the plane
219  // Maintain the base's current orientation
220  goal.target_pose.pose.orientation.x = 0.;
221  goal.target_pose.pose.orientation.y = 0.;
222  goal.target_pose.pose.orientation.z = 0.;
223  goal.target_pose.pose.orientation.w = 1.;
224 
225  // May want to disable collision checking or the manipulator will not approach an object.
226  bool clear_costmaps;
227  if ( nh_.getParam("/move_base_to_manip/clear_costmaps", clear_costmaps) )
229 
230  ac.sendGoal(goal);
231 
233  // Tell the action client that we succeeded
235  result.success = true;
236  as_.setSucceeded( result );
237 
238  return;
239 }
240 
241 
243 // Helper function to plan a Cartesian motion
245 const double base_planner::cartesian_motion(const std::vector<geometry_msgs::Pose>& waypoints, moveit_msgs::RobotTrajectory& trajectory, moveit::planning_interface::MoveGroupInterface& move_group)
246 {
247  // May want to disable collision checking or the manipulator will not approach an object.
248  bool clear_octomap;
249  if ( nh_.getParam("/move_base_to_manip/clear_octomap", clear_octomap) )
250  {
252  }
253  double cartesian_path_resolution;
254  nh_.getParam("/move_base_to_manip/cartesian_plan_res", cartesian_path_resolution);
255  double fraction = move_group.computeCartesianPath( waypoints, cartesian_path_resolution, 0.0, trajectory);
256 
257  return fraction;
258 }
259 
260 
262 // Set node parameters, if they aren't defined in a launch file
265 {
266  // Make the base move just a bit farther than the minimum req'd distance.
267  // This should be a fraction between 0-1
268  // Smaller ==> Will move closer to the goal pose
269  if (!nh_.hasParam("/move_base_to_manip/motion_buffer"))
270  nh_.setParam("/move_base_to_manip/motion_buffer", 0.15);
271 
272  // Use a Cartesian motion plan or a regular motion plan?
273  if (!nh_.hasParam("/move_base_to_manip/move_cartesian"))
274  {
275  nh_.setParam("/move_base_to_manip/move_cartesian", false);
276  }
277 
278  // Clear the Octomap collision scene before planning the final arm motion?
279  if (!nh_.hasParam("/move_base_to_manip/clear_octomap"))
280  {
281  nh_.setParam("/move_base_to_manip/clear_octomap", false);
282  }
283 
284  // Clear the move_base costmaps before moving the base?
285  if (!nh_.hasParam("/move_base_to_manip/clear_costmaps"))
286  {
287  nh_.setParam("/move_base_to_manip/clear_costmaps", false);
288  }
289 
290  // Prompt the user to approve each arm motion before it executes?
291  if (!nh_.hasParam("/move_base_to_manip/prompt_before_motion"))
292  {
293  nh_.setParam("/move_base_to_manip/prompt_before_motion", true);
294  }
295 
296  // Cartesian planning resolution, in meters
297  if (!nh_.hasParam("/move_base_to_manip/cartesian_plan_res"))
298  nh_.setParam("/move_base_to_manip/cartesian_plan_res", 0.005);
299 
300  if (!nh_.hasParam("/move_base_to_manip/move_group_name"))
301  nh_.setParam("/move_base_to_manip/move_group_name", "right_ur5");
302 
303  if (!nh_.hasParam("/move_base_to_manip/move_group_planner"))
304  nh_.setParam("/move_base_to_manip/move_group_planner", "RRTConnectkConfigDefault");
305 
306  if (!nh_.hasParam("/move_base_to_manip/velocity_scale"))
307  nh_.setParam("/move_base_to_manip/velocity_scale", 0.1);
308 
309  if (!nh_.hasParam("/move_base_to_manip/base_frame_name"))
310  nh_.setParam("/move_base_to_manip/base_frame_name", "base_link");
311 
312  if (!nh_.hasParam("/move_base_to_manip/position_tolerance"))
313  nh_.setParam("/move_base_to_manip/position_tolerance", 0.01);
314 
315  if (!nh_.hasParam("/move_base_to_manip/orientation_tolerance"))
316  nh_.setParam("/move_base_to_manip/orientation_tolerance", 0.01);
317 
318  // If true, the planner will try to flip the gripper +/-180 deg about Z when it cannot reach a pose
319  if (!nh_.hasParam("/move_base_to_manip/ok_to_flip"))
320  {
321  nh_.setParam("/move_base_to_manip/ok_to_flip", true);
322  }
323 
324  return;
325 }
bool waitForServer(const ros::Duration &timeout=ros::Duration(0, 0)) const
bool call(const std::string &service_name, MReq &req, MRes &res)
ROSCPP_DECL void init(int &argc, char **argv, const std::string &name, uint32_t options=0)
bool call(MReq &req, MRes &res)
actionlib::SimpleActionServer< move_base_to_manip::desired_poseAction > as_
double computeCartesianPath(const std::vector< geometry_msgs::Pose > &waypoints, double eef_step, double jump_threshold, moveit_msgs::RobotTrajectory &trajectory, bool avoid_collisions=true, moveit_msgs::MoveItErrorCodes *error_code=NULL)
bool setPoseTarget(const Eigen::Affine3d &end_effector_pose, const std::string &end_effector_link="")
void setSucceeded(const Result &result=Result(), const std::string &text=std::string(""))
ROSCPP_DECL void spin(Spinner &spinner)
ros::ServiceClient clear_octomap_client_
std_srvs::Empty empty_srv_
const double cartesian_motion(const std::vector< geometry_msgs::Pose > &waypoints, moveit_msgs::RobotTrajectory &trajectory, moveit::planning_interface::MoveGroupInterface &moveGroup)
static void quaternionMsgToTF(const geometry_msgs::Quaternion &msg, Quaternion &bt)
void getRPY(tfScalar &roll, tfScalar &pitch, tfScalar &yaw, unsigned int solution_number=1) const
#define ROS_INFO(...)
robot_trajectory::RobotTrajectory trajectory(rmodel,"right_arm")
int main(int argc, char **argv)
geometry_msgs::PoseStamped getCurrentPose(const std::string &end_effector_link="")
ros::NodeHandle nh_
MoveItErrorCode execute(const Plan &plan)
#define ROS_WARN_STREAM(args)
ros::ServiceClient clear_costmaps_client_
void sendGoal(const Goal &goal, SimpleDoneCallback done_cb=SimpleDoneCallback(), SimpleActiveCallback active_cb=SimpleActiveCallback(), SimpleFeedbackCallback feedback_cb=SimpleFeedbackCallback())
#define ROS_INFO_STREAM(args)
bool getParam(const std::string &key, std::string &s) const
static geometry_msgs::Quaternion createQuaternionMsgFromRollPitchYaw(double roll, double pitch, double yaw)
static Time now()
void do_motion_CB(const move_base_to_manip::desired_poseGoalConstPtr &goal)
ROSCPP_DECL void shutdown()
bool hasParam(const std::string &key) const
#define ROS_ERROR(...)
void setParam(const std::string &key, const XmlRpc::XmlRpcValue &v) const


move_base_to_manip
Author(s): Andy Zelenak, Andy Zelenak
autogenerated on Sat Jul 18 2020 03:48:17