fast_gicp_impl.hpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. #ifndef FAST_GICP_FAST_GICP_IMPL_HPP
  2. #define FAST_GICP_FAST_GICP_IMPL_HPP
  3. #include <fast_gicp/so3/so3.hpp>
  4. namespace fast_gicp {
  5. template <typename PointSource, typename PointTarget>
  6. FastGICP<PointSource, PointTarget>::FastGICP() {
  7. #ifdef _OPENMP
  8. num_threads_ = omp_get_max_threads();
  9. #else
  10. num_threads_ = 1;
  11. #endif
  12. k_correspondences_ = 20;
  13. reg_name_ = "FastGICP";
  14. corr_dist_threshold_ = std::numeric_limits<float>::max();
  15. regularization_method_ = RegularizationMethod::PLANE;
  16. source_kdtree_.reset(new pcl::search::KdTree<PointSource>);
  17. target_kdtree_.reset(new pcl::search::KdTree<PointTarget>);
  18. }
  19. template <typename PointSource, typename PointTarget>
  20. FastGICP<PointSource, PointTarget>::~FastGICP() {}
  21. template <typename PointSource, typename PointTarget>
  22. void FastGICP<PointSource, PointTarget>::setNumThreads(int n) {
  23. num_threads_ = n;
  24. #ifdef _OPENMP
  25. if (n == 0) {
  26. num_threads_ = omp_get_max_threads();
  27. }
  28. #endif
  29. }
  30. template <typename PointSource, typename PointTarget>
  31. void FastGICP<PointSource, PointTarget>::setCorrespondenceRandomness(int k) {
  32. k_correspondences_ = k;
  33. }
  34. template <typename PointSource, typename PointTarget>
  35. void FastGICP<PointSource, PointTarget>::setRegularizationMethod(RegularizationMethod method) {
  36. regularization_method_ = method;
  37. }
  38. template <typename PointSource, typename PointTarget>
  39. void FastGICP<PointSource, PointTarget>::swapSourceAndTarget() {
  40. input_.swap(target_);
  41. source_kdtree_.swap(target_kdtree_);
  42. source_covs_.swap(target_covs_);
  43. correspondences_.clear();
  44. sq_distances_.clear();
  45. }
  46. template <typename PointSource, typename PointTarget>
  47. void FastGICP<PointSource, PointTarget>::clearSource() {
  48. input_.reset();
  49. source_covs_.clear();
  50. }
  51. template <typename PointSource, typename PointTarget>
  52. void FastGICP<PointSource, PointTarget>::clearTarget() {
  53. target_.reset();
  54. target_covs_.clear();
  55. }
  56. template <typename PointSource, typename PointTarget>
  57. void FastGICP<PointSource, PointTarget>::setInputSource(const PointCloudSourceConstPtr& cloud) {
  58. if (input_ == cloud) {
  59. return;
  60. }
  61. pcl::Registration<PointSource, PointTarget, Scalar>::setInputSource(cloud);
  62. source_kdtree_->setInputCloud(cloud);
  63. source_covs_.clear();
  64. }
  65. template <typename PointSource, typename PointTarget>
  66. void FastGICP<PointSource, PointTarget>::setInputTarget(const PointCloudTargetConstPtr& cloud) {
  67. if (target_ == cloud) {
  68. return;
  69. }
  70. pcl::Registration<PointSource, PointTarget, Scalar>::setInputTarget(cloud);
  71. target_kdtree_->setInputCloud(cloud);
  72. target_covs_.clear();
  73. }
  74. template <typename PointSource, typename PointTarget>
  75. void FastGICP<PointSource, PointTarget>::setSourceCovariances(const std::vector<Eigen::Matrix4d, Eigen::aligned_allocator<Eigen::Matrix4d>>& covs) {
  76. source_covs_ = covs;
  77. }
  78. template <typename PointSource, typename PointTarget>
  79. void FastGICP<PointSource, PointTarget>::setTargetCovariances(const std::vector<Eigen::Matrix4d, Eigen::aligned_allocator<Eigen::Matrix4d>>& covs) {
  80. target_covs_ = covs;
  81. }
  82. template <typename PointSource, typename PointTarget>
  83. void FastGICP<PointSource, PointTarget>::computeTransformation(PointCloudSource& output, const Matrix4& guess) {
  84. if (source_covs_.size() != input_->size()) {
  85. calculate_covariances(input_, *source_kdtree_, source_covs_);
  86. }
  87. if (target_covs_.size() != target_->size()) {
  88. calculate_covariances(target_, *target_kdtree_, target_covs_);
  89. }
  90. LsqRegistration<PointSource, PointTarget>::computeTransformation(output, guess);
  91. }
  92. template <typename PointSource, typename PointTarget>
  93. void FastGICP<PointSource, PointTarget>::update_correspondences(const Eigen::Isometry3d& trans) {
  94. assert(source_covs_.size() == input_->size());
  95. assert(target_covs_.size() == target_->size());
  96. Eigen::Isometry3f trans_f = trans.cast<float>();
  97. correspondences_.resize(input_->size());
  98. sq_distances_.resize(input_->size());
  99. mahalanobis_.resize(input_->size());
  100. std::vector<int> k_indices(1);
  101. std::vector<float> k_sq_dists(1);
  102. #pragma omp parallel for num_threads(num_threads_) firstprivate(k_indices, k_sq_dists) schedule(guided, 8)
  103. for (int i = 0; i < input_->size(); i++) {
  104. PointTarget pt;
  105. pt.getVector4fMap() = trans_f * input_->at(i).getVector4fMap();
  106. target_kdtree_->nearestKSearch(pt, 1, k_indices, k_sq_dists);
  107. sq_distances_[i] = k_sq_dists[0];
  108. correspondences_[i] = k_sq_dists[0] < corr_dist_threshold_ * corr_dist_threshold_ ? k_indices[0] : -1;
  109. if (correspondences_[i] < 0) {
  110. continue;
  111. }
  112. const int target_index = correspondences_[i];
  113. const auto& cov_A = source_covs_[i];
  114. const auto& cov_B = target_covs_[target_index];
  115. Eigen::Matrix4d RCR = cov_B + trans.matrix() * cov_A * trans.matrix().transpose();
  116. RCR(3, 3) = 1.0;
  117. mahalanobis_[i] = RCR.inverse();
  118. mahalanobis_[i](3, 3) = 0.0f;
  119. }
  120. }
  121. template <typename PointSource, typename PointTarget>
  122. double FastGICP<PointSource, PointTarget>::linearize(const Eigen::Isometry3d& trans, Eigen::Matrix<double, 6, 6>* H, Eigen::Matrix<double, 6, 1>* b) {
  123. update_correspondences(trans);
  124. double sum_errors = 0.0;
  125. std::vector<Eigen::Matrix<double, 6, 6>, Eigen::aligned_allocator<Eigen::Matrix<double, 6, 6>>> Hs(num_threads_);
  126. std::vector<Eigen::Matrix<double, 6, 1>, Eigen::aligned_allocator<Eigen::Matrix<double, 6, 1>>> bs(num_threads_);
  127. for (int i = 0; i < num_threads_; i++) {
  128. Hs[i].setZero();
  129. bs[i].setZero();
  130. }
  131. #pragma omp parallel for num_threads(num_threads_) reduction(+ : sum_errors) schedule(guided, 8)
  132. for (int i = 0; i < input_->size(); i++) {
  133. int target_index = correspondences_[i];
  134. if (target_index < 0) {
  135. continue;
  136. }
  137. const Eigen::Vector4d mean_A = input_->at(i).getVector4fMap().template cast<double>();
  138. const auto& cov_A = source_covs_[i];
  139. const Eigen::Vector4d mean_B = target_->at(target_index).getVector4fMap().template cast<double>();
  140. const auto& cov_B = target_covs_[target_index];
  141. const Eigen::Vector4d transed_mean_A = trans * mean_A;
  142. const Eigen::Vector4d error = mean_B - transed_mean_A;
  143. sum_errors += error.transpose() * mahalanobis_[i] * error;
  144. if (H == nullptr || b == nullptr) {
  145. continue;
  146. }
  147. Eigen::Matrix<double, 4, 6> dtdx0 = Eigen::Matrix<double, 4, 6>::Zero();
  148. dtdx0.block<3, 3>(0, 0) = skewd(transed_mean_A.head<3>());
  149. dtdx0.block<3, 3>(0, 3) = -Eigen::Matrix3d::Identity();
  150. Eigen::Matrix<double, 4, 6> jlossexp = dtdx0;
  151. Eigen::Matrix<double, 6, 6> Hi = jlossexp.transpose() * mahalanobis_[i] * jlossexp;
  152. Eigen::Matrix<double, 6, 1> bi = jlossexp.transpose() * mahalanobis_[i] * error;
  153. Hs[omp_get_thread_num()] += Hi;
  154. bs[omp_get_thread_num()] += bi;
  155. }
  156. if (H && b) {
  157. H->setZero();
  158. b->setZero();
  159. for (int i = 0; i < num_threads_; i++) {
  160. (*H) += Hs[i];
  161. (*b) += bs[i];
  162. }
  163. }
  164. return sum_errors;
  165. }
  166. template <typename PointSource, typename PointTarget>
  167. double FastGICP<PointSource, PointTarget>::compute_error(const Eigen::Isometry3d& trans) {
  168. double sum_errors = 0.0;
  169. #pragma omp parallel for num_threads(num_threads_) reduction(+ : sum_errors) schedule(guided, 8)
  170. for (int i = 0; i < input_->size(); i++) {
  171. int target_index = correspondences_[i];
  172. if (target_index < 0) {
  173. continue;
  174. }
  175. const Eigen::Vector4d mean_A = input_->at(i).getVector4fMap().template cast<double>();
  176. const auto& cov_A = source_covs_[i];
  177. const Eigen::Vector4d mean_B = target_->at(target_index).getVector4fMap().template cast<double>();
  178. const auto& cov_B = target_covs_[target_index];
  179. const Eigen::Vector4d transed_mean_A = trans * mean_A;
  180. const Eigen::Vector4d error = mean_B - transed_mean_A;
  181. sum_errors += error.transpose() * mahalanobis_[i] * error;
  182. }
  183. return sum_errors;
  184. }
  185. template <typename PointSource, typename PointTarget>
  186. template <typename PointT>
  187. bool FastGICP<PointSource, PointTarget>::calculate_covariances(
  188. const typename pcl::PointCloud<PointT>::ConstPtr& cloud,
  189. pcl::search::KdTree<PointT>& kdtree,
  190. std::vector<Eigen::Matrix4d, Eigen::aligned_allocator<Eigen::Matrix4d>>& covariances) {
  191. if (kdtree.getInputCloud() != cloud) {
  192. kdtree.setInputCloud(cloud);
  193. }
  194. covariances.resize(cloud->size());
  195. #pragma omp parallel for num_threads(num_threads_) schedule(guided, 8)
  196. for (int i = 0; i < cloud->size(); i++) {
  197. std::vector<int> k_indices;
  198. std::vector<float> k_sq_distances;
  199. kdtree.nearestKSearch(cloud->at(i), k_correspondences_, k_indices, k_sq_distances);
  200. Eigen::Matrix<double, 4, -1> neighbors(4, k_correspondences_);
  201. for (int j = 0; j < k_indices.size(); j++) {
  202. neighbors.col(j) = cloud->at(k_indices[j]).getVector4fMap().template cast<double>();
  203. }
  204. neighbors.colwise() -= neighbors.rowwise().mean().eval();
  205. Eigen::Matrix4d cov = neighbors * neighbors.transpose() / k_correspondences_;
  206. if (regularization_method_ == RegularizationMethod::NONE) {
  207. covariances[i] = cov;
  208. } else if (regularization_method_ == RegularizationMethod::FROBENIUS) {
  209. double lambda = 1e-3;
  210. Eigen::Matrix3d C = cov.block<3, 3>(0, 0).cast<double>() + lambda * Eigen::Matrix3d::Identity();
  211. Eigen::Matrix3d C_inv = C.inverse();
  212. covariances[i].setZero();
  213. covariances[i].template block<3, 3>(0, 0) = (C_inv / C_inv.norm()).inverse();
  214. } else {
  215. Eigen::JacobiSVD<Eigen::Matrix3d> svd(cov.block<3, 3>(0, 0), Eigen::ComputeFullU | Eigen::ComputeFullV);
  216. Eigen::Vector3d values;
  217. switch (regularization_method_) {
  218. default:
  219. std::cerr << "here must not be reached" << std::endl;
  220. abort();
  221. case RegularizationMethod::PLANE:
  222. values = Eigen::Vector3d(1, 1, 1e-3);
  223. break;
  224. case RegularizationMethod::MIN_EIG:
  225. values = svd.singularValues().array().max(1e-3);
  226. break;
  227. case RegularizationMethod::NORMALIZED_MIN_EIG:
  228. values = svd.singularValues() / svd.singularValues().maxCoeff();
  229. values = values.array().max(1e-3);
  230. break;
  231. }
  232. covariances[i].setZero();
  233. covariances[i].template block<3, 3>(0, 0) = svd.matrixU() * values.asDiagonal() * svd.matrixV().transpose();
  234. }
  235. }
  236. return true;
  237. }
  238. } // namespace fast_gicp
  239. #endif