4#include <gtsam_points/factors/integrated_loam_factor.hpp>
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>
12#ifdef GTSAM_POINTS_USE_TBB
13#include <tbb/parallel_for.h>
16namespace gtsam_points {
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),
27 max_correspondence_distance_sq(1.0),
28 correspondence_update_tolerance_rot(0.0),
29 correspondence_update_tolerance_trans(0.0),
33 if (!frame::has_points(*target)) {
34 std::cerr <<
"error: target frame doesn't have required attributes for loam" << std::endl;
38 if (!frame::has_points(*source)) {
39 std::cerr <<
"error: source frame doesn't have required attributes for loam" << std::endl;
44 this->target_tree = target_tree;
46 this->target_tree.reset(
new KdTree2<TargetFrame>(target));
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) {}
58template <
typename TargetFrame,
typename SourceFrame>
59IntegratedPointToPlaneFactor_<TargetFrame, SourceFrame>::~IntegratedPointToPlaneFactor_() {}
61template <
typename TargetFrame,
typename SourceFrame>
63 std::cout << s <<
"IntegratedPointToPlaneFactor";
65 std::cout <<
"(" << keyFormatter(this->keys()[0]) <<
", " << keyFormatter(this->keys()[1]) <<
")" << std::endl;
67 std::cout <<
"(fixed, " << keyFormatter(this->keys()[0]) <<
")" << std::endl;
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;
74template <
typename TargetFrame,
typename SourceFrame>
76 return sizeof(*this) +
sizeof(std::tuple<long, long, long>) * correspondences.capacity();
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) {
90 last_correspondence_point = delta;
91 correspondences.resize(frame::size(*source));
93 const auto perpoint_task = [&](
int i) {
94 Eigen::Vector4d pt = delta * frame::point(*source, i);
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);
100 if (num_found < 3 || k_sq_dists.back() > max_correspondence_distance_sq) {
101 correspondences[i] = std::make_tuple(-1, -1, -1);
103 correspondences[i] = std::make_tuple(k_indices[0], k_indices[1], k_indices[2]);
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++) {
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++) {
120 std::cerr <<
"error: TBB is not available" << std::endl;
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 {
135 if (correspondences.size() != frame::size(*source)) {
136 update_correspondences(delta);
139 const auto perpoint_task = [&](
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) {
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));
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();
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;
165 if (H_target ==
nullptr) {
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();
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();
177 J_target = normal.asDiagonal() * J_target;
178 J_source = normal.asDiagonal() * J_source;
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;
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);
192 return scan_matching_reduce_tbb(perpoint_task, frame::size(*source), H_target, H_source, H_target_source, b_target, b_source);
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),
205 max_correspondence_distance_sq(1.0),
206 correspondence_update_tolerance_rot(0.0),
207 correspondence_update_tolerance_trans(0.0),
211 if (!frame::has_points(*target) || !frame::has_points(*source)) {
212 std::cerr <<
"error: target or source points has not been allocated!!" << std::endl;
217 this->target_tree = target_tree;
219 this->target_tree.reset(
new KdTree2<TargetFrame>(target));
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) {}
231template <
typename TargetFrame,
typename SourceFrame>
232IntegratedPointToEdgeFactor_<TargetFrame, SourceFrame>::~IntegratedPointToEdgeFactor_() {}
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) {
245 correspondences.resize(frame::size(*source));
246 last_correspondence_point = delta;
248 const auto perpoint_task = [&](
int i) {
249 Eigen::Vector4d pt = delta * frame::point(*source, i);
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);
255 if (num_found < 2 || k_sq_dists.back() > max_correspondence_distance_sq) {
256 correspondences[i] = std::make_tuple(-1, -1);
258 correspondences[i] = std::make_tuple(k_indices[0], k_indices[1]);
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++) {
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++) {
275 std::cerr <<
"error: TBB is not available" << std::endl;
281template <
typename TargetFrame,
typename SourceFrame>
283 std::cout << s <<
"IntegratedPointToEdgeFactor";
285 std::cout <<
"(" << keyFormatter(this->keys()[0]) <<
", " << keyFormatter(this->keys()[1]) <<
")" << std::endl;
287 std::cout <<
"(fixed, " << keyFormatter(this->keys()[0]) <<
")" << std::endl;
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;
294template <
typename TargetFrame,
typename SourceFrame>
296 return sizeof(*this) +
sizeof(std::tuple<long, long>) * correspondences.capacity();
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 {
308 if (correspondences.size() != frame::size(*source)) {
309 update_correspondences(delta);
312 const auto perpoint_task = [&](
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) {
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));
328 Eigen::Vector4d transed_x_i = delta * x_i;
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;
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);
338 if (H_target ==
nullptr) {
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();
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();
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;
355 J_target = c_inv * J_e * J_target;
356 J_source = c_inv * J_e * J_source;
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;
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);
370 return scan_matching_reduce_tbb(perpoint_task, frame::size(*source), H_target, H_source, H_target_source, b_target, b_source);
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) {
388 new IntegratedPointToEdgeFactor_<TargetFrame, SourceFrame>(target_key, source_key, target_edges, source_edges, target_edges_tree));
390 new IntegratedPointToPlaneFactor_<TargetFrame, SourceFrame>(target_key, source_key, target_planes, source_planes, target_planes_tree));
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) {
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));
408template <
typename TargetFrame,
typename SourceFrame>
409IntegratedLOAMFactor_<TargetFrame, SourceFrame>::~IntegratedLOAMFactor_() {}
411template <
typename TargetFrame,
typename SourceFrame>
413 std::cout << s <<
"IntegratedLOAMFactor";
415 std::cout <<
"(" << keyFormatter(this->keys()[0]) <<
", " << keyFormatter(this->keys()[1]) <<
")" << std::endl;
417 std::cout <<
"(fixed, " << keyFormatter(this->keys()[0]) <<
")" << std::endl;
421template <
typename TargetFrame,
typename SourceFrame>
423 return edge_factor->memory_usage() + plane_factor->memory_usage();
426template <
typename TargetFrame,
typename SourceFrame>
428 edge_factor->set_num_threads(n);
429 plane_factor->set_num_threads(n);
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);
438template <
typename TargetFrame,
typename SourceFrame>
439void IntegratedLOAMFactor_<TargetFrame, SourceFrame>::set_enable_correspondence_validation(
bool enable) {
440 enable_correspondence_validation = enable;
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);
448 validate_correspondences();
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 {
461 double error = edge_factor->evaluate(delta, H_target, H_source, H_target_source, b_target, b_source);
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);
470 (*H_target_source) += H_ts;
474 error += plane_factor->evaluate(delta);
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);
486template <
typename TargetFrame,
typename SourceFrame>
487void IntegratedLOAMFactor_<TargetFrame, SourceFrame>::validate_correspondences()
const {
488 if (!enable_correspondence_validation) {
493 for (
auto& corr : edge_factor->correspondences) {
494 if (std::get<0>(corr) < 0) {
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));
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());
505 if (std::abs(v_theta1 - v_theta2) < 0.1 * M_PI / 180.0) {
506 corr = std::make_tuple(-1, -1);
511 for (
auto& corr : plane_factor->correspondences) {
512 if (std::get<0>(corr) < 0) {
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));
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());
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);
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