gtsam_points
Loading...
Searching...
No Matches
integrated_icp_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_icp_factor.hpp>
5
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/types/frame_traits.hpp>
11#include <gtsam_points/util/parallelism.hpp>
12#include <gtsam_points/factors/impl/scan_matching_reduction.hpp>
13
14#ifdef GTSAM_POINTS_USE_TBB
15#include <tbb/parallel_for.h>
16#endif
17
18namespace gtsam_points {
19
20template <typename TargetFrame, typename SourceFrame>
22 gtsam::Key target_key,
23 gtsam::Key source_key,
24 const std::shared_ptr<const TargetFrame>& target,
25 const std::shared_ptr<const SourceFrame>& source,
26 const std::shared_ptr<const NearestNeighborSearch>& target_tree,
27 bool use_point_to_plane)
28: gtsam_points::IntegratedMatchingCostFactor(target_key, source_key),
29 num_threads(1),
30 max_correspondence_distance_sq(1.0),
31 use_point_to_plane(use_point_to_plane),
32 correspondence_update_tolerance_rot(0.0),
33 correspondence_update_tolerance_trans(0.0),
34 target(target),
35 source(source) {
36 //
37 if (!frame::has_points(*target) || (use_point_to_plane && !frame::has_normals(*target))) {
38 std::cerr << "error: target frame doesn't have required attributes for icp" << std::endl;
39 abort();
40 }
41
42 if (!frame::has_points(*source)) {
43 std::cerr << "error: source frame doesn't have required attributes for icp" << std::endl;
44 abort();
45 }
46
47 if (target_tree) {
48 this->target_tree = target_tree;
49 } else {
50 this->target_tree.reset(new KdTree2<TargetFrame>(target));
51 }
52}
53
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,
60 bool use_point_to_plane)
61: gtsam_points::IntegratedICPFactor_<TargetFrame, SourceFrame>(target_key, source_key, target, source, nullptr, use_point_to_plane) {}
62
63template <typename TargetFrame, typename SourceFrame>
65 const gtsam::Pose3& fixed_target_pose,
66 gtsam::Key source_key,
67 const std::shared_ptr<const TargetFrame>& target,
68 const std::shared_ptr<const SourceFrame>& source,
69 const std::shared_ptr<const NearestNeighborSearch>& target_tree,
70 bool use_point_to_plane)
71: gtsam_points::IntegratedMatchingCostFactor(fixed_target_pose, source_key),
72 num_threads(1),
73 max_correspondence_distance_sq(1.0),
74 use_point_to_plane(use_point_to_plane),
75 correspondence_update_tolerance_rot(0.0),
76 correspondence_update_tolerance_trans(0.0),
77 target(target),
78 source(source) {
79 //
80 if (!frame::has_points(*target) || (use_point_to_plane && !frame::has_normals(*target))) {
81 std::cerr << "error: target frame doesn't have required attributes for icp" << std::endl;
82 abort();
83 }
84
85 if (!frame::has_points(*source)) {
86 std::cerr << "error: source frame doesn't have required attributes for icp" << std::endl;
87 abort();
88 }
89
90 if (target_tree) {
91 this->target_tree = target_tree;
92 } else {
93 this->target_tree.reset(new KdTree2<TargetFrame>(target));
94 }
95}
96
97template <typename TargetFrame, typename SourceFrame>
99 const gtsam::Pose3& fixed_target_pose,
100 gtsam::Key source_key,
101 const std::shared_ptr<const TargetFrame>& target,
102 const std::shared_ptr<const SourceFrame>& source,
103 bool use_point_to_plane)
104: gtsam_points::IntegratedICPFactor_<TargetFrame, SourceFrame>(fixed_target_pose, source_key, target, source, nullptr, use_point_to_plane) {}
105
106template <typename TargetFrame, typename SourceFrame>
108
109template <typename TargetFrame, typename SourceFrame>
110void IntegratedICPFactor_<TargetFrame, SourceFrame>::print(const std::string& s, const gtsam::KeyFormatter& keyFormatter) const {
111 std::cout << s << "IntegratedICPFactor";
112 if (is_binary) {
113 std::cout << "(" << keyFormatter(this->keys()[0]) << ", " << keyFormatter(this->keys()[1]) << ")" << std::endl;
114 } else {
115 std::cout << "(fixed, " << keyFormatter(this->keys()[0]) << ")" << std::endl;
116 }
117
118 std::cout << "|target|=" << frame::size(*target) << "pts, |source|=" << frame::size(*source) << "pts" << std::endl;
119 std::cout << "num_threads=" << num_threads << ", max_corr_dist=" << std::sqrt(max_correspondence_distance_sq) << std::endl;
120}
121
122template <typename TargetFrame, typename SourceFrame>
124 return sizeof(*this) + sizeof(long) * correspondences.capacity();
125}
126
127template <typename TargetFrame, typename SourceFrame>
128void IntegratedICPFactor_<TargetFrame, SourceFrame>::update_correspondences(const Eigen::Isometry3d& delta) const {
129 bool do_update = true;
130 if (correspondences.size() == frame::size(*source) && (correspondence_update_tolerance_trans > 0.0 || correspondence_update_tolerance_rot > 0.0)) {
131 Eigen::Isometry3d diff = delta.inverse() * last_correspondence_point;
132 double diff_rot = Eigen::AngleAxisd(diff.linear()).angle();
133 double diff_trans = diff.translation().norm();
134 if (diff_rot < correspondence_update_tolerance_rot && diff_trans < correspondence_update_tolerance_trans) {
135 do_update = false;
136 }
137 }
138
139 if (!do_update) {
140 return;
141 }
142
143 last_correspondence_point = delta;
144 correspondences.resize(frame::size(*source));
145
146 const auto perpoint_task = [&](int i) {
147 Eigen::Vector4d pt = delta * frame::point(*source, i);
148
149 size_t k_index = -1;
150 double k_sq_dist = -1;
151 size_t num_found = target_tree->knn_search(pt.data(), 1, &k_index, &k_sq_dist, max_correspondence_distance_sq);
152
153 if (num_found == 0 || k_sq_dist > max_correspondence_distance_sq) {
154 correspondences[i] = -1;
155 } else {
156 correspondences[i] = k_index;
157 }
158 };
159
160 if (is_omp_default() || num_threads == 1) {
161#pragma omp parallel for num_threads(num_threads) schedule(guided, 8)
162 for (int i = 0; i < frame::size(*source); i++) {
163 perpoint_task(i);
164 }
165 } else {
166#ifdef GTSAM_POINTS_USE_TBB
167 tbb::parallel_for(tbb::blocked_range<int>(0, frame::size(*source), 8), [&](const tbb::blocked_range<int>& range) {
168 for (int i = range.begin(); i < range.end(); i++) {
169 perpoint_task(i);
170 }
171 });
172#else
173 std::cerr << "error: TBB is not available" << std::endl;
174 abort();
175#endif
176 }
177}
178
179template <typename TargetFrame, typename SourceFrame>
180double IntegratedICPFactor_<TargetFrame, SourceFrame>::evaluate(
181 const Eigen::Isometry3d& delta,
182 Eigen::Matrix<double, 6, 6>* H_target,
183 Eigen::Matrix<double, 6, 6>* H_source,
184 Eigen::Matrix<double, 6, 6>* H_target_source,
185 Eigen::Matrix<double, 6, 1>* b_target,
186 Eigen::Matrix<double, 6, 1>* b_source) const {
187 //
188 if (correspondences.size() != frame::size(*source)) {
189 update_correspondences(delta);
190 }
191
192 const auto perpoint_task = [&](
193 int i,
194 Eigen::Matrix<double, 6, 6>* H_target,
195 Eigen::Matrix<double, 6, 6>* H_source,
196 Eigen::Matrix<double, 6, 6>* H_target_source,
197 Eigen::Matrix<double, 6, 1>* b_target,
198 Eigen::Matrix<double, 6, 1>* b_source) {
199 long target_index = correspondences[i];
200 if (target_index < 0) {
201 return 0.0;
202 }
203
204 const auto& mean_A = frame::point(*source, i);
205 const auto& mean_B = frame::point(*target, target_index);
206
207 const Eigen::Vector4d transed_mean_A = delta * mean_A;
208 Eigen::Vector4d residual = mean_B - transed_mean_A;
209
210 if (use_point_to_plane) {
211 const auto& normal_B = frame::normal(*target, target_index);
212 residual = normal_B.array() * residual.array();
213 }
214
215 const double error = residual.transpose() * residual;
216 if (H_target == nullptr) {
217 return error;
218 }
219
220 Eigen::Matrix<double, 4, 6> J_target = Eigen::Matrix<double, 4, 6>::Zero();
221 J_target.block<3, 3>(0, 0) = -gtsam::SO3::Hat(transed_mean_A.head<3>());
222 J_target.block<3, 3>(0, 3) = Eigen::Matrix3d::Identity();
223
224 Eigen::Matrix<double, 4, 6> J_source = Eigen::Matrix<double, 4, 6>::Zero();
225 J_source.block<3, 3>(0, 0) = delta.linear() * gtsam::SO3::Hat(mean_A.template head<3>());
226 J_source.block<3, 3>(0, 3) = -delta.linear();
227
228 if (use_point_to_plane) {
229 const auto& normal_B = frame::normal(*target, target_index);
230 J_target = normal_B.asDiagonal() * J_target;
231 J_source = normal_B.asDiagonal() * J_source;
232 }
233
234 *H_target += J_target.transpose() * J_target;
235 *H_source += J_source.transpose() * J_source;
236 *H_target_source += J_target.transpose() * J_source;
237 *b_target += J_target.transpose() * residual;
238 *b_source += J_source.transpose() * residual;
239
240 return error;
241 };
242
243 if (is_omp_default() || num_threads == 1) {
244 return scan_matching_reduce_omp(perpoint_task, frame::size(*source), num_threads, H_target, H_source, H_target_source, b_target, b_source);
245 } else {
246 return scan_matching_reduce_tbb(perpoint_task, frame::size(*source), H_target, H_source, H_target_source, b_target, b_source);
247 }
248}
249
250} // namespace gtsam_points
Naive point-to-point ICP matching cost factor Zhang, "Iterative Point Matching for Registration of Fr...
Definition integrated_icp_factor.hpp:22
virtual size_t memory_usage() const override
Calculate the memory usage of this factor.
Definition integrated_icp_factor_impl.hpp:123
IntegratedICPFactor_(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, bool use_point_to_plane=false)
Create a binary ICP factor between two poses.
Definition integrated_icp_factor_impl.hpp:21
virtual void print(const std::string &s="", const gtsam::KeyFormatter &keyFormatter=gtsam::DefaultKeyFormatter) const override
Print the factor information.
Definition integrated_icp_factor_impl.hpp:110
Abstraction of LSQ-based scan matching constraints between point clouds.
Definition integrated_matching_cost_factor.hpp:19