00001
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
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
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
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
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
00155 param_nh.param("renderGTmap", renderGTmap,false);
00156 renderGTmap &= plotGTTrack;
00157
00158 param_nh.param("initPoseFromTF",initPoseFromTF,false);
00159
00160 param_nh.param<std::string>("init_pose_frame",init_pose_frame,"/state_base_link");
00161
00162 param_nh.param<std::string>("world_frame",world_frame,"/world");
00163
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
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
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
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
00268 tf::transformEigenToTF(pose_, transform);
00269 #else
00270
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
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
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
00324
00325
00326
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
00340 void laserCallback(const sensor_msgs::LaserScan::ConstPtr& msg_in)
00341 {
00342
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
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
00360 T.setIdentity();
00361 this->processFrame(pcl_cloud,T);
00363 publish_map();
00364 };
00365
00366
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
00385
00386 if (nb_added_clouds_ == 0)
00387 {
00388 Tm.setIdentity();
00389 } else {
00390 Tm = last_odom.inverse()*this_odom;
00391
00392
00393
00394
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
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
00414 this->processFrame(pcl_cloud,Tm);
00416 publish_map();
00418 };
00419
00420
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
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
00446 #endif
00447 }
00448 m.unlock();
00449 }
00450
00451 public:
00452
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