motion_estimation_icp_prob_model.cpp
Go to the documentation of this file.
00001 
00024 #include "rgbdtools/registration/motion_estimation_icp_prob_model.h"
00025 
00026 namespace rgbdtools {
00027 
00028 MotionEstimationICPProbModel::MotionEstimationICPProbModel():
00029   MotionEstimation(),
00030   model_idx_(0),
00031   model_size_(0)
00032 {
00033   // parameters
00034   tf_epsilon_linear_ = 1e-4; // 1 mm
00035   tf_epsilon_angular_ = 1.7e-3; // 1 deg
00036   max_iterations_ = 10;
00037   min_correspondences_ = 15;
00038   max_model_size_ = 3000;
00039   max_corresp_dist_eucl_ = 0.15;
00040   max_assoc_dist_mah_ = 10.0;
00041   n_nearest_neighbors_ = 4;
00042 
00043   // derived params
00044   max_corresp_dist_eucl_sq_ = max_corresp_dist_eucl_ * max_corresp_dist_eucl_;
00045   max_assoc_dist_mah_sq_ = max_assoc_dist_mah_ * max_assoc_dist_mah_;
00046   
00047   // state variables
00048   model_ptr_.reset(new PointCloudFeature());
00049 
00050   f2b_.setIdentity(); 
00051   I_.setIdentity();
00052 }
00053 
00054 MotionEstimationICPProbModel::~MotionEstimationICPProbModel()
00055 {
00056 
00057 }
00058 
00059 void MotionEstimationICPProbModel::addToModel(
00060   const Vector3f& data_mean,
00061   const Matrix3f& data_cov)
00062 {
00063   // **** create a PCL point
00064 
00065   PointFeature data_point;
00066   data_point.x = data_mean(0,0);
00067   data_point.y = data_mean(1,0);
00068   data_point.z = data_mean(2,0);
00069 
00070   if (model_size_ < max_model_size_)
00071   { 
00072     covariances_.push_back(data_cov);
00073     means_.push_back(data_mean);
00074     model_ptr_->push_back(data_point);
00075     
00076     model_size_++;
00077   }
00078   else // model_size_ == max_model_size_
00079   {   
00080     if (model_idx_ == max_model_size_)
00081       model_idx_ = 0;
00082 
00083     covariances_.at(model_idx_) = data_cov;
00084     means_.at(model_idx_) = data_mean;
00085     model_ptr_->at(model_idx_) = data_point;
00086   }
00087 
00088   model_idx_++;
00089 }
00090 void MotionEstimationICPProbModel::resetModel()
00091 {
00092     model_size_ = 0;
00093 }
00094 
00095 bool MotionEstimationICPProbModel::getMotionEstimationImpl(
00096   RGBDFrame& frame,
00097   const AffineTransform& prediction,
00098   AffineTransform& motion)
00099 {
00101 
00102   bool result;
00103   Vector3fVector data_means;
00104   Matrix3fVector data_covariances;
00105 
00106   // remove nans from distributinos
00107   removeInvalidDistributions(
00108     frame.kp_means, frame.kp_covariances, frame.kp_valid,
00109     data_means, data_covariances);
00110    
00111   // transform distributions to world frame
00112   transformDistributions(data_means, data_covariances, prediction * f2b_* b2c_);
00113        
00114   // **** perform registration
00115 
00116   if (model_size_ == 0)
00117   {
00118     std::cout << "No points in model: initializing from features." << std::endl;
00119     motion.setIdentity();
00120     initializeModelFromData(data_means, data_covariances);
00121     result = true;
00122   }
00123   else
00124   {
00125     AffineTransform motion_icp;
00126     
00127     // align using icp 
00128     result = alignICPEuclidean(data_means, motion_icp);
00129     
00130     if (!result) 
00131     {
00132       motion_icp.setIdentity();
00133       std::cerr << "ICP alignment failed. Using identity transform." << std::endl;
00134     }
00135     
00136     motion = motion_icp * prediction;
00137     
00138     constrainMotion(motion);
00139     f2b_ = motion * f2b_;
00140     
00141     // transform distributions to world frame
00142     transformDistributions(data_means, data_covariances, motion_icp);
00143 
00144     // update model: inserts new features and updates old ones with KF
00145     updateModelFromData(data_means, data_covariances);
00146   }
00147 
00148   // update the model tree
00149   model_tree_.setInputCloud(model_ptr_);
00150 
00151   // update model metadata
00152   model_ptr_->width = model_ptr_->points.size();
00153   model_ptr_->header.stamp.sec  = frame.header.stamp.sec;
00154   model_ptr_->header.stamp.nsec = frame.header.stamp.nsec;
00155   
00156   return result;
00157 }
00158 
00159 bool MotionEstimationICPProbModel::alignICPEuclidean(
00160   const Vector3fVector& data_means,
00161   AffineTransform& correction)
00162 {
00163   TransformationEstimationSVD svd;
00164 
00165   // create a point cloud from the means
00166   PointCloudFeature data_cloud;
00167   pointCloudFromMeans(data_means, data_cloud);
00168 
00169   // initialize the result transform
00170   AffineTransform final_transformation; 
00171   final_transformation.setIdentity();
00172   
00173   for (int iteration = 0; iteration < max_iterations_; ++iteration)
00174   {    
00175     // get corespondences
00176     IntVector data_indices, model_indices;
00177     getCorrespEuclidean(data_cloud, data_indices, model_indices);
00178    
00179     if ((int)data_indices.size() <  min_correspondences_)
00180     {
00181       std::cerr << "[ICP] Not enough correspondences ("
00182                 << (int)data_indices.size() << " of "
00183                 << min_correspondences_  << " minimum). Leaving ICP loop"
00184                 << std::endl;
00185       return false;
00186     }
00187 
00188     // estimae transformation
00189     Eigen::Matrix4f transform_eigen; 
00190     svd.estimateRigidTransformation (data_cloud, data_indices,
00191                                      *model_ptr_, model_indices,
00192                                      transform_eigen);
00193                                      
00194                                      
00195     AffineTransform transformation(transform_eigen);                     
00196                          
00197     // rotate   
00198     pcl::transformPointCloud(data_cloud, data_cloud, transformation);
00199     
00200     // accumulate incremental tf
00201     final_transformation = transformation * final_transformation;
00202 
00203     // check for convergence
00204     double linear, angular;
00205     getTfDifference(transformation, linear, angular);
00206     if (linear  < tf_epsilon_linear_ &&
00207         angular < tf_epsilon_angular_)
00208     {
00209       //printf("(%f %f) conv. at [%d] leaving loop\n", 
00210       //  linear*1000.0, angular*10.0*180.0/3.14, iteration);
00211       break; 
00212     }
00213   }
00214   
00215   correction = final_transformation;
00216   return true;
00217 }
00218 
00219 void MotionEstimationICPProbModel::getCorrespEuclidean(
00220   const PointCloudFeature& data_cloud,
00221   IntVector& data_indices,
00222   IntVector& model_indices)
00223 {
00224   for (unsigned int data_idx = 0; data_idx < data_cloud.size(); ++data_idx)
00225   {
00226     const PointFeature& data_point = data_cloud.points[data_idx];
00227     
00228     int eucl_nn_idx;
00229     double eucl_dist_sq;
00230     
00231     bool nn_result = getNNEuclidean(data_point, eucl_nn_idx, eucl_dist_sq);
00232     
00233     if (nn_result && eucl_dist_sq < max_corresp_dist_eucl_sq_)
00234     {
00235       data_indices.push_back(data_idx);
00236       model_indices.push_back(eucl_nn_idx);
00237     }
00238   }  
00239 }
00240 
00241 bool MotionEstimationICPProbModel::getNNEuclidean(
00242   const PointFeature& data_point,
00243   int& eucl_nn_idx, double& eucl_dist_sq)
00244 {
00245   // find n Euclidean nearest neighbors
00246   IntVector indices;
00247   FloatVector dist_sq;
00248   
00249   indices.resize(1);
00250   dist_sq.resize(1);
00251   
00252   int n_retrieved = model_tree_.nearestKSearch(data_point, 1, indices, dist_sq);
00253   
00254   if (n_retrieved != 0)
00255   {
00256     eucl_nn_idx = indices[0];
00257     eucl_dist_sq = dist_sq[0];
00258     return true;
00259   }
00260   else return false;
00261 }
00262 
00263 bool MotionEstimationICPProbModel::getNNMahalanobis(
00264   const Vector3f& data_mean, const Matrix3f& data_cov,
00265   int& mah_nn_idx, double& mah_dist_sq,
00266   IntVector& indices, FloatVector& dists_sq)
00267 {
00268   PointFeature p_data;
00269   p_data.x = data_mean(0,0);
00270   p_data.y = data_mean(1,0);
00271   p_data.z = data_mean(2,0);
00272 
00273   int n_retrieved = model_tree_.nearestKSearch(p_data, n_nearest_neighbors_, indices, dists_sq);
00274 
00275   // iterate over Euclidean NNs to find Mah. NN
00276   double best_mah_dist_sq = 0;
00277   int best_mah_nn_idx = -1;
00278   //int best_i = 0; // optionally print this to check how far in we found the best one
00279   for (int i = 0; i < n_retrieved; i++)
00280   {
00281     int nn_idx = indices[i];
00282    
00283     const Vector3f& model_mean = means_[nn_idx];
00284     const Matrix3f& model_cov  = covariances_[nn_idx];
00285 
00286     Vector3f diff_mat = model_mean - data_mean;
00287     Matrix3f sum_cov = model_cov + data_cov;
00288     Matrix3f sum_cov_inv = sum_cov.inverse();
00289 
00290     Eigen::Matrix<float,1,1> mah_mat = diff_mat.transpose() * sum_cov_inv * diff_mat;
00291 
00292     double mah_dist_sq = mah_mat(0,0);
00293   
00294     if (best_mah_nn_idx == -1 || mah_dist_sq < best_mah_dist_sq)
00295     {
00296       best_mah_dist_sq = mah_dist_sq;
00297       best_mah_nn_idx  = nn_idx;
00298       //best_i = i;
00299     }
00300   }
00301 
00302   if (best_mah_nn_idx != -1)
00303   {  
00304     //if (best_i != 0) printf("BEST NEIGHBOR WAS #%d\n", best_i);
00305     mah_dist_sq = best_mah_dist_sq;
00306     mah_nn_idx  = best_mah_nn_idx;
00307     return true;
00308   }
00309   else return false;
00310 }
00311   
00312 void MotionEstimationICPProbModel::initializeModelFromData(
00313   const Vector3fVector& data_means,
00314   const Matrix3fVector& data_covariances)
00315 {
00316   for (unsigned int idx = 0; idx < data_means.size(); ++idx)
00317   {
00318     const Vector3f& mean = data_means[idx];
00319     const Matrix3f& cov  = data_covariances[idx];     
00320     addToModel(mean, cov);
00321   }
00322 }
00323 
00324 void MotionEstimationICPProbModel::updateModelFromData(
00325   const Vector3fVector& data_means,
00326   const Matrix3fVector& data_covariances)
00327 {
00328   // pre-allocate search vectors
00329   IntVector indices;
00330   FloatVector dists_sq;
00331   indices.resize(n_nearest_neighbors_);
00332   dists_sq.resize(n_nearest_neighbors_);
00333 
00334   for (unsigned int idx = 0; idx < data_means.size(); ++idx)
00335   {
00336     const Vector3f& data_mean = data_means[idx];
00337     const Matrix3f& data_cov  = data_covariances[idx];
00338     
00339     // find nearest neighbor in model 
00340     double mah_dist_sq;
00341     int mah_nn_idx;   
00342     bool nn_result = getNNMahalanobis(
00343       data_mean, data_cov, mah_nn_idx, mah_dist_sq, indices, dists_sq);
00344   
00345     if (nn_result && mah_dist_sq < max_assoc_dist_mah_sq_)
00346     {
00347       // **** KF update *********************************
00348 
00349       // predicted state
00350       const Vector3f& model_mean_pred = means_[mah_nn_idx];
00351       const Matrix3f& model_cov_pred  = covariances_[mah_nn_idx];
00352       
00353       // calculate measurement and cov residual
00354       Vector3f y = data_mean - model_mean_pred;
00355       Matrix3f S = data_cov + model_cov_pred;
00356 
00357       // calculate Kalman gain
00358       Matrix3f K = model_cov_pred * S.inverse();
00359       
00360       // updated state estimate (mean and cov)
00361       Vector3f model_mean_upd = model_mean_pred + K * y;
00362       Matrix3f model_cov_upd  = (I_ - K) * model_cov_pred;
00363       
00364       // update in model
00365       means_[mah_nn_idx] = model_mean_upd;
00366       covariances_[mah_nn_idx] = model_cov_upd;
00367 
00368       PointFeature updated_point;
00369       updated_point.x = model_mean_upd(0,0);
00370       updated_point.y = model_mean_upd(1,0);
00371       updated_point.z = model_mean_upd(2,0);
00372 
00373       model_ptr_->points[mah_nn_idx] = updated_point;
00374     }
00375     else
00376     {
00377       addToModel(data_mean, data_cov);
00378     }
00379   }
00380 }
00381 
00382 bool MotionEstimationICPProbModel::saveModel(const std::string& filename)
00383 {
00385 /*
00386   // save as OpenCV yml matrix
00387   std::string filename_yml = filename + ".yml";
00388 
00389   cv::FileStorage fs(filename_yml, cv::FileStorage::WRITE);
00390   fs << "means"       << means_;
00391   fs << "covariances" << covariances_; 
00392   fs << "model_idx"   << model_idx_;  
00393   fs << "model_size"  << model_size_;    
00394 */   
00395 
00396   // save as pcd
00397   std::string filename_pcd = filename + ".pcd";
00398   pcl::PCDWriter writer;
00399   int result_pcd = writer.writeBinary<PointFeature>(filename_pcd, *model_ptr_);
00400 
00401   return (result_pcd == 0); 
00402 }
00403 
00404 void MotionEstimationICPProbModel::setMaxIterations(int max_iterations)
00405 {
00406   max_iterations_ = max_iterations;
00407 }
00408 
00409 void MotionEstimationICPProbModel::setMinCorrespondences(int min_correspondences)
00410 {
00411   min_correspondences_ = min_correspondences;
00412 }
00413 
00414 void MotionEstimationICPProbModel::setNNearestNeighbors(int n_nearest_neighbors)
00415 {
00416   n_nearest_neighbors_ = n_nearest_neighbors;
00417 }
00418 
00419 void MotionEstimationICPProbModel::setMaxModelSize(int max_model_size)
00420 {
00421   max_model_size_ = max_model_size;
00422 }
00423 
00424 void MotionEstimationICPProbModel::setTfEpsilonLinear(double tf_epsilon_linear)
00425 {
00426   tf_epsilon_linear_ = tf_epsilon_linear;
00427 }
00428 
00429 void MotionEstimationICPProbModel::setTfEpsilonAngular(double tf_epsilon_angular)
00430 {
00431   tf_epsilon_angular_ = tf_epsilon_angular;
00432 }
00433     
00434 void MotionEstimationICPProbModel::setMaxAssociationDistMahalanobis(double max_assoc_dist_mah)
00435 {
00436   max_assoc_dist_mah_ = max_assoc_dist_mah;
00437   
00438   max_assoc_dist_mah_sq_ =  max_assoc_dist_mah_ * max_assoc_dist_mah_;
00439 }
00440 
00441 void MotionEstimationICPProbModel::setMaxCorrespondenceDistEuclidean(double max_corresp_dist_eucl)
00442 {
00443   max_corresp_dist_eucl_ = max_corresp_dist_eucl;
00444   
00445   max_corresp_dist_eucl_sq_ = max_corresp_dist_eucl_ * max_corresp_dist_eucl_;
00446 }
00447 
00448 } // namespace rgbdtools
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends


lib_rgbdtools
Author(s): Ivan Dryanovski
autogenerated on Tue Aug 27 2013 10:33:54