Note: Some of the files in here haven't been released yet and may not work. Sorry!. This tutorial assumes that you have completed the previous tutorials: Checking State Validity.
(!) Please ask about problems and questions regarding this tutorial on answers.ros.org. Don't forget to include in your question the link to this page, the versions of your OS & ROS, and also add appropriate tags.

Adding Virtual Objects to the Planning Scene

Description: In this tutorial we show how to add virtual objects to the Planning Scene and to check state validity against the virtual objects.

Keywords: planning_scene electric

Tutorial Level: INTERMEDIATE

Setting up the tutorial

In one terminal run the following:

roslaunch pr2_arm_navigation_tutorials pr2_floorobj_world.launch

In another terminal run:

roslaunch pr2_arm_navigation_tutorials planning_scene_architecture.launch

This will bring up an Rviz window.

Running the tutorial

This tutorial uses the executable in pr2_arm_navigation_tutorials called get_state_validity_virtual_object. In another terminal, run the following command:

rosrun pr2_arm_navigation_tutorials get_state_validity_virtual_object 0

You should see something like the following:

current state

In this program we're still doing a state validity call, but now with the joint r_shoulder_lift_link set to a value of .9 . You'll also note that given the position of the tilting laser and the arms that we can only see part of the pole in the collision map even though it goes all the way down to the floor in the real world. This is a problem for checking validity, as you can see if you call the following command:

rosrun pr2_arm_navigation_tutorials get_state_validity_virtual_object 0 -.5

This gives you the following:

under pole

It's hard to see in a 2D view, but use Rviz's camera commands to convince yourself that the robot's hand is actually under the pole. The fact that this state is registering as green is a problem - if we were attempting to plan to get the robot's arm to its side it might attempt to go under the pole, knocking it over in the process. To avoid such collisions we might need to use some sort of inference algorithm to reason about space we don't have any information about - this kind of algorithm is outside the scope of this tutorial. Here we assume that we have reliable information that a pole that goes down to the floor exists in a particular space and we want to check validity based on that information. Note that there are two methods for adding an object to the planning scene - publishing on a topic or adding the object to the planning_scene_diff - in this tutorial we do the latter.

Quit the last command, and execute the following:

rosrun pr2_arm_navigation_tutorials get_state_validity_virtual_object 1 -.5

Changing the first argument to 1 tells the program to add the virtual object to the planning scene. Now you should get the following:

contact with pole

Note that when you add an object to the planning scene it masks out the parts of the collision map that lie near the object. The assumption is that if you are doing object recognition you have more reliable information about what's there than is contained in the collision map, and thus we filter out nearby points. With the virtual object in place the pose is now shown to be in collision, and a planning algorithm would need to plan around the entire pole.

The code

   1 #include <ros/ros.h>
   2 #include <arm_navigation_msgs/GetPlanningScene.h>
   3 #include <planning_environment/models/collision_models.h>
   4 
   5 static const std::string SET_PLANNING_SCENE_DIFF_NAME = "/environment_server/set_planning_scene_diff";
   6 
   7 int main(int argc, char **argv){
   8   ros::init (argc, argv, "get_state_validity_test");
   9   ros::NodeHandle rh;
  10 
  11   ros::Publisher vis_marker_publisher_;
  12   ros::Publisher vis_marker_array_publisher_;
  13 
  14   vis_marker_publisher_ = rh.advertise<visualization_msgs::Marker>("state_validity_markers", 128);
  15   vis_marker_array_publisher_ = rh.advertise<visualization_msgs::MarkerArray>("state_validity_markers_array", 128);
  16 
  17   ros::service::waitForService(SET_PLANNING_SCENE_DIFF_NAME);
  18   ros::ServiceClient get_planning_scene_client = 
  19     rh.serviceClient<arm_navigation_msgs::GetPlanningScene>(SET_PLANNING_SCENE_DIFF_NAME);
  20 
  21   arm_navigation_msgs::GetPlanningScene::Request planning_scene_req;
  22   arm_navigation_msgs::GetPlanningScene::Response planning_scene_res;
  23 
  24   if(argc > 1) {
  25 
  26     std::stringstream s(argv[1]);
  27     bool add;
  28     s >> add;
  29 
  30     if(add) {
  31       //add the cylinder into the collision space
  32       arm_navigation_msgs::CollisionObject cylinder_object;
  33       cylinder_object.id = "pole";
  34       cylinder_object.operation.operation = arm_navigation_msgs::CollisionObjectOperation::ADD;
  35       cylinder_object.header.frame_id = "odom_combined";
  36       cylinder_object.header.stamp = ros::Time::now();
  37       arm_navigation_msgs::Shape object;
  38       object.type = arm_navigation_msgs::Shape::CYLINDER;
  39       object.dimensions.resize(2);
  40       object.dimensions[0] = .1;
  41       object.dimensions[1] = 1.0;
  42       geometry_msgs::Pose pose;
  43       pose.position.x = .6;
  44       pose.position.y = -.6;
  45       pose.position.z = .5;
  46       pose.orientation.x = 0;
  47       pose.orientation.y = 0;
  48       pose.orientation.z = 0;
  49       pose.orientation.w = 1;
  50       cylinder_object.shapes.push_back(object);
  51       cylinder_object.poses.push_back(pose);
  52       
  53       planning_scene_req.planning_scene_diff.collision_objects.push_back(cylinder_object);
  54     }
  55   }
  56 
  57   if(!get_planning_scene_client.call(planning_scene_req, planning_scene_res)) {
  58     ROS_WARN("Can't get planning scene");
  59     return -1;
  60   }
  61 
  62   planning_environment::CollisionModels collision_models("robot_description");
  63   planning_models::KinematicState* state = 
  64     collision_models.setPlanningScene(planning_scene_res.planning_scene);
  65 
  66   std::vector<std::string> arm_names = 
  67     collision_models.getKinematicModel()->getModelGroup("right_arm")->getUpdatedLinkModelNames();
  68   std::vector<std::string> joint_names = 
  69     collision_models.getKinematicModel()->getModelGroup("right_arm")->getJointModelNames();
  70 
  71   std::map<std::string, double> nvalues;
  72   nvalues["r_shoulder_lift_joint"] = .9;
  73 
  74   if(argc > 2) {
  75     std::stringstream s(argv[2]);
  76     double val;
  77     s >> val;
  78     nvalues["r_shoulder_pan_joint"] = val;
  79   }
  80   state->setKinematicState(nvalues);
  81 
  82   
  83   std_msgs::ColorRGBA good_color, collision_color, joint_limits_color;
  84   good_color.a = collision_color.a = joint_limits_color.a = .8;
  85 
  86   good_color.g = 1.0;
  87   collision_color.r = 1.0;
  88   joint_limits_color.b = 1.0;
  89   
  90   std_msgs::ColorRGBA point_markers;
  91   point_markers.a = 1.0;
  92   point_markers.r = 1.0;
  93   point_markers.g = .8;
  94 
  95   std_msgs::ColorRGBA color;
  96   visualization_msgs::MarkerArray arr;
  97   if(!state->areJointsWithinBounds(joint_names)) {
  98     color = joint_limits_color;
  99   } else if(collision_models.isKinematicStateInCollision(*state)) {
 100     color = collision_color;
 101     collision_models.getAllCollisionPointMarkers(*state,
 102                                                  arr,
 103                                                  point_markers,
 104                                                  ros::Duration(0.2));
 105   } else {
 106     color = good_color;
 107   }
 108 
 109   collision_models.getRobotMarkersGivenState(*state,
 110                                              arr,
 111                                              color,
 112                                              "right_arm",
 113                                              ros::Duration(0.2),
 114                                              &arm_names);
 115 
 116   while(ros::ok()) {    
 117     vis_marker_array_publisher_.publish(arr);
 118     ros::spinOnce();
 119     ros::Duration(.1).sleep();
 120   }
 121   collision_models.revertPlanningScene(state);
 122   ros::shutdown();
 123 }

Understanding the code

Most of this code is that same as in this tutorial, with a few exceptions. Most substantially, here is the code where we potentially add the virtual pole:

  30     if(add) {
  31       //add the cylinder into the collision space
  32       arm_navigation_msgs::CollisionObject cylinder_object;
  33       cylinder_object.id = "pole";
  34       cylinder_object.operation.operation = arm_navigation_msgs::CollisionObjectOperation::ADD;
  35       cylinder_object.header.frame_id = "odom_combined";
  36       cylinder_object.header.stamp = ros::Time::now();
  37       arm_navigation_msgs::Shape object;
  38       object.type = arm_navigation_msgs::Shape::CYLINDER;
  39       object.dimensions.resize(2);
  40       object.dimensions[0] = .1;
  41       object.dimensions[1] = 1.0;
  42       geometry_msgs::Pose pose;
  43       pose.position.x = .6;
  44       pose.position.y = -.6;
  45       pose.position.z = .5;
  46       pose.orientation.x = 0;
  47       pose.orientation.y = 0;
  48       pose.orientation.z = 0;
  49       pose.orientation.w = 1;
  50       cylinder_object.shapes.push_back(object);
  51       cylinder_object.poses.push_back(pose);
  52       
  53       planning_scene_req.planning_scene_diff.collision_objects.push_back(cylinder_object);
  54     }

We give the object a name, describe the type, assign dimensions, and set a pose for the object. We also set the operation to ADD, meaning that it will be included in the planning scene. This goes into the planning_scene_diff, meaning that we recognize that this is a change from the current state of the world. The object will be added to the current state and pushed to all components, and also returned to us in the response. We can add any number of objects to the planning scene using this methodology. Adding objects in this manner does not change the current state of the world - if we don't include them with our next diff they will no longer be in the planning scene.

Finally, we potentially set the state for two joints instead of one in this block:

  71   std::map<std::string, double> nvalues;
  72   nvalues["r_shoulder_lift_joint"] = .9;
  73 
  74   if(argc > 2) {
  75     std::stringstream s(argv[2]);
  76     double val;
  77     s >> val;
  78     nvalues["r_shoulder_pan_joint"] = val;
  79   }
  80   state->setKinematicState(nvalues);

Wiki: arm_navigation/Tutorials/Planning Scene/Adding Virtual Objects to the Planning Scene (last edited 2011-10-24 18:06:20 by EGilJones)