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


ndt_fuser
Author(s): Todor Stoyanov, Jari Saarinen
autogenerated on Wed Aug 26 2015 15:25:16