robotlinks_filter_node.cpp
Go to the documentation of this file.
00001 // Software License Agreement (BSD License)
00002 // Copyright (c) 2008-2010, Rosen Diankov (rdiankov@cs.cmu.edu), Willow Garage, Inc.
00003 // Redistribution and use in source and binary forms, with or without
00004 // modification, are permitted provided that the following conditions are met:
00005 //   * Redistributions of source code must retain the above copyright notice,
00006 //     this list of conditions and the following disclaimer.
00007 //   * Redistributions in binary form must reproduce the above copyright
00008 //     notice, this list of conditions and the following disclaimer in the
00009 //     documentation and/or other materials provided with the distribution.
00010 //   * The name of the author may not be used to endorse or promote products
00011 //     derived from this software without specific prior written permission.
00012 //
00013 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00014 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00015 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00016 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00017 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00018 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00019 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00020 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00021 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00022 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00023 // POSSIBILITY OF SUCH DAMAGE.
00024 //
00026 #include <openrave-core.h>
00027 
00028 #include <vector>
00029 #include <sstream>
00030 #include <cstdio>
00031 
00032 #include <ros/node_handle.h>
00033 #include <ros/master.h>
00034 #include <sensor_msgs/PointCloud.h>
00035 #include <tf/transform_listener.h>
00036 #include <tf/message_filter.h>
00037 #include <message_filters/subscriber.h>
00038 #include <boost/format.hpp>
00039 
00040 #include <cstdio>
00041 #include <cstdlib>
00042 
00043 extern "C"
00044 {
00045 #include <qhull/qhull.h>
00046 #include <qhull/mem.h>
00047 #include <qhull/qset.h>
00048 #include <qhull/geom.h>
00049 #include <qhull/merge.h>
00050 #include <qhull/poly.h>
00051 #include <qhull/io.h>
00052 #include <qhull/stat.h>
00053 }
00054 
00055 using namespace OpenRAVE;
00056 using namespace std;
00057 
00058 #define FOREACH(it, v) for(typeof((v).begin())it = (v).begin(); it != (v).end(); (it)++)
00059 
00060 class RobotLinksFilter
00061 {
00062     struct LINK
00063     {
00064         string tfframe;
00065         vector<Vector> vconvexhull; 
00066         Transform tstart, tend;
00067         vector<Vector> vnewconvexhull;
00068     };
00069     struct LASERPOINT
00070     {
00071         LASERPOINT() {
00072         }
00073         LASERPOINT(const Vector& ptnew, dReal timenew) : pt(ptnew), time(timenew),inside(0) {
00074         }
00075         Vector pt;
00076         dReal time; // 0-1 value specifying (stamp-stampstart)/(stampend-stampstart) where stamp is time of point,
00077                     // startstart and startend are start and end times of the entire scan
00078         int inside; // inside robot frame
00079     };
00080 
00081     tf::TransformListener _tf;
00082     ros::NodeHandle _nh, _root_handle;
00083     boost::shared_ptr< tf::MessageFilter<sensor_msgs::PointCloud> > _mn;
00084     boost::shared_ptr< message_filters::Subscriber<sensor_msgs::PointCloud> > _sub;
00085     std::string _sensor_frame;
00086     ros::Publisher _pointCloudPublisher;
00087     ros::Subscriber _no_filter_sub;
00088 
00089     vector<LINK> _vLinkHulls;
00090     boost::shared_ptr<ros::NodeHandle> _ros;
00091     vector<LASERPOINT> _vlaserpoints;
00092     sensor_msgs::PointCloud _pointcloudout;
00093     string _robotname;
00094     double _convexpadding, _min_sensor_dist;
00095     bool _bAccurateTiming; 
00096 
00097 public:
00098     RobotLinksFilter(const string& robotname) : _nh("~"), _robotname(robotname), _convexpadding(0.01), _bAccurateTiming(false)
00099     {
00100         _nh.param<std::string>("sensor_frame", _sensor_frame, std::string());
00101         _nh.param<double>("min_sensor_dist", _min_sensor_dist, 0.01);;
00102         _nh.param<double>("self_see_default_padding", _convexpadding, .01);
00103         //_nh.param<double>("self_see_default_scale", default_scale, 1.0);
00104 
00105         if( InitRobotLinksFromOpenRAVE(robotname) ) {
00106 
00107             _sub.reset(new message_filters::Subscriber<sensor_msgs::PointCloud>(_root_handle, "cloud_in", 1));
00108             _mn.reset(new tf::MessageFilter<sensor_msgs::PointCloud>(*_sub, _tf, "", 2));
00109 
00110             //_mn = new tf::MessageNotifier<sensor_msgs::PointCloud>(tf_, boost::bind(&SelfFilter::cloudCallback, this, _1), "cloud_in", "", 1);
00111             _pointCloudPublisher = _root_handle.advertise<sensor_msgs::PointCloud>("cloud_out", 1);
00112 
00113             vector<string> frames;
00114             FOREACH(itlink,_vLinkHulls)
00115             frames.push_back(itlink->tfframe);
00116             _mn->setTargetFrames(frames);
00117             _mn->registerCallback(boost::bind(&RobotLinksFilter::PointCloudCallback, this, _1));
00118         }
00119         else {
00120             RAVELOG_ERROR(str(boost::format("failed to init robot %s\n")%robotname));
00121             _no_filter_sub = _root_handle.subscribe<sensor_msgs::PointCloud>("cloud_in", 1, boost::bind(&RobotLinksFilter::noFilterCallback, this, _1));
00122         }
00123         RAVELOG_INFO(str(boost::format("self collision with robot %s, padding=%f\n")%_robotname%_convexpadding));
00124     }
00125     virtual ~RobotLinksFilter()
00126     {
00127         _mn.reset();
00128         _sub.reset();
00129         _no_filter_sub.shutdown();
00130     }
00131 
00132     bool InitRobotLinksFromOpenRAVE(const string& robotname)
00133     {
00134         _vLinkHulls.clear();
00135 
00136         if( robotname.size() == 0 )
00137             return false;
00138 
00139         RAVELOG_INFO("opening OpenRAVE robot file %s\n", robotname.c_str());
00140 
00141         // create the main environment
00142         EnvironmentBasePtr penv = RaveCreateEnvironment();
00143         if( !penv ) {
00144             return false;
00145         }
00146         // load the scene
00147         if( !penv->Load(robotname) ) {
00148             RAVELOG_ERROR("RobotLinksFilter failed create robot %s\n", robotname.c_str());
00149             return false;
00150         }
00151 
00152         // get the first robot
00153         vector<RobotBasePtr> vrobots;
00154         penv->GetRobots(vrobots);
00155         if( vrobots.size() == 0 ) {
00156             RAVELOG_ERROR("RobotLinksFilter no robots in file %s\n", robotname.c_str());
00157             return false;
00158         }
00159 
00160         RobotBasePtr probot = vrobots.front();
00161         RAVELOG_INFOA(str(boost::format("generating convex hulls for robot %s, num links: %d")%probot->GetName()%probot->GetLinks().size()));
00162 
00163         ros::Time starthull = ros::Time::now();
00164         _vLinkHulls.resize(probot->GetLinks().size());
00165         vector<LINK>::iterator ithull = _vLinkHulls.begin();
00166         size_t totalplanes = 0;
00167         FOREACH(itlink, probot->GetLinks()) {
00168             // compute convex hull
00169             if( compute_convex_hull((*itlink)->GetCollisionData().vertices, ithull->vconvexhull) ) {
00170                 totalplanes += ithull->vconvexhull.size();
00171                 RAVELOG_DEBUG(str(boost::format("link %s convex hull has %d planes\n")%(*itlink)->GetName()%ithull->vconvexhull.size()));
00172             }
00173             else
00174                 RAVELOG_ERROR(str(boost::format("failed to compute convex hull for link %s\n")%(*itlink)->GetName()));
00175 
00176             ithull->tfframe = (*itlink)->GetName();
00177             ++ithull;
00178         }
00179 
00180         RAVELOG_INFOA(str(boost::format("total convex planes: %d, time: %fs")%totalplanes%(ros::Time::now()-starthull).toSec()));
00181         return true;
00182     }
00183 
00184 private:
00185 
00186     void noFilterCallback(const sensor_msgs::PointCloudConstPtr &cloud){
00187         _pointCloudPublisher.publish(cloud);
00188         ROS_DEBUG("Self filter publishing unfiltered frame");
00189     }
00190 
00191     void PointCloudCallback(const sensor_msgs::PointCloudConstPtr& _pointcloudin)
00192     {
00193         if( _vLinkHulls.size() == 0 ) {
00194             // just pass the data
00195             _pointCloudPublisher.publish(_pointcloudin);
00196             return;
00197         }
00198 
00199         if( _pointcloudin->points.size() == 0 )
00200             return;
00201 
00202         ros::WallTime tm = ros::WallTime::now();
00203 
00204         if( _bAccurateTiming )
00205             PruneWithAccurateTiming(*_pointcloudin, _vlaserpoints);
00206         else
00207             PruneWithSimpleTiming(*_pointcloudin, _vlaserpoints);
00208 
00209         int totalpoints = 0;
00210         FOREACH(itpoint, _vlaserpoints)
00211         totalpoints += itpoint->inside==0;
00212 
00213         _pointcloudout.header = _pointcloudin->header;
00214         _pointcloudout.points.resize(totalpoints);
00215         _pointcloudout.channels.resize(_pointcloudin->channels.size());
00216         for(int ichan = 0; ichan < (int)_pointcloudin->channels.size(); ++ichan) {
00217             _pointcloudout.channels[ichan].name = _pointcloudin->channels[ichan].name;
00218             _pointcloudout.channels[ichan].values.resize(totalpoints);
00219         }
00220 
00221         for(int oldindex = 0, newindex = 0; oldindex < (int)_vlaserpoints.size(); ++oldindex) {
00222             if( _vlaserpoints[oldindex].inside )
00223                 continue;
00224 
00225             _pointcloudout.points[newindex] = _pointcloudin->points[oldindex];
00226             for(int ichan = 0; ichan < (int)_pointcloudin->channels.size(); ++ichan)
00227                 _pointcloudout.channels[ichan].values[newindex] = _pointcloudin->channels[ichan].values[oldindex];
00228             ++newindex;
00229         }
00230 
00231         RAVELOG_DEBUG("robotlinks_filter_node published %d points, processing time=%fs\n", totalpoints, (ros::WallTime::now()-tm).toSec());
00232         _pointCloudPublisher.publish(_pointcloudout);
00233     }
00234 
00237     void PruneWithAccurateTiming(const sensor_msgs::PointCloud& pointcloudin, vector<LASERPOINT>& vlaserpoints)
00238     {
00239         int istampchan = 0;
00240         while(istampchan < (int)pointcloudin.channels.size()) {
00241             if( pointcloudin.channels[istampchan].name == "stamps" )
00242                 break;
00243             ++istampchan;
00244         }
00245 
00246         if( istampchan >= (int)pointcloudin.channels.size()) {
00247             RAVELOG_DEBUG("accurate timing needs 'stamp' channel to be published in point cloud, reverting to simple timing\n");
00248             PruneWithSimpleTiming(pointcloudin, vlaserpoints);
00249             return;
00250         }
00251 
00252         // look for timestamp channel
00253         float fdeltatime = 0;
00254         for(int i = 0; i < (int)pointcloudin.channels[istampchan].values.size(); ++i)
00255             fdeltatime = pointcloudin.channels[istampchan].values[i];
00256 
00257         if( fdeltatime == 0 ) {
00258             PruneWithSimpleTiming(pointcloudin, vlaserpoints);
00259             return;
00260         }
00261 
00262         vlaserpoints.resize(pointcloudin.points.size());
00263         float ideltatime=1.0f/fdeltatime;
00264         int index = 0;
00265         FOREACH(itp, pointcloudin.points) {
00266             vlaserpoints[index] = LASERPOINT(Vector(itp->x,itp->y,itp->z,1),pointcloudin.channels[istampchan].values[index]*ideltatime);
00267             ++index;
00268         }
00269 
00270         try {
00271             geometry_msgs::PoseStamped poseout, posestart,poseend;
00272             posestart.pose.orientation.w = 1;
00273             posestart.header = pointcloudin.header;
00274             poseend.pose.orientation.w = 1;
00275             poseend.header = pointcloudin.header;
00276             poseend.header.stamp += ros::Duration().fromSec(fdeltatime);
00277             FOREACH(ithull, _vLinkHulls) {
00278                 _tf.transformPose(ithull->tfframe, posestart, poseout);
00279                 ithull->tstart = GetTransform(poseout.pose);
00280                 _tf.transformPose(ithull->tfframe, poseend, poseout);
00281                 ithull->tend = GetTransform(poseout.pose);
00282             }
00283         }
00284         catch(tf::TransformException& ex) {
00285             RAVELOG_WARN("failed to get tf frame: %s\n", ex.what());
00286             return;
00287         }
00288 
00289         // points are independent from each and loop can be parallelized
00290         #pragma omp parallel for schedule(dynamic,64)
00291         for(int i = 0; i < (int)vlaserpoints.size(); ++i) {
00292             LASERPOINT& laserpoint = vlaserpoints[i];
00293             FOREACH(ithull, _vLinkHulls) {
00294                 Transform tinv, tinvstart = ithull->tstart.inverse(), tinvend = ithull->tend.inverse();
00295                 tinv.rot = quatSlerp(tinvstart.rot,tinvend.rot,laserpoint.time);
00296                 tinv.trans = tinvstart.trans*(1-laserpoint.time) + tinvend.trans*laserpoint.time;
00297 
00298                 bool bInside = true;
00299                 FOREACH(itplane, ithull->vconvexhull) {
00300                     Vector v = tinv * laserpoint.pt; v.w = 1;
00301                     if( v.dot(*itplane) > 0 ) {
00302                         bInside = false;
00303                         break;
00304                     }
00305                 }
00306 
00307                 if( bInside ) {
00308                     laserpoint.inside = 1;
00309                     break;
00310                 }
00311             }
00312         }
00313     }
00314 
00317     void PruneWithSimpleTiming(const sensor_msgs::PointCloud& pointcloudin, vector<LASERPOINT>& vlaserpoints)
00318     {
00319         tf::StampedTransform bttransform;
00320         vlaserpoints.resize(0);
00321 
00322         // compute new hulls
00323         try {
00324             geometry_msgs::PoseStamped poseout, posestart,poseend;
00325             posestart.pose.orientation.w = 1;
00326             posestart.header = pointcloudin.header;
00327             FOREACH(ithull, _vLinkHulls) {
00328                 _tf.transformPose(ithull->tfframe, posestart, poseout);
00329                 ithull->tstart = GetTransform(poseout.pose);
00330             }
00331 //            stringstream ss;
00332 //            ss << _vLinkHulls.front().tfframe << ": " << _vLinkHulls.front().tstart << endl;
00333 //            RAVELOG_INFO(ss.str());
00334         }
00335         catch(tf::TransformException& ex) {
00336             RAVELOG_WARN("failed to get tf frame: %s\n", ex.what());
00337             return;
00338         }
00339 
00340         vlaserpoints.resize(pointcloudin.points.size());
00341 
00342         int index = 0;
00343         FOREACH(itp, pointcloudin.points)
00344         vlaserpoints[index++] = LASERPOINT(Vector(itp->x,itp->y,itp->z,1),0);
00345 
00346         FOREACH(ithull, _vLinkHulls) {
00347             TransformMatrix tinvstart = ithull->tstart; //.inverse();
00348             ithull->vnewconvexhull.resize(ithull->vconvexhull.size());
00349             vector<Vector>::iterator itnewplane = ithull->vnewconvexhull.begin();
00350             FOREACH(itplane, ithull->vconvexhull) {
00351                 itnewplane->x = itplane->x*tinvstart.m[0]+itplane->y*tinvstart.m[4]+itplane->z*tinvstart.m[8];
00352                 itnewplane->y = itplane->x*tinvstart.m[1]+itplane->y*tinvstart.m[5]+itplane->z*tinvstart.m[9];
00353                 itnewplane->z = itplane->x*tinvstart.m[2]+itplane->y*tinvstart.m[6]+itplane->z*tinvstart.m[10];
00354                 itnewplane->w = itplane->x*tinvstart.trans.x+itplane->y*tinvstart.trans.y+itplane->z*tinvstart.trans.z+itplane->w;
00355                 ++itnewplane;
00356             }
00357         }
00358 
00359         // points are independent from each and loop can be parallelized
00360         #pragma omp parallel for schedule(dynamic,64)
00361         for(int i = 0; i < (int)vlaserpoints.size(); ++i) {
00362             LASERPOINT& laserpoint = vlaserpoints[i];
00363             FOREACH(ithull, _vLinkHulls) {
00364 
00365                 bool bInside = true;
00366                 FOREACH(itplane, ithull->vnewconvexhull) {
00367                     if( laserpoint.pt.dot(*itplane) > 0 ) {
00368                         bInside = false;
00369                         break;
00370                     }
00371                 }
00372 
00373                 if( bInside ) {
00374                     laserpoint.inside = 1;
00375                     break;
00376                 }
00377             }
00378         }
00379     }
00380 
00381     bool compute_convex_hull(const vector<Vector>& verts, vector<Vector>& vconvexplanes)
00382     {
00383         if( verts.size() <= 3 )
00384             return false;
00385 
00386         vconvexplanes.resize(0);
00387 
00388         int dim = 3;                  // dimension of points
00389         vector<coordT> qpoints(3*verts.size());
00390         for(size_t i = 0; i < verts.size(); ++i) {
00391             qpoints[3*i+0] = verts[i].x;
00392             qpoints[3*i+1] = verts[i].y;
00393             qpoints[3*i+2] = verts[i].z;
00394         }
00395 
00396         bool bSuccess = false;
00397         boolT ismalloc = 0;           // True if qhull should free points in qh_freeqhull() or reallocation
00398         char flags[]= "qhull Tv"; // option flags for qhull, see qh_opt.htm
00399         FILE *outfile = NULL;    // stdout, output from qh_produce_output(), use NULL to skip qh_produce_output()
00400         FILE *errfile = tmpfile();    // stderr, error messages from qhull code
00401 
00402         int exitcode= qh_new_qhull (dim, qpoints.size()/3, &qpoints[0], ismalloc, flags, outfile, errfile);
00403         if (!exitcode) { // no error
00404             vconvexplanes.reserve(100);
00405 
00406             facetT *facet;            // set by FORALLfacets
00407             FORALLfacets { // 'qh facet_list' contains the convex hull
00408                 vconvexplanes.push_back(Vector(facet->normal[0], facet->normal[1], facet->normal[2], facet->offset-_convexpadding));
00409             }
00410 
00411             bSuccess = true;
00412         }
00413 
00414         qh_freeqhull(!qh_ALL);
00415         int curlong, totlong;     // memory remaining after qh_memfreeshort
00416         qh_memfreeshort (&curlong, &totlong);
00417         if (curlong || totlong)
00418             RAVELOG_ERROR("qhull internal warning (main): did not free %d bytes of long memory (%d pieces)\n", totlong, curlong);
00419 
00420         fclose(errfile);
00421         return bSuccess;
00422     }
00423 
00424     static Transform GetTransform(const btTransform& bt)
00425     {
00426         btQuaternion q = bt.getRotation();
00427         btVector3 o = bt.getOrigin();
00428         return Transform(Vector(q.w(),q.x(),q.y(),q.z()),Vector(o.x(),o.y(),o.z()));
00429     }
00430     static Transform GetTransform(const geometry_msgs::Pose& pose)
00431     {
00432         return Transform(Vector(pose.orientation.w, pose.orientation.x, pose.orientation.y, pose.orientation.z), Vector(pose.position.x, pose.position.y, pose.position.z));
00433     }
00434 };
00435 
00436 int main(int argc, char ** argv)
00437 {
00438     // parse the command line options
00439     string robotname;
00440     int i = 1;
00441     while(i < argc) {
00442         if( strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0 ) {
00443             // print the arguments and exit
00444             printf("robotlinks_filter_node [--robotfile openravefile]\n"
00445                    "  Start a node to prune points that are on the robot surface.\n"
00446                    "  Currently the robot file specified has to be in OpenRAVE format\n"
00447                    "  (see openrave_robot_descirption ros package for details)\n");
00448 
00449             return 0;
00450         }
00451         if( strcmp(argv[i], "--robotfile") == 0 ) {
00452             robotname = argv[i+1];
00453             i += 2;
00454         }
00455         else
00456             break;
00457     }
00458 
00459     ros::init(argc,argv,"robotlinks_filter");
00460     if( !ros::master::check() ) {
00461         return 1;
00462     }
00463     RaveInitialize(true);
00464     boost::shared_ptr<RobotLinksFilter> plinksfilter(new RobotLinksFilter(robotname));
00465     ros::spin();
00466     plinksfilter.reset();
00467 
00468     return 0;
00469 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Properties Friends Defines


openrave_robot_filter
Author(s): Rosen Diankov (rdiankov@cs.cmu.edu)
autogenerated on Sun Mar 24 2013 04:50:35