fast_gicp_mp_impl.hpp 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. #ifndef FAST_GICP_FAST_GICP_MP_IMPL_HPP
  2. #define FAST_GICP_FAST_GICP_MP_IMPL_HPP
  3. #include <Eigen/Core>
  4. #include <Eigen/Geometry>
  5. #include <pcl/point_types.h>
  6. #include <pcl/point_cloud.h>
  7. #include <pcl/search/kdtree.h>
  8. #include <pcl/registration/registration.h>
  9. #include <sophus/so3.hpp>
  10. #include <fast_gicp/so3/so3.hpp>
  11. #include <fast_gicp/opt/gauss_newton.hpp>
  12. #include <fast_gicp/gicp/experimental/fast_gicp_mp.hpp>
  13. namespace fast_gicp {
  14. template<typename PointSource, typename PointTarget>
  15. FastGICPMultiPoints<PointSource, PointTarget>::FastGICPMultiPoints() {
  16. #ifdef _OPENMP
  17. num_threads_ = omp_get_max_threads();
  18. #else
  19. num_threads_ = 1;
  20. #endif
  21. k_correspondences_ = 20;
  22. reg_name_ = "FastGICPMultiPoints";
  23. max_iterations_ = 64;
  24. rotation_epsilon_ = 1e-5;
  25. transformation_epsilon_ = 1e-5;
  26. // corr_dist_threshold_ = 1.0;
  27. regularization_method_ = RegularizationMethod::PLANE;
  28. corr_dist_threshold_ = std::numeric_limits<float>::max();
  29. neighbor_search_radius_ = 0.5;
  30. }
  31. template<typename PointSource, typename PointTarget>
  32. FastGICPMultiPoints<PointSource, PointTarget>::~FastGICPMultiPoints() {}
  33. template<typename PointSource, typename PointTarget>
  34. void FastGICPMultiPoints<PointSource, PointTarget>::setRotationEpsilon(double eps) {
  35. rotation_epsilon_ = eps;
  36. }
  37. template<typename PointSource, typename PointTarget>
  38. void FastGICPMultiPoints<PointSource, PointTarget>::setNumThreads(int n) {
  39. num_threads_ = n;
  40. #ifdef _OPENMP
  41. if(n == 0) {
  42. num_threads_ = omp_get_max_threads();
  43. }
  44. #endif
  45. }
  46. template<typename PointSource, typename PointTarget>
  47. void FastGICPMultiPoints<PointSource, PointTarget>::setCorrespondenceRandomness(int k) {
  48. k_correspondences_ = k;
  49. }
  50. template<typename PointSource, typename PointTarget>
  51. void FastGICPMultiPoints<PointSource, PointTarget>::setRegularizationMethod(RegularizationMethod method) {
  52. regularization_method_ = method;
  53. }
  54. template<typename PointSource, typename PointTarget>
  55. void FastGICPMultiPoints<PointSource, PointTarget>::setInputSource(const PointCloudSourceConstPtr& cloud) {
  56. pcl::Registration<PointSource, PointTarget, Scalar>::setInputSource(cloud);
  57. calculate_covariances(*cloud, source_kdtree, source_covs);
  58. }
  59. template<typename PointSource, typename PointTarget>
  60. void FastGICPMultiPoints<PointSource, PointTarget>::setInputTarget(const PointCloudTargetConstPtr& cloud) {
  61. pcl::Registration<PointSource, PointTarget, Scalar>::setInputTarget(cloud);
  62. calculate_covariances(cloud, target_kdtree, target_covs);
  63. }
  64. template<typename PointSource, typename PointTarget>
  65. void FastGICPMultiPoints<PointSource, PointTarget>::computeTransformation(PointCloudSource& output, const Matrix4& guess) {
  66. Eigen::Matrix<float, 6, 1> x0;
  67. x0.head<3>() = Sophus::SO3f(guess.template block<3, 3>(0, 0)).log();
  68. x0.tail<3>() = guess.template block<3, 1>(0, 3);
  69. // if(x0.head<3>().norm() < 1e-2) {
  70. // x0.head<3>() = (Eigen::Vector3f::Random()).normalized() * 1e-2;
  71. // }
  72. converged_ = false;
  73. GaussNewton<double, 6> solver;
  74. for(int i = 0; i < max_iterations_; i++) {
  75. nr_iterations_ = i;
  76. update_correspondences(x0);
  77. Eigen::MatrixXf J;
  78. Eigen::VectorXf loss = loss_ls(x0, &J);
  79. Eigen::Matrix<float, 6, 1> delta = solver.delta(loss.cast<double>(), J.cast<double>()).cast<float>();
  80. x0.head<3>() = (Sophus::SO3f::exp(-delta.head<3>()) * Sophus::SO3f::exp(x0.head<3>())).log();
  81. x0.tail<3>() -= delta.tail<3>();
  82. if(is_converged(delta)) {
  83. converged_ = true;
  84. break;
  85. }
  86. }
  87. final_transformation_.setIdentity();
  88. final_transformation_.template block<3, 3>(0, 0) = Sophus::SO3f::exp(x0.head<3>()).matrix();
  89. final_transformation_.template block<3, 1>(0, 3) = x0.tail<3>();
  90. pcl::transformPointCloud(*input_, output, final_transformation_);
  91. }
  92. template<typename PointSource, typename PointTarget>
  93. bool FastGICPMultiPoints<PointSource, PointTarget>::is_converged(const Eigen::Matrix<float, 6, 1>& delta) const {
  94. double accum = 0.0;
  95. Eigen::Matrix3f R = Sophus::SO3f::exp(delta.head<3>()).matrix() - Eigen::Matrix3f::Identity();
  96. Eigen::Vector3f t = delta.tail<3>();
  97. Eigen::Matrix3f r_delta = 1.0 / rotation_epsilon_ * R.array().abs();
  98. Eigen::Vector3f t_delta = 1.0 / transformation_epsilon_ * t.array().abs();
  99. return std::max(r_delta.maxCoeff(), t_delta.maxCoeff()) < 1;
  100. }
  101. template<typename PointSource, typename PointTarget>
  102. void FastGICPMultiPoints<PointSource, PointTarget>::update_correspondences(const Eigen::Matrix<float, 6, 1>& x) {
  103. Eigen::Matrix4f trans = Eigen::Matrix4f::Identity();
  104. trans.block<3, 3>(0, 0) = Sophus::SO3f::exp(x.head<3>()).matrix();
  105. trans.block<3, 1>(0, 3) = x.tail<3>();
  106. correspondences.resize(input_->size());
  107. sq_distances.resize(input_->size());
  108. #pragma omp parallel for num_threads(num_threads_)
  109. for(int i = 0; i < input_->size(); i++) {
  110. PointTarget pt;
  111. pt.getVector4fMap() = trans * input_->at(i).getVector4fMap();
  112. std::vector<int> k_indices;
  113. std::vector<float> k_sq_dists;
  114. target_kdtree.radiusSearch(pt, neighbor_search_radius_, k_indices, k_sq_dists);
  115. if(k_indices.empty()) {
  116. // target_kdtree.nearestKSearch(pt, 1, k_indices, k_sq_dists);
  117. }
  118. correspondences[i] = k_indices;
  119. sq_distances[i] = k_sq_dists;
  120. }
  121. }
  122. template<typename PointSource, typename PointTarget>
  123. Eigen::VectorXf FastGICPMultiPoints<PointSource, PointTarget>::loss_ls(const Eigen::Matrix<float, 6, 1>& x, Eigen::MatrixXf* J) const {
  124. Eigen::Matrix4f trans = Eigen::Matrix4f::Identity();
  125. trans.block<3, 3>(0, 0) = Sophus::SO3f::exp(x.head<3>()).matrix();
  126. trans.block<3, 1>(0, 3) = x.tail<3>();
  127. std::vector<Eigen::Vector3f, Eigen::aligned_allocator<Eigen::Vector3f>> losses(input_->size());
  128. // use row-major arrangement for ease of repacking
  129. std::vector<Eigen::Matrix<float, 3, 6, Eigen::RowMajor>, Eigen::aligned_allocator<Eigen::Matrix<float, 3, 6, Eigen::RowMajor>>> Js(input_->size());
  130. std::atomic_int count(0);
  131. #pragma omp parallel for num_threads(num_threads_)
  132. for(int i = 0; i < correspondences.size(); i++) {
  133. int source_index = i;
  134. const auto& mean_A = input_->at(source_index).getVector4fMap();
  135. const auto& cov_A = source_covs[source_index];
  136. const auto& k_indices = correspondences[i];
  137. const auto& k_sq_dists = sq_distances[i];
  138. if(k_indices.empty()) {
  139. continue;
  140. }
  141. double sum_w = 0.0;
  142. Eigen::Vector4d sum_mean_B = Eigen::Vector4d::Zero();
  143. Eigen::Matrix4d sum_cov_B = Eigen::Matrix4d::Zero();
  144. for(int j = 0; j < k_indices.size(); j++) {
  145. double w = 1 - std::sqrt(k_sq_dists[j]) / neighbor_search_radius_;
  146. w = std::max(1e-3, std::min(1.0, w));
  147. sum_w += w;
  148. int target_index = k_indices[j];
  149. sum_mean_B += w * target_->at(target_index).getVector4fMap().template cast<double>();
  150. sum_cov_B += w * target_covs[target_index].template cast<double>();
  151. }
  152. Eigen::Vector4f mean_B = (sum_mean_B / sum_w).template cast<float>();
  153. Eigen::Matrix4f cov_B = (sum_cov_B / sum_w).template cast<float>();
  154. Eigen::Vector4f transed_mean_A = trans * mean_A;
  155. Eigen::Vector4f d = mean_B - transed_mean_A;
  156. Eigen::Matrix4f RCR = cov_B + trans * cov_A * trans.transpose();
  157. RCR(3, 3) = 1;
  158. Eigen::Matrix4f RCR_inv = RCR.inverse();
  159. Eigen::Vector4f RCRd = RCR_inv * d;
  160. Eigen::Matrix<float, 4, 6> dtdx0 = Eigen::Matrix<float, 4, 6>::Zero();
  161. dtdx0.block<3, 3>(0, 0) = skew(transed_mean_A.head<3>());
  162. dtdx0.block<3, 3>(0, 3) = -Eigen::Matrix3f::Identity();
  163. Eigen::Matrix<float, 4, 6> jlossexp = RCR_inv * dtdx0;
  164. int n = count++;
  165. losses[n] = RCRd.head<3>();
  166. Js[n] = jlossexp.block<3, 6>(0, 0);
  167. }
  168. int final_size = count;
  169. Eigen::VectorXf loss = Eigen::Map<Eigen::VectorXf>(losses.front().data(), final_size * 3);
  170. *J = Eigen::Map<Eigen::MatrixXf>(Js.front().data(), 6, final_size * 3).transpose();
  171. return loss;
  172. }
  173. template<typename PointSource, typename PointTarget>
  174. template<typename PointT>
  175. 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) {
  176. kdtree.setInputCloud(cloud);
  177. covariances.resize(cloud->size());
  178. #pragma omp parallel for num_threads(num_threads_)
  179. for(int i = 0; i < cloud->size(); i++) {
  180. std::vector<int> k_indices;
  181. std::vector<float> k_sq_distances;
  182. kdtree.nearestKSearch(cloud->at(i), k_correspondences_, k_indices, k_sq_distances);
  183. Eigen::Matrix<float, 4, -1> data(4, k_correspondences_);
  184. for(int j = 0; j < k_indices.size(); j++) {
  185. data.col(j) = cloud->at(k_indices[j]).getVector4fMap();
  186. }
  187. data.colwise() -= data.rowwise().mean().eval();
  188. Eigen::Matrix4f cov = data * data.transpose();
  189. if(regularization_method_ == RegularizationMethod::FROBENIUS) {
  190. double lambda = 1e-3;
  191. Eigen::Matrix3d C = cov.block<3, 3>(0, 0).cast<double>() + lambda * Eigen::Matrix3d::Identity();
  192. Eigen::Matrix3d C_inv = C.inverse();
  193. covariances[i].setZero();
  194. covariances[i].template block<3, 3>(0, 0) = (C_inv / C_inv.norm()).inverse().cast<float>();
  195. } else {
  196. Eigen::JacobiSVD<Eigen::Matrix3f> svd(cov.block<3, 3>(0, 0), Eigen::ComputeFullU | Eigen::ComputeFullV);
  197. Eigen::Vector3f values;
  198. switch(regularization_method_) {
  199. default:
  200. std::cerr << "here must not be reached" << std::endl;
  201. abort();
  202. case RegularizationMethod::PLANE:
  203. values = Eigen::Vector3f(1, 1, 1e-3);
  204. break;
  205. case RegularizationMethod::MIN_EIG:
  206. values = svd.singularValues().array().max(1e-3);
  207. break;
  208. case RegularizationMethod::NORMALIZED_MIN_EIG:
  209. values = svd.singularValues() / svd.singularValues().maxCoeff();
  210. values = values.array().max(1e-3);
  211. break;
  212. }
  213. covariances[i].setZero();
  214. covariances[i].template block<3, 3>(0, 0) = svd.matrixU() * values.asDiagonal() * svd.matrixV().transpose();
  215. }
  216. }
  217. return true;
  218. }
  219. } // namespace fast_gicp
  220. #endif