ndt_fuser_node.cpp
Go to the documentation of this file.
00001 //#include <ndt_fuser.h>
00002 #include <ndt_fuser/ndt_fuser_hmt.h>
00003 #include <ros/ros.h>
00004 
00005 #include "pcl/point_cloud.h"
00006 #include "sensor_msgs/PointCloud2.h"
00007 #include "pcl/io/pcd_io.h"
00008 #include <cstdio>
00009 #include <Eigen/Eigen>
00010 #include <fstream>
00011 #include "message_filters/subscriber.h"
00012 #include "tf/message_filter.h"
00013 #include <tf/transform_broadcaster.h>
00014 #include <tf_conversions/tf_eigen.h>
00015 #include <boost/circular_buffer.hpp>
00016 #include <laser_geometry/laser_geometry.h>
00017 #include <nav_msgs/Odometry.h>
00018 #include <geometry_msgs/PoseStamped.h>
00019 
00020 #include <message_filters/subscriber.h>
00021 #include <message_filters/synchronizer.h>
00022 #include <message_filters/sync_policies/approximate_time.h>
00023 #include <std_srvs/Empty.h>
00024 
00025 #ifndef SYNC_FRAMES
00026 #define SYNC_FRAMES 20
00027 #endif
00028 
00032 typedef message_filters::sync_policies::ApproximateTime<sensor_msgs::LaserScan, nav_msgs::Odometry> LaserOdomSync;
00033 typedef message_filters::sync_policies::ApproximateTime<sensor_msgs::LaserScan, geometry_msgs::PoseStamped> LaserPoseSync;
00034 typedef message_filters::sync_policies::ApproximateTime<sensor_msgs::PointCloud2, nav_msgs::Odometry> PointsOdomSync;
00035 typedef message_filters::sync_policies::ApproximateTime<sensor_msgs::PointCloud2, geometry_msgs::PoseStamped> PointsPoseSync;
00036 
00037 class NDTFuserNode {
00038 
00039     protected:
00040         // Our NodeHandle
00041         ros::NodeHandle nh_;
00042 
00043         message_filters::Subscriber<sensor_msgs::PointCloud2> *points2_sub_;
00044         message_filters::Subscriber<sensor_msgs::LaserScan> *laser_sub_;
00045         message_filters::Subscriber<nav_msgs::Odometry> *odom_sub_;
00046         ros::Subscriber gt_sub;
00047 
00048         // Components for publishing
00049         tf::TransformBroadcaster tf_;
00050         ros::Publisher output_pub_;
00051         Eigen::Affine3d pose_, T, sensor_pose_;
00052 
00053         unsigned int nb_added_clouds_;
00054         double varz;
00055         
00056         boost::mutex m, message_m;
00057         lslgeneric::NDTFuserHMT<pcl::PointXYZ> *fuser;
00058         std::string points_topic, laser_topic, map_dir, map_name, odometry_topic, 
00059                     world_frame, fuser_frame, init_pose_frame, gt_topic;
00060         double size_x, size_y, size_z, resolution, sensor_range, min_laser_range_;
00061         bool visualize, match2D, matchLaser, beHMT, useOdometry, plotGTTrack, 
00062              initPoseFromGT, initPoseFromTF, initPoseSet;
00063 
00064         double pose_init_x,pose_init_y,pose_init_z,
00065                pose_init_r,pose_init_p,pose_init_t;
00066         double sensor_pose_x,sensor_pose_y,sensor_pose_z,
00067                sensor_pose_r,sensor_pose_p,sensor_pose_t;
00068         laser_geometry::LaserProjection projector_;
00069 
00070         message_filters::Synchronizer< LaserOdomSync > *sync_lo_;
00071         message_filters::Synchronizer< LaserPoseSync > *sync_lp_;
00072         
00073         message_filters::Synchronizer< PointsOdomSync > *sync_po_;
00074         message_filters::Synchronizer< PointsPoseSync > *sync_pp_;
00075         ros::ServiceServer save_map_;
00076 
00077         Eigen::Affine3d last_odom, this_odom;
00078     public:
00079         // Constructor
00080         NDTFuserNode(ros::NodeHandle param_nh) : nb_added_clouds_(0)
00081         {
00083             param_nh.param<std::string>("points_topic",points_topic,"points");
00085             param_nh.param<std::string>("laser_topic",laser_topic,"laser_scan");
00086 
00090             param_nh.param<std::string>("map_directory",map_dir,"map");
00091             param_nh.param<std::string>("map_name_prefix",map_name,"");
00092             
00094             param_nh.param("pose_init_x",pose_init_x,0.);
00095             param_nh.param("pose_init_y",pose_init_y,0.);
00096             param_nh.param("pose_init_z",pose_init_z,0.);
00097             param_nh.param("pose_init_r",pose_init_r,0.);
00098             param_nh.param("pose_init_p",pose_init_p,0.);
00099             param_nh.param("pose_init_t",pose_init_t,0.);
00100             
00102             param_nh.param("sensor_pose_x",sensor_pose_x,0.);
00103             param_nh.param("sensor_pose_y",sensor_pose_y,0.);
00104             param_nh.param("sensor_pose_z",sensor_pose_z,0.);
00105             param_nh.param("sensor_pose_r",sensor_pose_r,0.);
00106             param_nh.param("sensor_pose_p",sensor_pose_p,0.);
00107             param_nh.param("sensor_pose_t",sensor_pose_t,0.);
00108             
00110             param_nh.param("size_x_meters",size_x,10.);
00111             param_nh.param("size_y_meters",size_y,10.);
00112             param_nh.param("size_z_meters",size_z,10.);
00113             
00115             param_nh.param("sensor_range",sensor_range,3.);
00117             param_nh.param("min_laser_range",min_laser_range_,0.1);
00118             
00119             //map resolution
00120             param_nh.param("resolution",resolution,0.10);
00121             param_nh.param("laser_variance_z",varz,resolution/4);
00122 
00124             param_nh.param("visualize",visualize,true);
00126             param_nh.param("match2D",match2D,false);
00128             param_nh.param("beHMT",beHMT,false);
00130             param_nh.param("useOdometry",useOdometry,false);
00132             param_nh.param<std::string>("odometry_topic",odometry_topic,"odometry");
00134             param_nh.param("plotGTTrack",plotGTTrack,false);
00135             param_nh.param<std::string>("gt_topic",gt_topic,"groundtruth");
00137             param_nh.param("initPoseFromGT",initPoseFromGT,false);
00138             //get it from TF?
00139             param_nh.param("initPoseFromTF",initPoseFromTF,false);
00140             //the frame to initialize to
00141             param_nh.param<std::string>("init_pose_frame",init_pose_frame,"/state_base_link");
00142             //the world frame
00143             param_nh.param<std::string>("world_frame",world_frame,"/world");
00144             //our frame
00145             param_nh.param<std::string>("fuser_frame",fuser_frame,"/fuser");
00146  
00148             param_nh.param("matchLaser",matchLaser,false);
00149             
00150             pose_ =  Eigen::Translation<double,3>(pose_init_x,pose_init_y,pose_init_z)*
00151                 Eigen::AngleAxis<double>(pose_init_r,Eigen::Vector3d::UnitX()) *
00152                 Eigen::AngleAxis<double>(pose_init_p,Eigen::Vector3d::UnitY()) *
00153                 Eigen::AngleAxis<double>(pose_init_t,Eigen::Vector3d::UnitZ()) ;
00154             
00155             sensor_pose_ =  Eigen::Translation<double,3>(sensor_pose_x,sensor_pose_y,sensor_pose_z)*
00156                 Eigen::AngleAxis<double>(sensor_pose_r,Eigen::Vector3d::UnitX()) *
00157                 Eigen::AngleAxis<double>(sensor_pose_p,Eigen::Vector3d::UnitY()) *
00158                 Eigen::AngleAxis<double>(sensor_pose_t,Eigen::Vector3d::UnitZ()) ;
00159 
00160             if(matchLaser) match2D=true;
00161             fuser = new lslgeneric::NDTFuserHMT<pcl::PointXYZ>(resolution,size_x,size_y,size_z,
00162                     sensor_range, visualize,match2D, false, false, 30, map_name, beHMT, map_dir, true);
00163 
00164             fuser->setSensorPose(sensor_pose_);
00165 
00166             if(!matchLaser) {
00167                 points2_sub_ = new message_filters::Subscriber<sensor_msgs::PointCloud2>(nh_,points_topic,1);
00168                 if(useOdometry) {
00169                     odom_sub_ = new message_filters::Subscriber<nav_msgs::Odometry>(nh_,odometry_topic,10);
00170                     sync_po_ = new message_filters::Synchronizer< PointsOdomSync >(PointsOdomSync(SYNC_FRAMES), *points2_sub_, *odom_sub_);
00171                     sync_po_->registerCallback(boost::bind(&NDTFuserNode::points2OdomCallback, this, _1, _2));
00172                 } else {
00173                     points2_sub_->registerCallback(boost::bind( &NDTFuserNode::points2Callback, this, _1));
00174                 }
00175             } else {
00176                 laser_sub_ = new message_filters::Subscriber<sensor_msgs::LaserScan>(nh_,laser_topic,2);
00177                 if(useOdometry) {
00178                     odom_sub_ = new message_filters::Subscriber<nav_msgs::Odometry>(nh_,odometry_topic,10);
00179                     sync_lo_ = new message_filters::Synchronizer< LaserOdomSync >(LaserOdomSync(SYNC_FRAMES), *laser_sub_, *odom_sub_);
00180                     sync_lo_->registerCallback(boost::bind(&NDTFuserNode::laserOdomCallback, this, _1, _2));
00181 
00182                 } else {
00183                     laser_sub_->registerCallback(boost::bind( &NDTFuserNode::laserCallback, this, _1));
00184                 }
00185             }
00186             save_map_ = param_nh.advertiseService("save_map", &NDTFuserNode::save_map_callback, this);
00187 
00188             if(plotGTTrack) {
00189                 gt_sub = nh_.subscribe<nav_msgs::Odometry>(gt_topic,10,&NDTFuserNode::gt_callback, this);       
00190             }
00191             initPoseSet = false;
00192         }
00193 
00194         ~NDTFuserNode()
00195         {
00196             delete fuser;
00197         }
00198 
00199         void processFrame(pcl::PointCloud<pcl::PointXYZ> &cloud, 
00200                           Eigen::Affine3d Tmotion) {
00201             
00202             m.lock();
00203             if (nb_added_clouds_  == 0)
00204             {
00205                 ROS_INFO("initializing fuser map. Init pose from GT? %d, TF? %d", initPoseFromGT, initPoseFromTF);
00206                 if(initPoseFromGT || initPoseFromTF) {
00207                     //check if initial pose was set already 
00208                     if(!initPoseSet) {
00209                         ROS_WARN("skipping frame, init pose not acquired yet!");
00210                         m.unlock();
00211                         return;
00212                     }
00213                 }
00214                 ROS_INFO("Init pose is (%lf,%lf,%lf)", pose_.translation()(0), pose_.translation()(1), 
00215                         pose_.rotation().eulerAngles(0,1,2)(0));
00216                 fuser->initialize(pose_,cloud);
00217                 nb_added_clouds_++;
00218             } else {
00219                 nb_added_clouds_++;
00220                 pose_ = fuser->update(Tmotion,cloud);
00221             }
00222             m.unlock();
00223 
00224             tf::Transform transform;
00225 #if ROS_VERSION_MINIMUM(1,9,0)
00226 //groovy
00227             tf::transformEigenToTF(pose_, transform);
00228 #else
00229 //fuerte
00230             tf::TransformEigenToTF(pose_, transform);
00231 #endif
00232             tf_.sendTransform(tf::StampedTransform(transform, ros::Time::now(), world_frame, fuser_frame));
00233 
00234         }
00235         
00236         bool save_map_callback(std_srvs::Empty::Request  &req,
00237                 std_srvs::Empty::Response &res ) {
00238 
00239             bool ret = false;
00240             ROS_INFO("Saving current map to map directory %s", map_dir.c_str());
00241             m.lock();
00242             ret = fuser->saveMap(); 
00243             m.unlock();
00244 
00245             return ret;
00246         }
00247 
00248 
00249         // Callback
00250         void points2Callback(const sensor_msgs::PointCloud2::ConstPtr& msg_in)
00251         {
00252             pcl::PointCloud<pcl::PointXYZ> cloud;
00253             message_m.lock();
00254             pcl::fromROSMsg (*msg_in, cloud);
00255             message_m.unlock();
00256             T.setIdentity();
00257             this->processFrame(cloud,T);
00258 
00259         };
00260         
00261         // Callback
00262         void points2OdomCallback(const sensor_msgs::PointCloud2::ConstPtr& msg_in,
00263                   const nav_msgs::Odometry::ConstPtr& odo_in)
00264         {
00265             Eigen::Quaterniond qd;
00266             Eigen::Affine3d Tm;
00267             pcl::PointCloud<pcl::PointXYZ> cloud;
00268             
00269             message_m.lock();
00270             qd.x() = odo_in->pose.pose.orientation.x;
00271             qd.y() = odo_in->pose.pose.orientation.y;
00272             qd.z() = odo_in->pose.pose.orientation.z;
00273             qd.w() = odo_in->pose.pose.orientation.w;
00274             
00275             this_odom = Eigen::Translation3d (odo_in->pose.pose.position.x,
00276                     odo_in->pose.pose.position.y,odo_in->pose.pose.position.z) * qd;
00277             if (nb_added_clouds_  == 0)
00278             {
00279                 Tm.setIdentity();
00280             } else {
00281                 Tm = last_odom.inverse()*this_odom;
00282                 //std::cout<<"delta from last update: "<<Tm.translation().transpose()<<" "<<Tm.rotation().eulerAngles(0,1,2)[2] << std::endl;
00283                 //if(Tm.translation().norm()<0.2 && fabs(Tm.rotation().eulerAngles(0,1,2)[2])<(5*M_PI/180.0)) {
00284                 //    message_m.unlock();
00285                 //    return;
00286                 //}
00287             }
00288             last_odom = this_odom;
00289 
00290             pcl::fromROSMsg (*msg_in, cloud);
00291             message_m.unlock();
00292             
00293             this->processFrame(cloud,Tm);
00294 
00295         };
00296         
00297         // Callback
00298         void laserCallback(const sensor_msgs::LaserScan::ConstPtr& msg_in)
00299         {
00300             // Add to a queue
00301             sensor_msgs::PointCloud2 cloud;
00302             pcl::PointCloud<pcl::PointXYZ> pcl_cloud_unfiltered, pcl_cloud;
00303             message_m.lock();
00304             projector_.projectLaser(*msg_in, cloud);
00305             message_m.unlock();
00306             pcl::fromROSMsg (cloud, pcl_cloud_unfiltered);
00307 
00308             pcl::PointXYZ pt;
00309             //add some variance on z
00310             for(int i=0; i<pcl_cloud_unfiltered.points.size(); i++) {
00311                 pt = pcl_cloud_unfiltered.points[i];
00312                 if(sqrt(pt.x*pt.x+pt.y*pt.y) > min_laser_range_) {
00313                     pt.z += varz*((double)rand())/(double)INT_MAX;
00314                     pcl_cloud.points.push_back(pt);
00315                 }
00316             }
00317             //ROS_INFO("Got laser points");
00318 
00319             T.setIdentity();
00320             this->processFrame(pcl_cloud,T);
00321 
00322         };
00323         
00324         // Callback
00325         void laserOdomCallback(const sensor_msgs::LaserScan::ConstPtr& msg_in,
00326                   const nav_msgs::Odometry::ConstPtr& odo_in)
00327         {
00328             Eigen::Quaterniond qd;
00329             sensor_msgs::PointCloud2 cloud;
00330             pcl::PointCloud<pcl::PointXYZ> pcl_cloud, pcl_cloud_unfiltered;
00331             Eigen::Affine3d Tm;
00332 
00333             message_m.lock();
00334             qd.x() = odo_in->pose.pose.orientation.x;
00335             qd.y() = odo_in->pose.pose.orientation.y;
00336             qd.z() = odo_in->pose.pose.orientation.z;
00337             qd.w() = odo_in->pose.pose.orientation.w;
00338             
00339             this_odom = Eigen::Translation3d (odo_in->pose.pose.position.x,
00340                     odo_in->pose.pose.position.y,odo_in->pose.pose.position.z) * qd;
00341 
00342             //std::cout<<"AT: "<<this_odom.translation().transpose()<<" "<<this_odom.rotation().eulerAngles(0,1,2)[2] << std::endl;
00343             
00344             if (nb_added_clouds_  == 0)
00345             {
00346                 Tm.setIdentity();
00347             } else {
00348                 Tm = last_odom.inverse()*this_odom;
00349                 //std::cout<<"delta from last update: "<<Tm.translation().transpose()<<" "<<Tm.rotation().eulerAngles(0,1,2)[2] << std::endl;
00350                 //if(Tm.translation().norm()<0.2 && fabs(Tm.rotation().eulerAngles(0,1,2)[2])<(5*M_PI/180.0)) {
00351                 //    message_m.unlock();
00352                 //    return;
00353                 //}
00354             }
00355             last_odom = this_odom;
00356 
00357             projector_.projectLaser(*msg_in, cloud);
00358             message_m.unlock();
00359             
00360             pcl::fromROSMsg (cloud, pcl_cloud_unfiltered);
00361 
00362             pcl::PointXYZ pt;
00363             //add some variance on z
00364             for(int i=0; i<pcl_cloud_unfiltered.points.size(); i++) {
00365                 pt = pcl_cloud_unfiltered.points[i];
00366                 if(sqrt(pt.x*pt.x+pt.y*pt.y) > min_laser_range_) {
00367                     pt.z += varz*((double)rand())/(double)INT_MAX;
00368                     pcl_cloud.points.push_back(pt);
00369                 }
00370             }
00371             //ROS_INFO("Got laser and odometry!");
00372 
00373             this->processFrame(pcl_cloud,Tm);
00374 
00375         };
00376         
00377         // Callback
00378         void gt_callback(const nav_msgs::Odometry::ConstPtr& msg_in)
00379         {
00380             Eigen::Quaterniond qd;
00381             Eigen::Affine3d gt_pose;
00382 
00383             qd.x() = msg_in->pose.pose.orientation.x;
00384             qd.y() = msg_in->pose.pose.orientation.y;
00385             qd.z() = msg_in->pose.pose.orientation.z;
00386             qd.w() = msg_in->pose.pose.orientation.w;
00387             
00388             gt_pose = Eigen::Translation3d (msg_in->pose.pose.position.x,
00389                     msg_in->pose.pose.position.y,msg_in->pose.pose.position.z) * qd;
00390              
00391             //ROS_INFO("got GT pose from GT track");
00392             m.lock();
00393             if(initPoseFromGT && !initPoseSet) {
00394                 initPoseSet = true;
00395                 pose_ = gt_pose;
00396                 ROS_INFO("Set initial pose from GT track");
00397             }
00398             fuser->viewer->addTrajectoryPoint(gt_pose.translation()(0),gt_pose.translation()(1),gt_pose.translation()(2)+0.2,1,1,1);
00399             fuser->viewer->displayTrajectory();
00400             fuser->viewer->repaint();   
00401             m.unlock();
00402         }
00403 
00404     public:
00405         EIGEN_MAKE_ALIGNED_OPERATOR_NEW
00406 };
00407 
00408 int main(int argc, char **argv)
00409 {
00410     ros::init(argc, argv, "ndt_fuser_node");
00411 
00412     ros::NodeHandle param("~");
00413     NDTFuserNode t(param);
00414     ros::spin();
00415 
00416     return 0;
00417 }
00418 


ndt_fuser
Author(s): Todor Stoyanov, Jari Saarinen
autogenerated on Mon Oct 6 2014 03:20:34