00001
00019 #include <ndt_visualisation/ndt_viz.h>
00020
00021 #include <ros/ros.h>
00022 #include <tf/transform_listener.h>
00023 #include <boost/foreach.hpp>
00024 #include <sensor_msgs/LaserScan.h>
00025 #include <message_filters/subscriber.h>
00026 #include <message_filters/sync_policies/approximate_time.h>
00027 #include <nav_msgs/Odometry.h>
00028 #include <nav_msgs/OccupancyGrid.h>
00029 #include <visualization_msgs/MarkerArray.h>
00030 #include <tf/transform_broadcaster.h>
00031 #include "geometry_msgs/PoseWithCovarianceStamped.h"
00032 #include "geometry_msgs/Pose.h"
00033 #include <pcl_conversions/pcl_conversions.h>
00034
00035 #include "message_filters/subscriber.h"
00036 #include "tf/message_filter.h"
00037 #include <tf/transform_broadcaster.h>
00038 #include <tf_conversions/tf_eigen.h>
00039 #include <message_filters/subscriber.h>
00040 #include <message_filters/synchronizer.h>
00041 #include <message_filters/sync_policies/approximate_time.h>
00042 #include <nav_msgs/Odometry.h>
00043 #include <geometry_msgs/PoseStamped.h>
00044
00045 #include "sensor_msgs/PointCloud2.h"
00046
00047
00048 #include "ndt_mcl/3d_ndt_mcl.h"
00049 #include <ndt_map/ndt_map.h>
00050
00051 #define SYNC_FRAMES 20
00052
00053 typedef message_filters::sync_policies::ApproximateTime<sensor_msgs::PointCloud2, nav_msgs::Odometry> PointsOdomSync;
00054
00055
00056 class NDTMCL3DNode {
00057
00058 private:
00059 ros::NodeHandle nh_;
00060 NDTMCL3D *ndtmcl;
00061 boost::mutex mcl_m,message_m;
00062
00063 message_filters::Subscriber<sensor_msgs::PointCloud2> *points2_sub_;
00064 message_filters::Subscriber<nav_msgs::Odometry> *odom_sub_;
00065
00067 Eigen::Affine3d sensorPoseT;
00068 Eigen::Affine3d Told,Todo,Todo_old,Tcum;
00069 Eigen::Affine3d initPoseT;
00070
00071 bool hasSensorPose, hasInitialPose;
00072 bool isFirstLoad;
00073 bool forceSIR, do_visualize;
00074 bool saveMap;
00075 std::string mapName;
00076 std::string output_map_name;
00077 double resolution;
00078 double subsample_level;
00079 int pcounter;
00080 NDTViz ndt_viz;
00081
00082 ros::Publisher mcl_pub;
00083 message_filters::Synchronizer< PointsOdomSync > *sync_po_;
00084
00085 std::string tf_base_link, tf_sensor_link, points_topic, odometry_topic;
00086 public:
00087 NDTMCL3DNode(ros::NodeHandle param_nh) {
00088
00089
00093 bool use_sensor_pose, use_initial_pose;
00094 double pose_init_x,pose_init_y,pose_init_z,
00095 pose_init_r,pose_init_p,pose_init_t;
00096 double sensor_pose_x,sensor_pose_y,sensor_pose_z,
00097 sensor_pose_r,sensor_pose_p,sensor_pose_t;
00098
00099 param_nh.param<bool>("set_sensor_pose", use_sensor_pose, true);
00100 param_nh.param<bool>("set_initial_pose", use_initial_pose, false);
00101
00102 if(use_initial_pose) {
00104 param_nh.param("pose_init_x",pose_init_x,0.);
00105 param_nh.param("pose_init_y",pose_init_y,0.);
00106 param_nh.param("pose_init_z",pose_init_z,0.);
00107 param_nh.param("pose_init_r",pose_init_r,0.);
00108 param_nh.param("pose_init_p",pose_init_p,0.);
00109 param_nh.param("pose_init_t",pose_init_t,0.);
00110 initPoseT = Eigen::Translation<double,3>(pose_init_x,pose_init_y,pose_init_z)*
00111 Eigen::AngleAxis<double>(pose_init_r,Eigen::Vector3d::UnitX()) *
00112 Eigen::AngleAxis<double>(pose_init_p,Eigen::Vector3d::UnitY()) *
00113 Eigen::AngleAxis<double>(pose_init_t,Eigen::Vector3d::UnitZ()) ;
00114
00115 hasInitialPose=true;
00116 } else {
00117 hasInitialPose=false;
00118 }
00119
00120 if(use_sensor_pose) {
00122 param_nh.param("sensor_pose_x",sensor_pose_x,0.);
00123 param_nh.param("sensor_pose_y",sensor_pose_y,0.);
00124 param_nh.param("sensor_pose_z",sensor_pose_z,0.);
00125 param_nh.param("sensor_pose_r",sensor_pose_r,0.);
00126 param_nh.param("sensor_pose_p",sensor_pose_p,0.);
00127 param_nh.param("sensor_pose_t",sensor_pose_t,0.);
00128 hasSensorPose = true;
00129 sensorPoseT = Eigen::Translation<double,3>(sensor_pose_x,sensor_pose_y,sensor_pose_z)*
00130 Eigen::AngleAxis<double>(sensor_pose_r,Eigen::Vector3d::UnitX()) *
00131 Eigen::AngleAxis<double>(sensor_pose_p,Eigen::Vector3d::UnitY()) *
00132 Eigen::AngleAxis<double>(sensor_pose_t,Eigen::Vector3d::UnitZ()) ;
00133 } else {
00134 hasSensorPose = false;
00135 }
00136
00140 param_nh.param<std::string>("map_file_name", mapName, std::string("basement.ndmap"));
00141 param_nh.param<bool>("save_output_map", saveMap, true);
00142 param_nh.param<bool>("do_visualize", do_visualize, true);
00143 param_nh.param<std::string>("output_map_file_name", output_map_name, std::string("ndt_mapper_output.ndmap"));
00144 param_nh.param<double>("map_resolution", resolution , 0.2);
00145 param_nh.param<double>("subsample_level", subsample_level , 1);
00146
00147 fprintf(stderr,"USING RESOLUTION %lf\n",resolution);
00148
00149 lslgeneric::NDTMap ndmap(new lslgeneric::LazyGrid(resolution));
00150 ndmap.loadFromJFF(mapName.c_str());
00151
00155
00156 ndtmcl = new NDTMCL3D(resolution,ndmap,-5);
00157 param_nh.param<bool>("forceSIR", forceSIR, false);
00158 if(forceSIR) ndtmcl->forceSIR=true;
00159
00160 fprintf(stderr,"*** FORCE SIR = %d****",forceSIR);
00161 mcl_pub = nh_.advertise<nav_msgs::Odometry>("ndt_mcl",10);
00162
00166
00167 param_nh.param<std::string>("tf_base_link", tf_base_link, std::string("/base_link"));
00168
00169 param_nh.param<std::string>("tf_laser_link", tf_sensor_link, std::string("/velodyne_link"));
00171 param_nh.param<std::string>("points_topic",points_topic,"points");
00173 param_nh.param<std::string>("odometry_topic",odometry_topic,"odometry");
00174
00175 points2_sub_ = new message_filters::Subscriber<sensor_msgs::PointCloud2>(nh_,points_topic,1);
00176 odom_sub_ = new message_filters::Subscriber<nav_msgs::Odometry>(nh_,odometry_topic,10);
00177 sync_po_ = new message_filters::Synchronizer< PointsOdomSync >(PointsOdomSync(SYNC_FRAMES), *points2_sub_, *odom_sub_);
00178 sync_po_->registerCallback(boost::bind(&NDTMCL3DNode::callback, this, _1, _2));
00179
00180 isFirstLoad=true;
00181 pcounter =0;
00182 }
00183 ~NDTMCL3DNode() {
00184 delete points2_sub_;
00185 delete odom_sub_;
00186 delete sync_po_;
00187 delete ndtmcl;
00188 }
00190
00191 void callback(const sensor_msgs::PointCloud2::ConstPtr& cloud_in,
00192 const nav_msgs::Odometry::ConstPtr& odo_in)
00193 {
00194 mcl_m.lock();
00195
00196 Eigen::Quaterniond qd;
00197 Eigen::Affine3d Tm;
00198 pcl::PointCloud<pcl::PointXYZ> cloud, cloudT, cloudTO;
00199
00200 qd.x() = odo_in->pose.pose.orientation.x;
00201 qd.y() = odo_in->pose.pose.orientation.y;
00202 qd.z() = odo_in->pose.pose.orientation.z;
00203 qd.w() = odo_in->pose.pose.orientation.w;
00204
00205 Todo = Eigen::Translation3d (odo_in->pose.pose.position.x,
00206 odo_in->pose.pose.position.y,odo_in->pose.pose.position.z) * qd;
00207
00208 pcl::fromROSMsg (*cloud_in, cloud);
00209
00210 fprintf(stderr,"cloud has %d points\n",cloud.points.size());
00211
00212 if(!hasSensorPose) {
00213
00214 }
00215
00216 if(isFirstLoad) {
00217
00218 if(!hasInitialPose) {
00219
00220 mcl_m.unlock();
00221 return;
00222 }
00223
00224 Eigen::Vector3d tr = initPoseT.translation();
00225 Eigen::Vector3d rot = initPoseT.rotation().eulerAngles(0,1,2);
00226
00227 Todo_old=Todo;
00228 Tcum = initPoseT;
00229
00230 ndtmcl->initializeFilter(tr[0], tr[1],tr[2],rot[0],rot[1],rot[2],0.5, 0.5, 0.1, 2.0*M_PI/180.0, 2.0*M_PI/180.0 ,2.0*M_PI/180.0, 100);
00231
00232 ndt_viz.plotNDTSAccordingToOccupancy(-1,&ndtmcl->map);
00233 isFirstLoad = false;
00234 mcl_m.unlock();
00235 return;
00236 }
00237
00238 Tm = Todo_old.inverse()*Todo;
00239 if(Tm.translation().norm()<0.01 && fabs(Tm.rotation().eulerAngles(0,1,2)[2])<(0.5*M_PI/180.0)) {
00240 mcl_m.unlock();
00241 return;
00242 }
00243
00244 Tcum = Tcum*Tm;
00245 Todo_old=Todo;
00246
00247
00248
00249
00251 lslgeneric::transformPointCloudInPlace(sensorPoseT, cloud);
00252
00253
00254
00255 ndtmcl->updateAndPredictEff(Tm, cloud, subsample_level);
00256
00257
00258
00260 if(do_visualize) {
00261 pcounter++;
00262 if(pcounter%500==0){
00263 ndt_viz.clear();
00264 ndt_viz.plotNDTSAccordingToOccupancy(-1,&ndtmcl->map);
00265
00266 }
00267
00268
00269
00270 ndt_viz.clearParticles();
00271 for(int i=0;i<ndtmcl->pf.size();i++){
00272 double x,y,z;
00273 ndtmcl->pf.pcloud[i].getXYZ(x,y,z);
00274 ndt_viz.addParticle(x, y,z+0.5, 1.0, 1.0, 1.0);
00275 }
00276
00277 Eigen::Affine3d mean = ndtmcl->pf.getMean();
00278 ndt_viz.addTrajectoryPoint(mean.translation()[0],mean.translation()[1],mean.translation()[2]+0.5,1.0,0,0);
00279 Eigen::Vector3d tr = Tcum.translation();
00280 ndt_viz.addTrajectoryPoint(tr[0],tr[1],tr[2]+0.5,0.0,1.0,0);
00281
00282
00283 ndt_viz.displayParticles();
00284 ndt_viz.displayTrajectory();
00285 ndt_viz.win3D->setCameraPointingToPoint(mean.translation()[0],mean.translation()[1],3.0);
00286 ndt_viz.repaint();
00287
00288 }
00289
00290 sendROSOdoMessage(ndtmcl->pf.getMean(),odo_in->header.stamp);
00291 mcl_m.unlock();
00292
00293 }
00295
00297 bool sendROSOdoMessage(Eigen::Affine3d mean,ros::Time ts){
00298 nav_msgs::Odometry O;
00299 static int seq = 0;
00300 O.header.stamp = ts;
00301 O.header.seq = seq;
00302 O.header.frame_id = "/world";
00303 O.child_frame_id = "/mcl_pose";
00304
00305 O.pose.pose.position.x = mean.translation()[0];
00306 O.pose.pose.position.y = mean.translation()[1];
00307 O.pose.pose.position.z = mean.translation()[2];
00308 Eigen::Quaterniond q (mean.rotation());
00309 tf::Quaternion qtf;
00310 tf::quaternionEigenToTF (q, qtf);
00311 O.pose.pose.orientation.x = q.x();
00312 O.pose.pose.orientation.y = q.y();
00313 O.pose.pose.orientation.z = q.z();
00314 O.pose.pose.orientation.w = q.w();
00315
00316 seq++;
00317 mcl_pub.publish(O);
00318
00319 static tf::TransformBroadcaster br;
00320 tf::Transform transform;
00321 transform.setOrigin( tf::Vector3(mean.translation()[0],mean.translation()[1], mean.translation()[2]) );
00322
00323 transform.setRotation( qtf );
00324 br.sendTransform(tf::StampedTransform(transform, ts, "world", "mcl_pose"));
00325
00326 return true;
00327 }
00329 };
00330
00331
00332 int main(int argc, char **argv){
00333 ros::init(argc, argv, "NDT-MCL");
00334 ros::NodeHandle paramHandle ("~");
00335 NDTMCL3DNode mclnode(paramHandle);
00336 ros::spin();
00337 return 0;
00338 }