00001
00020 #define USE_VISUALIZATION_DEBUG ///< Enable / Disable visualization
00021
00022
00023
00024 #include <mrpt/utils/CTicTac.h>
00025
00026 #ifdef USE_VISUALIZATION_DEBUG
00027 #include <mrpt/gui.h>
00028 #include <mrpt/base.h>
00029 #include <mrpt/opengl.h>
00030 #include <GL/gl.h>
00031 #include "ndt_mcl/CMyEllipsoid.h"
00032 #endif
00033
00034 #include <ros/ros.h>
00035 #include <rosbag/bag.h>
00036 #include <rosbag/view.h>
00037 #include <tf/transform_listener.h>
00038 #include <boost/foreach.hpp>
00039 #include <sensor_msgs/LaserScan.h>
00040 #include <message_filters/subscriber.h>
00041 #include <message_filters/sync_policies/approximate_time.h>
00042 #include <nav_msgs/Odometry.h>
00043 #include <nav_msgs/OccupancyGrid.h>
00044 #include <visualization_msgs/MarkerArray.h>
00045 #include <tf/transform_broadcaster.h>
00046 #include "geometry_msgs/PoseWithCovarianceStamped.h"
00047 #include "geometry_msgs/Pose.h"
00048
00049 #include "ndt_mcl/impl/ndt_mcl.hpp"
00050 #include <ndt_map/ndt_map.h>
00051
00052
00053
00057 #ifdef USE_VISUALIZATION_DEBUG
00058 #include "ndt_mcl/impl/mcl_visualization.hpp"
00059 #endif
00060 ros::Publisher ndtmap_pub;
00061
00065 void sendMapToRviz(lslgeneric::NDTMap<pcl::PointXYZ> &map){
00066
00067 std::vector<lslgeneric::NDTCell<pcl::PointXYZ>*> ndts;
00068 ndts = map.getAllCells();
00069 fprintf(stderr,"SENDING MARKER ARRAY MESSAGE (%lu components)\n",ndts.size());
00070 visualization_msgs::MarkerArray marray;
00071
00072 for(unsigned int i=0;i<ndts.size();i++){
00073 Eigen::Matrix3d cov = ndts[i]->getCov();
00074 Eigen::Vector3d m = ndts[i]->getMean();
00075
00076 Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> Sol (cov);
00077
00078 Eigen::Matrix3d evecs;
00079 Eigen::Vector3d evals;
00080
00081 evecs = Sol.eigenvectors().real();
00082 evals = Sol.eigenvalues().real();
00083
00084 Eigen::Quaternion<double> q(evecs);
00085
00086 visualization_msgs::Marker marker;
00087 marker.header.frame_id = "world";
00088 marker.header.stamp = ros::Time();
00089 marker.ns = "NDT";
00090 marker.id = i;
00091 marker.type = visualization_msgs::Marker::SPHERE;
00092 marker.action = visualization_msgs::Marker::ADD;
00093 marker.pose.position.x = m[0];
00094 marker.pose.position.y = m[1];
00095 marker.pose.position.z = m[2];
00096
00097 marker.pose.orientation.x = q.x();
00098 marker.pose.orientation.y = q.y();
00099 marker.pose.orientation.z = q.z();
00100 marker.pose.orientation.w = q.w();
00101
00102 marker.scale.x = 100.0*evals(0);
00103 marker.scale.y = 100.0*evals(1);
00104 marker.scale.z = 100.0*evals(2);
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116 marker.color.a = 1.0;
00117 marker.color.r = 0.0;
00118 marker.color.g = 1.0;
00119 marker.color.b = 0.0;
00120
00121 marray.markers.push_back(marker);
00122 }
00123
00124 ndtmap_pub.publish(marray);
00125
00126 for(unsigned int i=0;i<ndts.size();i++){
00127 delete ndts[i];
00128 }
00129
00130 }
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00159 bool userInitialPose = false;
00160 bool hasNewInitialPose = false;
00161
00162 double ipos_x=0,ipos_y=0,ipos_yaw=0;
00163 double ivar_x=0,ivar_y=0,ivar_yaw=0;
00164
00165 ros::Subscriber initial_pose_sub_;
00166
00167 void initialPoseReceived(const geometry_msgs::PoseWithCovarianceStampedConstPtr& msg){
00168 tf::Pose ipose;
00169 tf::poseMsgToTF(msg->pose.pose, ipose);
00170 ipos_x = ipose.getOrigin().x();
00171 ipos_y = ipose.getOrigin().y();
00172 double pitch, roll;
00173 ipose.getBasis().getEulerYPR(ipos_yaw,pitch,roll);
00174
00175 ivar_x = msg->pose.covariance[0];
00176 ivar_x = msg->pose.covariance[6];
00177 ivar_x = msg->pose.covariance[35];
00178
00179 hasNewInitialPose=true;
00180 }
00181
00182
00183
00184
00187
00190 Eigen::Affine3d getAsAffine(float x, float y, float yaw ){
00191 Eigen::Matrix3d m;
00192 m = Eigen::AngleAxisd(0, Eigen::Vector3d::UnitX())
00193 * Eigen::AngleAxisd(0, Eigen::Vector3d::UnitY())
00194 * Eigen::AngleAxisd(yaw, Eigen::Vector3d::UnitZ());
00195 Eigen::Translation3d v(x,y,0);
00196 Eigen::Affine3d T = v*m;
00197
00198 return T;
00199 }
00200
00210 NDTMCL<pcl::PointXYZ> *ndtmcl;
00212 float offx = 0;
00213 float offy = 0;
00214 float offa = 0;
00215 static bool has_sensor_offset_set = false;
00216 static bool isFirstLoad=true;
00217 Eigen::Affine3d Told,Todo;
00218
00219 ros::Publisher mcl_pub;
00220
00221
00222
00223
00224
00225 bool sendROSOdoMessage(Eigen::Vector3d mean,Eigen::Matrix3d cov, ros::Time ts){
00226 nav_msgs::Odometry O;
00227 static int seq = 0;
00228 O.header.stamp = ts;
00229 O.header.seq = seq;
00230 O.header.frame_id = "/world";
00231 O.child_frame_id = "/mcl_pose";
00232
00233 O.pose.pose.position.x = mean[0];
00234 O.pose.pose.position.y = mean[1];
00235 tf::Quaternion q;
00236 q.setRPY(0,0,mean[2]);
00237 O.pose.pose.orientation.x = q.getX();
00238 O.pose.pose.orientation.y = q.getY();
00239 O.pose.pose.orientation.z = q.getZ();
00240 O.pose.pose.orientation.w = q.getW();
00241
00242
00251 O.pose.covariance[0] = cov(0,0);
00252 O.pose.covariance[1] = cov(0,1);
00253 O.pose.covariance[2] = 0;
00254 O.pose.covariance[3] = 0;
00255 O.pose.covariance[4] = 0;
00256 O.pose.covariance[5] = 0;
00257
00258 O.pose.covariance[6] = cov(1,0);
00259 O.pose.covariance[7] = cov(1,1);
00260 O.pose.covariance[8] = 0;
00261 O.pose.covariance[9] = 0;
00262 O.pose.covariance[10] = 0;
00263 O.pose.covariance[11] = 0;
00264
00265 O.pose.covariance[12] = 0;
00266 O.pose.covariance[13] = 0;
00267 O.pose.covariance[14] = 0;
00268 O.pose.covariance[15] = 0;
00269 O.pose.covariance[16] = 0;
00270 O.pose.covariance[17] = 0;
00271
00272 O.pose.covariance[18] = 0;
00273 O.pose.covariance[19] = 0;
00274 O.pose.covariance[20] = 0;
00275 O.pose.covariance[21] = 0;
00276 O.pose.covariance[22] = 0;
00277 O.pose.covariance[23] = 0;
00278
00279 O.pose.covariance[24] = 0;
00280 O.pose.covariance[25] = 0;
00281 O.pose.covariance[26] = 0;
00282 O.pose.covariance[27] = 0;
00283 O.pose.covariance[28] = 0;
00284 O.pose.covariance[29] = 0;
00285
00286 O.pose.covariance[30] = 0;
00287 O.pose.covariance[31] = 0;
00288 O.pose.covariance[32] = 0;
00289 O.pose.covariance[33] = 0;
00290 O.pose.covariance[34] = 0;
00291 O.pose.covariance[35] = cov(2,2);
00292
00293 seq++;
00294 mcl_pub.publish(O);
00295
00296 static tf::TransformBroadcaster br;
00297 tf::Transform transform;
00298 transform.setOrigin( tf::Vector3(mean[0],mean[1], 0.0) );
00299
00300 transform.setRotation( q );
00301 br.sendTransform(tf::StampedTransform(transform, ts, "world", "mcl_pose"));
00302
00303 return true;
00304 }
00305
00306
00307
00312 mrpt::utils::CTicTac TT;
00313
00314 std::string tf_odo_topic = "odom_base_link";
00315 std::string tf_state_topic = "base_link";
00316 std::string tf_laser_link = "base_laser_link";
00317
00321 void callback(const sensor_msgs::LaserScan::ConstPtr& scan)
00322 {
00323 static int counter = 0;
00324 counter++;
00325
00326 static tf::TransformListener tf_listener;
00327 double looptime = TT.Tac();
00328 TT.Tic();
00329 fprintf(stderr,"Lt( %.1lfms %.1lfHz seq:%d) -",looptime*1000,1.0/looptime,scan->header.seq);
00330
00331 if(has_sensor_offset_set == false) return;
00332 double gx,gy,gyaw,x,y,yaw;
00333
00335 tf::StampedTransform transform;
00336 tf_listener.waitForTransform("world", tf_state_topic, scan->header.stamp,ros::Duration(1.0));
00338 try{
00339 tf_listener.lookupTransform("world", tf_state_topic, scan->header.stamp, transform);
00340 gyaw = tf::getYaw(transform.getRotation());
00341 gx = transform.getOrigin().x();
00342 gy = transform.getOrigin().y();
00343 }
00344 catch (tf::TransformException ex){
00345 ROS_ERROR("%s",ex.what());
00346 return;
00347 }
00348
00350 try{
00351 tf_listener.lookupTransform("world", tf_odo_topic, scan->header.stamp, transform);
00352 yaw = tf::getYaw(transform.getRotation());
00353 x = transform.getOrigin().x();
00354 y = transform.getOrigin().y();
00355 }
00356 catch (tf::TransformException ex){
00357 ROS_ERROR("%s",ex.what());
00358 return;
00359 }
00360
00361
00362 mrpt::utils::CTicTac tictac;
00363 tictac.Tic();
00364
00366 int N =(scan->angle_max - scan->angle_min)/scan->angle_increment;
00370
00371 Eigen::Affine3d T = getAsAffine(x,y,yaw);
00372 Eigen::Affine3d Tgt = getAsAffine(gx,gy,gyaw);
00373
00374 if(userInitialPose && hasNewInitialPose){
00375 gx = ipos_x;
00376 gy = ipos_y;
00377 gyaw = ipos_yaw;
00378 }
00379
00380 if(isFirstLoad || hasNewInitialPose){
00381 fprintf(stderr,"Initializing to (%lf, %lf, %lf)\n",gx,gy,gyaw);
00383 ndtmcl->initializeFilter(gx, gy,gyaw,0.2, 0.2, 2.0*M_PI/180.0, 150);
00384 Told = T;
00385 Todo = Tgt;
00386 hasNewInitialPose = false;
00387 }
00388
00390 Eigen::Affine3d Tmotion = Told.inverse() * T;
00391 Todo = Todo*Tmotion;
00392
00393 if(isFirstLoad==false){
00394 if( (Tmotion.translation().norm()<0.005 && fabs(Tmotion.rotation().eulerAngles(0,1,2)[2])<(0.2*M_PI/180.0))){
00395 Eigen::Vector3d dm = ndtmcl->getMean();
00396 Eigen::Matrix3d cov = ndtmcl->pf.getDistributionVariances();
00397 sendROSOdoMessage(dm,cov, scan->header.stamp);
00398 double Time = tictac.Tac();
00399 fprintf(stderr,"Time elapsed %.1lfms\n",Time*1000);
00400 return;
00401 }
00402 }
00403
00404 Told = T;
00405
00407 float dy =offy;
00408 float dx = offx;
00409 float alpha = atan2(dy,dx);
00410 float L = sqrt(dx*dx+dy*dy);
00411
00413 float lpx = L * cos(alpha);
00414 float lpy = L * sin(alpha);
00415 float lpa = offa;
00416
00418 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);
00419 for (int j=0;j<N;j++){
00420 double r = scan->ranges[j];
00421 if(r>=scan->range_min && r<scan->range_max && r>0.3 && r<20.0){
00422 double a = scan->angle_min + j*scan->angle_increment;
00423 pcl::PointXYZ pt;
00424 pt.x = r*cos(a+lpa)+lpx;
00425 pt.y = r*sin(a+lpa)+lpy;
00426 pt.z = 0.1+0.02 * (double)rand()/(double)RAND_MAX;
00427 cloud->push_back(pt);
00428 }
00429 }
00433 ndtmcl->updateAndPredict(Tmotion, *cloud);
00434
00435 Eigen::Vector3d dm = ndtmcl->getMean();
00436 Eigen::Matrix3d cov = ndtmcl->pf.getDistributionVariances();
00437
00438 double Time = tictac.Tac();
00439 fprintf(stderr,"Time elapsed %.1lfms (%lf %lf %lf) \n",Time*1000,dm[0],dm[1],dm[2]);
00440 isFirstLoad = false;
00442
00443 sendROSOdoMessage(dm,cov, scan->header.stamp);
00444
00445 if(counter%50==0){
00446 sendMapToRviz(ndtmcl->map);
00447 }
00448
00450 #ifdef USE_VISUALIZATION_DEBUG
00451 Eigen::Vector3d origin(dm[0] + L * cos(dm[2]+alpha),dm[1] + L * sin(dm[2]+alpha),0.1);
00452 Eigen::Affine3d ppos = getAsAffine(dm[0],dm[1],dm[2]);
00453
00454
00455 lslgeneric::transformPointCloudInPlace(ppos, *cloud);
00456 mrpt::opengl::COpenGLScenePtr &scene = win3D.get3DSceneAndLock();
00457 win3D.setCameraPointingToPoint(gx,gy,1);
00458 if(counter%2000==0) gl_points->clear();
00459 scene->clear();
00460 scene->insert(plane);
00461
00462 addMap2Scene(ndtmcl->map, origin, scene);
00463 addPoseCovariance(dm[0],dm[1],cov,scene);
00464 addScanToScene(scene, origin, cloud);
00465 addParticlesToWorld(ndtmcl->pf,Tgt.translation(),dm, Todo.translation());
00466 scene->insert(gl_points);
00467 scene->insert(gl_particles);
00468 win3D.unlockAccess3DScene();
00469 win3D.repaint();
00470
00471 if (win3D.keyHit())
00472 {
00473 mrpt::gui::mrptKeyModifier kmods;
00474 int key = win3D.getPushedKey(&kmods);
00475 }
00476 #endif
00477 }
00478
00479
00480 int main(int argc, char **argv){
00481 ros::init(argc, argv, "NDT-MCL");
00482 double resolution=0.4;
00483
00484 #ifdef USE_VISUALIZATION_DEBUG
00485 initializeScene();
00486 #endif
00487
00488 ros::NodeHandle n;
00489 ros::NodeHandle nh;
00490 ros::NodeHandle paramHandle ("~");
00491 TT.Tic();
00492
00493 std::string tf_base_link,input_laser_topic;
00494
00500 bool loadMap = false;
00501 std::string mapName("basement.ndmap");
00502
00503 bool saveMap = true;
00504 std::string output_map_name = std::string("ndt_mapper_output.ndmap");
00505
00511
00512 paramHandle.param<std::string>("input_laser_topic", input_laser_topic, std::string("/base_scan"));
00513 paramHandle.param<std::string>("tf_base_link", tf_state_topic, std::string("/base_link"));
00514 paramHandle.param<std::string>("tf_laser_link", tf_laser_link, std::string("/hokuyo1_link"));
00515
00516 bool use_sensor_pose;
00517 paramHandle.param<bool>("use_sensor_pose", use_sensor_pose, false);
00518 double sensor_pose_x, sensor_pose_y, sensor_pose_th;
00519 paramHandle.param<double>("sensor_pose_x", sensor_pose_x, 0.);
00520 paramHandle.param<double>("sensor_pose_y", sensor_pose_y, 0.);
00521 paramHandle.param<double>("sensor_pose_th", sensor_pose_th, 0.);
00522
00523 paramHandle.param<bool>("load_map_from_file", loadMap, false);
00524 paramHandle.param<std::string>("map_file_name", mapName, std::string("basement.ndmap"));
00525
00526 paramHandle.param<bool>("save_output_map", saveMap, true);
00527 paramHandle.param<std::string>("output_map_file_name", output_map_name, std::string("ndt_mapper_output.ndmap"));
00528
00529 paramHandle.param<double>("map_resolution", resolution , 0.2);
00530 bool forceSIR=false;
00531 paramHandle.param<bool>("forceSIR", forceSIR, false);
00532
00533 paramHandle.param<bool>("set_initial_pose", userInitialPose, false);
00534 paramHandle.param<double>("initial_pose_x", ipos_x, 0.);
00535 paramHandle.param<double>("initial_pose_y", ipos_y, 0.);
00536 paramHandle.param<double>("initial_pose_yaw", ipos_yaw, 0.);
00537
00538 if(userInitialPose==true) hasNewInitialPose=true;
00539
00545 fprintf(stderr,"USING RESOLUTION %lf\n",resolution);
00546 lslgeneric::NDTMap<pcl::PointXYZ> ndmap(new lslgeneric::LazyGrid<pcl::PointXYZ>(resolution));
00547
00548 ndmap.setMapSize(80.0, 80.0, 1.0);
00549
00550 if(loadMap){
00551 fprintf(stderr,"Loading Map from '%s'\n",mapName.c_str());
00552 ndmap.loadFromJFF(mapName.c_str());
00553 }
00554
00555 ndtmcl = new NDTMCL<pcl::PointXYZ>(resolution,ndmap,-0.5);
00556 if(forceSIR) ndtmcl->forceSIR=true;
00557
00558 fprintf(stderr,"*** FORCE SIR = %d****",forceSIR);
00564 mcl_pub = nh.advertise<nav_msgs::Odometry>("ndt_mcl",10);
00570 ros::Subscriber scansub=nh.subscribe(input_laser_topic, 1, callback);
00571
00572 ndtmap_pub = nh.advertise<visualization_msgs::MarkerArray>( "NDTMAP", 0 );
00573
00574 initial_pose_sub_ = nh.subscribe("initialpose", 1, initialPoseReceived);
00575
00576 offa = sensor_pose_th;
00577 offx = sensor_pose_x;
00578 offy = sensor_pose_y;
00579
00580
00581 has_sensor_offset_set = true;
00582
00583 fprintf(stderr,"Sensor Pose = (%lf %lf %lf)\n",offx, offy, offa);
00584
00585 ros::spin();
00586
00587
00588 return 0;
00589 }