visual_odometry.cpp
Go to the documentation of this file.
00001 
00024 #include "ccny_rgbd/apps/visual_odometry.h"
00025 #include "rgbdtools/rgbdtools.h"
00026 #include "tf_conversions/tf_eigen.h"
00027 namespace ccny_rgbd {
00028 
00029 VisualOdometry::VisualOdometry(
00030         const ros::NodeHandle& nh,
00031         const ros::NodeHandle& nh_private):
00032     nh_(nh),
00033     nh_private_(nh_private),
00034     initialized_(false),
00035     frame_count_(0)
00036 {
00037     ROS_INFO("Starting RGBD Visual Odometry");
00038 
00039     // **** initialize ROS parameters
00040 
00041     initParams();
00042 
00043     // **** inititialize state variables
00044 
00045     f2b_.setIdentity();
00046 
00047     // **** publishers
00048 
00049     odom_publisher_ = nh_.advertise<OdomMsg>(
00050                 "vo", queue_size_);
00051     pose_stamped_publisher_ = nh_.advertise<geometry_msgs::PoseStamped>(
00052                 "pose", queue_size_);
00053     path_pub_ = nh_.advertise<PathMsg>(
00054                 "path", queue_size_);
00055     
00056     feature_cloud_publisher_ = nh_.advertise<PointCloudFeature>(
00057                 "feature/cloud", 1);
00058     feature_cov_publisher_ = nh_.advertise<visualization_msgs::Marker>(
00059                 "feature/covariances", 1);
00060     
00061     model_cloud_publisher_ = nh_.advertise<PointCloudFeature>(
00062                 "model/cloud", 1);
00063     model_cov_publisher_ = nh_.advertise<visualization_msgs::Marker>(
00064                 "model/covariances", 1);
00065     reset_pos_ = nh_.advertiseService(
00066                 "reset_pos_", &VisualOdometry::resetposSrvCallback, this);
00067     // **** subscribers
00068 
00069     ImageTransport rgb_it(nh_);
00070     ImageTransport depth_it(nh_);
00071 
00072     sub_rgb_.subscribe(rgb_it,     "/camera/rgb/image_rect_color",   queue_size_);
00073     sub_depth_.subscribe(depth_it, "/camera/depth_registered/image_rect_raw", queue_size_);
00074     sub_info_.subscribe(nh_,       "/camera/rgb/camera_info",  queue_size_);
00075 
00076     // Synchronize inputs.
00077     sync_.reset(new RGBDSynchronizer3(
00078                     RGBDSyncPolicy3(queue_size_), sub_rgb_, sub_depth_, sub_info_));
00079 
00080     sync_->registerCallback(boost::bind(&VisualOdometry::RGBDCallback, this, _1, _2, _3));
00081 }
00082 
00083 VisualOdometry::~VisualOdometry()
00084 {
00085     fclose(diagnostics_file_);
00086     ROS_INFO("Destroying RGBD Visual Odometry");
00087 }
00088 
00089 bool VisualOdometry::resetposSrvCallback(
00090         Save::Request& request,
00091         Save::Response& response)
00092 {
00093 
00094     const std::string& new_tr = request.filename;
00095 //    f2b_
00096     double m00,m01,m02,m03,m10,m11,m12,m13,m20,m21,m22,m23,m30,m31,m32,m33;
00097     int res;
00098 
00099     res = sscanf(new_tr.c_str(), "%lg %lg %lg %lg\n%lg %lg %lg %lg\n%lg %lg %lg %lg\n%lg %lg %lg %lg\n)",
00100                      &m00,&m01,&m02,&m03,&m10,&m11,&m12,&m13,&m20,&m21,&m22,&m23,&m30,&m31,&m32,&m33);
00101     if (res != 16)
00102     {
00103          res = sscanf(new_tr.c_str(), "%lg %lg %lg %lg %lg %lg %lg %lg %lg %lg %lg %lg %lg %lg %lg %lg)",
00104                          &m00,&m01,&m02,&m03,&m10,&m11,&m12,&m13,&m20,&m21,&m22,&m23,&m30,&m31,&m32,&m33);
00105          if (res != 16)
00106          {
00107              return false;
00108          }
00109     }
00110 
00111 //    Eigen::Affine3d pose;
00112 //    pose(0,0) = m00;
00113 //    pose(0,1) = m01;
00114 //    pose(0,2) = m02;
00115 //    pose(0,3) = m03;
00116 //    pose(1,0) = m10;
00117 //    pose(1,1) = m11;
00118 //    pose(1,2) = m12;
00119 //    pose(1,3) = m13;
00120 //    pose(2,0) = m20;
00121 //    pose(2,1) = m21;
00122 //    pose(2,2) = m22;
00123 //    pose(2,3) = m23;
00124 //    pose(3,0) = m30;
00125 //    pose(3,1) = m31;
00126 //    pose(3,2) = m32;
00127 //    pose(3,3) = m33;
00128     double yaw,pitch,roll;
00129     tf::Matrix3x3 rot(m00,m01,m02,m10,m11,m12,m20,m21,m22);
00130     tf::Matrix3x3 correction(0,0,1,-1,0,0,0,-1,0); //convert from camera pose to world
00131     rot = rot*correction.inverse();
00132     rot.getEulerYPR(yaw,pitch,roll);
00133 
00134     Eigen::Affine3d pose;
00135     pose.setIdentity();
00136     pose =  Eigen::Translation<double,3>(m03,m13,m23)*
00137             Eigen::AngleAxis<double>(yaw,Eigen::Vector3d::UnitZ()) *
00138             Eigen::AngleAxis<double>(pitch,Eigen::Vector3d::UnitY()) *
00139             Eigen::AngleAxis<double>(roll,Eigen::Vector3d::UnitX());
00140 //    pose = pose *   Eigen::AngleAxis<double>( M_PI,Eigen::Vector3d::UnitZ());
00141     tf::TransformEigenToTF(pose,f2b_);
00142     motion_estimation_.resetModel();
00143 //    tf::Vector3 ori(m03,m13,m23);
00144 //    f2b_.setBasis(rot);
00145 //    f2b_.setOrigin(ori);
00146     return true;
00147 }
00148 
00149 void VisualOdometry::initParams()
00150 {
00151     if (!nh_private_.getParam ("publish_tf", publish_tf_))
00152         publish_tf_ = true;
00153     if (!nh_private_.getParam ("publish_path", publish_path_))
00154         publish_path_ = true;
00155     if (!nh_private_.getParam ("publish_odom", publish_odom_))
00156         publish_odom_ = true;
00157     if (!nh_private_.getParam ("publish_pose", publish_pose_))
00158         publish_pose_ = true;
00159     if (!nh_private_.getParam ("fixed_frame", fixed_frame_))
00160         fixed_frame_ = "/odom";
00161     if (!nh_private_.getParam ("base_frame", base_frame_))
00162         base_frame_ = "/camera_link";
00163     if (!nh_private_.getParam ("queue_size", queue_size_))
00164         queue_size_ = 5;
00165 
00166     // detector params
00167 
00168     if (!nh_private_.getParam ("feature/publish_feature_cloud", publish_feature_cloud_))
00169         publish_feature_cloud_ = false;
00170     if (!nh_private_.getParam ("feature/publish_feature_covariances", publish_feature_cov_))
00171         publish_feature_cov_ = false;
00172     if (!nh_private_.getParam ("feature/detector_type", detector_type_))
00173         detector_type_ = "GFT";
00174 
00175     resetDetector();
00176 
00177     int smooth;
00178     double max_range, max_stdev;
00179 
00180     if (!nh_private_.getParam ("feature/smooth", smooth))
00181         smooth = 0;
00182     if (!nh_private_.getParam ("feature/max_range", max_range))
00183         max_range = 25.5;
00184     if (!nh_private_.getParam ("feature/max_stdev", max_stdev))
00185         max_stdev = 20.03;
00186 
00187     feature_detector_->setSmooth(smooth);
00188     feature_detector_->setMaxRange(max_range);
00189     feature_detector_->setMaxStDev(max_stdev);
00190 
00191     // registration params
00192 
00193     configureMotionEstimation();
00194 
00195     // diagnostic params
00196 
00197     if (!nh_private_.getParam("verbose", verbose_))
00198         verbose_ = true;
00199     if (!nh_private_.getParam("save_diagnostics", save_diagnostics_))
00200         save_diagnostics_ = false;
00201     if (!nh_private_.getParam("diagnostics_file_name", diagnostics_file_name_))
00202         diagnostics_file_name_ = "diagnostics.csv";
00203 
00204     if(save_diagnostics_)
00205     {
00206         diagnostics_file_ = fopen(diagnostics_file_name_.c_str(), "w");
00207 
00208         if (diagnostics_file_ == NULL)
00209         {
00210             ROS_ERROR("Can't create diagnostic file %s\n", diagnostics_file_name_.c_str());
00211             return;
00212         }
00213 
00214         // print header
00215         fprintf(diagnostics_file_, "%s, %s, %s, %s, %s, %s, %s, %s\n",
00216                 "Frame id",
00217                 "Frame dur.",
00218                 "All features", "Valid features",
00219                 "Feat extr. dur.",
00220                 "Model points", "Registration dur.",
00221                 "Total dur.");
00222     }
00223 }
00224 
00225 void VisualOdometry::configureMotionEstimation()
00226 {
00227     int motion_constraint;
00228 
00229     if (!nh_private_.getParam ("reg/motion_constraint", motion_constraint))
00230         motion_constraint = 0;
00231 
00232     motion_estimation_.setMotionConstraint(motion_constraint);
00233 
00234     double tf_epsilon_linear;
00235     double tf_epsilon_angular;
00236     int max_iterations;
00237     int min_correspondences;
00238     int max_model_size;
00239     double max_corresp_dist_eucl;
00240     double max_assoc_dist_mah;
00241     int n_nearest_neighbors;
00242 
00243     if (!nh_private_.getParam ("reg/ICPProbModel/tf_epsilon_linear", tf_epsilon_linear))
00244         tf_epsilon_linear = 1e-4; // 1 mm
00245     if (!nh_private_.getParam ("reg/ICPProbModel/tf_epsilon_angular", tf_epsilon_angular))
00246         tf_epsilon_angular = 1.7e-3; // 1 deg
00247     if (!nh_private_.getParam ("reg/ICPProbModel/max_iterations", max_iterations))
00248         max_iterations = 10;
00249     if (!nh_private_.getParam ("reg/ICPProbModel/min_correspondences", min_correspondences))
00250         min_correspondences = 15;
00251     if (!nh_private_.getParam ("reg/ICPProbModel/max_model_size", max_model_size))
00252         max_model_size = 3000;
00253     if (!nh_private_.getParam ("reg/ICPProbModel/max_corresp_dist_eucl", max_corresp_dist_eucl))
00254         max_corresp_dist_eucl = 0.15;
00255     if (!nh_private_.getParam ("reg/ICPProbModel/max_assoc_dist_mah", max_assoc_dist_mah))
00256         max_assoc_dist_mah = 10.0;
00257     if (!nh_private_.getParam ("reg/ICPProbModel/n_nearest_neighbors", n_nearest_neighbors))
00258         n_nearest_neighbors = 4;
00259     
00260     if (!nh_private_.getParam ("reg/ICPProbModel/publish_model_cloud", publish_model_cloud_))
00261         publish_model_cloud_ = false;
00262     if (!nh_private_.getParam ("reg/ICPProbModel/publish_model_covariances", publish_model_cov_))
00263         publish_model_cov_ = false;
00264     
00265     motion_estimation_.setTfEpsilonLinear(tf_epsilon_linear);
00266     motion_estimation_.setTfEpsilonAngular(tf_epsilon_angular);
00267     motion_estimation_.setMaxIterations(max_iterations);
00268     motion_estimation_.setMinCorrespondences(min_correspondences);
00269     motion_estimation_.setMaxModelSize(max_model_size);
00270     motion_estimation_.setMaxCorrespondenceDistEuclidean(max_corresp_dist_eucl);
00271     motion_estimation_.setMaxAssociationDistMahalanobis(max_assoc_dist_mah);
00272     motion_estimation_.setNNearestNeighbors(n_nearest_neighbors);
00273 }
00274 
00275 void VisualOdometry::resetDetector()
00276 {  
00277     gft_config_server_.reset();
00278     star_config_server_.reset();
00279     orb_config_server_.reset();
00280     surf_config_server_.reset();
00281 
00282     if (detector_type_ == "ORB")
00283     {
00284         ROS_INFO("Creating ORB detector");
00285         feature_detector_.reset(new rgbdtools::OrbDetector());
00286         orb_config_server_.reset(new
00287                                  OrbDetectorConfigServer(ros::NodeHandle(nh_private_, "feature/ORB")));
00288 
00289         // dynamic reconfigure
00290         OrbDetectorConfigServer::CallbackType f = boost::bind(
00291                     &VisualOdometry::orbReconfigCallback, this, _1, _2);
00292         orb_config_server_->setCallback(f);
00293     }
00294     else if (detector_type_ == "SURF")
00295     {
00296         ROS_INFO("Creating SURF detector");
00297         feature_detector_.reset(new rgbdtools::SurfDetector());
00298         surf_config_server_.reset(new
00299                                   SurfDetectorConfigServer(ros::NodeHandle(nh_private_, "feature/SURF")));
00300 
00301         // dynamic reconfigure
00302         SurfDetectorConfigServer::CallbackType f = boost::bind(
00303                     &VisualOdometry::surfReconfigCallback, this, _1, _2);
00304         surf_config_server_->setCallback(f);
00305     }
00306     else if (detector_type_ == "GFT")
00307     {
00308         ROS_INFO("Creating GFT detector");
00309         feature_detector_.reset(new rgbdtools::GftDetector());
00310         gft_config_server_.reset(new
00311                                  GftDetectorConfigServer(ros::NodeHandle(nh_private_, "feature/GFT")));
00312 
00313         // dynamic reconfigure
00314         GftDetectorConfigServer::CallbackType f = boost::bind(
00315                     &VisualOdometry::gftReconfigCallback, this, _1, _2);
00316         gft_config_server_->setCallback(f);
00317     }
00318     else if (detector_type_ == "STAR")
00319     {
00320         ROS_INFO("Creating STAR detector");
00321         feature_detector_.reset(new rgbdtools::StarDetector());
00322         star_config_server_.reset(new
00323                                   StarDetectorConfigServer(ros::NodeHandle(nh_private_, "feature/STAR")));
00324 
00325         // dynamic reconfigure
00326         StarDetectorConfigServer::CallbackType f = boost::bind(
00327                     &VisualOdometry::starReconfigCallback, this, _1, _2);
00328         star_config_server_->setCallback(f);
00329     }
00330     else
00331     {
00332         ROS_FATAL("%s is not a valid detector type! Using GFT", detector_type_.c_str());
00333         feature_detector_.reset(new rgbdtools::GftDetector());
00334         gft_config_server_.reset(new
00335                                  GftDetectorConfigServer(ros::NodeHandle(nh_private_, "feature/GFT")));
00336 
00337         // dynamic reconfigure
00338         GftDetectorConfigServer::CallbackType f = boost::bind(
00339                     &VisualOdometry::gftReconfigCallback, this, _1, _2);
00340         gft_config_server_->setCallback(f);
00341     }
00342 }
00343 
00344 void VisualOdometry::RGBDCallback(
00345         const ImageMsg::ConstPtr& rgb_msg,
00346         const ImageMsg::ConstPtr& depth_msg,
00347         const CameraInfoMsg::ConstPtr& info_msg)
00348 {
00349     ros::WallTime start = ros::WallTime::now();
00350 
00351     // **** initialize ***************************************************
00352 
00353     if (!initialized_)
00354     {
00355         initialized_ = getBaseToCameraTf(rgb_msg->header);
00356         init_time_ = rgb_msg->header.stamp;
00357         if (!initialized_) return;
00358 
00359         motion_estimation_.setBaseToCameraTf(eigenAffineFromTf(b2c_));
00360     }
00361 
00362     // **** create frame *************************************************
00363 
00364     ros::WallTime start_frame = ros::WallTime::now();
00365     rgbdtools::RGBDFrame frame;
00366     createRGBDFrameFromROSMessages(rgb_msg, depth_msg, info_msg, frame);
00367     ros::WallTime end_frame = ros::WallTime::now();
00368 
00369     // **** find features ************************************************
00370 
00371     ros::WallTime start_features = ros::WallTime::now();
00372     feature_detector_->findFeatures(frame);
00373     ros::WallTime end_features = ros::WallTime::now();
00374 
00375     // **** registration *************************************************
00376 
00377     ros::WallTime start_reg = ros::WallTime::now();
00378     AffineTransform m = motion_estimation_.getMotionEstimation(frame);
00379     tf::Transform motion = tfFromEigenAffine(m);
00380     f2b_ = motion * f2b_;
00381     ros::WallTime end_reg = ros::WallTime::now();
00382 
00383     // **** publish outputs **********************************************
00384 
00385     if (publish_tf_)    publishTf(rgb_msg->header);
00386     if (publish_odom_)  publishOdom(rgb_msg->header);
00387     if (publish_path_)  publishPath(rgb_msg->header);
00388     if (publish_pose_)  publishPoseStamped(rgb_msg->header);
00389 
00390     if (publish_feature_cloud_) publishFeatureCloud(frame);
00391     if (publish_feature_cov_) publishFeatureCovariances(frame);
00392 
00393     if (publish_model_cloud_) publishModelCloud();
00394     if (publish_model_cov_)   publishModelCovariances();
00395 
00396     // **** print diagnostics *******************************************
00397 
00398     ros::WallTime end = ros::WallTime::now();
00399 
00400     frame_count_++;
00401 
00402     int n_features = frame.keypoints.size();
00403     int n_valid_features = frame.n_valid_keypoints;
00404     int n_model_pts = motion_estimation_.getModelSize();
00405 
00406     double d_frame    = 1000.0 * (end_frame    - start_frame   ).toSec();
00407     double d_features = 1000.0 * (end_features - start_features).toSec();
00408     double d_reg      = 1000.0 * (end_reg      - start_reg     ).toSec();
00409     double d_total    = 1000.0 * (end          - start         ).toSec();
00410 
00411     diagnostics(n_features, n_valid_features, n_model_pts,
00412                 d_frame, d_features, d_reg, d_total);
00413 }
00414 
00415 void VisualOdometry::publishTf(const std_msgs::Header& header)
00416 {
00417     tf::StampedTransform transform_msg(
00418                 f2b_, header.stamp, fixed_frame_, base_frame_);
00419     tf_broadcaster_.sendTransform (transform_msg);
00420 }
00421 
00422 void VisualOdometry::publishOdom(const std_msgs::Header& header)
00423 {
00424     OdomMsg odom;
00425     odom.header.stamp = header.stamp;
00426     odom.header.frame_id = fixed_frame_;
00427     tf::poseTFToMsg(f2b_, odom.pose.pose);
00428     odom_publisher_.publish(odom);
00429 }
00430 
00431 void VisualOdometry::publishPoseStamped(const std_msgs::Header& header)
00432 {
00433     geometry_msgs::PoseStamped::Ptr pose_stamped_msg;
00434     pose_stamped_msg = boost::make_shared<geometry_msgs::PoseStamped>();
00435     pose_stamped_msg->header.stamp    = header.stamp;
00436     pose_stamped_msg->header.frame_id = fixed_frame_;
00437     tf::poseTFToMsg(f2b_, pose_stamped_msg->pose);
00438     pose_stamped_publisher_.publish(pose_stamped_msg);
00439 }
00440 
00441 
00442 void VisualOdometry::publishPath(const std_msgs::Header& header)
00443 {
00444     path_msg_.header.stamp = header.stamp;
00445     path_msg_.header.frame_id = fixed_frame_;
00446 
00447     geometry_msgs::PoseStamped pose_stamped;
00448     pose_stamped.header.stamp = header.stamp;
00449     pose_stamped.header.frame_id = fixed_frame_;
00450     tf::poseTFToMsg(f2b_, pose_stamped.pose);
00451 
00452     path_msg_.poses.push_back(pose_stamped);
00453     path_pub_.publish(path_msg_);
00454 }
00455 
00456 bool VisualOdometry::getBaseToCameraTf(const std_msgs::Header& header)
00457 {
00458     tf::StampedTransform tf_m;
00459 
00460     try
00461     {
00462         tf_listener_.waitForTransform(
00463                     base_frame_, header.frame_id, header.stamp, ros::Duration(1.0));
00464         tf_listener_.lookupTransform (
00465                     base_frame_, header.frame_id, header.stamp, tf_m);
00466     }
00467     catch (tf::TransformException& ex)
00468     {
00469         ROS_WARN("Base to camera transform unavailable %s", ex.what());
00470         return false;
00471     }
00472 
00473     b2c_ = tf_m;
00474 
00475     return true;
00476 }
00477 
00478 void VisualOdometry::gftReconfigCallback(GftDetectorConfig& config, uint32_t level)
00479 {
00480     rgbdtools::GftDetectorPtr gft_detector =
00481             boost::static_pointer_cast<rgbdtools::GftDetector>(feature_detector_);
00482     
00483     gft_detector->setNFeatures(config.n_features);
00484     gft_detector->setMinDistance(config.min_distance);
00485 }
00486 
00487 void VisualOdometry::starReconfigCallback(StarDetectorConfig& config, uint32_t level)
00488 {
00489     rgbdtools::StarDetectorPtr star_detector =
00490             boost::static_pointer_cast<rgbdtools::StarDetector>(feature_detector_);
00491     
00492     star_detector->setThreshold(config.threshold);
00493     star_detector->setMinDistance(config.min_distance);
00494 }
00495 
00496 void VisualOdometry::surfReconfigCallback(SurfDetectorConfig& config, uint32_t level)
00497 {
00498     rgbdtools::SurfDetectorPtr surf_detector =
00499             boost::static_pointer_cast<rgbdtools::SurfDetector>(feature_detector_);
00500     
00501     surf_detector->setThreshold(config.threshold);
00502 }
00503 
00504 void VisualOdometry::orbReconfigCallback(OrbDetectorConfig& config, uint32_t level)
00505 {
00506     rgbdtools::OrbDetectorPtr orb_detector =
00507             boost::static_pointer_cast<rgbdtools::OrbDetector>(feature_detector_);
00508     
00509     orb_detector->setThreshold(config.threshold);
00510     orb_detector->setNFeatures(config.n_features);
00511 }
00512 
00513 void VisualOdometry::diagnostics(
00514         int n_features, int n_valid_features, int n_model_pts,
00515         double d_frame, double d_features, double d_reg, double d_total)
00516 {
00517     if(save_diagnostics_ && diagnostics_file_ != NULL)
00518     {
00519         // print to file
00520         fprintf(diagnostics_file_, "%d, %2.1f, %d, %3.1f, %d, %4.1f, %4.1f\n",
00521                 frame_count_,
00522                 d_frame,
00523                 n_valid_features, d_features,
00524                 n_model_pts, d_reg,
00525                 d_total);
00526     }
00527     if (verbose_)
00528     {
00529         // print to screen
00530         ROS_INFO("[VO %d] %s[%d]: %.1f Reg[%d]: %.1f TOT: %.1f\n",
00531                  frame_count_,
00532                  detector_type_.c_str(), n_valid_features, d_features,
00533                  n_model_pts, d_reg,
00534                  d_total);
00535     }
00536 
00537     return;
00538 }
00539 
00540 void VisualOdometry::publishFeatureCloud(rgbdtools::RGBDFrame& frame)
00541 {
00542     PointCloudFeature feature_cloud;
00543     frame.constructFeaturePointCloud(feature_cloud);
00544     feature_cloud_publisher_.publish(feature_cloud);
00545 }
00546 
00547 void VisualOdometry::publishFeatureCovariances(rgbdtools::RGBDFrame& frame)
00548 {
00550 }
00551 
00552 void VisualOdometry::publishModelCloud()
00553 {
00554     PointCloudFeature::Ptr model_cloud_ptr = motion_estimation_.getModel();
00555     model_cloud_ptr->header.frame_id = fixed_frame_;
00556     model_cloud_publisher_.publish(model_cloud_ptr);
00557 }
00558 
00559 void VisualOdometry::publishModelCovariances()
00560 {
00562 }
00563 
00564 } // namespace ccny_rgbd
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Friends


ccny_rgbd
Author(s): Ivan Dryanovski
autogenerated on Tue Aug 27 2013 10:34:02