4#include <gtsam_points/factors/integrated_gicp_factor.hpp>
6#include <gtsam/geometry/Pose3.h>
7#include <gtsam/linear/HessianFactor.h>
8#include <gtsam_points/config.hpp>
9#include <gtsam_points/ann/kdtree2.hpp>
10#include <gtsam_points/util/compact.hpp>
11#include <gtsam_points/util/parallelism.hpp>
12#include <gtsam_points/types/frame_traits.hpp>
13#include <gtsam_points/factors/impl/scan_matching_reduction.hpp>
15#ifdef GTSAM_POINTS_USE_TBB
16#include <tbb/parallel_for.h>
19namespace gtsam_points {
21template <
typename TargetFrame,
typename SourceFrame>
23 gtsam::Key target_key,
24 gtsam::Key source_key,
25 const std::shared_ptr<const TargetFrame>& target,
26 const std::shared_ptr<const SourceFrame>& source,
27 const std::shared_ptr<const NearestNeighborSearch>& target_tree)
30 max_correspondence_distance_sq(1.0),
31 mahalanobis_cache_mode(FusedCovCacheMode::FULL),
32 correspondence_update_tolerance_rot(0.0),
33 correspondence_update_tolerance_trans(0.0),
37 if (!frame::has_points(*target) || !frame::has_covs(*target)) {
38 std::cerr <<
"error: target frame doesn't have required attributes for gicp" << std::endl;
42 if (!frame::has_points(*source) || !frame::has_covs(*source)) {
43 std::cerr <<
"error: source frame doesn't have required attributes for gicp" << std::endl;
48 this->target_tree = target_tree;
50 this->target_tree.reset(
new KdTree2<TargetFrame>(target));
54template <
typename TargetFrame,
typename SourceFrame>
56 gtsam::Key target_key,
57 gtsam::Key source_key,
58 const std::shared_ptr<const TargetFrame>& target,
59 const std::shared_ptr<const SourceFrame>& source)
62template <
typename TargetFrame,
typename SourceFrame>
64 const gtsam::Pose3& fixed_target_pose,
65 gtsam::Key source_key,
66 const std::shared_ptr<const TargetFrame>& target,
67 const std::shared_ptr<const SourceFrame>& source,
68 const std::shared_ptr<const NearestNeighborSearch>& target_tree)
71 max_correspondence_distance_sq(1.0),
72 mahalanobis_cache_mode(FusedCovCacheMode::FULL),
73 correspondence_update_tolerance_rot(0.0),
74 correspondence_update_tolerance_trans(0.0),
78 if (!frame::has_points(*target) || !frame::has_covs(*target)) {
79 std::cerr <<
"error: target frame doesn't have required attributes for gicp" << std::endl;
83 if (!frame::has_points(*source) || !frame::has_covs(*source)) {
84 std::cerr <<
"error: source frame doesn't have required attributes for gicp" << std::endl;
89 this->target_tree = target_tree;
91 this->target_tree.reset(
new KdTree2<TargetFrame>(target));
95template <
typename TargetFrame,
typename SourceFrame>
97 const gtsam::Pose3& fixed_target_pose,
98 gtsam::Key source_key,
99 const std::shared_ptr<const TargetFrame>& target,
100 const std::shared_ptr<const SourceFrame>& source)
103template <
typename TargetFrame,
typename SourceFrame>
104IntegratedGICPFactor_<TargetFrame, SourceFrame>::~IntegratedGICPFactor_() {}
106template <
typename TargetFrame,
typename SourceFrame>
108 std::cout << s <<
"IntegratedGICPFactor";
110 std::cout <<
"(" << keyFormatter(this->keys()[0]) <<
", " << keyFormatter(this->keys()[1]) <<
")" << std::endl;
112 std::cout <<
"(fixed, " << keyFormatter(this->keys()[0]) <<
")" << std::endl;
115 std::cout <<
"|target|=" << frame::size(*target) <<
"pts, |source|=" << frame::size(*source) <<
"pts" << std::endl;
116 std::cout <<
"num_threads=" << num_threads <<
", max_corr_dist=" << std::sqrt(max_correspondence_distance_sq)
117 <<
", cache_mode=" <<
static_cast<int>(mahalanobis_cache_mode) << std::endl;
125template <
typename TargetFrame,
typename SourceFrame>
127 return sizeof(*this) +
sizeof(long) * correspondences.capacity() +
sizeof(Eigen::Matrix4d) * mahalanobis_full.capacity() +
128 sizeof(Eigen::Matrix<float, 6, 1>) * mahalanobis_compact.capacity();
131template <
typename TargetFrame,
typename SourceFrame>
133 linearization_point = delta;
135 bool do_update =
true;
136 if (correspondences.size() == frame::size(*source) && (correspondence_update_tolerance_trans > 0.0 || correspondence_update_tolerance_rot > 0.0)) {
137 Eigen::Isometry3d diff = delta.inverse() * last_correspondence_point;
138 double diff_rot = Eigen::AngleAxisd(diff.linear()).angle();
139 double diff_trans = diff.translation().norm();
140 if (diff_rot < correspondence_update_tolerance_rot && diff_trans < correspondence_update_tolerance_trans) {
146 last_correspondence_point = delta;
149 correspondences.resize(frame::size(*source));
151 switch (mahalanobis_cache_mode) {
152 case FusedCovCacheMode::FULL:
153 mahalanobis_full.resize(frame::size(*source));
155 case FusedCovCacheMode::COMPACT:
156 mahalanobis_compact.resize(frame::size(*source));
158 case FusedCovCacheMode::NONE:
162 const auto perpoint_task = [&](
int i) {
164 Eigen::Vector4d pt = delta * frame::point(*source, i);
167 double k_sq_dist = -1;
168 size_t num_found = target_tree->knn_search(pt.data(), 1, &k_index, &k_sq_dist, max_correspondence_distance_sq);
169 correspondences[i] = (num_found && k_sq_dist < max_correspondence_distance_sq) ? k_index : -1;
172 switch (mahalanobis_cache_mode) {
173 case FusedCovCacheMode::FULL:
174 if (correspondences[i] < 0) {
175 mahalanobis_full[i].setZero();
177 const auto& target_cov = frame::cov(*target, correspondences[i]);
178 const Eigen::Matrix4d RCR = (target_cov + delta.matrix() * frame::cov(*source, i) * delta.matrix().transpose());
179 mahalanobis_full[i].setZero();
180 mahalanobis_full[i].template topLeftCorner<3, 3>() = RCR.topLeftCorner<3, 3>().inverse();
183 case FusedCovCacheMode::COMPACT:
184 if (correspondences[i] < 0) {
185 mahalanobis_compact[i].setZero();
187 const auto& target_cov = frame::cov(*target, correspondences[i]);
188 const Eigen::Matrix4d RCR = (target_cov + delta.matrix() * frame::cov(*source, i) * delta.matrix().transpose());
189 const Eigen::Matrix3d maha = RCR.topLeftCorner<3, 3>().inverse();
190 mahalanobis_compact[i] = compact_cov(maha);
193 case FusedCovCacheMode::NONE:
198 if (is_omp_default() || num_threads == 1) {
199#pragma omp parallel for num_threads(num_threads) schedule(guided, 8)
200 for (
int i = 0; i < frame::size(*source); i++) {
204#ifdef GTSAM_POINTS_USE_TBB
205 tbb::parallel_for(tbb::blocked_range<int>(0, frame::size(*source), 8), [&](
const tbb::blocked_range<int>& range) {
206 for (
int i = range.begin(); i < range.end(); i++) {
211 std::cerr <<
"error: TBB is not available" << std::endl;
217template <
typename TargetFrame,
typename SourceFrame>
218double IntegratedGICPFactor_<TargetFrame, SourceFrame>::evaluate(
219 const Eigen::Isometry3d& delta,
220 Eigen::Matrix<double, 6, 6>* H_target,
221 Eigen::Matrix<double, 6, 6>* H_source,
222 Eigen::Matrix<double, 6, 6>* H_target_source,
223 Eigen::Matrix<double, 6, 1>* b_target,
224 Eigen::Matrix<double, 6, 1>* b_source)
const {
226 if (correspondences.size() != frame::size(*source)) {
227 update_correspondences(delta);
230 const auto perpoint_task = [&](
232 Eigen::Matrix<double, 6, 6>* H_target,
233 Eigen::Matrix<double, 6, 6>* H_source,
234 Eigen::Matrix<double, 6, 6>* H_target_source,
235 Eigen::Matrix<double, 6, 1>* b_target,
236 Eigen::Matrix<double, 6, 1>* b_source) {
237 const long target_index = correspondences[i];
238 if (target_index < 0) {
242 const auto& mean_A = frame::point(*source, i);
243 const auto& cov_A = frame::cov(*source, i);
244 const auto& mean_B = frame::point(*target, target_index);
245 const auto& cov_B = frame::cov(*target, target_index);
247 const Eigen::Vector4d transed_mean_A = delta * mean_A;
248 const Eigen::Vector4d residual = mean_B - transed_mean_A;
250 Eigen::Matrix4d mahalanobis;
251 switch (mahalanobis_cache_mode) {
252 case FusedCovCacheMode::FULL:
253 mahalanobis = mahalanobis_full[i];
255 case FusedCovCacheMode::COMPACT:
256 mahalanobis = uncompact_cov(mahalanobis_compact[i]);
258 case FusedCovCacheMode::NONE: {
259 const auto& delta_l = linearization_point;
260 const Eigen::Matrix4d RCR = (cov_B + delta_l.matrix() * cov_A * delta_l.matrix().transpose());
261 mahalanobis.setZero();
262 mahalanobis.topLeftCorner<3, 3>() = RCR.topLeftCorner<3, 3>().inverse();
266 const double error = residual.transpose() * mahalanobis * residual;
267 if (H_target ==
nullptr) {
271 Eigen::Matrix<double, 4, 6> J_target = Eigen::Matrix<double, 4, 6>::Zero();
272 J_target.block<3, 3>(0, 0) = -gtsam::SO3::Hat(transed_mean_A.head<3>());
273 J_target.block<3, 3>(0, 3) = Eigen::Matrix3d::Identity();
275 Eigen::Matrix<double, 4, 6> J_source = Eigen::Matrix<double, 4, 6>::Zero();
276 J_source.block<3, 3>(0, 0) = delta.linear() * gtsam::SO3::Hat(mean_A.template head<3>());
277 J_source.block<3, 3>(0, 3) = -delta.linear();
279 Eigen::Matrix<double, 6, 4> J_target_mahalanobis = J_target.transpose() * mahalanobis;
280 Eigen::Matrix<double, 6, 4> J_source_mahalanobis = J_source.transpose() * mahalanobis;
282 *H_target += J_target_mahalanobis * J_target;
283 *H_source += J_source_mahalanobis * J_source;
284 *H_target_source += J_target_mahalanobis * J_source;
285 *b_target += J_target_mahalanobis * residual;
286 *b_source += J_source_mahalanobis * residual;
291 if (is_omp_default() || num_threads == 1) {
292 return scan_matching_reduce_omp(perpoint_task, frame::size(*source), num_threads, H_target, H_source, H_target_source, b_target, b_source);
294 return scan_matching_reduce_tbb(perpoint_task, frame::size(*source), H_target, H_source, H_target_source, b_target, b_source);
Generalized ICP matching cost factor Segal et al., "Generalized-ICP", RSS2005.
Definition integrated_gicp_factor.hpp:31
IntegratedGICPFactor_(gtsam::Key target_key, gtsam::Key source_key, const std::shared_ptr< const TargetFrame > &target, const std::shared_ptr< const SourceFrame > &source, const std::shared_ptr< const NearestNeighborSearch > &target_tree)
Create a binary ICP factor between target and source poses.
Definition integrated_gicp_factor_impl.hpp:22
virtual size_t memory_usage() const override
Calculate the memory usage of this factor.
Definition integrated_gicp_factor_impl.hpp:126
virtual void print(const std::string &s="", const gtsam::KeyFormatter &keyFormatter=gtsam::DefaultKeyFormatter) const override
Print the factor information.
Definition integrated_gicp_factor_impl.hpp:107
Abstraction of LSQ-based scan matching constraints between point clouds.
Definition integrated_matching_cost_factor.hpp:19