gtsam_points
Loading...
Searching...
No Matches
integrated_loam_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_loam_factor.hpp>
5
6#include <gtsam/geometry/SO3.h>
7#include <gtsam_points/config.hpp>
8#include <gtsam_points/ann/kdtree2.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>
19IntegratedPointToPlaneFactor_<TargetFrame, SourceFrame>::IntegratedPointToPlaneFactor_(
20 gtsam::Key target_key,
21 gtsam::Key source_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: gtsam_points::IntegratedMatchingCostFactor(target_key, source_key),
26 num_threads(1),
27 max_correspondence_distance_sq(1.0),
28 correspondence_update_tolerance_rot(0.0),
29 correspondence_update_tolerance_trans(0.0),
30 target(target),
31 source(source) {
32 //
33 if (!frame::has_points(*target)) {
34 std::cerr << "error: target frame doesn't have required attributes for loam" << std::endl;
35 abort();
36 }
37
38 if (!frame::has_points(*source)) {
39 std::cerr << "error: source frame doesn't have required attributes for loam" << std::endl;
40 abort();
41 }
42
43 if (target_tree) {
44 this->target_tree = target_tree;
45 } else {
46 this->target_tree.reset(new KdTree2<TargetFrame>(target));
47 }
48}
49
50template <typename TargetFrame, typename SourceFrame>
51IntegratedPointToPlaneFactor_<TargetFrame, SourceFrame>::IntegratedPointToPlaneFactor_(
52 gtsam::Key target_key,
53 gtsam::Key source_key,
54 const std::shared_ptr<const TargetFrame>& target,
55 const std::shared_ptr<const SourceFrame>& source)
56: IntegratedPointToPlaneFactor_(target_key, source_key, target, source, nullptr) {}
57
58template <typename TargetFrame, typename SourceFrame>
59IntegratedPointToPlaneFactor_<TargetFrame, SourceFrame>::~IntegratedPointToPlaneFactor_() {}
60
61template <typename TargetFrame, typename SourceFrame>
62void IntegratedPointToPlaneFactor_<TargetFrame, SourceFrame>::print(const std::string& s, const gtsam::KeyFormatter& keyFormatter) const {
63 std::cout << s << "IntegratedPointToPlaneFactor";
64 if (is_binary) {
65 std::cout << "(" << keyFormatter(this->keys()[0]) << ", " << keyFormatter(this->keys()[1]) << ")" << std::endl;
66 } else {
67 std::cout << "(fixed, " << keyFormatter(this->keys()[0]) << ")" << std::endl;
68 }
69
70 std::cout << "|target|=" << frame::size(*target) << "pts, |source|=" << frame::size(*source) << "pts" << std::endl;
71 std::cout << "num_threads=" << num_threads << ", max_corr_dist=" << std::sqrt(max_correspondence_distance_sq) << std::endl;
72}
73
74template <typename TargetFrame, typename SourceFrame>
76 return sizeof(*this) + sizeof(std::tuple<long, long, long>) * correspondences.capacity();
77}
78
79template <typename TargetFrame, typename SourceFrame>
81 if (correspondences.size() == frame::size(*source) && (correspondence_update_tolerance_trans > 0.0 || correspondence_update_tolerance_rot > 0.0)) {
82 Eigen::Isometry3d diff = delta.inverse() * last_correspondence_point;
83 double diff_rot = Eigen::AngleAxisd(diff.linear()).angle();
84 double diff_trans = diff.translation().norm();
85 if (diff_rot < correspondence_update_tolerance_rot && diff_trans < correspondence_update_tolerance_trans) {
86 return;
87 }
88 }
89
90 last_correspondence_point = delta;
91 correspondences.resize(frame::size(*source));
92
93 const auto perpoint_task = [&](int i) {
94 Eigen::Vector4d pt = delta * frame::point(*source, i);
95
96 std::array<size_t, 3> k_indices;
97 std::array<double, 3> k_sq_dists;
98 size_t num_found = target_tree->knn_search(pt.data(), 3, k_indices.data(), k_sq_dists.data(), max_correspondence_distance_sq);
99
100 if (num_found < 3 || k_sq_dists.back() > max_correspondence_distance_sq) {
101 correspondences[i] = std::make_tuple(-1, -1, -1);
102 } else {
103 correspondences[i] = std::make_tuple(k_indices[0], k_indices[1], k_indices[2]);
104 }
105 };
106
107 if (is_omp_default() || num_threads == 1) {
108#pragma omp parallel for num_threads(num_threads) schedule(guided, 8)
109 for (int i = 0; i < frame::size(*source); i++) {
110 perpoint_task(i);
111 }
112 } else {
113#ifdef GTSAM_POINTS_USE_TBB
114 tbb::parallel_for(tbb::blocked_range<int>(0, frame::size(*source), 8), [&](const tbb::blocked_range<int>& range) {
115 for (int i = range.begin(); i < range.end(); i++) {
116 perpoint_task(i);
117 }
118 });
119#else
120 std::cerr << "error: TBB is not available" << std::endl;
121 abort();
122#endif
123 }
124}
125
126template <typename TargetFrame, typename SourceFrame>
127double IntegratedPointToPlaneFactor_<TargetFrame, SourceFrame>::evaluate(
128 const Eigen::Isometry3d& delta,
129 Eigen::Matrix<double, 6, 6>* H_target,
130 Eigen::Matrix<double, 6, 6>* H_source,
131 Eigen::Matrix<double, 6, 6>* H_target_source,
132 Eigen::Matrix<double, 6, 1>* b_target,
133 Eigen::Matrix<double, 6, 1>* b_source) const {
134 //
135 if (correspondences.size() != frame::size(*source)) {
136 update_correspondences(delta);
137 }
138
139 const auto perpoint_task = [&](
140 int i,
141 Eigen::Matrix<double, 6, 6>* H_target,
142 Eigen::Matrix<double, 6, 6>* H_source,
143 Eigen::Matrix<double, 6, 6>* H_target_source,
144 Eigen::Matrix<double, 6, 1>* b_target,
145 Eigen::Matrix<double, 6, 1>* b_source) {
146 auto target_indices = correspondences[i];
147 if (std::get<0>(target_indices) < 0) {
148 return 0.0;
149 }
150
151 const auto& x_i = frame::point(*source, i);
152 const auto& x_j = frame::point(*target, std::get<0>(target_indices));
153 const auto& x_l = frame::point(*target, std::get<1>(target_indices));
154 const auto& x_m = frame::point(*target, std::get<2>(target_indices));
155
156 Eigen::Vector4d transed_x_i = delta * x_i;
157 Eigen::Vector4d normal = Eigen::Vector4d::Zero();
158 normal.head<3>() = (x_j - x_l).template head<3>().cross((x_j - x_m).template head<3>());
159 normal = normal / normal.norm();
160
161 Eigen::Vector4d residual = x_j - transed_x_i;
162 Eigen::Vector4d plane_residual = residual.array() * normal.array();
163 const double error = plane_residual.transpose() * plane_residual;
164
165 if (H_target == nullptr) {
166 return error;
167 }
168
169 Eigen::Matrix<double, 4, 6> J_target = Eigen::Matrix<double, 4, 6>::Zero();
170 J_target.block<3, 3>(0, 0) = -gtsam::SO3::Hat(transed_x_i.head<3>());
171 J_target.block<3, 3>(0, 3) = Eigen::Matrix3d::Identity();
172
173 Eigen::Matrix<double, 4, 6> J_source = Eigen::Matrix<double, 4, 6>::Zero();
174 J_source.block<3, 3>(0, 0) = delta.linear() * gtsam::SO3::Hat(x_i.template head<3>());
175 J_source.block<3, 3>(0, 3) = -delta.linear();
176
177 J_target = normal.asDiagonal() * J_target;
178 J_source = normal.asDiagonal() * J_source;
179
180 *H_target += J_target.transpose() * J_target;
181 *H_source += J_source.transpose() * J_source;
182 *H_target_source += J_target.transpose() * J_source;
183 *b_target += J_target.transpose() * plane_residual;
184 *b_source += J_source.transpose() * plane_residual;
185
186 return error;
187 };
188
189 if (is_omp_default() || num_threads == 1) {
190 return scan_matching_reduce_omp(perpoint_task, frame::size(*source), num_threads, H_target, H_source, H_target_source, b_target, b_source);
191 } else {
192 return scan_matching_reduce_tbb(perpoint_task, frame::size(*source), H_target, H_source, H_target_source, b_target, b_source);
193 }
194}
195
196template <typename TargetFrame, typename SourceFrame>
197IntegratedPointToEdgeFactor_<TargetFrame, SourceFrame>::IntegratedPointToEdgeFactor_(
198 gtsam::Key target_key,
199 gtsam::Key source_key,
200 const std::shared_ptr<const TargetFrame>& target,
201 const std::shared_ptr<const SourceFrame>& source,
202 const std::shared_ptr<const NearestNeighborSearch>& target_tree)
203: gtsam_points::IntegratedMatchingCostFactor(target_key, source_key),
204 num_threads(1),
205 max_correspondence_distance_sq(1.0),
206 correspondence_update_tolerance_rot(0.0),
207 correspondence_update_tolerance_trans(0.0),
208 target(target),
209 source(source) {
210 //
211 if (!frame::has_points(*target) || !frame::has_points(*source)) {
212 std::cerr << "error: target or source points has not been allocated!!" << std::endl;
213 abort();
214 }
215
216 if (target_tree) {
217 this->target_tree = target_tree;
218 } else {
219 this->target_tree.reset(new KdTree2<TargetFrame>(target));
220 }
221}
222
223template <typename TargetFrame, typename SourceFrame>
224IntegratedPointToEdgeFactor_<TargetFrame, SourceFrame>::IntegratedPointToEdgeFactor_(
225 gtsam::Key target_key,
226 gtsam::Key source_key,
227 const std::shared_ptr<const TargetFrame>& target,
228 const std::shared_ptr<const SourceFrame>& source)
229: gtsam_points::IntegratedPointToEdgeFactor_<TargetFrame, SourceFrame>(target_key, source_key, target, source, nullptr) {}
230
231template <typename TargetFrame, typename SourceFrame>
232IntegratedPointToEdgeFactor_<TargetFrame, SourceFrame>::~IntegratedPointToEdgeFactor_() {}
233
234template <typename TargetFrame, typename SourceFrame>
235void IntegratedPointToEdgeFactor_<TargetFrame, SourceFrame>::update_correspondences(const Eigen::Isometry3d& delta) const {
236 if (correspondences.size() == frame::size(*source) && (correspondence_update_tolerance_trans > 0.0 || correspondence_update_tolerance_rot > 0.0)) {
237 Eigen::Isometry3d diff = delta.inverse() * last_correspondence_point;
238 double diff_rot = Eigen::AngleAxisd(diff.linear()).angle();
239 double diff_trans = diff.translation().norm();
240 if (diff_rot < correspondence_update_tolerance_rot && diff_trans < correspondence_update_tolerance_trans) {
241 return;
242 }
243 }
244
245 correspondences.resize(frame::size(*source));
246 last_correspondence_point = delta;
247
248 const auto perpoint_task = [&](int i) {
249 Eigen::Vector4d pt = delta * frame::point(*source, i);
250
251 std::array<size_t, 2> k_indices;
252 std::array<double, 2> k_sq_dists;
253 size_t num_found = target_tree->knn_search(pt.data(), 2, k_indices.data(), k_sq_dists.data(), max_correspondence_distance_sq);
254
255 if (num_found < 2 || k_sq_dists.back() > max_correspondence_distance_sq) {
256 correspondences[i] = std::make_tuple(-1, -1);
257 } else {
258 correspondences[i] = std::make_tuple(k_indices[0], k_indices[1]);
259 }
260 };
261
262 if (is_omp_default() || num_threads == 1) {
263#pragma omp parallel for num_threads(num_threads) schedule(guided, 8)
264 for (int i = 0; i < frame::size(*source); i++) {
265 perpoint_task(i);
266 }
267 } else {
268#ifdef GTSAM_POINTS_USE_TBB
269 tbb::parallel_for(tbb::blocked_range<int>(0, frame::size(*source), 8), [&](const tbb::blocked_range<int>& range) {
270 for (int i = range.begin(); i < range.end(); i++) {
271 perpoint_task(i);
272 }
273 });
274#else
275 std::cerr << "error: TBB is not available" << std::endl;
276 abort();
277#endif
278 }
279}
280
281template <typename TargetFrame, typename SourceFrame>
282void IntegratedPointToEdgeFactor_<TargetFrame, SourceFrame>::print(const std::string& s, const gtsam::KeyFormatter& keyFormatter) const {
283 std::cout << s << "IntegratedPointToEdgeFactor";
284 if (is_binary) {
285 std::cout << "(" << keyFormatter(this->keys()[0]) << ", " << keyFormatter(this->keys()[1]) << ")" << std::endl;
286 } else {
287 std::cout << "(fixed, " << keyFormatter(this->keys()[0]) << ")" << std::endl;
288 }
289
290 std::cout << "|target|=" << frame::size(*target) << "pts, |source|=" << frame::size(*source) << "pts" << std::endl;
291 std::cout << "num_threads=" << num_threads << ", max_corr_dist=" << std::sqrt(max_correspondence_distance_sq) << std::endl;
292}
293
294template <typename TargetFrame, typename SourceFrame>
296 return sizeof(*this) + sizeof(std::tuple<long, long>) * correspondences.capacity();
297}
298
299template <typename TargetFrame, typename SourceFrame>
301 const Eigen::Isometry3d& delta,
302 Eigen::Matrix<double, 6, 6>* H_target,
303 Eigen::Matrix<double, 6, 6>* H_source,
304 Eigen::Matrix<double, 6, 6>* H_target_source,
305 Eigen::Matrix<double, 6, 1>* b_target,
306 Eigen::Matrix<double, 6, 1>* b_source) const {
307 //
308 if (correspondences.size() != frame::size(*source)) {
309 update_correspondences(delta);
310 }
311
312 const auto perpoint_task = [&](
313 int i,
314 Eigen::Matrix<double, 6, 6>* H_target,
315 Eigen::Matrix<double, 6, 6>* H_source,
316 Eigen::Matrix<double, 6, 6>* H_target_source,
317 Eigen::Matrix<double, 6, 1>* b_target,
318 Eigen::Matrix<double, 6, 1>* b_source) {
319 auto target_indices = correspondences[i];
320 if (std::get<0>(target_indices) < 0) {
321 return 0.0;
322 }
323
324 const auto& x_i = frame::point(*source, i);
325 const auto& x_j = frame::point(*target, std::get<0>(target_indices));
326 const auto& x_l = frame::point(*target, std::get<1>(target_indices));
327
328 Eigen::Vector4d transed_x_i = delta * x_i;
329
330 double c_inv = 1.0 / (x_j - x_l).norm();
331 Eigen::Vector4d x_ij = transed_x_i - x_j;
332 Eigen::Vector4d x_il = transed_x_i - x_l;
333
334 Eigen::Vector4d residual = Eigen::Vector4d::Zero();
335 residual.head<3>() = x_ij.head<3>().cross(x_il.head<3>()) * c_inv;
336 const double error = residual.dot(residual);
337
338 if (H_target == nullptr) {
339 return error;
340 }
341
342 Eigen::Matrix<double, 4, 6> J_target = Eigen::Matrix<double, 4, 6>::Zero();
343 J_target.block<3, 3>(0, 0) = -gtsam::SO3::Hat(transed_x_i.template head<3>());
344 J_target.block<3, 3>(0, 3) = Eigen::Matrix3d::Identity();
345
346 Eigen::Matrix<double, 4, 6> J_source = Eigen::Matrix<double, 4, 6>::Zero();
347 J_source.block<3, 3>(0, 0) = delta.linear() * gtsam::SO3::Hat(x_i.template head<3>());
348 J_source.block<3, 3>(0, 3) = -delta.linear();
349
350 Eigen::Matrix<double, 4, 4> J_e = Eigen::Matrix4d::Zero();
351 J_e.block<1, 3>(0, 0) << 0.0, -x_j[2] + x_l[2], x_j[1] - x_l[1];
352 J_e.block<1, 3>(1, 0) << x_j[2] - x_l[2], 0.0, -x_j[0] + x_l[0];
353 J_e.block<1, 3>(2, 0) << -x_j[1] + x_l[1], x_j[0] - x_l[0], 0.0;
354
355 J_target = c_inv * J_e * J_target;
356 J_source = c_inv * J_e * J_source;
357
358 *H_target += J_target.transpose() * J_target;
359 *H_source += J_source.transpose() * J_source;
360 *H_target_source += J_target.transpose() * J_source;
361 *b_target += J_target.transpose() * residual;
362 *b_source += J_source.transpose() * residual;
363
364 return error;
365 };
366
367 if (is_omp_default() || num_threads == 1) {
368 return scan_matching_reduce_omp(perpoint_task, frame::size(*source), num_threads, H_target, H_source, H_target_source, b_target, b_source);
369 } else {
370 return scan_matching_reduce_tbb(perpoint_task, frame::size(*source), H_target, H_source, H_target_source, b_target, b_source);
371 }
372}
373
374template <typename TargetFrame, typename SourceFrame>
375IntegratedLOAMFactor_<TargetFrame, SourceFrame>::IntegratedLOAMFactor_(
376 gtsam::Key target_key,
377 gtsam::Key source_key,
378 const std::shared_ptr<const TargetFrame>& target_edges,
379 const std::shared_ptr<const TargetFrame>& target_planes,
380 const std::shared_ptr<const SourceFrame>& source_edges,
381 const std::shared_ptr<const SourceFrame>& source_planes,
382 const std::shared_ptr<const NearestNeighborSearch>& target_edges_tree,
383 const std::shared_ptr<const NearestNeighborSearch>& target_planes_tree)
384: gtsam_points::IntegratedMatchingCostFactor(target_key, source_key),
385 enable_correspondence_validation(false) {
386 //
387 edge_factor.reset(
388 new IntegratedPointToEdgeFactor_<TargetFrame, SourceFrame>(target_key, source_key, target_edges, source_edges, target_edges_tree));
389 plane_factor.reset(
390 new IntegratedPointToPlaneFactor_<TargetFrame, SourceFrame>(target_key, source_key, target_planes, source_planes, target_planes_tree));
391}
392
393template <typename TargetFrame, typename SourceFrame>
394IntegratedLOAMFactor_<TargetFrame, SourceFrame>::IntegratedLOAMFactor_(
395 gtsam::Key target_key,
396 gtsam::Key source_key,
397 const std::shared_ptr<const TargetFrame>& target_edges,
398 const std::shared_ptr<const TargetFrame>& target_planes,
399 const std::shared_ptr<const SourceFrame>& source_edges,
400 const std::shared_ptr<const SourceFrame>& source_planes)
401: gtsam_points::IntegratedMatchingCostFactor(target_key, source_key),
402 enable_correspondence_validation(false) {
403 //
404 edge_factor.reset(new IntegratedPointToEdgeFactor_<TargetFrame, SourceFrame>(target_key, source_key, target_edges, source_edges));
405 plane_factor.reset(new IntegratedPointToPlaneFactor_<TargetFrame, SourceFrame>(target_key, source_key, target_planes, source_planes));
406}
407
408template <typename TargetFrame, typename SourceFrame>
409IntegratedLOAMFactor_<TargetFrame, SourceFrame>::~IntegratedLOAMFactor_() {}
410
411template <typename TargetFrame, typename SourceFrame>
412void IntegratedLOAMFactor_<TargetFrame, SourceFrame>::print(const std::string& s, const gtsam::KeyFormatter& keyFormatter) const {
413 std::cout << s << "IntegratedLOAMFactor";
414 if (is_binary) {
415 std::cout << "(" << keyFormatter(this->keys()[0]) << ", " << keyFormatter(this->keys()[1]) << ")" << std::endl;
416 } else {
417 std::cout << "(fixed, " << keyFormatter(this->keys()[0]) << ")" << std::endl;
418 }
419}
420
421template <typename TargetFrame, typename SourceFrame>
423 return edge_factor->memory_usage() + plane_factor->memory_usage();
424}
425
426template <typename TargetFrame, typename SourceFrame>
428 edge_factor->set_num_threads(n);
429 plane_factor->set_num_threads(n);
430}
431
432template <typename TargetFrame, typename SourceFrame>
433void IntegratedLOAMFactor_<TargetFrame, SourceFrame>::set_max_correspondence_distance(double dist_edge, double dist_plane) {
434 edge_factor->set_max_correspondence_distance(dist_edge);
435 plane_factor->set_max_correspondence_distance(dist_plane);
436}
437
438template <typename TargetFrame, typename SourceFrame>
439void IntegratedLOAMFactor_<TargetFrame, SourceFrame>::set_enable_correspondence_validation(bool enable) {
440 enable_correspondence_validation = enable;
441}
442
443template <typename TargetFrame, typename SourceFrame>
444void IntegratedLOAMFactor_<TargetFrame, SourceFrame>::update_correspondences(const Eigen::Isometry3d& delta) const {
445 edge_factor->update_correspondences(delta);
446 plane_factor->update_correspondences(delta);
447
448 validate_correspondences();
449}
450
451template <typename TargetFrame, typename SourceFrame>
452double IntegratedLOAMFactor_<TargetFrame, SourceFrame>::evaluate(
453 const Eigen::Isometry3d& delta,
454 Eigen::Matrix<double, 6, 6>* H_target,
455 Eigen::Matrix<double, 6, 6>* H_source,
456 Eigen::Matrix<double, 6, 6>* H_target_source,
457 Eigen::Matrix<double, 6, 1>* b_target,
458 Eigen::Matrix<double, 6, 1>* b_source) const {
459 //
460
461 double error = edge_factor->evaluate(delta, H_target, H_source, H_target_source, b_target, b_source);
462
463 if (H_target && H_source && H_target_source && b_target && b_source) {
464 Eigen::Matrix<double, 6, 6> H_t, H_s, H_ts;
465 Eigen::Matrix<double, 6, 1> b_t, b_s;
466 error += plane_factor->evaluate(delta, &H_t, &H_s, &H_ts, &b_t, &b_s);
467
468 (*H_target) += H_t;
469 (*H_source) += H_s;
470 (*H_target_source) += H_ts;
471 (*b_target) += b_t;
472 (*b_source) += b_s;
473 } else {
474 error += plane_factor->evaluate(delta);
475 }
476
477 return error;
478}
479
480template <typename TargetFrame, typename SourceFrame>
481void IntegratedLOAMFactor_<TargetFrame, SourceFrame>::set_correspondence_update_tolerance(double angle, double trans) {
482 plane_factor->set_correspondence_update_tolerance(angle, trans);
483 edge_factor->set_correspondence_update_tolerance(angle, trans);
484}
485
486template <typename TargetFrame, typename SourceFrame>
487void IntegratedLOAMFactor_<TargetFrame, SourceFrame>::validate_correspondences() const {
488 if (!enable_correspondence_validation) {
489 return;
490 }
491
492 // Validate edge correspondences
493 for (auto& corr : edge_factor->correspondences) {
494 if (std::get<0>(corr) < 0) {
495 continue;
496 }
497
498 const auto& pt1 = frame::point(*edge_factor->target, std::get<0>(corr));
499 const auto& pt2 = frame::point(*edge_factor->target, std::get<1>(corr));
500
501 const double v_theta1 = std::atan2(pt1.z(), pt1.template head<2>().norm());
502 const double v_theta2 = std::atan2(pt2.z(), pt2.template head<2>().norm());
503
504 // Reject pairs in a same scan line
505 if (std::abs(v_theta1 - v_theta2) < 0.1 * M_PI / 180.0) {
506 corr = std::make_tuple(-1, -1);
507 }
508 }
509
510 // Validate plane correspondences
511 for (auto& corr : plane_factor->correspondences) {
512 if (std::get<0>(corr) < 0) {
513 continue;
514 }
515
516 const auto& pt1 = frame::point(*plane_factor->target, std::get<0>(corr));
517 const auto& pt2 = frame::point(*plane_factor->target, std::get<1>(corr));
518 const auto& pt3 = frame::point(*plane_factor->target, std::get<2>(corr));
519
520 const double v_theta1 = std::atan2(pt1.z(), pt1.template head<2>().norm());
521 const double v_theta2 = std::atan2(pt2.z(), pt2.template head<2>().norm());
522 const double v_theta3 = std::atan2(pt3.z(), pt3.template head<2>().norm());
523
524 // Reject pairs in a same scan line
525 if (std::abs(v_theta1 - v_theta2) < 0.1 * M_PI / 180.0 && std::abs(v_theta1 - v_theta3) < 0.1 * M_PI * 180.0) {
526 corr = std::make_tuple(-1, -1, -1);
527 }
528 }
529}
530
531} // namespace gtsam_points
Scan matching factor based on the combination of point-to-plane and point-to-edge distances.
Definition integrated_loam_factor.hpp:30
virtual size_t memory_usage() const override
Calculate the memory usage of this factor.
Definition integrated_loam_factor_impl.hpp:422
virtual void print(const std::string &s="", const gtsam::KeyFormatter &keyFormatter=gtsam::DefaultKeyFormatter) const override
Print the factor information.
Definition integrated_loam_factor_impl.hpp:412
Definition integrated_loam_factor.hpp:153
virtual void print(const std::string &s="", const gtsam::KeyFormatter &keyFormatter=gtsam::DefaultKeyFormatter) const override
Print the factor information.
Definition integrated_loam_factor_impl.hpp:282
virtual size_t memory_usage() const override
Calculate the memory usage of this factor.
Definition integrated_loam_factor_impl.hpp:295
Definition integrated_loam_factor.hpp:91