00001
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
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
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
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
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
00139 param_nh.param("initPoseFromTF",initPoseFromTF,false);
00140
00141 param_nh.param<std::string>("init_pose_frame",init_pose_frame,"/state_base_link");
00142
00143 param_nh.param<std::string>("world_frame",world_frame,"/world");
00144
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
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
00227 tf::transformEigenToTF(pose_, transform);
00228 #else
00229
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
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
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
00283
00284
00285
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
00298 void laserCallback(const sensor_msgs::LaserScan::ConstPtr& msg_in)
00299 {
00300
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
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
00318
00319 T.setIdentity();
00320 this->processFrame(pcl_cloud,T);
00321
00322 };
00323
00324
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
00343
00344 if (nb_added_clouds_ == 0)
00345 {
00346 Tm.setIdentity();
00347 } else {
00348 Tm = last_odom.inverse()*this_odom;
00349
00350
00351
00352
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
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
00372
00373 this->processFrame(pcl_cloud,Tm);
00374
00375 };
00376
00377
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
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