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
00016 readParameters();
00017
00018
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
00050 PointCloud::Ptr pcl_cloud(new PointCloud);
00051 pcl::fromROSMsg(*cloud_msg, *pcl_cloud);
00052 pcl::copyPointCloud(*pcl_cloud, pcl_cloud_);
00053
00054
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
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
00080 if (first_iter_)
00081 {
00082
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
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
00094 pose_.publish(*odom_msg, current_odom, true);
00095 first_iter_ = false;
00096 return;
00097 }
00098
00099
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
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
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
00119 if (params_.save_clouds && pcl_cloud_.size() > 0)
00120 {
00121
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
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
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
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
00170 if (any_loop_closure) graph_.update();
00171
00172
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
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
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
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
00222 nh_private_.param("save_clouds", params.save_clouds, false);
00223 nh_private_.getParam("min_displacement", params.min_displacement);
00224
00225
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
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
00241 setParams(params);
00242 pose_.setParams(pose_params);
00243 graph_.setParams(graph_params);
00244 lc_.setParams(lc_params);
00245
00246
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
00266 lc_.init();
00267
00268
00269 pose_.advertisePoseMsg(nh_private_);
00270
00271
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 }