00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
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;
00077
00078 int inside;
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
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
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
00142 EnvironmentBasePtr penv = RaveCreateEnvironment();
00143 if( !penv ) {
00144 return false;
00145 }
00146
00147 if( !penv->Load(robotname) ) {
00148 RAVELOG_ERROR("RobotLinksFilter failed create robot %s\n", robotname.c_str());
00149 return false;
00150 }
00151
00152
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
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
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
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
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
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
00332
00333
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;
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
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;
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;
00398 char flags[]= "qhull Tv";
00399 FILE *outfile = NULL;
00400 FILE *errfile = tmpfile();
00401
00402 int exitcode= qh_new_qhull (dim, qpoints.size()/3, &qpoints[0], ismalloc, flags, outfile, errfile);
00403 if (!exitcode) {
00404 vconvexplanes.reserve(100);
00405
00406 facetT *facet;
00407 FORALLfacets {
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;
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
00439 string robotname;
00440 int i = 1;
00441 while(i < argc) {
00442 if( strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0 ) {
00443
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 }