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
00034 tf_epsilon_linear_ = 1e-4;
00035 tf_epsilon_angular_ = 1.7e-3;
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
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
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
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
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
00107 removeInvalidDistributions(
00108 frame.kp_means, frame.kp_covariances, frame.kp_valid,
00109 data_means, data_covariances);
00110
00111
00112 transformDistributions(data_means, data_covariances, prediction * f2b_* b2c_);
00113
00114
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
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
00142 transformDistributions(data_means, data_covariances, motion_icp);
00143
00144
00145 updateModelFromData(data_means, data_covariances);
00146 }
00147
00148
00149 model_tree_.setInputCloud(model_ptr_);
00150
00151
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
00166 PointCloudFeature data_cloud;
00167 pointCloudFromMeans(data_means, data_cloud);
00168
00169
00170 AffineTransform final_transformation;
00171 final_transformation.setIdentity();
00172
00173 for (int iteration = 0; iteration < max_iterations_; ++iteration)
00174 {
00175
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
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
00198 pcl::transformPointCloud(data_cloud, data_cloud, transformation);
00199
00200
00201 final_transformation = transformation * final_transformation;
00202
00203
00204 double linear, angular;
00205 getTfDifference(transformation, linear, angular);
00206 if (linear < tf_epsilon_linear_ &&
00207 angular < tf_epsilon_angular_)
00208 {
00209
00210
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
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
00276 double best_mah_dist_sq = 0;
00277 int best_mah_nn_idx = -1;
00278
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
00299 }
00300 }
00301
00302 if (best_mah_nn_idx != -1)
00303 {
00304
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
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
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
00348
00349
00350 const Vector3f& model_mean_pred = means_[mah_nn_idx];
00351 const Matrix3f& model_cov_pred = covariances_[mah_nn_idx];
00352
00353
00354 Vector3f y = data_mean - model_mean_pred;
00355 Matrix3f S = data_cov + model_cov_pred;
00356
00357
00358 Matrix3f K = model_cov_pred * S.inverse();
00359
00360
00361 Vector3f model_mean_upd = model_mean_pred + K * y;
00362 Matrix3f model_cov_upd = (I_ - K) * model_cov_pred;
00363
00364
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
00387
00388
00389
00390
00391
00392
00393
00394
00395
00396
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 }