gtsam_points
Loading...
Searching...
No Matches
integrated_ct_gicp_factor_impl.hpp
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2021 Kenji Koide (k.koide@aist.go.jp)
3
4#include <gtsam_points/factors/integrated_ct_gicp_factor.hpp>
5
6#include <gtsam/linear/HessianFactor.h>
7#include <gtsam_points/config.hpp>
8#include <gtsam_points/ann/nearest_neighbor_search.hpp>
9#include <gtsam_points/util/parallelism.hpp>
10#include <gtsam_points/factors/impl/scan_matching_reduction.hpp>
11
12#ifdef GTSAM_POINTS_USE_TBB
13#include <tbb/parallel_for.h>
14#endif
15
16namespace gtsam_points {
17
18template <typename TargetFrame, typename SourceFrame>
20 gtsam::Key source_t0_key,
21 gtsam::Key source_t1_key,
22 const std::shared_ptr<const TargetFrame>& target,
23 const std::shared_ptr<const SourceFrame>& source,
24 const std::shared_ptr<const NearestNeighborSearch>& target_tree)
25: IntegratedCT_ICPFactor_<TargetFrame, SourceFrame>(source_t0_key, source_t1_key, target, source, target_tree) {
26 //
27 if (!frame::has_points(*target) || !frame::has_covs(*target)) {
28 std::cerr << "error: target frame doesn't have required attributes for ct_gicp" << std::endl;
29 abort();
30 }
31
32 if (!frame::has_points(*source) || !frame::has_covs(*source)) {
33 std::cerr << "error: source frame doesn't have required attributes for ct_gicp" << std::endl;
34 abort();
35 }
36}
37
38template <typename TargetFrame, typename SourceFrame>
40 gtsam::Key source_t0_key,
41 gtsam::Key source_t1_key,
42 const std::shared_ptr<const TargetFrame>& target,
43 const std::shared_ptr<const SourceFrame>& source)
44: IntegratedCT_GICPFactor_(source_t0_key, source_t1_key, target, source, nullptr) {}
45
46template <typename TargetFrame, typename SourceFrame>
48
49template <typename TargetFrame, typename SourceFrame>
50void IntegratedCT_GICPFactor_<TargetFrame, SourceFrame>::print(const std::string& s, const gtsam::KeyFormatter& keyFormatter) const {
51 std::cout << s << "IntegratedCT_GICPFactor";
52 std::cout << "(" << keyFormatter(this->keys()[0]) << ", " << keyFormatter(this->keys()[1]) << ")" << std::endl;
53
54 std::cout << "|target|=" << frame::size(*this->target) << "pts, |source|=" << frame::size(*this->source) << "pts" << std::endl;
55 std::cout << "num_threads=" << this->num_threads << ", max_corr_dist=" << std::sqrt(this->max_correspondence_distance_sq) << std::endl;
56}
57
58template <typename TargetFrame, typename SourceFrame>
60 return IntegratedCT_ICPFactor_<TargetFrame, SourceFrame>::memory_usage() + sizeof(Eigen::Matrix4d) * mahalanobis.capacity();
61}
62
63template <typename TargetFrame, typename SourceFrame>
64double IntegratedCT_GICPFactor_<TargetFrame, SourceFrame>::error(const gtsam::Values& values) const {
65 this->update_poses(values);
66 if (this->correspondences.size() != frame::size(*this->source)) {
67 this->update_correspondences();
68 }
69
70 const auto perpoint_task = [&](
71 int i,
72 Eigen::Matrix<double, 6, 6>* H_target,
73 Eigen::Matrix<double, 6, 6>* H_source,
74 Eigen::Matrix<double, 6, 6>* H_target_source,
75 Eigen::Matrix<double, 6, 1>* b_target,
76 Eigen::Matrix<double, 6, 1>* b_source) {
77 const long target_index = this->correspondences[i];
78 if (target_index < 0) {
79 return 0.0;
80 }
81
82 const int time_index = this->time_indices[i];
83 const Eigen::Isometry3d pose(this->source_poses[time_index].matrix());
84
85 const auto& source_pt = frame::point(*this->source, i);
86 const auto& target_pt = frame::point(*this->target, target_index);
87
88 const Eigen::Vector4d transed_source_pt = pose * source_pt;
89 const Eigen::Vector4d residual = transed_source_pt - target_pt;
90 const double error = residual.transpose() * mahalanobis[i] * residual;
91
92 return error;
93 };
94
95 if (is_omp_default() || this->num_threads == 1) {
96 return scan_matching_reduce_omp(perpoint_task, frame::size(*this->source), this->num_threads, nullptr, nullptr, nullptr, nullptr, nullptr);
97 } else {
98 return scan_matching_reduce_tbb(perpoint_task, frame::size(*this->source), nullptr, nullptr, nullptr, nullptr, nullptr);
99 }
100}
101
102template <typename TargetFrame, typename SourceFrame>
103gtsam::GaussianFactor::shared_ptr IntegratedCT_GICPFactor_<TargetFrame, SourceFrame>::linearize(const gtsam::Values& values) const {
104 this->update_poses(values);
105 this->update_correspondences();
106
107 const auto perpoint_task = [&](
108 int i,
109 Eigen::Matrix<double, 6, 6>* H_00,
110 Eigen::Matrix<double, 6, 6>* H_11,
111 Eigen::Matrix<double, 6, 6>* H_01,
112 Eigen::Matrix<double, 6, 1>* b_0,
113 Eigen::Matrix<double, 6, 1>* b_1) {
114 const long target_index = this->correspondences[i];
115 if (target_index < 0) {
116 return 0.0;
117 }
118
119 const int time_index = this->time_indices[i];
120
121 const Eigen::Isometry3d pose(this->source_poses[time_index].matrix());
122 const auto& H_pose_0 = this->pose_derivatives_t0[time_index];
123 const auto& H_pose_1 = this->pose_derivatives_t1[time_index];
124
125 const auto& source_pt = frame::point(*this->source, i);
126 const auto& target_pt = frame::point(*this->target, target_index);
127
128 gtsam::Matrix46 H_transed_pose = gtsam::Matrix46::Zero();
129 H_transed_pose.block<3, 3>(0, 0) = pose.linear() * -gtsam::SO3::Hat(source_pt.template head<3>());
130 H_transed_pose.block<3, 3>(0, 3) = pose.linear();
131 const Eigen::Vector4d transed_source_pt = pose * source_pt;
132
133 const auto& H_residual_pose = H_transed_pose;
134 const Eigen::Vector4d residual = transed_source_pt - target_pt;
135
136 const gtsam::Matrix46 H_0 = H_residual_pose * H_pose_0;
137 const gtsam::Matrix46 H_1 = H_residual_pose * H_pose_1;
138
139 const gtsam::Vector4 mahalanobis_residual = mahalanobis[i] * residual;
140 const gtsam::Matrix64 H_0_mahalanobis = H_0.transpose() * mahalanobis[i];
141 const gtsam::Matrix64 H_1_mahalanobis = H_1.transpose() * mahalanobis[i];
142
143 const double error = residual.transpose() * mahalanobis_residual;
144 *H_00 += H_0_mahalanobis * H_0;
145 *H_11 += H_1_mahalanobis * H_1;
146 *H_01 += H_0_mahalanobis * H_1;
147 *b_0 += H_0.transpose() * mahalanobis_residual;
148 *b_1 += H_1.transpose() * mahalanobis_residual;
149
150 return error;
151 };
152
153 double error = 0.0;
154 gtsam::Matrix6 H_00;
155 gtsam::Matrix6 H_01;
156 gtsam::Matrix6 H_11;
157 gtsam::Vector6 b_0;
158 gtsam::Vector6 b_1;
159
160 if (is_omp_default() || this->num_threads == 1) {
161 error = scan_matching_reduce_omp(perpoint_task, frame::size(*this->source), this->num_threads, &H_00, &H_11, &H_01, &b_0, &b_1);
162 } else {
163 error = scan_matching_reduce_tbb(perpoint_task, frame::size(*this->source), &H_00, &H_11, &H_01, &b_0, &b_1);
164 }
165
166 auto factor = gtsam::HessianFactor::shared_ptr(new gtsam::HessianFactor(this->keys_[0], this->keys_[1], H_00, H_01, -b_0, H_11, -b_1, error));
167 return factor;
168}
169
170template <typename TargetFrame, typename SourceFrame>
171void IntegratedCT_GICPFactor_<TargetFrame, SourceFrame>::update_correspondences() const {
172 this->correspondences.resize(frame::size(*this->source));
173 this->mahalanobis.resize(frame::size(*this->source));
174
175 const auto perpoint_task = [&](int i) {
176 const int time_index = this->time_indices[i];
177 const Eigen::Matrix4d pose = this->source_poses[time_index].matrix();
178
179 const auto& pt = frame::point(*this->source, i);
180 const Eigen::Vector4d transed_pt = pose * pt;
181
182 size_t k_index = -1;
183 double k_sq_dist = std::numeric_limits<double>::max();
184 size_t num_found = this->target_tree->knn_search(transed_pt.data(), 1, &k_index, &k_sq_dist, this->max_correspondence_distance_sq);
185
186 if (num_found == 0 || k_sq_dist > this->max_correspondence_distance_sq) {
187 this->correspondences[i] = -1;
188 this->mahalanobis[i].setZero();
189 } else {
190 this->correspondences[i] = k_index;
191
192 const long target_index = this->correspondences[i];
193 const auto& cov_A = frame::cov(*this->source, i);
194 const auto& cov_B = frame::cov(*this->target, target_index);
195 const Eigen::Matrix4d RCR = (cov_B + pose * cov_A * pose.transpose());
196
197 mahalanobis[i].setZero();
198 mahalanobis[i].template block<3, 3>(0, 0) = RCR.block<3, 3>(0, 0).inverse();
199 }
200 };
201
202 if (is_omp_default() || this->num_threads == 1) {
203#pragma omp parallel for num_threads(this->num_threads) schedule(guided, 8)
204 for (int i = 0; i < frame::size(*this->source); i++) {
205 perpoint_task(i);
206 }
207 } else {
208#ifdef GTSAM_POINTS_USE_TBB
209 tbb::parallel_for(tbb::blocked_range<int>(0, frame::size(*this->source), 8), [&](const tbb::blocked_range<int>& range) {
210 for (int i = range.begin(); i < range.end(); i++) {
211 perpoint_task(i);
212 }
213 });
214#else
215 std::cerr << "error: TBB is not available" << std::endl;
216 abort();
217#endif
218 }
219}
220} // namespace gtsam_points
Continuous Time ICP with GICP's D2D cost Bellenbach et al., "CT-ICP: Real-time Elastic LiDAR Odometry...
Definition integrated_ct_gicp_factor.hpp:17
IntegratedCT_GICPFactor_(gtsam::Key source_t0_key, gtsam::Key source_t1_key, const std::shared_ptr< const TargetFrame > &target, const std::shared_ptr< const SourceFrame > &source, const std::shared_ptr< const NearestNeighborSearch > &target_tree)
Constructor.
Definition integrated_ct_gicp_factor_impl.hpp:19
virtual void print(const std::string &s="", const gtsam::KeyFormatter &keyFormatter=gtsam::DefaultKeyFormatter) const override
Print the factor information.
Definition integrated_ct_gicp_factor_impl.hpp:50
Continuous Time ICP Factor Bellenbach et al., "CT-ICP: Real-time Elastic LiDAR Odometry with Loop Clo...
Definition integrated_ct_icp_factor.hpp:21