Point Cloud Library (PCL) 1.15.1-dev
Loading...
Searching...
No Matches
gicp.hpp
1/*
2 * Software License Agreement (BSD License)
3 *
4 * Point Cloud Library (PCL) - www.pointclouds.org
5 * Copyright (c) 2010, Willow Garage, Inc.
6 * Copyright (c) 2012-, Open Perception, Inc.
7 *
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 *
14 * * Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * * Redistributions in binary form must reproduce the above
17 * copyright notice, this list of conditions and the following
18 * disclaimer in the documentation and/or other materials provided
19 * with the distribution.
20 * * Neither the name of the copyright holder(s) nor the names of its
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
36 *
37 * $Id$
38 *
39 */
40
41#ifndef PCL_REGISTRATION_IMPL_GICP_HPP_
42#define PCL_REGISTRATION_IMPL_GICP_HPP_
43
44#include <pcl/registration/exceptions.h>
45
46namespace pcl {
47
48template <typename PointSource, typename PointTarget, typename Scalar>
49void
51 unsigned int nr_threads)
52{
53#ifdef _OPENMP
54 if (nr_threads == 0)
55 threads_ = omp_get_num_procs();
56 else
57 threads_ = nr_threads;
58 PCL_DEBUG("[pcl::GeneralizedIterativeClosestPoint::setNumberOfThreads] Setting "
59 "number of threads to %u.\n",
60 threads_);
61#else
62 threads_ = 1;
63 if (nr_threads != 1)
64 PCL_WARN("[pcl::GeneralizedIterativeClosestPoint::setNumberOfThreads] "
65 "Parallelization is requested, but OpenMP is not available! Continuing "
66 "without parallelization.\n");
67#endif // _OPENMP
68}
69
70template <typename PointSource, typename PointTarget, typename Scalar>
71template <typename PointT>
72void
75 const typename pcl::search::Search<PointT>::Ptr kdtree,
76 MatricesVector& cloud_covariances)
77{
78 if (k_correspondences_ > static_cast<int>(cloud->size())) {
79 PCL_ERROR("[pcl::GeneralizedIterativeClosestPoint::computeCovariances] Number or "
80 "points in cloud (%lu) is less than k_correspondences_ (%lu)!\n",
81 cloud->size(),
82 k_correspondences_);
83 return;
84 }
85
86 Eigen::Vector3d mean;
87 Eigen::Matrix3d cov;
88 pcl::Indices nn_indices(k_correspondences_);
89 std::vector<float> nn_dist_sq(k_correspondences_);
90
91 // We should never get there but who knows
92 if (cloud_covariances.size() < cloud->size())
93 cloud_covariances.resize(cloud->size());
94
95#pragma omp parallel for num_threads(threads_) schedule(dynamic, 32) \
96 shared(cloud, cloud_covariances) firstprivate(mean, cov, nn_indices, nn_dist_sq)
97 for (std::ptrdiff_t i = 0; i < static_cast<std::ptrdiff_t>(cloud->size()); ++i) {
98 const PointT& query_point = (*cloud)[i];
99 // Zero out the cov and mean
100 cov.setZero();
101 mean.setZero();
102
103 // Search for the K nearest neighbours
104 kdtree->nearestKSearch(query_point, k_correspondences_, nn_indices, nn_dist_sq);
105
106 // Find the covariance matrix
107 for (int j = 0; j < k_correspondences_; j++) {
108 // de-mean neighbourhood to avoid inaccuracies when far away from origin
109 const double ptx = (*cloud)[nn_indices[j]].x - query_point.x,
110 pty = (*cloud)[nn_indices[j]].y - query_point.y,
111 ptz = (*cloud)[nn_indices[j]].z - query_point.z;
112
113 mean[0] += ptx;
114 mean[1] += pty;
115 mean[2] += ptz;
116
117 cov(0, 0) += ptx * ptx;
118
119 cov(1, 0) += pty * ptx;
120 cov(1, 1) += pty * pty;
121
122 cov(2, 0) += ptz * ptx;
123 cov(2, 1) += ptz * pty;
124 cov(2, 2) += ptz * ptz;
125 }
126
127 mean /= static_cast<double>(k_correspondences_);
128 // Get the actual covariance
129 for (int k = 0; k < 3; k++)
130 for (int l = 0; l <= k; l++) {
131 cov(k, l) /= static_cast<double>(k_correspondences_);
132 cov(k, l) -= mean[k] * mean[l];
133 cov(l, k) = cov(k, l);
134 }
135
136 // We know that cov is symmetric (self-adjoint), so SelfAdjointEigenSolver is the
137 // best choice. The smallest eigenvalue is replaced with gicp_epsilon_, the other
138 // two eigenvalues with 1.
139 Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> solver_M;
140 solver_M.computeDirect(cov);
141 cloud_covariances[i] = solver_M.eigenvectors() *
142 Eigen::Vector3d(gicp_epsilon_, 1.0, 1.0).asDiagonal() *
143 solver_M.eigenvectors().transpose();
144 }
145}
146
147template <typename PointSource, typename PointTarget, typename Scalar>
148void
150 double phi,
151 double theta,
152 double psi,
153 Eigen::Matrix3d& dR_dPhi,
154 Eigen::Matrix3d& dR_dTheta,
155 Eigen::Matrix3d& dR_dPsi) const
156{
157 const double cphi = std::cos(phi), sphi = std::sin(phi);
158 const double ctheta = std::cos(theta), stheta = std::sin(theta);
159 const double cpsi = std::cos(psi), spsi = std::sin(psi);
160 dR_dPhi(0, 0) = 0.;
161 dR_dPhi(1, 0) = 0.;
162 dR_dPhi(2, 0) = 0.;
163
164 dR_dPhi(0, 1) = sphi * spsi + cphi * cpsi * stheta;
165 dR_dPhi(1, 1) = -cpsi * sphi + cphi * spsi * stheta;
166 dR_dPhi(2, 1) = cphi * ctheta;
167
168 dR_dPhi(0, 2) = cphi * spsi - cpsi * sphi * stheta;
169 dR_dPhi(1, 2) = -cphi * cpsi - sphi * spsi * stheta;
170 dR_dPhi(2, 2) = -ctheta * sphi;
171
172 dR_dTheta(0, 0) = -cpsi * stheta;
173 dR_dTheta(1, 0) = -spsi * stheta;
174 dR_dTheta(2, 0) = -ctheta;
175
176 dR_dTheta(0, 1) = cpsi * ctheta * sphi;
177 dR_dTheta(1, 1) = ctheta * sphi * spsi;
178 dR_dTheta(2, 1) = -sphi * stheta;
179
180 dR_dTheta(0, 2) = cphi * cpsi * ctheta;
181 dR_dTheta(1, 2) = cphi * ctheta * spsi;
182 dR_dTheta(2, 2) = -cphi * stheta;
183
184 dR_dPsi(0, 0) = -ctheta * spsi;
185 dR_dPsi(1, 0) = cpsi * ctheta;
186 dR_dPsi(2, 0) = 0.;
187
188 dR_dPsi(0, 1) = -cphi * cpsi - sphi * spsi * stheta;
189 dR_dPsi(1, 1) = -cphi * spsi + cpsi * sphi * stheta;
190 dR_dPsi(2, 1) = 0.;
191
192 dR_dPsi(0, 2) = cpsi * sphi - cphi * spsi * stheta;
193 dR_dPsi(1, 2) = sphi * spsi + cphi * cpsi * stheta;
194 dR_dPsi(2, 2) = 0.;
195}
196
197template <typename PointSource, typename PointTarget, typename Scalar>
198void
200 const Vector6d& x, const Eigen::Matrix3d& dCost_dR_T, Vector6d& g) const
201{
202 Eigen::Matrix3d dR_dPhi;
203 Eigen::Matrix3d dR_dTheta;
204 Eigen::Matrix3d dR_dPsi;
205 getRDerivatives(x[3], x[4], x[5], dR_dPhi, dR_dTheta, dR_dPsi);
206
207 g[3] = (dR_dPhi * dCost_dR_T).trace();
208 g[4] = (dR_dTheta * dCost_dR_T).trace();
209 g[5] = (dR_dPsi * dCost_dR_T).trace();
210}
211
212template <typename PointSource, typename PointTarget, typename Scalar>
213void
215 double phi,
216 double theta,
217 double psi,
218 Eigen::Matrix3d& ddR_dPhi_dPhi,
219 Eigen::Matrix3d& ddR_dPhi_dTheta,
220 Eigen::Matrix3d& ddR_dPhi_dPsi,
221 Eigen::Matrix3d& ddR_dTheta_dTheta,
222 Eigen::Matrix3d& ddR_dTheta_dPsi,
223 Eigen::Matrix3d& ddR_dPsi_dPsi) const
224{
225 const double sphi = std::sin(phi), stheta = std::sin(theta), spsi = std::sin(psi);
226 const double cphi = std::cos(phi), ctheta = std::cos(theta), cpsi = std::cos(psi);
227 ddR_dPhi_dPhi(0, 0) = 0.0;
228 ddR_dPhi_dPhi(1, 0) = 0.0;
229 ddR_dPhi_dPhi(2, 0) = 0.0;
230 ddR_dPhi_dPhi(0, 1) = -cpsi * stheta * sphi + spsi * cphi;
231 ddR_dPhi_dPhi(1, 1) = -cpsi * cphi - spsi * stheta * sphi;
232 ddR_dPhi_dPhi(2, 1) = -ctheta * sphi;
233 ddR_dPhi_dPhi(0, 2) = -spsi * sphi - cpsi * stheta * cphi;
234 ddR_dPhi_dPhi(1, 2) = -spsi * stheta * cphi + cpsi * sphi;
235 ddR_dPhi_dPhi(2, 2) = -ctheta * cphi;
236
237 ddR_dPhi_dTheta(0, 0) = 0.0;
238 ddR_dPhi_dTheta(1, 0) = 0.0;
239 ddR_dPhi_dTheta(2, 0) = 0.0;
240 ddR_dPhi_dTheta(0, 1) = cpsi * ctheta * cphi;
241 ddR_dPhi_dTheta(1, 1) = spsi * ctheta * cphi;
242 ddR_dPhi_dTheta(2, 1) = -stheta * cphi;
243 ddR_dPhi_dTheta(0, 2) = -cpsi * ctheta * sphi;
244 ddR_dPhi_dTheta(1, 2) = -spsi * ctheta * sphi;
245 ddR_dPhi_dTheta(2, 2) = stheta * sphi;
246
247 ddR_dPhi_dPsi(0, 0) = 0.0;
248 ddR_dPhi_dPsi(1, 0) = 0.0;
249 ddR_dPhi_dPsi(2, 0) = 0.0;
250 ddR_dPhi_dPsi(0, 1) = -spsi * stheta * cphi + cpsi * sphi;
251 ddR_dPhi_dPsi(1, 1) = spsi * sphi + cpsi * stheta * cphi;
252 ddR_dPhi_dPsi(2, 1) = 0.0;
253 ddR_dPhi_dPsi(0, 2) = cpsi * cphi + spsi * stheta * sphi;
254 ddR_dPhi_dPsi(1, 2) = -cpsi * stheta * sphi + spsi * cphi;
255 ddR_dPhi_dPsi(2, 2) = 0.0;
256
257 ddR_dTheta_dTheta(0, 0) = -cpsi * ctheta;
258 ddR_dTheta_dTheta(1, 0) = -spsi * ctheta;
259 ddR_dTheta_dTheta(2, 0) = stheta;
260 ddR_dTheta_dTheta(0, 1) = -cpsi * stheta * sphi;
261 ddR_dTheta_dTheta(1, 1) = -spsi * stheta * sphi;
262 ddR_dTheta_dTheta(2, 1) = -ctheta * sphi;
263 ddR_dTheta_dTheta(0, 2) = -cpsi * stheta * cphi;
264 ddR_dTheta_dTheta(1, 2) = -spsi * stheta * cphi;
265 ddR_dTheta_dTheta(2, 2) = -ctheta * cphi;
266
267 ddR_dTheta_dPsi(0, 0) = spsi * stheta;
268 ddR_dTheta_dPsi(1, 0) = -cpsi * stheta;
269 ddR_dTheta_dPsi(2, 0) = 0.0;
270 ddR_dTheta_dPsi(0, 1) = -spsi * ctheta * sphi;
271 ddR_dTheta_dPsi(1, 1) = cpsi * ctheta * sphi;
272 ddR_dTheta_dPsi(2, 1) = 0.0;
273 ddR_dTheta_dPsi(0, 2) = -spsi * ctheta * cphi;
274 ddR_dTheta_dPsi(1, 2) = cpsi * ctheta * cphi;
275 ddR_dTheta_dPsi(2, 2) = 0.0;
276
277 ddR_dPsi_dPsi(0, 0) = -cpsi * ctheta;
278 ddR_dPsi_dPsi(1, 0) = -spsi * ctheta;
279 ddR_dPsi_dPsi(2, 0) = 0.0;
280 ddR_dPsi_dPsi(0, 1) = -cpsi * stheta * sphi + spsi * cphi;
281 ddR_dPsi_dPsi(1, 1) = -cpsi * cphi - spsi * stheta * sphi;
282 ddR_dPsi_dPsi(2, 1) = 0.0;
283 ddR_dPsi_dPsi(0, 2) = -spsi * sphi - cpsi * stheta * cphi;
284 ddR_dPsi_dPsi(1, 2) = -spsi * stheta * cphi + cpsi * sphi;
285 ddR_dPsi_dPsi(2, 2) = 0.0;
286}
287
288template <typename PointSource, typename PointTarget, typename Scalar>
289void
292 const pcl::Indices& indices_src,
293 const PointCloudTarget& cloud_tgt,
294 const pcl::Indices& indices_tgt,
295 Matrix4& transformation_matrix)
296{
297 // need at least min_number_correspondences_ samples
298 if (indices_src.size() < min_number_correspondences_) {
299 PCL_THROW_EXCEPTION(
301 "[pcl::GeneralizedIterativeClosestPoint::estimateRigidTransformationBFGS] Need "
302 "at least "
303 << min_number_correspondences_
304 << " points to estimate a transform! "
305 "Source and target have "
306 << indices_src.size() << " points!");
307 return;
308 }
309 // Set the initial solution
310 Vector6d x = Vector6d::Zero();
311 // translation part
312 x[0] = transformation_matrix(0, 3);
313 x[1] = transformation_matrix(1, 3);
314 x[2] = transformation_matrix(2, 3);
315 // rotation part (Z Y X euler angles convention)
316 // see: https://en.wikipedia.org/wiki/Rotation_matrix#General_rotations
317 x[3] = std::atan2(transformation_matrix(2, 1), transformation_matrix(2, 2));
318 x[4] = asin(-transformation_matrix(2, 0));
319 x[5] = std::atan2(transformation_matrix(1, 0), transformation_matrix(0, 0));
320
321 // Set temporary pointers
322 tmp_src_ = &cloud_src;
323 tmp_tgt_ = &cloud_tgt;
324 tmp_idx_src_ = &indices_src;
325 tmp_idx_tgt_ = &indices_tgt;
326
327 // Optimize using BFGS
328 OptimizationFunctorWithIndices functor(this);
330 bfgs.parameters.sigma = 0.01;
331 bfgs.parameters.rho = 0.01;
332 bfgs.parameters.tau1 = 9;
333 bfgs.parameters.tau2 = 0.05;
334 bfgs.parameters.tau3 = 0.5;
335 bfgs.parameters.order = 3;
336
337 int inner_iterations_ = 0;
338 int result = bfgs.minimizeInit(x);
339 result = BFGSSpace::Running;
340 do {
341 inner_iterations_++;
342 result = bfgs.minimizeOneStep(x);
343 if (result) {
344 break;
345 }
346 result = bfgs.testGradient();
347 } while (result == BFGSSpace::Running && inner_iterations_ < max_inner_iterations_);
348 if (result == BFGSSpace::NoProgress || result == BFGSSpace::Success ||
349 inner_iterations_ == max_inner_iterations_) {
350 PCL_DEBUG("[pcl::registration::TransformationEstimationBFGS::"
351 "estimateRigidTransformation]");
352 PCL_DEBUG("BFGS solver finished with exit code %i \n", result);
353 transformation_matrix.setIdentity();
354 applyState(transformation_matrix, x);
355 }
356 else
357 PCL_THROW_EXCEPTION(
359 "[pcl::" << getClassName()
360 << "::TransformationEstimationBFGS::estimateRigidTransformation] BFGS "
361 "solver didn't converge!");
362}
363
364template <typename PointSource, typename PointTarget, typename Scalar>
365void
368 const pcl::Indices& indices_src,
369 const PointCloudTarget& cloud_tgt,
370 const pcl::Indices& indices_tgt,
371 Matrix4& transformation_matrix)
372{
373 // need at least min_number_correspondences_ samples
374 if (indices_src.size() < min_number_correspondences_) {
375 PCL_THROW_EXCEPTION(NotEnoughPointsException,
376 "[pcl::GeneralizedIterativeClosestPoint::"
377 "estimateRigidTransformationNewton] Need "
378 "at least "
379 << min_number_correspondences_
380 << " points to estimate a transform! "
381 "Source and target have "
382 << indices_src.size() << " points!");
383 return;
384 }
385 // Set the initial solution
386 Vector6d x = Vector6d::Zero();
387 // translation part
388 x[0] = transformation_matrix(0, 3);
389 x[1] = transformation_matrix(1, 3);
390 x[2] = transformation_matrix(2, 3);
391 // rotation part (Z Y X euler angles convention)
392 // see: https://en.wikipedia.org/wiki/Rotation_matrix#General_rotations
393 x[3] = std::atan2(transformation_matrix(2, 1), transformation_matrix(2, 2));
394 x[4] = std::asin(
395 std::min<double>(1.0, std::max<double>(-1.0, -transformation_matrix(2, 0))));
396 x[5] = std::atan2(transformation_matrix(1, 0), transformation_matrix(0, 0));
397
398 // Set temporary pointers
399 tmp_src_ = &cloud_src;
400 tmp_tgt_ = &cloud_tgt;
401 tmp_idx_src_ = &indices_src;
402 tmp_idx_tgt_ = &indices_tgt;
403
404 // Optimize using Newton
405 OptimizationFunctorWithIndices functor(this);
406 Eigen::Matrix<double, 6, 6> hessian;
407 Eigen::Matrix<double, 6, 1> gradient;
408 double current_x_value = functor(x);
409 functor.dfddf(x, gradient, hessian);
410 Eigen::Matrix<double, 6, 1> delta;
411 int inner_iterations_ = 0;
412 do {
413 ++inner_iterations_;
414 // compute descent direction from hessian and gradient. Take special measures if
415 // hessian is not positive-definite (positive Eigenvalues)
416 Eigen::SelfAdjointEigenSolver<Eigen::Matrix<double, 6, 6>> eigensolver(hessian);
417 Eigen::Matrix<double, 6, 6> inverted_eigenvalues =
418 Eigen::Matrix<double, 6, 6>::Zero();
419 for (int i = 0; i < 6; ++i) {
420 const double ev = eigensolver.eigenvalues()[i];
421 if (ev < 0)
422 inverted_eigenvalues(i, i) = 1.0 / eigensolver.eigenvalues()[5];
423 else
424 inverted_eigenvalues(i, i) = 1.0 / ev;
425 }
426 delta.noalias() = eigensolver.eigenvectors() * inverted_eigenvalues *
427 eigensolver.eigenvectors().transpose() * gradient;
428
429 // simple line search to guarantee a decrease in the function value
430 double alpha = 1.0;
431 double candidate_x_value;
432 bool improvement_found = false;
433 for (int i = 0; i < 10; ++i, alpha /= 2) {
434 Vector6d candidate_x = x - alpha * delta;
435 candidate_x_value = functor(candidate_x);
436 if (candidate_x_value < current_x_value) {
437 PCL_DEBUG("[estimateRigidTransformationNewton] Using stepsize=%g, function "
438 "value previously: %g, now: %g, "
439 "improvement: %g\n",
440 alpha,
441 current_x_value,
442 candidate_x_value,
443 current_x_value - candidate_x_value);
444 x = candidate_x;
445 current_x_value = candidate_x_value;
446 improvement_found = true;
447 break;
448 }
449 }
450 if (!improvement_found) {
451 PCL_DEBUG("[estimateRigidTransformationNewton] finishing because no progress\n");
452 break;
453 }
454 functor.dfddf(x, gradient, hessian);
455 if (gradient.head<3>().norm() < translation_gradient_tolerance_ &&
456 gradient.tail<3>().norm() < rotation_gradient_tolerance_) {
457 PCL_DEBUG("[estimateRigidTransformationNewton] finishing because gradient below "
458 "threshold: translation: %g<%g, rotation: %g<%g\n",
459 gradient.head<3>().norm(),
460 translation_gradient_tolerance_,
461 gradient.tail<3>().norm(),
462 rotation_gradient_tolerance_);
463 break;
464 }
465 } while (inner_iterations_ < max_inner_iterations_);
466 PCL_DEBUG("[estimateRigidTransformationNewton] solver finished after %i iterations "
467 "(of max %i)\n",
468 inner_iterations_,
469 max_inner_iterations_);
470 transformation_matrix.setIdentity();
471 applyState(transformation_matrix, x);
472}
473
474template <typename PointSource, typename PointTarget, typename Scalar>
475inline double
478{
479 Matrix4 transformation_matrix = gicp_->base_transformation_;
480 gicp_->applyState(transformation_matrix, x);
481 double f = 0;
482 int m = static_cast<int>(gicp_->tmp_idx_src_->size());
483 for (int i = 0; i < m; ++i) {
484 // The last coordinate, p_src[3] is guaranteed to be set to 1.0 in registration.hpp
485 Vector4fMapConst p_src =
486 (*gicp_->tmp_src_)[(*gicp_->tmp_idx_src_)[i]].getVector4fMap();
487 // The last coordinate, p_tgt[3] is guaranteed to be set to 1.0 in registration.hpp
489 (*gicp_->tmp_tgt_)[(*gicp_->tmp_idx_tgt_)[i]].getVector4fMap();
490 Eigen::Vector4f p_trans_src(transformation_matrix.template cast<float>() * p_src);
491 // Estimate the distance (cost function)
492 // The last coordinate is still guaranteed to be set to 1.0
493 // The d here is the negative of the d in the paper
494 Eigen::Vector3d d(p_trans_src[0] - p_tgt[0],
495 p_trans_src[1] - p_tgt[1],
496 p_trans_src[2] - p_tgt[2]);
497 Eigen::Vector3d Md(gicp_->mahalanobis((*gicp_->tmp_idx_src_)[i]) * d);
498 // increment= d'*Md/num_matches = d'*M*d/num_matches (we postpone
499 // 1/num_matches after the loop closes)
500 f += static_cast<double>(d.transpose() * Md);
501 }
502 return f / m;
503}
505template <typename PointSource, typename PointTarget, typename Scalar>
506inline void
509{
510 Matrix4 transformation_matrix = gicp_->base_transformation_;
511 gicp_->applyState(transformation_matrix, x);
512 // Zero out g
513 g.setZero();
514 // Eigen::Vector3d g_t = g.head<3> ();
515 // the transpose of the derivative of the cost function w.r.t rotation matrix
516 Eigen::Matrix3d dCost_dR_T = Eigen::Matrix3d::Zero();
517 int m = static_cast<int>(gicp_->tmp_idx_src_->size());
518 for (int i = 0; i < m; ++i) {
519 // The last coordinate, p_src[3] is guaranteed to be set to 1.0 in registration.hpp
520 Vector4fMapConst p_src =
521 (*gicp_->tmp_src_)[(*gicp_->tmp_idx_src_)[i]].getVector4fMap();
522 // The last coordinate, p_tgt[3] is guaranteed to be set to 1.0 in registration.hpp
523 Vector4fMapConst p_tgt =
524 (*gicp_->tmp_tgt_)[(*gicp_->tmp_idx_tgt_)[i]].getVector4fMap();
525
526 Eigen::Vector4f p_trans_src(transformation_matrix.template cast<float>() * p_src);
527 // The last coordinate is still guaranteed to be set to 1.0
528 // The d here is the negative of the d in the paper
529 Eigen::Vector3d d(p_trans_src[0] - p_tgt[0],
530 p_trans_src[1] - p_tgt[1],
531 p_trans_src[2] - p_tgt[2]);
532 // Md = M*d
533 Eigen::Vector3d Md(gicp_->mahalanobis((*gicp_->tmp_idx_src_)[i]) * d);
534 // Increment translation gradient
535 // g.head<3> ()+= 2*M*d/num_matches (we postpone 2/num_matches after the loop
536 // closes)
537 g.head<3>() += Md;
538 // Increment rotation gradient
539 p_trans_src.noalias() = gicp_->base_transformation_.template cast<float>() * p_src;
540 Eigen::Vector3d p_base_src(p_trans_src[0], p_trans_src[1], p_trans_src[2]);
541 dCost_dR_T.noalias() += p_base_src * Md.transpose();
542 }
543 g.head<3>() *= 2.0 / m;
544 dCost_dR_T *= 2.0 / m;
545 gicp_->computeRDerivative(x, dCost_dR_T, g);
546}
547
548template <typename PointSource, typename PointTarget, typename Scalar>
549inline void
552{
553 Matrix4 transformation_matrix = gicp_->base_transformation_;
554 gicp_->applyState(transformation_matrix, x);
555 f = 0;
556 g.setZero();
557 // the transpose of the derivative of the cost function w.r.t rotation matrix
558 Eigen::Matrix3d dCost_dR_T = Eigen::Matrix3d::Zero();
559 const int m = static_cast<int>(gicp_->tmp_idx_src_->size());
560 for (int i = 0; i < m; ++i) {
561 // The last coordinate, p_src[3] is guaranteed to be set to 1.0 in registration.hpp
562 Vector4fMapConst p_src =
563 (*gicp_->tmp_src_)[(*gicp_->tmp_idx_src_)[i]].getVector4fMap();
564 // The last coordinate, p_tgt[3] is guaranteed to be set to 1.0 in registration.hpp
565 Vector4fMapConst p_tgt =
566 (*gicp_->tmp_tgt_)[(*gicp_->tmp_idx_tgt_)[i]].getVector4fMap();
567 Eigen::Vector4f p_trans_src(transformation_matrix.template cast<float>() * p_src);
568 // The last coordinate is still guaranteed to be set to 1.0
569 // The d here is the negative of the d in the paper
570 Eigen::Vector3d d(p_trans_src[0] - p_tgt[0],
571 p_trans_src[1] - p_tgt[1],
572 p_trans_src[2] - p_tgt[2]);
573 // Md = M*d
574 Eigen::Vector3d Md(gicp_->mahalanobis((*gicp_->tmp_idx_src_)[i]) * d);
575 // Increment total error
576 f += static_cast<double>(d.transpose() * Md);
577 // Increment translation gradient
578 // g.head<3> ()+= 2*M*d/num_matches (we postpone 2/num_matches after the loop
579 // closes)
580 g.head<3>() += Md;
581 p_trans_src.noalias() = gicp_->base_transformation_.template cast<float>() * p_src;
582 Eigen::Vector3d p_base_src(p_trans_src[0], p_trans_src[1], p_trans_src[2]);
583 // Increment rotation gradient
584 dCost_dR_T.noalias() += p_base_src * Md.transpose();
585 }
586 f /= static_cast<double>(m);
587 g.head<3>() *= (2.0 / m);
588 dCost_dR_T *= 2.0 / m;
589 gicp_->computeRDerivative(x, dCost_dR_T, g);
590}
591
592template <typename PointSource, typename PointTarget, typename Scalar>
593inline void
596 Vector6d& gradient,
597 Matrix6d& hessian)
598{
599 Matrix4 transformation_matrix = gicp_->base_transformation_;
600 gicp_->applyState(transformation_matrix, x);
601 const Eigen::Matrix4f transformation_matrix_float =
602 transformation_matrix.template cast<float>();
603 const Eigen::Matrix4f base_transformation_float =
604 gicp_->base_transformation_.template cast<float>();
605 // Zero out gradient and hessian
606 gradient.setZero();
607 hessian.setZero();
608 // Helper matrices
609 Eigen::Matrix3d dR_dPhi;
610 Eigen::Matrix3d dR_dTheta;
611 Eigen::Matrix3d dR_dPsi;
612 gicp_->getRDerivatives(x[3], x[4], x[5], dR_dPhi, dR_dTheta, dR_dPsi);
613 Eigen::Matrix3d ddR_dPhi_dPhi;
614 Eigen::Matrix3d ddR_dPhi_dTheta;
615 Eigen::Matrix3d ddR_dPhi_dPsi;
616 Eigen::Matrix3d ddR_dTheta_dTheta;
617 Eigen::Matrix3d ddR_dTheta_dPsi;
618 Eigen::Matrix3d ddR_dPsi_dPsi;
619 gicp_->getR2ndDerivatives(x[3],
620 x[4],
621 x[5],
622 ddR_dPhi_dPhi,
623 ddR_dPhi_dTheta,
624 ddR_dPhi_dPsi,
625 ddR_dTheta_dTheta,
626 ddR_dTheta_dPsi,
627 ddR_dPsi_dPsi);
628 Eigen::Matrix3d dCost_dR_T = Eigen::Matrix3d::Zero();
629 Eigen::Matrix3d dCost_dR_T1 = Eigen::Matrix3d::Zero();
630 Eigen::Matrix3d dCost_dR_T2 = Eigen::Matrix3d::Zero();
631 Eigen::Matrix3d dCost_dR_T3 = Eigen::Matrix3d::Zero();
632 Eigen::Matrix3d dCost_dR_T1b = Eigen::Matrix3d::Zero();
633 Eigen::Matrix3d dCost_dR_T2b = Eigen::Matrix3d::Zero();
634 Eigen::Matrix3d dCost_dR_T3b = Eigen::Matrix3d::Zero();
635 Eigen::Matrix3d hessian_rot_phi = Eigen::Matrix3d::Zero();
636 Eigen::Matrix3d hessian_rot_theta = Eigen::Matrix3d::Zero();
637 Eigen::Matrix3d hessian_rot_psi = Eigen::Matrix3d::Zero();
638 Eigen::Matrix<double, 9, 6> hessian_rot_tmp = Eigen::Matrix<double, 9, 6>::Zero();
639
640 int m = static_cast<int>(gicp_->tmp_idx_src_->size());
641 for (int i = 0; i < m; ++i) {
642 // The last coordinate, p_src[3] is guaranteed to be set to 1.0 in registration.hpp
643 const auto& src_idx = (*gicp_->tmp_idx_src_)[i];
644 Vector4fMapConst p_src = (*gicp_->tmp_src_)[src_idx].getVector4fMap();
645 // The last coordinate, p_tgt[3] is guaranteed to be set to 1.0 in registration.hpp
646 Vector4fMapConst p_tgt =
647 (*gicp_->tmp_tgt_)[(*gicp_->tmp_idx_tgt_)[i]].getVector4fMap();
648 Eigen::Vector4f p_trans_src(transformation_matrix_float * p_src);
649 // The last coordinate is still guaranteed to be set to 1.0
650 // The d here is the negative of the d in the paper
651 const Eigen::Vector3d d(p_trans_src[0] - p_tgt[0],
652 p_trans_src[1] - p_tgt[1],
653 p_trans_src[2] - p_tgt[2]);
654 const Eigen::Matrix3d& M = gicp_->mahalanobis(src_idx);
655 const Eigen::Vector3d Md(M * d); // Md = M*d
656 gradient.head<3>() += Md; // translation gradient
657 hessian.topLeftCorner<3, 3>() += M; // translation-translation hessian
658 p_trans_src.noalias() = base_transformation_float * p_src;
659 const Eigen::Vector3d p_base_src(p_trans_src[0], p_trans_src[1], p_trans_src[2]);
660 dCost_dR_T.noalias() += p_base_src * Md.transpose();
661 dCost_dR_T1b += p_base_src[0] * M;
662 dCost_dR_T2b += p_base_src[1] * M;
663 dCost_dR_T3b += p_base_src[2] * M;
664 hessian_rot_tmp.noalias() +=
665 Eigen::Map<const Eigen::Matrix<double, 9, 1>>{M.data()} *
666 (Eigen::Matrix<double, 1, 6>() << p_base_src[0] * p_base_src[0],
667 p_base_src[0] * p_base_src[1],
668 p_base_src[0] * p_base_src[2],
669 p_base_src[1] * p_base_src[1],
670 p_base_src[1] * p_base_src[2],
671 p_base_src[2] * p_base_src[2])
672 .finished();
673 }
674 gradient.head<3>() *= 2.0 / m; // translation gradient
675 dCost_dR_T *= 2.0 / m;
676 gicp_->computeRDerivative(x, dCost_dR_T, gradient); // rotation gradient
677 hessian.topLeftCorner<3, 3>() *= 2.0 / m; // translation-translation hessian
678 // translation-rotation hessian
679 dCost_dR_T1.row(0) = dCost_dR_T1b.col(0);
680 dCost_dR_T1.row(1) = dCost_dR_T2b.col(0);
681 dCost_dR_T1.row(2) = dCost_dR_T3b.col(0);
682 dCost_dR_T2.row(0) = dCost_dR_T1b.col(1);
683 dCost_dR_T2.row(1) = dCost_dR_T2b.col(1);
684 dCost_dR_T2.row(2) = dCost_dR_T3b.col(1);
685 dCost_dR_T3.row(0) = dCost_dR_T1b.col(2);
686 dCost_dR_T3.row(1) = dCost_dR_T2b.col(2);
687 dCost_dR_T3.row(2) = dCost_dR_T3b.col(2);
688 dCost_dR_T1 *= 2.0 / m;
689 dCost_dR_T2 *= 2.0 / m;
690 dCost_dR_T3 *= 2.0 / m;
691 hessian(3, 0) = (dR_dPhi * dCost_dR_T1).trace();
692 hessian(4, 0) = (dR_dTheta * dCost_dR_T1).trace();
693 hessian(5, 0) = (dR_dPsi * dCost_dR_T1).trace();
694 hessian(3, 1) = (dR_dPhi * dCost_dR_T2).trace();
695 hessian(4, 1) = (dR_dTheta * dCost_dR_T2).trace();
696 hessian(5, 1) = (dR_dPsi * dCost_dR_T2).trace();
697 hessian(3, 2) = (dR_dPhi * dCost_dR_T3).trace();
698 hessian(4, 2) = (dR_dTheta * dCost_dR_T3).trace();
699 hessian(5, 2) = (dR_dPsi * dCost_dR_T3).trace();
700 hessian.block<3, 3>(0, 3) = hessian.block<3, 3>(3, 0).transpose();
701 // rotation-rotation hessian
702 int lookup[3][3] = {{0, 1, 2}, {1, 3, 4}, {2, 4, 5}};
703 for (int l = 0; l < 3; ++l) {
704 for (int i = 0; i < 3; ++i) {
705 double phi_tmp = 0.0, theta_tmp = 0.0, psi_tmp = 0.0;
706 for (int j = 0; j < 3; ++j) {
707 for (int k = 0; k < 3; ++k) {
708 phi_tmp += hessian_rot_tmp(3 * j + i, lookup[l][k]) * dR_dPhi(j, k);
709 theta_tmp += hessian_rot_tmp(3 * j + i, lookup[l][k]) * dR_dTheta(j, k);
710 psi_tmp += hessian_rot_tmp(3 * j + i, lookup[l][k]) * dR_dPsi(j, k);
711 }
712 }
713 hessian_rot_phi(i, l) = phi_tmp;
714 hessian_rot_theta(i, l) = theta_tmp;
715 hessian_rot_psi(i, l) = psi_tmp;
716 }
717 }
718 hessian_rot_phi *= 2.0 / m;
719 hessian_rot_theta *= 2.0 / m;
720 hessian_rot_psi *= 2.0 / m;
721 hessian(3, 3) = (dR_dPhi.transpose() * hessian_rot_phi).trace() +
722 (ddR_dPhi_dPhi * dCost_dR_T).trace();
723 hessian(3, 4) = (dR_dPhi.transpose() * hessian_rot_theta).trace() +
724 (ddR_dPhi_dTheta * dCost_dR_T).trace();
725 hessian(3, 5) = (dR_dPhi.transpose() * hessian_rot_psi).trace() +
726 (ddR_dPhi_dPsi * dCost_dR_T).trace();
727 hessian(4, 4) = (dR_dTheta.transpose() * hessian_rot_theta).trace() +
728 (ddR_dTheta_dTheta * dCost_dR_T).trace();
729 hessian(4, 5) = (dR_dTheta.transpose() * hessian_rot_psi).trace() +
730 (ddR_dTheta_dPsi * dCost_dR_T).trace();
731 hessian(5, 5) = (dR_dPsi.transpose() * hessian_rot_psi).trace() +
732 (ddR_dPsi_dPsi * dCost_dR_T).trace();
733 hessian(4, 3) = hessian(3, 4);
734 hessian(5, 3) = hessian(3, 5);
735 hessian(5, 4) = hessian(4, 5);
736}
737
738template <typename PointSource, typename PointTarget, typename Scalar>
742{
743 auto translation_epsilon = gicp_->translation_gradient_tolerance_;
744 auto rotation_epsilon = gicp_->rotation_gradient_tolerance_;
745
746 if ((translation_epsilon < 0.) || (rotation_epsilon < 0.))
748
749 // express translation gradient as norm of translation parameters
750 auto translation_grad = g.head<3>().norm();
751
752 // express rotation gradient as a norm of rotation parameters
753 auto rotation_grad = g.tail<3>().norm();
754
755 if ((translation_grad < translation_epsilon) && (rotation_grad < rotation_epsilon))
756 return BFGSSpace::Success;
757
758 return BFGSSpace::Running;
759}
760
761template <typename PointSource, typename PointTarget, typename Scalar>
762inline void
765{
767 // Difference between consecutive transforms
768 double delta = 0;
769 // Get the size of the source point cloud
770 const std::size_t N = indices_->size();
771 // Set the mahalanobis matrices to identity
772 mahalanobis_.resize(N, Eigen::Matrix3d::Identity());
773 // Compute target cloud covariance matrices
774 if ((!target_covariances_) || (target_covariances_->empty())) {
775 target_covariances_.reset(new MatricesVector);
776 computeCovariances<PointTarget>(target_, tree_, *target_covariances_);
777 }
778 // Compute input cloud covariance matrices
779 if ((!input_covariances_) || (input_covariances_->empty())) {
780 input_covariances_.reset(new MatricesVector);
781 computeCovariances<PointSource>(input_, tree_reciprocal_, *input_covariances_);
782 }
783
784 base_transformation_ = Matrix4::Identity();
785 nr_iterations_ = 0;
786 converged_ = false;
787 pcl::Indices nn_indices(1);
788 std::vector<float> nn_dists(1);
789
790 pcl::transformPointCloud(output, output, guess);
792 corr_estimation;
793 corr_estimation.setNumberOfThreads(threads_);
794 // setSearchMethodSource is not necessary because we do not use
795 // determineReciprocalCorrespondences
796 corr_estimation.setSearchMethodTarget(this->getSearchMethodTarget());
797 corr_estimation.setInputTarget(target_);
798 auto output_transformed = pcl::make_shared<PointCloudSource>();
799 output_transformed->resize(output.size());
800 pcl::Correspondences correspondences;
801
802 while (!converged_) {
803 std::size_t cnt = 0;
804 pcl::Indices source_indices(indices_->size());
805 pcl::Indices target_indices(indices_->size());
806
807 // guess corresponds to base_t and transformation_ to t
808 Eigen::Matrix4d transform_R = Eigen::Matrix4d::Zero();
809 for (std::size_t i = 0; i < 4; i++)
810 for (std::size_t j = 0; j < 4; j++)
811 for (std::size_t k = 0; k < 4; k++)
812 transform_R(i, j) += static_cast<double>(transformation_(i, k)) *
813 static_cast<double>(guess(k, j));
814
815 Eigen::Matrix3d R = transform_R.topLeftCorner<3, 3>();
816
818 output, *output_transformed, transformation_.template cast<float>(), false);
819 corr_estimation.setInputSource(output_transformed);
820 corr_estimation.determineCorrespondences(correspondences, corr_dist_threshold_);
821 cnt = 0;
822 for (const auto& corr : correspondences) {
823 source_indices[cnt] = corr.index_query;
824 target_indices[cnt] = corr.index_match;
825 const Eigen::Matrix3d& C1 = (*input_covariances_)[corr.index_query];
826 const Eigen::Matrix3d& C2 = (*target_covariances_)[corr.index_match];
827 pcl::invert3x3SymMatrix<Eigen::Matrix3d>(R * C1 * R.transpose() + C2,
828 mahalanobis_[corr.index_query]);
829 ++cnt;
830 }
831 // Resize to the actual number of valid correspondences
832 source_indices.resize(cnt);
833 target_indices.resize(cnt);
834 /* optimize transformation using the current assignment and Mahalanobis metrics*/
835 previous_transformation_ = transformation_;
836 // optimization right here
837 try {
838 rigid_transformation_estimation_(
839 output, source_indices, *target_, target_indices, transformation_);
840 /* compute the delta from this iteration */
841 delta = 0.;
842 for (int k = 0; k < 4; k++) {
843 for (int l = 0; l < 4; l++) {
844 double ratio = 1;
845 if (k < 3 && l < 3) // rotation part of the transform
846 ratio = 1. / rotation_epsilon_;
847 else
848 ratio = 1. / transformation_epsilon_;
849 double c_delta =
850 ratio * std::abs(previous_transformation_(k, l) - transformation_(k, l));
851 if (c_delta > delta)
852 delta = c_delta;
853 }
854 }
855 } catch (PCLException& e) {
856 PCL_DEBUG("[pcl::%s::computeTransformation] Optimization issue %s\n",
857 getClassName().c_str(),
858 e.what());
859 break;
860 }
861 nr_iterations_++;
862
863 if (update_visualizer_ != nullptr) {
864 PointCloudSourcePtr input_transformed(new PointCloudSource);
865 pcl::transformPointCloud(output, *input_transformed, transformation_);
866 update_visualizer_(*input_transformed, source_indices, *target_, target_indices);
867 }
868
869 // Check for convergence
870 if (nr_iterations_ >= max_iterations_ || delta < 1) {
871 converged_ = true;
872 PCL_DEBUG("[pcl::%s::computeTransformation] Convergence reached. Number of "
873 "iterations: %d out of %d. Transformation difference: %f\n",
874 getClassName().c_str(),
875 nr_iterations_,
876 max_iterations_,
877 (transformation_ - previous_transformation_).array().abs().sum());
878 previous_transformation_ = transformation_;
879 }
880 else
881 PCL_DEBUG("[pcl::%s::computeTransformation] Convergence failed\n",
882 getClassName().c_str());
883 }
884 final_transformation_.noalias() = previous_transformation_ * guess;
885
886 PCL_DEBUG("Transformation "
887 "is:\n\t%5f\t%5f\t%5f\t%5f\n\t%5f\t%5f\t%5f\t%5f\n\t%5f\t%5f\t%5f\t%5f\n\t%"
888 "5f\t%5f\t%5f\t%5f\n",
889 final_transformation_(0, 0),
890 final_transformation_(0, 1),
891 final_transformation_(0, 2),
892 final_transformation_(0, 3),
893 final_transformation_(1, 0),
894 final_transformation_(1, 1),
895 final_transformation_(1, 2),
896 final_transformation_(1, 3),
897 final_transformation_(2, 0),
898 final_transformation_(2, 1),
899 final_transformation_(2, 2),
900 final_transformation_(2, 3),
901 final_transformation_(3, 0),
902 final_transformation_(3, 1),
903 final_transformation_(3, 2),
904 final_transformation_(3, 3));
905
906 // Transform the point cloud
907 pcl::transformPointCloud(*input_, output, final_transformation_);
908}
909
910template <typename PointSource, typename PointTarget, typename Scalar>
911void
913 Matrix4& t, const Vector6d& x) const
914{
915 // Z Y X euler angles convention
916 Matrix3 R = (AngleAxis(static_cast<Scalar>(x[5]), Vector3::UnitZ()) *
917 AngleAxis(static_cast<Scalar>(x[4]), Vector3::UnitY()) *
918 AngleAxis(static_cast<Scalar>(x[3]), Vector3::UnitX()))
919 .toRotationMatrix();
920 Matrix4 T = Matrix4::Identity();
921 T.template block<3, 3>(0, 0) = R;
922 T.template block<3, 1>(0, 3) = Vector3(
923 static_cast<Scalar>(x[0]), static_cast<Scalar>(x[1]), static_cast<Scalar>(x[2]));
924 t = T * t;
925}
926
927} // namespace pcl
928
929#endif // PCL_REGISTRATION_IMPL_GICP_HPP_
BFGS stands for Broyden–Fletcher–Goldfarb–Shanno (BFGS) method for solving unconstrained nonlinear op...
Definition bfgs.h:123
BFGSSpace::Status testGradient()
Definition bfgs.h:478
BFGSSpace::Status minimizeInit(FVectorType &x)
Definition bfgs.h:363
BFGSSpace::Status minimizeOneStep(FVectorType &x)
Definition bfgs.h:395
Parameters parameters
Definition bfgs.h:171
GeneralizedIterativeClosestPoint is an ICP variant that implements the generalized iterative closest ...
Definition gicp.h:76
void estimateRigidTransformationBFGS(const PointCloudSource &cloud_src, const pcl::Indices &indices_src, const PointCloudTarget &cloud_tgt, const pcl::Indices &indices_tgt, Matrix4 &transformation_matrix)
Estimate a rigid rotation transformation between a source and a target point cloud using an iterative...
Definition gicp.hpp:291
typename IterativeClosestPoint< PointSource, PointTarget, Scalar >::Matrix4 Matrix4
Definition gicp.h:130
void applyState(Matrix4 &t, const Vector6d &x) const
compute transformation matrix from transformation matrix
Definition gicp.hpp:912
Eigen::Matrix< double, 6, 6 > Matrix6d
Definition gicp.h:131
typename Eigen::Matrix< Scalar, 3, 1 > Vector3
Definition gicp.h:125
std::vector< Eigen::Matrix3d, Eigen::aligned_allocator< Eigen::Matrix3d > > MatricesVector
Definition gicp.h:112
void estimateRigidTransformationNewton(const PointCloudSource &cloud_src, const pcl::Indices &indices_src, const PointCloudTarget &cloud_tgt, const pcl::Indices &indices_tgt, Matrix4 &transformation_matrix)
Estimate a rigid rotation transformation between a source and a target point cloud using an iterative...
Definition gicp.hpp:367
typename PointCloudSource::Ptr PointCloudSourcePtr
Definition gicp.h:101
void setNumberOfThreads(unsigned int nr_threads=0)
Initialize the scheduler and set the number of threads to use.
Definition gicp.hpp:50
void computeRDerivative(const Vector6d &x, const Eigen::Matrix3d &dCost_dR_T, Vector6d &g) const
Computes the derivative of the cost function w.r.t rotation angles.
Definition gicp.hpp:199
typename Eigen::AngleAxis< Scalar > AngleAxis
Definition gicp.h:132
void computeTransformation(PointCloudSource &output, const Matrix4 &guess) override
Rigid transformation computation method with initial guess.
Definition gicp.hpp:764
void computeCovariances(typename pcl::PointCloud< PointT >::ConstPtr cloud, const typename pcl::search::Search< PointT >::Ptr tree, MatricesVector &cloud_covariances)
compute points covariances matrices according to the K nearest neighbors.
Definition gicp.hpp:73
typename Eigen::Matrix< Scalar, 3, 3 > Matrix3
Definition gicp.h:128
Eigen::Matrix< double, 6, 1 > Vector6d
Definition gicp.h:127
An exception that is thrown when the number of correspondents is not equal to the minimum required.
Definition exceptions.h:63
A base class for all pcl exceptions which inherits from std::runtime_error.
Definition exceptions.h:67
std::size_t size() const
shared_ptr< const PointCloud< PointT > > ConstPtr
bool initComputeReciprocal()
Internal computation when reciprocal lookup is needed.
An exception that is thrown when the non linear solver didn't converge.
Definition exceptions.h:49
void setSearchMethodTarget(const KdTreePtr &tree, const bool force_no_recompute=false)
Provide a pointer to the search object used to find correspondences in the target cloud.
void setNumberOfThreads(const unsigned int nr_threads)
Set the number of threads to use.
void setInputSource(const PointCloudSourceConstPtr &cloud)
Provide a pointer to the input source (e.g., the point cloud that we want to align to the target)
void setInputTarget(const PointCloudTargetConstPtr &cloud)
Provide a pointer to the input target (e.g., the point cloud that we want to align the input source t...
CorrespondenceEstimation represents a simple class for determining correspondences between target and...
void determineCorrespondences(pcl::Correspondences &correspondences, const double max_distance=std::numeric_limits< double >::max()) override
Determine the correspondences between input and target cloud.
shared_ptr< pcl::search::Search< PointT > > Ptr
Definition search.h:84
virtual int nearestKSearch(const PointT &point, int k, Indices &k_indices, std::vector< float > &k_sqr_distances) const =0
Search for the k-nearest neighbors for the given query point.
void transformPointCloud(const pcl::PointCloud< PointT > &cloud_in, pcl::PointCloud< PointT > &cloud_out, const Eigen::Matrix< Scalar, 4, 4 > &transform, bool copy_all_fields)
Apply a rigid transform defined by a 4x4 matrix.
@ NoProgress
Definition bfgs.h:77
@ Running
Definition bfgs.h:75
@ Success
Definition bfgs.h:76
@ NegativeGradientEpsilon
Definition bfgs.h:73
std::vector< pcl::Correspondence, Eigen::aligned_allocator< pcl::Correspondence > > Correspondences
const Eigen::Map< const Eigen::Vector4f, Eigen::Aligned > Vector4fMapConst
IndicesAllocator<> Indices
Type used for indices in PCL.
Definition types.h:133
Scalar sigma
Definition bfgs.h:149
Scalar tau2
Definition bfgs.h:151
Scalar rho
Definition bfgs.h:148
Scalar tau1
Definition bfgs.h:150
Scalar tau3
Definition bfgs.h:152
Index order
Definition bfgs.h:154
void dfddf(const Vector6d &x, Vector6d &df, Matrix6d &ddf)
Definition gicp.hpp:595
void df(const Vector6d &x, Vector6d &df) override
Definition gicp.hpp:508
BFGSSpace::Status checkGradient(const Vector6d &g) override
Definition gicp.hpp:741
void fdf(const Vector6d &x, double &f, Vector6d &df) override
Definition gicp.hpp:551
A point structure representing Euclidean xyz coordinates, and the RGB color.