segment_object_node.cpp
Go to the documentation of this file.
00001 // #
00002 // # Copyright (c) 2009, Georgia Tech Research Corporation
00003 // # All rights reserved.
00004 // #
00005 // # Redistribution and use in source and binary forms, with or without
00006 // # modification, are permitted provided that the following conditions are met:
00007 // #     * Redistributions of source code must retain the above copyright
00008 // #       notice, this list of conditions and the following disclaimer.
00009 // #     * Redistributions in binary form must reproduce the above copyright
00010 // #       notice, this list of conditions and the following disclaimer in the
00011 // #       documentation and/or other materials provided with the distribution.
00012 // #     * Neither the name of the Georgia Tech Research Corporation nor the
00013 // #       names of its contributors may be used to endorse or promote products
00014 // #       derived from this software without specific prior written permission.
00015 // #
00016 // # THIS SOFTWARE IS PROVIDED BY GEORGIA TECH RESEARCH CORPORATION ''AS IS'' AND
00017 // # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00018 // # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00019 // # DISCLAIMED. IN NO EVENT SHALL GEORGIA TECH BE LIABLE FOR ANY DIRECT, INDIRECT,
00020 // # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
00021 // # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
00022 // # OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00023 // # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
00024 // # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
00025 // # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00026 // #
00027 
00028 // #  \author Marc Killpack (Healthcare Robotics Lab, Georgia Tech.)
00029 
00030 
00031 #include <ros/ros.h>
00032 #include <boost/thread/mutex.hpp>
00033 #include <boost/thread.hpp>
00034 #include <pcl_ros/point_cloud.h>
00035 #include "pcl/ModelCoefficients.h"
00036 #include "pcl/point_types.h"
00037 #include "pcl/io/pcd_io.h"
00038 #include "pcl/sample_consensus/method_types.h"
00039 #include "pcl/sample_consensus/model_types.h"
00040 #include "pcl/segmentation/sac_segmentation.h"
00041 #include <pcl/filters/extract_indices.h>
00042 #include <pcl/filters/passthrough.h>
00043 #include "sensor_msgs/Image.h"
00044 #include "image_transport/image_transport.h"
00045 #include "cv_bridge/CvBridge.h"
00046 #include <opencv/cv.h>
00047 #include <opencv/highgui.h>
00048 #include <vector>
00049 #include "geometry_msgs/Point.h"
00050 #include "UI_segment_object/GetObject.h"
00051 #include "UI_segment_object/GetPt.h"
00052 #include "UI_segment_object/None_Bool.h"
00053 #include "UI_segment_object/Save.h"
00054 #include "pcl_ros/transforms.h"
00055 #include <tf/transform_listener.h>
00056 
00057 boost::mutex m;
00058 /*****************************************************************************/
00059 /*  Global variables and function declaration necessary for using mouse call back  */
00060 
00061 std::vector<std::vector<int> > poly_vec;
00062 std::vector<int> in_indices;
00063 int counter(0);
00064 int counter_ind(0);
00065 //bool mouse_active(false);
00066 CvPoint pts[1000];
00067 int done (0);
00068 
00069 /***********************************************************************************/
00070 
00071 
00072 class UI {
00073 
00074 public:
00075   //  bool got_image;
00076   UI(ros::NodeHandle &nh);
00077   ~UI();
00078   bool reset_cb(UI_segment_object::None_Bool::Request &reg, UI_segment_object::None_Bool::Response &res);
00079   static void mouse_callback(int event, int x, int y, int flags, void* param);
00080   void color_segment_callback();
00081   void pt_callback();
00082   void callback(const sensor_msgs::ImageConstPtr& msg_ptr);
00083   void cloud_callback();
00084   int pnpoly(int npol, std::vector<std::vector<int > > poly_vec, int x, int y);
00085   bool save_image(UI_segment_object::Save::Request &reg, UI_segment_object::Save::Response &res);
00086   ros::ServiceServer reset_service;
00087   ros::ServiceServer save_img;
00088   
00089 protected:
00090   ros::NodeHandle nh_;
00091   image_transport::ImageTransport it_;
00092   sensor_msgs::CvBridge bridge_;
00093   image_transport::Subscriber image_sub_;
00094   IplImage* cv_image;
00095   int height;
00096   int width;
00097 };
00098 
00099 
00100   
00101 UI::UI(ros::NodeHandle &nh) :
00102   nh_(nh), it_(nh_)
00103 {
00104   //    got_image = false;
00105   //    image_sub_ = it_.subscribe("/camera/rgb/image_color", 1, &UI::callback, this);
00106   cv_image = NULL;
00107   counter = 0;
00108   counter_ind = 0;
00109   //    cvNamedWindow("Select");                                                               
00110   //    cvSetMouseCallback("Select", mouse_callback);
00111   reset_service = nh_.advertiseService("UI_reset", &UI::reset_cb, this);
00112   save_img = nh_.advertiseService("save_image", &UI::save_image, this);
00113 }
00114 
00115 UI::~UI()
00116 {
00117   cvDestroyWindow("Select");                                                             
00118 }
00119 
00120 bool UI::reset_cb(UI_segment_object::None_Bool::Request &reg, UI_segment_object::None_Bool::Response &res)
00121 {
00122   printf("resetting vectors..");
00123   poly_vec.clear();
00124   in_indices.clear();
00125   counter = 0;
00126   counter_ind = 0;
00127   done = 0;
00128   res.state = true;
00129   return (true);
00130 }
00131 
00132 
00133 void UI::mouse_callback(int event, int x, int y, int flags, void* param)
00134 {
00135   // if (event == CV_EVENT_LBUTTONDOWN)
00136   //   {
00137   //     mouse_active = true;
00138   //   }
00139   // if (event == CV_EVENT_LBUTTONUP)
00140   //   {
00141   //     mouse_active = false;
00142   //   }
00143   // if (event == CV_EVENT_MOUSEMOVE && mouse_active == true)
00144   //   {
00145   //     poly_vec.push_back(std::vector<int>());
00146   //     poly_vec[counter].push_back(x);
00147   //     poly_vec[counter].push_back(y);
00148   //     counter++;
00149   //   }
00150 
00151   if (event == CV_EVENT_LBUTTONDOWN)
00152     {
00153       poly_vec.push_back(std::vector<int>());
00154       poly_vec[counter].push_back(x);
00155       poly_vec[counter].push_back(y);
00156       counter++;
00157     }
00158 
00159   if (event == CV_EVENT_RBUTTONDOWN)
00160     {
00161       std::cerr << "you did click the right mouse button" << std::endl;
00162       done = 1;
00163     }
00164 }
00165 
00166 void UI::color_segment_callback()
00167 {
00168   cvNamedWindow("Select");
00169   cvSetMouseCallback("Select", mouse_callback);
00170   sensor_msgs::ImageConstPtr image_in = ros::topic::waitForMessage<sensor_msgs::Image>("/camera/rgb/image_color", nh_);
00171   cv_image = bridge_.imgMsgToCv(image_in, "bgr8"); 
00172   while (done == 0)
00173     {
00174       for (int i = 0; i < counter; i++)
00175         {
00176           cvCircle(cv_image, cvPoint(poly_vec[i][0], poly_vec[i][1]), 2, CV_RGB(255, 0, 0), 2, 8);
00177         }
00178       cvShowImage("Select", cv_image);
00179       cvWaitKey(33);
00180     }
00181 
00182 
00183   //decrease the search in the image by finding the bounding box
00184   height = cv_image->height;
00185   width = cv_image->width;
00186   
00187   for (int j = 0; j < poly_vec.size(); j++)
00188     {
00189       pts[j] = cvPoint(poly_vec[j][0], poly_vec[j][1]);
00190     }
00191   IplImage* mask = cvCreateImage(cvSize(width, height), 8, 1);
00192   cvFillConvexPoly(mask, pts, poly_vec.size(), cvScalar(255));
00193   cvSaveImage("./mask.png", mask);
00194 
00195   //do some color segmentation selection for indices after selecting a point of certain color or maybe multiple, would
00196   //allow auto segmentation of a certain region automatically
00197 
00198 }
00199 
00200 void UI::pt_callback()
00201 {
00202   cvNamedWindow("Select");                                                               
00203   cvSetMouseCallback("Select", mouse_callback);
00204 
00205   //    sensor_msgs::ImageConstPtr image_in;
00206   sensor_msgs::ImageConstPtr image_in = ros::topic::waitForMessage<sensor_msgs::Image>("/camera/rgb/image_color", nh_);
00207   cv_image = bridge_.imgMsgToCv(image_in, "bgr8");
00208   //    cv_image = bridge_.imgMsgToCv(msg_ptr, "bgr8");
00209   while (done == 0)
00210     {
00211       for (int i = 0; i < counter; i++)
00212         {
00213           cvCircle(cv_image, cvPoint(poly_vec[i][0], poly_vec[i][1]), 2, CV_RGB(255, 0, 0), 2, 8);
00214         }
00215       cvShowImage("Select", cv_image);
00216       cvWaitKey(33);
00217     }
00218 
00219   //decrease the search in the image by finding the bounding box
00220   height = cv_image->height;
00221   width = cv_image->width;
00222   int index;
00223   index = poly_vec[0][1]*width+poly_vec[0][0];
00224   in_indices.push_back(index);
00225   cvDestroyWindow("Select");
00226 }
00227 
00228 void UI::callback(const sensor_msgs::ImageConstPtr& msg_ptr)
00229 {
00230   cv_image = bridge_.imgMsgToCv(msg_ptr, "bgr8");
00231 }
00232 
00233 void UI::cloud_callback(){
00234   cvNamedWindow("Select");                                                               
00235   cvSetMouseCallback("Select", mouse_callback);
00236 
00237   //    sensor_msgs::ImageConstPtr image_in;
00238   sensor_msgs::ImageConstPtr image_in = ros::topic::waitForMessage<sensor_msgs::Image>("/camera/rgb/image_color", nh_);
00239   cv_image = bridge_.imgMsgToCv(image_in, "bgr8");
00240   //    cv_image = bridge_.imgMsgToCv(msg_ptr, "bgr8");
00241   while (done == 0)
00242     {
00243       for (int i = 0; i < counter; i++)
00244         {
00245           cvCircle(cv_image, cvPoint(poly_vec[i][0], poly_vec[i][1]), 2, CV_RGB(255, 0, 0), 2, 8);
00246         }
00247       cvShowImage("Select", cv_image);
00248       cvWaitKey(33);
00249     }
00250 
00251   //decrease the search in the image by finding the bounding box
00252   height = cv_image->height;
00253   width = cv_image->width;
00254   int max_x(0);
00255   int min_x(width);
00256   int max_y(0);
00257   int min_y(height);
00258   for (int i=0; i<counter; i++)
00259     {
00260       if (poly_vec[i][0]>max_x)
00261         max_x = poly_vec[i][0];
00262       if (poly_vec[i][0]<min_x)
00263         min_x = poly_vec[i][0];
00264       if (poly_vec[i][1]>max_y)
00265         max_y = poly_vec[i][1];
00266       if (poly_vec[i][1]<min_y)
00267         min_y = poly_vec[i][1];
00268     }
00269 
00270   //check for image position of 3D points within
00271   //user defined polygon
00272   for (int i=min_y; i<max_y; i++)
00273     {
00274       for (int j=min_x; j<max_x; j++)
00275         {
00276           int in;
00277           in = pnpoly(counter, poly_vec, j, i);
00278           if (in == 1)
00279             {
00280               in_indices.push_back((i)*width+j);
00281               counter_ind ++;
00282             }
00283         }
00284     }
00285   //    got_image = true;
00286   cvDestroyWindow("Select");
00287 }
00288 
00289 
00290 int UI::pnpoly(int npol, std::vector<std::vector<int > > poly_vec, int x, int y)
00291 {
00292   int i, j, c = 0;
00293   for (i = 0, j = npol-1; i < npol; j = i++) 
00294     {
00295       if ((((poly_vec[i][1] <= y) && (y < poly_vec[j][1])) ||
00296            ((poly_vec[j][1] <= y) && (y < poly_vec[i][1]))) &&
00297           (x < (poly_vec[j][0] - poly_vec[i][0]) * (y - poly_vec[i][1]) / (poly_vec[j][1] - poly_vec[i][1]) + poly_vec[i][0]))
00298         c = !c;
00299     }
00300   return c;   //returns 1 for interior and 0 for exterior points
00301 }
00302 
00303 bool UI::save_image(UI_segment_object::Save::Request &reg, UI_segment_object::Save::Response &res)
00304 {
00305   //    sensor_msgs::ImageConstPtr image_in = 
00306   m.lock();
00307   cv_image = bridge_.imgMsgToCv(ros::topic::waitForMessage<sensor_msgs::Image>("/camera/rgb/image_color", nh_), "bgr8");
00308   cvSaveImage(reg.file.c_str(), cv_image);
00309   m.unlock();
00310   res.state = true;
00311   return(true);
00312 }
00313 
00314 
00315 
00316 
00317 class PointCloudPub {
00318 
00319 public:
00320   PointCloudPub(ros::NodeHandle &nh);
00321   ~PointCloudPub();
00322   //  void publish(const pcl::PointCloud<pcl::PointXYZ>::Ptr& msg);
00323   void publish_region(const sensor_msgs::PointCloud2 &msg);
00324   void publish_object(const sensor_msgs::PointCloud2 &msg);
00325   sensor_msgs::PointCloud2 cloud_callback(const sensor_msgs::PointCloud2ConstPtr& msg);
00326   bool get_cloud(UI_segment_object::GetObject::Request &reg, UI_segment_object::GetObject::Response &res);
00327   bool get_pt(UI_segment_object::GetPt::Request &reg, UI_segment_object::GetPt::Response &res);
00328   bool reset_plane_coeff(UI_segment_object::None_Bool::Request &reg, UI_segment_object::None_Bool::Response &res);
00329   bool save_cloud(UI_segment_object::Save::Request &reg, UI_segment_object::Save::Response &res);
00330   geometry_msgs::Point pt_callback(const sensor_msgs::PointCloud2ConstPtr& msg);
00331   UI::UI ui;
00332 
00333 
00334 protected:
00335   ros::NodeHandle nh_;
00336   ros::Publisher pub_region_;
00337   ros::Publisher pub_object_;
00338   pcl::PointCloud<pcl::PointXYZ> cloud_plane_; //(new pcl::PointCloud<pcl::PointXYZ>());
00339   pcl::PointCloud<pcl::PointXYZRGB> cloud_xyz_rgb;
00340   pcl::PointCloud<pcl::PointXYZRGB> cloud_xyz_rgb2;
00341   sensor_msgs::PointCloud2::ConstPtr cloud_msg;
00342   sensor_msgs::PointCloud2::ConstPtr ptcloud2_xyz_rgb;
00343   pcl::ModelCoefficients coefficients;
00344   pcl::SACSegmentation<pcl::PointXYZRGB> seg;  
00345   pcl::PointIndices inliers;
00346   bool new_plane_coeff;
00347   bool transform_pts;
00348   std::string transform_pts_frame_id;
00349 };
00350 
00351 PointCloudPub::PointCloudPub(ros::NodeHandle &nh):
00352     ui(nh)
00353 {
00354   nh_ = nh;
00355   pub_region_ = nh_.advertise<sensor_msgs::PointCloud2>("segment_plane", 1);
00356   pub_object_ = nh_.advertise<sensor_msgs::PointCloud2>("segment_object", 1);
00357   // Create the segmentation object
00358   new_plane_coeff = true;
00359   seg.setOptimizeCoefficients (true);
00360   seg.setModelType (pcl::SACMODEL_PLANE);
00361   seg.setMethodType (pcl::SAC_RANSAC);
00362   seg.setDistanceThreshold (0.01);
00363   seg.setProbability(0.98);
00364 
00365   while (nh_.getParam("/transform_cloud/use", transform_pts) == false)
00366     {
00367       std::cerr << "waiting for boolean param to transform pts or not ... \n";
00368       sleep(0.05);
00369     }
00370   if (transform_pts == true)
00371     {
00372       while (nh_.getParam("/transform_cloud/frame_id", transform_pts_frame_id) == false)
00373         {
00374           std::cerr << "waiting for frame_id to transform pts ... \n";
00375           sleep(0.05);
00376         }
00377     }
00378 }
00379 
00380 PointCloudPub::~PointCloudPub()
00381 {
00382 }
00383 
00384 
00385 void PointCloudPub::publish_region(const sensor_msgs::PointCloud2 &msg)
00386 {
00387   pub_region_.publish(msg);
00388 }
00389 
00390 void PointCloudPub::publish_object(const sensor_msgs::PointCloud2 &msg)
00391 {
00392   pub_object_.publish(msg);
00393 }
00394 
00395 
00396 bool PointCloudPub::get_cloud(UI_segment_object::GetObject::Request &reg, UI_segment_object::GetObject::Response &res)
00397 {
00398   m.lock();
00399   //  sensor_msgs::PointCloud2::ConstPtr msg;
00400   cloud_msg = ros::topic::waitForMessage<sensor_msgs::PointCloud2>("/camera/rgb/points", nh_, ros::Duration(30.0));
00401 
00402   if (done == 0)
00403     {
00404       ui.cloud_callback();
00405     }
00406   //process the point cloud for object and return object
00407   //  sensor_msgs::PointCloud2 msg2 = cloud_callback(ros::topic::waitForMessage<sensor_msgs::PointCloud2>("/camera/depth/points2", nh_, ros::Duration(6.0)));
00408 
00409   sensor_msgs::PointCloud2 msg2 = cloud_callback(cloud_msg);
00410   //  res.object = msg2;
00411   m.unlock();
00412   return (true);
00413 }
00414 
00415 
00416 bool PointCloudPub::save_cloud(UI_segment_object::Save::Request &reg, UI_segment_object::Save::Response &res)
00417 {
00418   m.lock();
00419   pcl::io::savePCDFileASCII (reg.file, cloud_xyz_rgb);
00420   res.state = true;
00421   m.unlock();
00422   return(true);
00423 }
00424 
00425 
00426 bool PointCloudPub::reset_plane_coeff(UI_segment_object::None_Bool::Request &reg, UI_segment_object::None_Bool::Response &res)
00427 {
00428   new_plane_coeff = true;
00429   res.state = true;
00430   return (true);
00431 }
00432 
00433 
00435 bool PointCloudPub::get_pt(UI_segment_object::GetPt::Request &reg, UI_segment_object::GetPt::Response &res)
00436 {
00437   //  sensor_msgs::PointCloud2::Ptr msg (new sensor_msgs::PointCloud2());
00438 
00439   cloud_msg = ros::topic::waitForMessage<sensor_msgs::PointCloud2>("/camera/rgb/points", nh_, ros::Duration(30.0));
00440 
00441   if (done == 0)
00442     {
00443       ui.pt_callback();
00444     }
00445   geometry_msgs::Point pt = pt_callback(cloud_msg);
00446   res.pt = pt;
00447 
00448   return (true);
00449 }
00450 
00451 geometry_msgs::Point PointCloudPub::pt_callback(const sensor_msgs::PointCloud2ConstPtr& msg)
00452 {
00453   m.lock();
00454   pcl::PointCloud<pcl::PointXYZ> cloud_xyz;
00455   pcl::fromROSMsg(*msg, cloud_xyz);
00456 
00457   geometry_msgs::Point pt;
00458 
00459   pt.x = cloud_xyz.points[in_indices[0]].x;
00460   pt.y = cloud_xyz.points[in_indices[0]].y;
00461   pt.z = cloud_xyz.points[in_indices[0]].z;
00462   std::cerr << "here is ind :" << in_indices[0] << std::endl;
00463   std::cerr << "here is pt :" << pt.x << "\t" << pt.y << "\t" << pt.z << std::endl;
00464   m.unlock();
00465   return pt;
00466 }
00467 
00469 
00470 sensor_msgs::PointCloud2 PointCloudPub::cloud_callback(const sensor_msgs::PointCloud2ConstPtr& msg)
00471 {
00472   tf::TransformListener tf_listener;
00473 
00474   pcl::PointCloud<pcl::PointXYZRGB> pt_cloud_before_trans;
00475   pcl::PointCloud<pcl::PointXYZ> cloud_xyz;
00476   pcl::PointCloud<pcl::PointXYZ> cloud_xyz2;
00477 
00478   pcl::PointCloud<pcl::PointXYZRGB> cloud_plane;  //(new pcl::PointCloud<pcl::PointXYZ>());
00479   sensor_msgs::PointCloud2 object;
00480   sensor_msgs::PointCloud2 region;
00481   std::cerr << "Point cloud data: " << msg->height*msg->width << " points" << std::endl;
00482   
00483   //     pcl::fromROSMsg(msg, cloud_xyz);pp
00484   //     pcl::getFields (cloud_xyz, fields);
00485   //   }
00486   
00487   pcl::fromROSMsg(*msg, cloud_xyz);
00488 
00489   if (transform_pts == true)
00490     {
00491       pcl::fromROSMsg(*msg, pt_cloud_before_trans);
00492       tf::StampedTransform transform;
00493       tf_listener.waitForTransform(transform_pts_frame_id, "/openni_rgb_optical_frame2", ros::Time(0), ros::Duration(5.0));
00494       tf_listener.lookupTransform(transform_pts_frame_id, "/openni_rgb_optical_frame2", ros::Time(0), transform);
00495       pcl_ros::transformPointCloud(pt_cloud_before_trans, cloud_xyz_rgb, transform);
00496     }
00497   else
00498     {
00499       pcl::fromROSMsg(*msg, cloud_xyz_rgb);
00500     }
00501 
00502   pcl::ExtractIndices<pcl::PointXYZRGB> extract;
00503 
00504   //  pcl::PointIndices::Ptr ui_ind (new pcl::PointIndices());
00505   pcl::PointIndices ui_ind;
00507   for (int i = 0; i < counter_ind ; i++)
00508     {
00509       ui_ind.indices.push_back(in_indices[i]);
00510     }
00511   
00512   extract.setInputCloud(cloud_xyz_rgb.makeShared());
00513 
00514   extract.setIndices(boost::make_shared<pcl::PointIndices>(ui_ind));
00515   extract.setNegative(false);
00516   extract.filter(cloud_xyz_rgb2);
00517   pcl::toROSMsg(cloud_xyz_rgb2, region);
00518   publish_region(region);
00519 
00520   if (new_plane_coeff==true)
00521     {
00522       new_plane_coeff = false;
00523       //      seg.setInputCloud (cloud_xyz2.makeShared());
00524       //      seg.segment (inliers, coefficients);
00525     }
00526 
00527   seg.setInputCloud (cloud_xyz_rgb2.makeShared());
00528   seg.segment (inliers, coefficients);
00529 
00530   extract.setInputCloud(cloud_xyz_rgb2.makeShared());
00531   extract.setIndices(boost::make_shared<pcl::PointIndices>(inliers));
00532   extract.setNegative(true);
00533   extract.filter(cloud_plane);
00534 
00535   pcl::toROSMsg(cloud_plane, object);
00536   publish_object(object);
00537 
00538   if (inliers.indices.size () == 0)
00539     {
00540       ROS_ERROR ("Could not estimate a planar model for the given dataset.");
00541     }
00542 
00543   std::cerr << "Model coefficients: " << coefficients.values[0] << " " << coefficients.values[1] << " "
00544             << coefficients.values[2] << " " << coefficients.values[3] << std::endl;
00545 
00546   return object;
00547 }
00548 
00549 
00550 int main (int argc, char** argv)
00551 {
00552   ros::init(argc, argv, "segment_object");
00553   ros::NodeHandle nh;
00554   PointCloudPub pcb(nh);
00555   ros::ServiceServer service = nh.advertiseService("get_object_on_plane", &PointCloudPub::get_cloud, &pcb);
00556   ros::ServiceServer service2 = nh.advertiseService("get_3D_pt", &PointCloudPub::get_pt, &pcb);
00557   ros::ServiceServer service3 = nh.advertiseService("save_pt_cloud", &PointCloudPub::save_cloud, &pcb);
00558   //  ros::ServiceServer service4 = nh.advertiseService("reset_plane_coeff", &PointCloudPub::reset_plane_coeff, &pcb);
00559   ros::spin();
00560 
00561   return (0);
00562 }


UI_segment_object
Author(s): Marc Killpack / mkillpack3@gatech.edu, Advisor: Prof. Charlie Kemp, Lab: Healthcare Robotics Lab at Georgia Tech
autogenerated on Wed Nov 27 2013 11:45:01