gtsam_points
Loading...
Searching...
No Matches
integrated_vgicp_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_vgicp_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/types/gaussian_voxelmap_cpu.hpp>
10#include <gtsam_points/util/parallelism.hpp>
11#include <gtsam_points/util/compact.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 SourceFrame>
22 gtsam::Key target_key,
23 gtsam::Key source_key,
24 const GaussianVoxelMap::ConstPtr& target_voxels,
25 const std::shared_ptr<const SourceFrame>& source)
26: gtsam_points::IntegratedMatchingCostFactor(target_key, source_key),
27 num_threads(1),
28 mahalanobis_cache_mode(FusedCovCacheMode::FULL),
29 target_voxels(std::dynamic_pointer_cast<const GaussianVoxelMapCPU>(target_voxels)),
30 source(source) {
31 //
32 if (!frame::has_points(*source)) {
33 std::cerr << "error: source points have not been allocated!!" << std::endl;
34 abort();
35 }
36
37 if (!frame::has_covs(*source)) {
38 std::cerr << "error: source don't have covs!!" << std::endl;
39 abort();
40 }
41
42 if (!this->target_voxels) {
43 std::cerr << "error: target voxelmap has not been created!!" << std::endl;
44 abort();
45 }
46}
47
48template <typename SourceFrame>
50 const gtsam::Pose3& fixed_target_pose,
51 gtsam::Key source_key,
52 const GaussianVoxelMap::ConstPtr& target_voxels,
53 const std::shared_ptr<const SourceFrame>& source)
54: gtsam_points::IntegratedMatchingCostFactor(fixed_target_pose, source_key),
55 num_threads(1),
56 mahalanobis_cache_mode(FusedCovCacheMode::FULL),
57 target_voxels(std::dynamic_pointer_cast<const GaussianVoxelMapCPU>(target_voxels)),
58 source(source) {
59 //
60 if (!frame::has_points(*source)) {
61 std::cerr << "error: source points have not been allocated!!" << std::endl;
62 abort();
63 }
64
65 if (!frame::has_covs(*source)) {
66 std::cerr << "error: source don't have covs!!" << std::endl;
67 abort();
68 }
69
70 if (!this->target_voxels) {
71 std::cerr << "error: target voxelmap has not been created!!" << std::endl;
72 abort();
73 }
74}
75
76template <typename SourceFrame>
78
79template <typename SourceFrame>
80void IntegratedVGICPFactor_<SourceFrame>::print(const std::string& s, const gtsam::KeyFormatter& keyFormatter) const {
81 std::cout << s << "IntegratedVGICPFactor";
82 if (is_binary) {
83 std::cout << "(" << keyFormatter(this->keys()[0]) << ", " << keyFormatter(this->keys()[1]) << ")" << std::endl;
84 } else {
85 std::cout << "(fixed, " << keyFormatter(this->keys()[0]) << ")" << std::endl;
86 }
87
88 std::cout << "target_resolusion=" << target_voxels->voxel_resolution() << ", |source|=" << frame::size(*source) << "pts" << std::endl;
89 std::cout << "num_threads=" << num_threads << std::endl;
90}
91
92template <typename SourceFrame>
94 return sizeof(*this) + sizeof(const GaussianVoxel*) * correspondences.capacity() + sizeof(Eigen::Matrix4d) * mahalanobis_full.capacity() +
95 sizeof(Eigen::Matrix<float, 6, 1>) * mahalanobis_compact.capacity();
96}
97
98template <typename SourceFrame>
99void IntegratedVGICPFactor_<SourceFrame>::update_correspondences(const Eigen::Isometry3d& delta) const {
100 linearization_point = delta;
101 correspondences.resize(frame::size(*source));
102
103 switch (mahalanobis_cache_mode) {
104 case FusedCovCacheMode::FULL:
105 mahalanobis_full.resize(frame::size(*source));
106 break;
107 case FusedCovCacheMode::COMPACT:
108 mahalanobis_compact.resize(frame::size(*source));
109 break;
110 case FusedCovCacheMode::NONE:
111 break;
112 }
113
114 const auto perpoint_task = [&](int i) {
115 Eigen::Vector4d pt = delta * frame::point(*source, i);
116 Eigen::Vector3i coord = target_voxels->voxel_coord(pt);
117 const auto voxel_id = target_voxels->lookup_voxel_index(coord);
118
119 if (voxel_id < 0) {
120 correspondences[i] = nullptr;
121
122 switch (mahalanobis_cache_mode) {
123 case FusedCovCacheMode::FULL:
124 mahalanobis_full[i].setZero();
125 break;
126 case FusedCovCacheMode::COMPACT:
127 mahalanobis_compact[i].setZero();
128 break;
129 case FusedCovCacheMode::NONE:
130 break;
131 }
132 } else {
133 const auto voxel = &target_voxels->lookup_voxel(voxel_id);
134 correspondences[i] = voxel;
135
136 switch (mahalanobis_cache_mode) {
137 case FusedCovCacheMode::FULL: {
138 const Eigen::Matrix4d RCR = (voxel->cov + delta.matrix() * frame::cov(*source, i) * delta.matrix().transpose());
139 mahalanobis_full[i].setZero();
140 mahalanobis_full[i].template topLeftCorner<3, 3>() = RCR.topLeftCorner<3, 3>().inverse();
141 break;
142 }
143 case FusedCovCacheMode::COMPACT: {
144 const Eigen::Matrix4d RCR = (voxel->cov + delta.matrix() * frame::cov(*source, i) * delta.matrix().transpose());
145 const Eigen::Matrix3d maha = RCR.topLeftCorner<3, 3>().inverse();
146 mahalanobis_compact[i] = compact_cov(maha);
147 break;
148 }
149 case FusedCovCacheMode::NONE:
150 break;
151 }
152 }
153 };
154
155 if (is_omp_default() || num_threads == 1) {
156#pragma omp parallel for num_threads(num_threads) schedule(guided, 8)
157 for (int i = 0; i < frame::size(*source); i++) {
158 perpoint_task(i);
159 }
160 } else {
161#ifdef GTSAM_POINTS_USE_TBB
162 tbb::parallel_for(tbb::blocked_range<int>(0, frame::size(*source), 8), [&](const tbb::blocked_range<int>& range) {
163 for (int i = range.begin(); i < range.end(); i++) {
164 perpoint_task(i);
165 }
166 });
167#else
168 std::cerr << "error: TBB is not available" << std::endl;
169 abort();
170#endif
171 }
172}
173
174template <typename SourceFrame>
175double IntegratedVGICPFactor_<SourceFrame>::evaluate(
176 const Eigen::Isometry3d& delta,
177 Eigen::Matrix<double, 6, 6>* H_target,
178 Eigen::Matrix<double, 6, 6>* H_source,
179 Eigen::Matrix<double, 6, 6>* H_target_source,
180 Eigen::Matrix<double, 6, 1>* b_target,
181 Eigen::Matrix<double, 6, 1>* b_source) const {
182 //
183 if (correspondences.size() != frame::size(*source)) {
184 update_correspondences(delta);
185 }
186
187 double sum_errors = 0.0;
188
189 const auto perpoint_task = [&](
190 int i,
191 Eigen::Matrix<double, 6, 6>* H_target,
192 Eigen::Matrix<double, 6, 6>* H_source,
193 Eigen::Matrix<double, 6, 6>* H_target_source,
194 Eigen::Matrix<double, 6, 1>* b_target,
195 Eigen::Matrix<double, 6, 1>* b_source) {
196 const auto& target_voxel = correspondences[i];
197 if (target_voxel == nullptr) {
198 return 0.0;
199 }
200
201 const auto& mean_A = frame::point(*source, i);
202 const auto& cov_A = frame::cov(*source, i);
203
204 const auto& mean_B = target_voxel->mean;
205 const auto& cov_B = target_voxel->cov;
206
207 Eigen::Vector4d transed_mean_A = delta * mean_A;
208 Eigen::Vector4d residual = mean_B - transed_mean_A;
209
210 Eigen::Matrix4d mahalanobis;
211 switch (mahalanobis_cache_mode) {
212 case FusedCovCacheMode::FULL:
213 mahalanobis = mahalanobis_full[i];
214 break;
215 case FusedCovCacheMode::COMPACT:
216 mahalanobis = uncompact_cov(mahalanobis_compact[i]);
217 break;
218 case FusedCovCacheMode::NONE: {
219 const auto& delta_l = linearization_point; // Delta at the linearization point
220 const Eigen::Matrix4d RCR = (cov_B + delta_l.matrix() * cov_A * delta_l.matrix().transpose());
221 mahalanobis.setZero();
222 mahalanobis.topLeftCorner<3, 3>() = RCR.topLeftCorner<3, 3>().inverse();
223 } break;
224 }
225
226 const double error = residual.transpose() * mahalanobis * residual;
227
228 if (!H_target) {
229 return error;
230 }
231
232 Eigen::Matrix<double, 4, 6> J_target = Eigen::Matrix<double, 4, 6>::Zero();
233 J_target.block<3, 3>(0, 0) = -gtsam::SO3::Hat(transed_mean_A.head<3>());
234 J_target.block<3, 3>(0, 3) = Eigen::Matrix3d::Identity();
235
236 Eigen::Matrix<double, 4, 6> J_source = Eigen::Matrix<double, 4, 6>::Zero();
237 J_source.block<3, 3>(0, 0) = delta.linear() * gtsam::SO3::Hat(mean_A.template head<3>());
238 J_source.block<3, 3>(0, 3) = -delta.linear();
239
240 Eigen::Matrix<double, 6, 4> J_target_mahalanobis = J_target.transpose() * mahalanobis;
241 Eigen::Matrix<double, 6, 4> J_source_mahalanobis = J_source.transpose() * mahalanobis;
242
243 *H_target += J_target_mahalanobis * J_target;
244 *H_source += J_source_mahalanobis * J_source;
245 *H_target_source += J_target_mahalanobis * J_source;
246 *b_target += J_target_mahalanobis * residual;
247 *b_source += J_source_mahalanobis * residual;
248
249 return error;
250 };
251
252 if (is_omp_default() || num_threads == 1) {
253 return scan_matching_reduce_omp(perpoint_task, frame::size(*source), num_threads, H_target, H_source, H_target_source, b_target, b_source);
254 } else {
255 return scan_matching_reduce_tbb(perpoint_task, frame::size(*source), H_target, H_source, H_target_source, b_target, b_source);
256 }
257}
258
259} // namespace gtsam_points
Definition gaussian_voxelmap_cpu.hpp:73
Abstraction of LSQ-based scan matching constraints between point clouds.
Definition integrated_matching_cost_factor.hpp:19
Voxelized GICP matching cost factor Koide et al., "Voxelized GICP for Fast and Accurate 3D Point Clou...
Definition integrated_vgicp_factor.hpp:25
virtual size_t memory_usage() const override
Calculate the memory usage of this factor.
Definition integrated_vgicp_factor_impl.hpp:93
virtual void print(const std::string &s="", const gtsam::KeyFormatter &keyFormatter=gtsam::DefaultKeyFormatter) const override
Print the factor information.
Definition integrated_vgicp_factor_impl.hpp:80
IntegratedVGICPFactor_(gtsam::Key target_key, gtsam::Key source_key, const GaussianVoxelMap::ConstPtr &target_voxels, const std::shared_ptr< const SourceFrame > &source)
Create a binary VGICP factor between target and source poses.
Definition integrated_vgicp_factor_impl.hpp:21
Gaussian voxel that computes and stores voxel mean and covariance.
Definition gaussian_voxelmap_cpu.hpp:13