123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280 |
- #ifndef FAST_GICP_FAST_GICP_MP_IMPL_HPP
- #define FAST_GICP_FAST_GICP_MP_IMPL_HPP
- #include <Eigen/Core>
- #include <Eigen/Geometry>
- #include <pcl/point_types.h>
- #include <pcl/point_cloud.h>
- #include <pcl/search/kdtree.h>
- #include <pcl/registration/registration.h>
- #include <sophus/so3.hpp>
- #include <fast_gicp/so3/so3.hpp>
- #include <fast_gicp/opt/gauss_newton.hpp>
- #include <fast_gicp/gicp/experimental/fast_gicp_mp.hpp>
- namespace fast_gicp {
- template<typename PointSource, typename PointTarget>
- FastGICPMultiPoints<PointSource, PointTarget>::FastGICPMultiPoints() {
- #ifdef _OPENMP
- num_threads_ = omp_get_max_threads();
- #else
- num_threads_ = 1;
- #endif
- k_correspondences_ = 20;
- reg_name_ = "FastGICPMultiPoints";
- max_iterations_ = 64;
- rotation_epsilon_ = 1e-5;
- transformation_epsilon_ = 1e-5;
- // corr_dist_threshold_ = 1.0;
- regularization_method_ = RegularizationMethod::PLANE;
- corr_dist_threshold_ = std::numeric_limits<float>::max();
- neighbor_search_radius_ = 0.5;
- }
- template<typename PointSource, typename PointTarget>
- FastGICPMultiPoints<PointSource, PointTarget>::~FastGICPMultiPoints() {}
- template<typename PointSource, typename PointTarget>
- void FastGICPMultiPoints<PointSource, PointTarget>::setRotationEpsilon(double eps) {
- rotation_epsilon_ = eps;
- }
- template<typename PointSource, typename PointTarget>
- void FastGICPMultiPoints<PointSource, PointTarget>::setNumThreads(int n) {
- num_threads_ = n;
- #ifdef _OPENMP
- if(n == 0) {
- num_threads_ = omp_get_max_threads();
- }
- #endif
- }
- template<typename PointSource, typename PointTarget>
- void FastGICPMultiPoints<PointSource, PointTarget>::setCorrespondenceRandomness(int k) {
- k_correspondences_ = k;
- }
- template<typename PointSource, typename PointTarget>
- void FastGICPMultiPoints<PointSource, PointTarget>::setRegularizationMethod(RegularizationMethod method) {
- regularization_method_ = method;
- }
- template<typename PointSource, typename PointTarget>
- void FastGICPMultiPoints<PointSource, PointTarget>::setInputSource(const PointCloudSourceConstPtr& cloud) {
- pcl::Registration<PointSource, PointTarget, Scalar>::setInputSource(cloud);
- calculate_covariances(*cloud, source_kdtree, source_covs);
- }
- template<typename PointSource, typename PointTarget>
- void FastGICPMultiPoints<PointSource, PointTarget>::setInputTarget(const PointCloudTargetConstPtr& cloud) {
- pcl::Registration<PointSource, PointTarget, Scalar>::setInputTarget(cloud);
- calculate_covariances(cloud, target_kdtree, target_covs);
- }
- template<typename PointSource, typename PointTarget>
- void FastGICPMultiPoints<PointSource, PointTarget>::computeTransformation(PointCloudSource& output, const Matrix4& guess) {
- Eigen::Matrix<float, 6, 1> x0;
- x0.head<3>() = Sophus::SO3f(guess.template block<3, 3>(0, 0)).log();
- x0.tail<3>() = guess.template block<3, 1>(0, 3);
- // if(x0.head<3>().norm() < 1e-2) {
- // x0.head<3>() = (Eigen::Vector3f::Random()).normalized() * 1e-2;
- // }
- converged_ = false;
- GaussNewton<double, 6> solver;
- for(int i = 0; i < max_iterations_; i++) {
- nr_iterations_ = i;
- update_correspondences(x0);
- Eigen::MatrixXf J;
- Eigen::VectorXf loss = loss_ls(x0, &J);
- Eigen::Matrix<float, 6, 1> delta = solver.delta(loss.cast<double>(), J.cast<double>()).cast<float>();
- x0.head<3>() = (Sophus::SO3f::exp(-delta.head<3>()) * Sophus::SO3f::exp(x0.head<3>())).log();
- x0.tail<3>() -= delta.tail<3>();
- if(is_converged(delta)) {
- converged_ = true;
- break;
- }
- }
- final_transformation_.setIdentity();
- final_transformation_.template block<3, 3>(0, 0) = Sophus::SO3f::exp(x0.head<3>()).matrix();
- final_transformation_.template block<3, 1>(0, 3) = x0.tail<3>();
- pcl::transformPointCloud(*input_, output, final_transformation_);
- }
- template<typename PointSource, typename PointTarget>
- bool FastGICPMultiPoints<PointSource, PointTarget>::is_converged(const Eigen::Matrix<float, 6, 1>& delta) const {
- double accum = 0.0;
- Eigen::Matrix3f R = Sophus::SO3f::exp(delta.head<3>()).matrix() - Eigen::Matrix3f::Identity();
- Eigen::Vector3f t = delta.tail<3>();
- Eigen::Matrix3f r_delta = 1.0 / rotation_epsilon_ * R.array().abs();
- Eigen::Vector3f t_delta = 1.0 / transformation_epsilon_ * t.array().abs();
- return std::max(r_delta.maxCoeff(), t_delta.maxCoeff()) < 1;
- }
- template<typename PointSource, typename PointTarget>
- void FastGICPMultiPoints<PointSource, PointTarget>::update_correspondences(const Eigen::Matrix<float, 6, 1>& x) {
- Eigen::Matrix4f trans = Eigen::Matrix4f::Identity();
- trans.block<3, 3>(0, 0) = Sophus::SO3f::exp(x.head<3>()).matrix();
- trans.block<3, 1>(0, 3) = x.tail<3>();
- correspondences.resize(input_->size());
- sq_distances.resize(input_->size());
- #pragma omp parallel for num_threads(num_threads_)
- for(int i = 0; i < input_->size(); i++) {
- PointTarget pt;
- pt.getVector4fMap() = trans * input_->at(i).getVector4fMap();
- std::vector<int> k_indices;
- std::vector<float> k_sq_dists;
- target_kdtree.radiusSearch(pt, neighbor_search_radius_, k_indices, k_sq_dists);
- if(k_indices.empty()) {
- // target_kdtree.nearestKSearch(pt, 1, k_indices, k_sq_dists);
- }
- correspondences[i] = k_indices;
- sq_distances[i] = k_sq_dists;
- }
- }
- template<typename PointSource, typename PointTarget>
- Eigen::VectorXf FastGICPMultiPoints<PointSource, PointTarget>::loss_ls(const Eigen::Matrix<float, 6, 1>& x, Eigen::MatrixXf* J) const {
- Eigen::Matrix4f trans = Eigen::Matrix4f::Identity();
- trans.block<3, 3>(0, 0) = Sophus::SO3f::exp(x.head<3>()).matrix();
- trans.block<3, 1>(0, 3) = x.tail<3>();
- std::vector<Eigen::Vector3f, Eigen::aligned_allocator<Eigen::Vector3f>> losses(input_->size());
- // use row-major arrangement for ease of repacking
- std::vector<Eigen::Matrix<float, 3, 6, Eigen::RowMajor>, Eigen::aligned_allocator<Eigen::Matrix<float, 3, 6, Eigen::RowMajor>>> Js(input_->size());
- std::atomic_int count(0);
- #pragma omp parallel for num_threads(num_threads_)
- for(int i = 0; i < correspondences.size(); i++) {
- int source_index = i;
- const auto& mean_A = input_->at(source_index).getVector4fMap();
- const auto& cov_A = source_covs[source_index];
- const auto& k_indices = correspondences[i];
- const auto& k_sq_dists = sq_distances[i];
- if(k_indices.empty()) {
- continue;
- }
- double sum_w = 0.0;
- Eigen::Vector4d sum_mean_B = Eigen::Vector4d::Zero();
- Eigen::Matrix4d sum_cov_B = Eigen::Matrix4d::Zero();
- for(int j = 0; j < k_indices.size(); j++) {
- double w = 1 - std::sqrt(k_sq_dists[j]) / neighbor_search_radius_;
- w = std::max(1e-3, std::min(1.0, w));
- sum_w += w;
- int target_index = k_indices[j];
- sum_mean_B += w * target_->at(target_index).getVector4fMap().template cast<double>();
- sum_cov_B += w * target_covs[target_index].template cast<double>();
- }
- Eigen::Vector4f mean_B = (sum_mean_B / sum_w).template cast<float>();
- Eigen::Matrix4f cov_B = (sum_cov_B / sum_w).template cast<float>();
- Eigen::Vector4f transed_mean_A = trans * mean_A;
- Eigen::Vector4f d = mean_B - transed_mean_A;
- Eigen::Matrix4f RCR = cov_B + trans * cov_A * trans.transpose();
- RCR(3, 3) = 1;
- Eigen::Matrix4f RCR_inv = RCR.inverse();
- Eigen::Vector4f RCRd = RCR_inv * d;
- Eigen::Matrix<float, 4, 6> dtdx0 = Eigen::Matrix<float, 4, 6>::Zero();
- dtdx0.block<3, 3>(0, 0) = skew(transed_mean_A.head<3>());
- dtdx0.block<3, 3>(0, 3) = -Eigen::Matrix3f::Identity();
- Eigen::Matrix<float, 4, 6> jlossexp = RCR_inv * dtdx0;
- int n = count++;
- losses[n] = RCRd.head<3>();
- Js[n] = jlossexp.block<3, 6>(0, 0);
- }
- int final_size = count;
- Eigen::VectorXf loss = Eigen::Map<Eigen::VectorXf>(losses.front().data(), final_size * 3);
- *J = Eigen::Map<Eigen::MatrixXf>(Js.front().data(), 6, final_size * 3).transpose();
- return loss;
- }
- template<typename PointSource, typename PointTarget>
- template<typename PointT>
- bool FastGICPMultiPoints<PointSource, PointTarget>::calculate_covariances(const pcl::shared_ptr<const pcl::PointCloud<PointT>>& cloud, pcl::search::KdTree<PointT>& kdtree, std::vector<Matrix4, Eigen::aligned_allocator<Matrix4>>& covariances) {
- kdtree.setInputCloud(cloud);
- covariances.resize(cloud->size());
- #pragma omp parallel for num_threads(num_threads_)
- for(int i = 0; i < cloud->size(); i++) {
- std::vector<int> k_indices;
- std::vector<float> k_sq_distances;
- kdtree.nearestKSearch(cloud->at(i), k_correspondences_, k_indices, k_sq_distances);
- Eigen::Matrix<float, 4, -1> data(4, k_correspondences_);
- for(int j = 0; j < k_indices.size(); j++) {
- data.col(j) = cloud->at(k_indices[j]).getVector4fMap();
- }
- data.colwise() -= data.rowwise().mean().eval();
- Eigen::Matrix4f cov = data * data.transpose();
- if(regularization_method_ == RegularizationMethod::FROBENIUS) {
- double lambda = 1e-3;
- Eigen::Matrix3d C = cov.block<3, 3>(0, 0).cast<double>() + lambda * Eigen::Matrix3d::Identity();
- Eigen::Matrix3d C_inv = C.inverse();
- covariances[i].setZero();
- covariances[i].template block<3, 3>(0, 0) = (C_inv / C_inv.norm()).inverse().cast<float>();
- } else {
- Eigen::JacobiSVD<Eigen::Matrix3f> svd(cov.block<3, 3>(0, 0), Eigen::ComputeFullU | Eigen::ComputeFullV);
- Eigen::Vector3f values;
- switch(regularization_method_) {
- default:
- std::cerr << "here must not be reached" << std::endl;
- abort();
- case RegularizationMethod::PLANE:
- values = Eigen::Vector3f(1, 1, 1e-3);
- break;
- case RegularizationMethod::MIN_EIG:
- values = svd.singularValues().array().max(1e-3);
- break;
- case RegularizationMethod::NORMALIZED_MIN_EIG:
- values = svd.singularValues() / svd.singularValues().maxCoeff();
- values = values.array().max(1e-3);
- break;
- }
- covariances[i].setZero();
- covariances[i].template block<3, 3>(0, 0) = svd.matrixU() * values.asDiagonal() * svd.matrixV().transpose();
- }
- }
- return true;
- }
- } // namespace fast_gicp
- #endif
|