|
Point Cloud Library (PCL)
1.4.0
|
00001 /* 00002 * Software License Agreement (BSD License) 00003 * 00004 * Copyright (c) 2010, Willow Garage, Inc. 00005 * All rights reserved. 00006 * 00007 * Redistribution and use in source and binary forms, with or without 00008 * modification, are permitted provided that the following conditions 00009 * are met: 00010 * 00011 * * Redistributions of source code must retain the above copyright 00012 * notice, this list of conditions and the following disclaimer. 00013 * * Redistributions in binary form must reproduce the above 00014 * copyright notice, this list of conditions and the following 00015 * disclaimer in the documentation and/or other materials provided 00016 * with the distribution. 00017 * * Neither the name of Willow Garage, Inc. nor the names of its 00018 * contributors may be used to endorse or promote products derived 00019 * from this software without specific prior written permission. 00020 * 00021 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 00022 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 00023 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 00024 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 00025 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 00026 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 00027 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 00028 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 00029 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 00030 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 00031 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 00032 * POSSIBILITY OF SUCH DAMAGE. 00033 * 00034 * $Id: gicp.hpp 3756 2011-12-31 23:54:41Z rusu $ 00035 * 00036 */ 00037 00038 #include <boost/unordered_map.hpp> 00039 #include "pcl/registration/exceptions.h" 00040 00042 template <typename PointSource, typename PointTarget> 00043 template<typename PointT> void 00044 pcl::GeneralizedIterativeClosestPoint<PointSource, PointTarget>::computeCovariances(typename pcl::PointCloud<PointT>::ConstPtr cloud, 00045 const typename pcl::KdTree<PointT>::Ptr kdtree, 00046 std::vector<Eigen::Matrix3d>& cloud_covariances) 00047 { 00048 if((size_t)k_correspondences_ > cloud->size ()) 00049 { 00050 PCL_ERROR ("[pcl::GeneralizedIterativeClosestPoint::computeCovariances] Number or points in cloud (%lu) is less than k_correspondences_ (%lu)!\n", (unsigned long)cloud->size (), (unsigned long)k_correspondences_); 00051 return; 00052 } 00053 00054 Eigen::Vector3d mean; 00055 std::vector<int> nn_indecies; nn_indecies.reserve (k_correspondences_); 00056 std::vector<float> nn_dist_sq; nn_dist_sq.reserve (k_correspondences_); 00057 00058 // We should never get there but who knows 00059 if(cloud_covariances.size () < cloud->size ()) 00060 cloud_covariances.resize (cloud->size ()); 00061 00062 typename pcl::PointCloud<PointT>::const_iterator points_iterator = cloud->begin (); 00063 std::vector<Eigen::Matrix3d>::iterator matrices_iterator = cloud_covariances.begin (); 00064 for(; 00065 points_iterator != cloud->end (); 00066 ++points_iterator, ++matrices_iterator) 00067 { 00068 const PointT &query_point = *points_iterator; 00069 Eigen::Matrix3d &cov = *matrices_iterator; 00070 // Zero out the cov and mean 00071 cov.setZero (); 00072 mean.setZero (); 00073 00074 // Search for the K nearest neighbours 00075 kdtree->nearestKSearch(query_point, k_correspondences_, nn_indecies, nn_dist_sq); 00076 00077 // Find the covariance matrix 00078 for(int j = 0; j < k_correspondences_; j++) { 00079 const PointT &pt = (*cloud)[nn_indecies[j]]; 00080 00081 mean[0] += pt.x; 00082 mean[1] += pt.y; 00083 mean[2] += pt.z; 00084 00085 cov(0,0) += pt.x*pt.x; 00086 00087 cov(1,0) += pt.y*pt.x; 00088 cov(1,1) += pt.y*pt.y; 00089 00090 cov(2,0) += pt.z*pt.x; 00091 cov(2,1) += pt.z*pt.y; 00092 cov(2,2) += pt.z*pt.z; 00093 } 00094 00095 mean/= (double)k_correspondences_; 00096 // Get the actual covariance 00097 for(int k = 0; k < 3; k++) 00098 for(int l = 0; l <= k; l++) 00099 { 00100 cov(k,l) /= (double)k_correspondences_; 00101 cov(k,l) -= mean[k]*mean[l]; 00102 cov(l,k) = cov(k,l); 00103 } 00104 00105 // Compute the SVD (covariance matrix is symmetric so U = V') 00106 Eigen::JacobiSVD<Eigen::Matrix3d> svd(cov, Eigen::ComputeFullU); 00107 cov.setZero (); 00108 Eigen::Matrix3d U = svd.matrixU (); 00109 // Reconstitute the covariance matrix with modified singular values using the column // vectors in V. 00110 for(int k = 0; k < 3; k++) { 00111 Eigen::Vector3d col = U.col(k); 00112 double v = 1.; // biggest 2 singular values replaced by 1 00113 if(k == 2) // smallest singular value replaced by gicp_epsilon 00114 v = gicp_epsilon_; 00115 cov+= v * col * col.transpose(); 00116 } 00117 } 00118 } 00119 00121 template <typename PointSource, typename PointTarget> void 00122 pcl::GeneralizedIterativeClosestPoint<PointSource, PointTarget>::computeRDerivative(const Vector6d &x, const Eigen::Matrix3d &R, Vector6d& g) const 00123 { 00124 Eigen::Matrix3d dR_dPhi; 00125 Eigen::Matrix3d dR_dTheta; 00126 Eigen::Matrix3d dR_dPsi; 00127 00128 double phi = x[3], theta = x[4], psi = x[5]; 00129 00130 double cphi = cos(phi), sphi = sin(phi); 00131 double ctheta = cos(theta), stheta = sin(theta); 00132 double cpsi = cos(psi), spsi = sin(psi); 00133 00134 dR_dPhi(0,0) = 0.; 00135 dR_dPhi(1,0) = 0.; 00136 dR_dPhi(2,0) = 0.; 00137 00138 dR_dPhi(0,1) = sphi*spsi + cphi*cpsi*stheta; 00139 dR_dPhi(1,1) = -cpsi*sphi + cphi*spsi*stheta; 00140 dR_dPhi(2,1) = cphi*ctheta; 00141 00142 dR_dPhi(0,2) = cphi*spsi - cpsi*sphi*stheta; 00143 dR_dPhi(1,2) = -cphi*cpsi - sphi*spsi*stheta; 00144 dR_dPhi(2,2) = -ctheta*sphi; 00145 00146 dR_dTheta(0,0) = -cpsi*stheta; 00147 dR_dTheta(1,0) = -spsi*stheta; 00148 dR_dTheta(2,0) = -ctheta; 00149 00150 dR_dTheta(0,1) = cpsi*ctheta*sphi; 00151 dR_dTheta(1,1) = ctheta*sphi*spsi; 00152 dR_dTheta(2,1) = -sphi*stheta; 00153 00154 dR_dTheta(0,2) = cphi*cpsi*ctheta; 00155 dR_dTheta(1,2) = cphi*ctheta*spsi; 00156 dR_dTheta(2,2) = -cphi*stheta; 00157 00158 dR_dPsi(0,0) = -ctheta*spsi; 00159 dR_dPsi(1,0) = cpsi*ctheta; 00160 dR_dPsi(2,0) = 0.; 00161 00162 dR_dPsi(0,1) = -cphi*cpsi - sphi*spsi*stheta; 00163 dR_dPsi(1,1) = -cphi*spsi + cpsi*sphi*stheta; 00164 dR_dPsi(2,1) = 0.; 00165 00166 dR_dPsi(0,2) = cpsi*sphi - cphi*spsi*stheta; 00167 dR_dPsi(1,2) = sphi*spsi + cphi*cpsi*stheta; 00168 dR_dPsi(2,2) = 0.; 00169 00170 g[3] = matricesInnerProd(dR_dPhi, R); 00171 g[4] = matricesInnerProd(dR_dTheta, R); 00172 g[5] = matricesInnerProd(dR_dPsi, R); 00173 } 00174 00176 template <typename PointSource, typename PointTarget> void 00177 pcl::GeneralizedIterativeClosestPoint<PointSource, PointTarget>::estimateRigidTransformationBFGS ( 00178 const PointCloudSource &cloud_src, const std::vector<int> &indices_src, const PointCloudTarget &cloud_tgt, const std::vector<int> &indices_tgt, Eigen::Matrix4f &transformation_matrix) 00179 { 00180 boost::mutex::scoped_lock lock (tmp_mutex_); 00181 00182 if (indices_src.size () < 4) // need at least 4 samples 00183 { 00184 PCL_THROW_EXCEPTION (NotEnoughPointsException, 00185 "[pcl::GeneralizedIterativeClosestPoint::estimateRigidTransformationBFGS] Need at least 4 points to estimate a transform! Source and target have " << indices_src.size () << " points!"); 00186 return; 00187 } 00188 // Set the initial solution 00189 Vector6d x = Vector6d::Zero (); 00190 x[0] = transformation_matrix (0,3); 00191 x[1] = transformation_matrix (1,3); 00192 x[2] = transformation_matrix (2,3); 00193 x[3] = atan2 (transformation_matrix (2,1), transformation_matrix (2,2)); 00194 x[4] = asin (-transformation_matrix (2,0)); 00195 x[5] = atan2 (transformation_matrix (1,0), transformation_matrix (0,0)); 00196 00197 // Set temporary pointers 00198 tmp_src_ = &cloud_src; 00199 tmp_tgt_ = &cloud_tgt; 00200 tmp_idx_src_ = &indices_src; 00201 tmp_idx_tgt_ = &indices_tgt; 00202 00203 // Optimize using forward-difference approximation LM 00204 const double gradient_tol = 1e-2; 00205 OptimizationFunctorWithIndices functor(this); 00206 BFGS<OptimizationFunctorWithIndices> bfgs (functor); 00207 bfgs.parameters.sigma = 0.01; 00208 bfgs.parameters.rho = 0.01; 00209 bfgs.parameters.tau1 = 9; 00210 bfgs.parameters.tau2 = 0.05; 00211 bfgs.parameters.tau3 = 0.5; 00212 bfgs.parameters.order = 3; 00213 00214 int inner_iterations_ = 0; 00215 int result = bfgs.minimizeInit (x); 00216 do 00217 { 00218 inner_iterations_++; 00219 result = bfgs.minimizeOneStep (x); 00220 if(result) 00221 { 00222 break; 00223 } 00224 result = bfgs.testGradient(gradient_tol); 00225 } while(result == BFGSSpace::Running && inner_iterations_ < max_inner_iterations_); 00226 if(result == BFGSSpace::Success || inner_iterations_ == max_inner_iterations_) 00227 { 00228 PCL_DEBUG ("[pcl::registration::TransformationEstimationBFGS::estimateRigidTransformation]"); 00229 PCL_DEBUG ("BFGS solver finished with exit code %i \n", result); 00230 transformation_matrix.setIdentity(); 00231 applyState(transformation_matrix, x); 00232 } 00233 else 00234 PCL_THROW_EXCEPTION(SolverDidntConvergeException, 00235 "[pcl::" << getClassName () << "::TransformationEstimationBFGS::estimateRigidTransformation] BFGS solver didn't converge!"); 00236 } 00237 00239 template <typename PointSource, typename PointTarget> inline double 00240 pcl::GeneralizedIterativeClosestPoint<PointSource, PointTarget>::OptimizationFunctorWithIndices::operator() (const Vector6d& x) 00241 { 00242 Eigen::Matrix4f transformation_matrix = gicp_->base_transformation_; 00243 gicp_->applyState(transformation_matrix, x); 00244 double f = 0; 00245 int m = gicp_->tmp_idx_src_->size (); 00246 for (int i = 0; i < m; ++i) 00247 { 00248 // The last coordinate, p_src[3] is guaranteed to be set to 1.0 in registration.hpp 00249 Vector4fMapConst p_src = gicp_->tmp_src_->points[(*gicp_->tmp_idx_src_)[i]].getVector4fMap (); 00250 // The last coordinate, p_tgt[3] is guaranteed to be set to 1.0 in registration.hpp 00251 Vector4fMapConst p_tgt = gicp_->tmp_tgt_->points[(*gicp_->tmp_idx_tgt_)[i]].getVector4fMap (); 00252 Eigen::Vector4f pp = transformation_matrix * p_src; 00253 // Estimate the distance (cost function) 00254 // The last coordiante is still guaranteed to be set to 1.0 00255 Eigen::Vector3d res(pp[0] - p_tgt[0], pp[1] - p_tgt[1], pp[2] - p_tgt[2]); 00256 Eigen::Vector3d temp = gicp_->mahalanobis((*gicp_->tmp_idx_src_)[i]) * res; 00257 //increment= res'*temp/num_matches = temp'*M*temp/num_matches (we postpone 1/num_matches after the loop closes) 00258 f+= double(res.transpose() * temp); 00259 } 00260 return f/m; 00261 } 00262 00264 template <typename PointSource, typename PointTarget> inline void 00265 pcl::GeneralizedIterativeClosestPoint<PointSource, PointTarget>::OptimizationFunctorWithIndices::df (const Vector6d& x, Vector6d& g) 00266 { 00267 Eigen::Matrix4f transformation_matrix = gicp_->base_transformation_; 00268 gicp_->applyState(transformation_matrix, x); 00269 //Zero out g 00270 g.setZero (); 00271 //Eigen::Vector3d g_t = g.head<3> (); 00272 Eigen::Matrix3d R = Eigen::Matrix3d::Zero (); 00273 int m = gicp_->tmp_idx_src_->size (); 00274 for (int i = 0; i < m; ++i) 00275 { 00276 // The last coordinate, p_src[3] is guaranteed to be set to 1.0 in registration.hpp 00277 Vector4fMapConst p_src = gicp_->tmp_src_->points[(*gicp_->tmp_idx_src_)[i]].getVector4fMap (); 00278 // The last coordinate, p_tgt[3] is guaranteed to be set to 1.0 in registration.hpp 00279 Vector4fMapConst p_tgt = gicp_->tmp_tgt_->points[(*gicp_->tmp_idx_tgt_)[i]].getVector4fMap (); 00280 00281 Eigen::Vector4f pp = transformation_matrix * p_src; 00282 // The last coordiante is still guaranteed to be set to 1.0 00283 Eigen::Vector3d res(pp[0] - p_tgt[0], pp[1] - p_tgt[1], pp[2] - p_tgt[2]); 00284 // temp = M*res 00285 Eigen::Vector3d temp = gicp_->mahalanobis((*gicp_->tmp_idx_src_)[i]) * res; 00286 // Increment translation gradient 00287 // g.head<3> ()+= 2*M*res/num_matches (we postpone 2/num_matches after the loop closes) 00288 g.head<3> ()+= temp; 00289 // Increment rotation gradient 00290 pp = gicp_->base_transformation_ * p_src; 00291 Eigen::Vector3d p_src3 (pp[0], pp[1], pp[2]); 00292 R+= p_src3 * temp.transpose(); 00293 } 00294 g.head<3> ()*= 2.0/m; 00295 R*= 2.0/m; 00296 gicp_->computeRDerivative(x, R, g); 00297 } 00298 00300 template <typename PointSource, typename PointTarget> inline void 00301 pcl::GeneralizedIterativeClosestPoint<PointSource, PointTarget>::OptimizationFunctorWithIndices::fdf (const Vector6d& x, double& f, Vector6d& g) 00302 { 00303 Eigen::Matrix4f transformation_matrix = gicp_->base_transformation_; 00304 gicp_->applyState(transformation_matrix, x); 00305 f = 0; 00306 g.setZero (); 00307 Eigen::Matrix3d R = Eigen::Matrix3d::Zero (); 00308 const int m = gicp_->tmp_idx_src_->size (); 00309 for (int i = 0; i < m; ++i) 00310 { 00311 // The last coordinate, p_src[3] is guaranteed to be set to 1.0 in registration.hpp 00312 Vector4fMapConst p_src = gicp_->tmp_src_->points[(*gicp_->tmp_idx_src_)[i]].getVector4fMap (); 00313 // The last coordinate, p_tgt[3] is guaranteed to be set to 1.0 in registration.hpp 00314 Vector4fMapConst p_tgt = gicp_->tmp_tgt_->points[(*gicp_->tmp_idx_tgt_)[i]].getVector4fMap (); 00315 Eigen::Vector4f pp = transformation_matrix * p_src; 00316 // The last coordiante is still guaranteed to be set to 1.0 00317 Eigen::Vector3d res(pp[0] - p_tgt[0], pp[1] - p_tgt[1], pp[2] - p_tgt[2]); 00318 // temp = M*res 00319 Eigen::Vector3d temp = gicp_->mahalanobis((*gicp_->tmp_idx_src_)[i]) * res; 00320 // Increment total error 00321 f+= double(res.transpose() * temp); 00322 // Increment translation gradient 00323 // g.head<3> ()+= 2*M*res/num_matches (we postpone 2/num_matches after the loop closes) 00324 g.head<3> ()+= temp; 00325 pp = gicp_->base_transformation_ * p_src; 00326 Eigen::Vector3d p_src3 (pp[0], pp[1], pp[2]); 00327 // Increment rotation gradient 00328 R+= p_src3 * temp.transpose(); 00329 } 00330 f/= double(m); 00331 g.head<3> ()*= double(2.0/m); 00332 R*= 2.0/m; 00333 gicp_->computeRDerivative(x, R, g); 00334 } 00335 00337 template <typename PointSource, typename PointTarget> inline void 00338 pcl::GeneralizedIterativeClosestPoint<PointSource, PointTarget>::computeTransformation (PointCloudSource &output, const Eigen::Matrix4f& guess) 00339 { 00340 using namespace std; 00341 // Difference between consecutive transforms 00342 double delta = 0; 00343 // Get the size of the target 00344 const size_t N = indices_->size (); 00345 // Set the mahalanobis matrices to identity 00346 mahalanobis_.resize (N, Eigen::Matrix3d::Identity ()); 00347 // Compute target cloud covariance matrices 00348 computeCovariances<PointTarget> (target_, tree_, target_covariances_); 00349 // Compute input cloud covariance matrices 00350 computeCovariances<PointSource> (input_, input_tree_, input_covariances_); 00351 00352 base_transformation_ = guess; 00353 nr_iterations_ = 0; 00354 converged_ = false; 00355 double dist_threshold = corr_dist_threshold_ * corr_dist_threshold_; 00356 std::vector<int> nn_indices (1); 00357 std::vector<float> nn_dists (1); 00358 00359 while(!converged_) 00360 { 00361 size_t cnt = 0; 00362 std::vector<int> source_indices (indices_->size ()); 00363 std::vector<int> target_indices (indices_->size ()); 00364 00365 // guess corresponds to base_t and transformation_ to t 00366 Eigen::Matrix4d transform_R = Eigen::Matrix4d::Zero (); 00367 for(size_t i = 0; i < 4; i++) 00368 for(size_t j = 0; j < 4; j++) 00369 for(size_t k = 0; k < 4; k++) 00370 transform_R(i,j)+= double(transformation_(i,k)) * double(guess(k,j)); 00371 00372 Eigen::Matrix3d R = transform_R.topLeftCorner<3,3> (); 00373 for(size_t i = 0; i < N; i++) 00374 { 00375 PointSource query = output[i]; 00376 query.getVector4fMap () = guess * query.getVector4fMap (); 00377 query.getVector4fMap () = transformation_ * query.getVector4fMap (); 00378 00379 if (!searchForNeighbors (query, nn_indices, nn_dists)) 00380 { 00381 PCL_ERROR ("[pcl::%s::computeTransformation] Unable to find a nearest neighbor in the target dataset for point %d in the source!\n", getClassName ().c_str (), (*indices_)[i]); 00382 return; 00383 } 00384 00385 // Check if the distance to the nearest neighbor is smaller than the user imposed threshold 00386 if (nn_dists[0] < dist_threshold) 00387 { 00388 Eigen::Matrix3d &C1 = input_covariances_[i]; 00389 Eigen::Matrix3d &C2 = target_covariances_[nn_indices[0]]; 00390 Eigen::Matrix3d &M = mahalanobis_[i]; 00391 // M = R*C1 00392 M = R * C1; 00393 // temp = M*R' + C2 = R*C1*R' + C2 00394 Eigen::Matrix3d temp = M * R.transpose(); 00395 temp+= C2; 00396 // M = temp^-1 00397 M = temp.inverse (); 00398 source_indices[cnt] = i; 00399 target_indices[cnt] = nn_indices[0]; 00400 cnt++; 00401 } 00402 } 00403 // Resize to the actual number of valid correspondences 00404 source_indices.resize(cnt); target_indices.resize(cnt); 00405 /* optimize transformation using the current assignment and Mahalanobis metrics*/ 00406 previous_transformation_ = transformation_; 00407 //optimization right here 00408 try 00409 { 00410 rigid_transformation_estimation_(output, source_indices, *target_, target_indices, transformation_); 00411 /* compute the delta from this iteration */ 00412 delta = 0.; 00413 for(int k = 0; k < 4; k++) { 00414 for(int l = 0; l < 4; l++) { 00415 double ratio = 1; 00416 if(k < 3 && l < 3) // rotation part of the transform 00417 ratio = 1./rotation_epsilon_; 00418 else 00419 ratio = 1./transformation_epsilon_; 00420 double c_delta = ratio*fabs(previous_transformation_(k,l) - transformation_(k,l)); 00421 if(c_delta > delta) 00422 delta = c_delta; 00423 } 00424 } 00425 } 00426 catch (PCLException &e) 00427 { 00428 PCL_DEBUG ("[pcl::%s::computeTransformation] Optimization issue %s\n", getClassName ().c_str (), e.what ()); 00429 break; 00430 } 00431 nr_iterations_++; 00432 // Check for convergence 00433 if (nr_iterations_ >= max_iterations_ || delta < 1) 00434 { 00435 converged_ = true; 00436 PCL_DEBUG ("[pcl::%s::computeTransformation] Convergence reached. Number of iterations: %d out of %d. Transformation difference: %f\n", 00437 getClassName ().c_str (), nr_iterations_, max_iterations_, fabs ((transformation_ - previous_transformation_).sum ())); 00438 } 00439 else { 00440 PCL_DEBUG ("[pcl::%s::computeTransformation] Convergence failed\n", getClassName ().c_str ()); 00441 } 00442 } 00443 final_transformation_ = transformation_; 00444 } 00445 00446 template <typename PointSource, typename PointTarget> void 00447 pcl::GeneralizedIterativeClosestPoint<PointSource, PointTarget>::applyState(Eigen::Matrix4f &t, const Vector6d& x) const 00448 { 00449 // !!! CAUTION Stanford GICP uses the Z Y X euler angles convention 00450 Eigen::Matrix3f R; 00451 R = Eigen::AngleAxisf(x[5], Eigen::Vector3f::UnitZ ()) 00452 * Eigen::AngleAxisf(x[4], Eigen::Vector3f::UnitY ()) 00453 * Eigen::AngleAxisf(x[3], Eigen::Vector3f::UnitX ()); 00454 t.topLeftCorner<3,3> () = R * t.topLeftCorner<3,3> (); 00455 Eigen::Vector4f T (x[0], x[1], x[2], 0); 00456 t.block <4, 1> (0, 3) += T; 00457 }
1.7.6.1