base.cpp
Go to the documentation of this file.
00001 #include "slam/base.h"
00002 #include "opencv2/core/core.hpp"
00003 #include <boost/filesystem.hpp>
00004 
00005 namespace fs=boost::filesystem;
00006 
00012 slam::SlamBase::SlamBase(
00013   ros::NodeHandle nh, ros::NodeHandle nhp) : nh_(nh), nh_private_(nhp)
00014 {
00015   // Read the node parameters
00016   readParameters();
00017 
00018   // Initialize the stereo slam
00019   init();
00020 }
00021 
00025 void slam::SlamBase::finalize()
00026 {
00027   ROS_INFO("[StereoSlam:] Finalizing...");
00028   lc_.finalize();
00029   ROS_INFO("[StereoSlam:] Done!");
00030 }
00031 
00042 void slam::SlamBase::msgsCallback(const nav_msgs::Odometry::ConstPtr& odom_msg,
00043                                   const sensor_msgs::ImageConstPtr& l_img_msg,
00044                                   const sensor_msgs::ImageConstPtr& r_img_msg,
00045                                   const sensor_msgs::CameraInfoConstPtr& l_info_msg,
00046                                   const sensor_msgs::CameraInfoConstPtr& r_info_msg,
00047                                   const sensor_msgs::PointCloud2ConstPtr& cloud_msg)
00048 {
00049   // Get the cloud
00050   PointCloud::Ptr pcl_cloud(new PointCloud);
00051   pcl::fromROSMsg(*cloud_msg, *pcl_cloud);
00052   pcl::copyPointCloud(*pcl_cloud, pcl_cloud_);
00053 
00054   // Call the general slam callback
00055   msgsCallback(odom_msg, l_img_msg, r_img_msg, l_info_msg, r_info_msg);
00056 }
00057 
00067 void slam::SlamBase::msgsCallback(const nav_msgs::Odometry::ConstPtr& odom_msg,
00068                                   const sensor_msgs::ImageConstPtr& l_img_msg,
00069                                   const sensor_msgs::ImageConstPtr& r_img_msg,
00070                                   const sensor_msgs::CameraInfoConstPtr& l_info_msg,
00071                                   const sensor_msgs::CameraInfoConstPtr& r_info_msg)
00072 {
00073   // Get the messages
00074   Mat l_img, r_img;
00075   Tools::imgMsgToMat(*l_img_msg, *r_img_msg, l_img, r_img);
00076   double timestamp = odom_msg->header.stamp.toSec();
00077   tf::Transform current_odom = Tools::odomTotf(*odom_msg);
00078 
00079   // Initialization
00080   if (first_iter_)
00081   {
00082     // Set the camera model (only once)
00083     Mat camera_matrix;
00084     image_geometry::StereoCameraModel stereo_camera_model;
00085     Tools::getCameraModel(*l_info_msg, *r_info_msg, stereo_camera_model, camera_matrix);
00086     lc_.setCameraModel(stereo_camera_model, camera_matrix);
00087 
00088     // Save the first node
00089     int cur_id = graph_.addVertice(current_odom, current_odom, timestamp);
00090     string lc_ref = boost::lexical_cast<string>(cur_id);
00091     lc_.setNode(l_img, r_img, lc_ref);
00092 
00093     // Publish and exit
00094     pose_.publish(*odom_msg, current_odom, true);
00095     first_iter_ = false;
00096     return;
00097   }
00098 
00099   // Correct current odometry with the graph information
00100   tf::Transform last_graph_pose, last_graph_odom;
00101   graph_.getLastPoses(current_odom, last_graph_pose, last_graph_odom);
00102   tf::Transform corrected_odom = pose_.correctOdom(current_odom, last_graph_pose, last_graph_odom);
00103 
00104    // Check if difference between poses is larger than minimum displacement
00105   double pose_diff = Tools::poseDiff(last_graph_odom, current_odom);
00106   if (pose_diff <= params_.min_displacement)
00107   {
00108     pose_.publish(*odom_msg, corrected_odom);
00109     return;
00110   }
00111 
00112   // Insert this new node into the graph
00113   int cur_id = graph_.addVertice(current_odom, corrected_odom, timestamp);
00114   string lc_ref = boost::lexical_cast<string>(cur_id);
00115   lc_.setNode(l_img, r_img, lc_ref);
00116   ROS_INFO_STREAM("[StereoSlam:] New node inserted with id " << cur_id << ".");
00117 
00118   // Save the pointcloud for this new node
00119   if (params_.save_clouds && pcl_cloud_.size() > 0)
00120   {
00121     // The file name will be the timestamp
00122     stringstream s;
00123     s << fixed << setprecision(9) << timestamp;
00124     string filename = s.str();
00125     filename.erase(std::remove(filename.begin(), filename.end(), '.'), filename.end());
00126     pcl::io::savePCDFileBinary(params_.clouds_dir + filename + ".pcd", pcl_cloud_);
00127   }
00128 
00129   // Correct the position of this new node by closing the loop with the previous one
00130   tf::Transform node_movement;
00131   string lc_prev = boost::lexical_cast<string>(cur_id - 1);
00132   bool valid_movement = lc_.getLoopClosureById(lc_ref, lc_prev, node_movement);
00133   if (valid_movement)
00134   {
00135     ROS_INFO_STREAM("[StereoSlam:] Updating node pose with the loop closure information.");
00136     tf::Transform new_estimate = last_graph_pose*node_movement.inverse();
00137     graph_.setVerticeEstimate(cur_id, new_estimate);
00138   }
00139 
00140   // Detect loop closures between nodes by distance
00141   bool any_loop_closure = false;
00142   vector<int> neighbors;
00143   graph_.findClosestNodes(20, 3, neighbors);
00144   for (uint i=0; i<neighbors.size(); i++)
00145   {
00146     tf::Transform edge;
00147     string lc_id = boost::lexical_cast<string>(neighbors[i]);
00148     bool valid_lc = lc_.getLoopClosureById(lc_ref, lc_id, edge);
00149     if (valid_lc)
00150     {
00151       ROS_INFO_STREAM("[StereoSlam:] Node with id " << cur_id << " closes loop with " << lc_id);
00152       graph_.addEdge(boost::lexical_cast<int>(lc_id), cur_id, edge);
00153       any_loop_closure = true;
00154       break;
00155     }
00156   }
00157 
00158   // Detect loop closures between nodes using hashes
00159   string lc_id = "";
00160   tf::Transform edge;
00161   bool valid_lc = lc_.getLoopClosure(lc_id, edge);
00162   if (valid_lc)
00163   {
00164     ROS_INFO_STREAM("[StereoSlam:] Node with id " << cur_id << " closes loop with " << lc_id);
00165     graph_.addEdge(boost::lexical_cast<int>(lc_id), cur_id, edge);
00166     any_loop_closure = true;
00167   }
00168 
00169   // Update the graph if any loop closing
00170   if (any_loop_closure) graph_.update();
00171 
00172   // Publish the corrected pose
00173   graph_.getLastPoses(current_odom, last_graph_pose, last_graph_odom);
00174   corrected_odom = pose_.correctOdom(current_odom, last_graph_pose, last_graph_odom);
00175   pose_.publish(*odom_msg, corrected_odom, true);
00176 
00177   // Save graph to file
00178   graph_.saveGraphToFile();
00179 
00180   return;
00181 }
00182 
00183 
00187 void slam::SlamBase::readParameters()
00188 {
00189   Params params;
00190   slam::Pose::Params pose_params;
00191   slam::Graph::Params graph_params;
00192   haloc::LoopClosure::Params lc_params;
00193   lc_params.num_proj = 2;
00194   lc_params.validate = false;
00195   lc_params.verbose = true;
00196 
00197   // Operational directories
00198   string work_dir;
00199   nh_private_.param("work_dir", work_dir, string(""));
00200   if (work_dir[work_dir.length()-1] != '/')
00201     work_dir += "/";
00202   lc_params.work_dir = work_dir;
00203   params.clouds_dir = work_dir + "clouds/";
00204   if (fs::is_directory(params.clouds_dir))
00205     fs::remove_all(params.clouds_dir);
00206   fs::path dir(params.clouds_dir);
00207   if (!fs::create_directory(dir))
00208     ROS_ERROR("[StereoSlam:] ERROR -> Impossible to create the clouds directory.");
00209 
00210   // Topic parameters
00211   string odom_topic, left_topic, right_topic, left_info_topic, right_info_topic, cloud_topic;
00212   nh_private_.param("pose_frame_id",        pose_params.pose_frame_id,        string("/map"));
00213   nh_private_.param("pose_child_frame_id",  pose_params.pose_child_frame_id,  string("/robot"));
00214   nh_private_.param("odom_topic",           odom_topic,                       string("/odometry"));
00215   nh_private_.param("left_topic",           left_topic,                       string("/left/image_rect_color"));
00216   nh_private_.param("right_topic",          right_topic,                      string("/right/image_rect_color"));
00217   nh_private_.param("left_info_topic",      left_info_topic,                  string("/left/camera_info"));
00218   nh_private_.param("right_info_topic",     right_info_topic,                 string("/right/camera_info"));
00219   nh_private_.param("cloud_topic",          cloud_topic,                      string("/points2"));
00220 
00221   // Motion parameters
00222   nh_private_.param("save_clouds",  params.save_clouds, false);
00223   nh_private_.getParam("min_displacement",  params.min_displacement);
00224 
00225   // Loop closure parameters
00226   nh_private_.param("desc_type",            lc_params.desc_type,              string("SIFT"));
00227   nh_private_.getParam("desc_thresh",       lc_params.desc_thresh);
00228   nh_private_.getParam("min_neighbour",     lc_params.min_neighbour);
00229   nh_private_.getParam("n_candidates",      lc_params.n_candidates);
00230   nh_private_.getParam("min_matches",       lc_params.min_matches);
00231   nh_private_.getParam("min_inliers",       lc_params.min_inliers);
00232 
00233   // G2O parameters
00234   nh_private_.getParam("g2o_algorithm",     graph_params.g2o_algorithm);
00235   nh_private_.getParam("g2o_opt_max_iter",  graph_params.go2_opt_max_iter);
00236   graph_params.save_dir = lc_params.work_dir;
00237   graph_params.pose_frame_id = pose_params.pose_frame_id;
00238   graph_params.pose_child_frame_id = pose_params.pose_child_frame_id;
00239 
00240   // Set the class parameters
00241   setParams(params);
00242   pose_.setParams(pose_params);
00243   graph_.setParams(graph_params);
00244   lc_.setParams(lc_params);
00245 
00246   // Topics subscriptions
00247   image_transport::ImageTransport it(nh_);
00248   odom_sub_       .subscribe(nh_, odom_topic,       1);
00249   left_sub_       .subscribe(it,  left_topic,       1);
00250   right_sub_      .subscribe(it,  right_topic,      1);
00251   left_info_sub_  .subscribe(nh_, left_info_topic,  1);
00252   right_info_sub_ .subscribe(nh_, right_info_topic, 1);
00253 
00254   if (params_.save_clouds)
00255     cloud_sub_    .subscribe(nh_, cloud_topic,      1);
00256 }
00257 
00261 void slam::SlamBase::init()
00262 {
00263   first_iter_ = true;
00264 
00265   // Init Haloc
00266   lc_.init();
00267 
00268   // Advertise the pose message
00269   pose_.advertisePoseMsg(nh_private_);
00270 
00271   // Callback synchronization
00272   if (params_.save_clouds)
00273   {
00274     exact_sync_cloud_.reset(new ExactSyncCloud(ExactPolicyCloud(1),
00275                                     odom_sub_,
00276                                     left_sub_,
00277                                     right_sub_,
00278                                     left_info_sub_,
00279                                     right_info_sub_,
00280                                     cloud_sub_) );
00281     exact_sync_cloud_->registerCallback(boost::bind(
00282         &slam::SlamBase::msgsCallback,
00283         this, _1, _2, _3, _4, _5, _6));
00284   }
00285   else
00286   {
00287     exact_sync_no_cloud_.reset(new ExactSyncNoCloud(ExactPolicyNoCloud(1),
00288                                     odom_sub_,
00289                                     left_sub_,
00290                                     right_sub_,
00291                                     left_info_sub_,
00292                                     right_info_sub_) );
00293     exact_sync_no_cloud_->registerCallback(boost::bind(
00294         &slam::SlamBase::msgsCallback,
00295         this, _1, _2, _3, _4, _5));
00296   }
00297 
00298 }


stereo_slam
Author(s): Pep Lluis Negre
autogenerated on Thu Aug 27 2015 15:24:22