14 #include <Eigen/Dense>
15 #include <Eigen/Sparse>
25 namespace registration {
39 const std::vector<Eigen::Matrix4d, utility::Matrix4d_allocator>
42 (Eigen::Matrix4d() << 0, 0, 0, 0,
45 0, 0, 0, 0).finished(),
47 (Eigen::Matrix4d() << 0, 0, 1, 0,
50 0, 0, 0, 0).finished(),
52 (Eigen::Matrix4d() << 0, -1, 0, 0,
55 0, 0, 0, 0).finished(),
57 (Eigen::Matrix4d() << 0, 0, 0, 1,
60 0, 0, 0, 0).finished(),
62 (Eigen::Matrix4d() << 0, 0, 0, 0,
65 0, 0, 0, 0).finished(),
67 (Eigen::Matrix4d() << 0, 0, 0, 0,
70 0, 0, 0, 0).finished()};
80 output(0) = (-input(1, 2) + input(2, 1)) / 2.0;
81 output(1) = (-input(2, 0) + input(0, 2)) / 2.0;
82 output(2) = (-input(0, 1) + input(1, 0)) / 2.0;
83 output.block<3, 1>(3, 0) = input.block<3, 1>(0, 3);
88 const Eigen::Matrix4d &Ts,
89 const Eigen::Matrix4d &Tt_inv) {
91 temp.noalias() = X_inv * Tt_inv * Ts;
95 inline std::tuple<Eigen::Matrix4d, Eigen::Matrix4d, Eigen::Matrix4d>
101 Eigen::Matrix4d Ts = ts.
pose_;
102 Eigen::Matrix4d Tt_inv = tt.
pose_.inverse();
103 return std::make_tuple(std::move(X_inv), std::move(Ts), std::move(Tt_inv));
107 const Eigen::Matrix4d &X_inv,
108 const Eigen::Matrix4d &Ts,
109 const Eigen::Matrix4d &Tt_inv) {
111 for (
int i = 0; i < 6; i++) {
116 for (
int i = 0; i < 6; i++) {
120 return std::make_tuple(std::move(Js), std::move(Jt));
126 const Eigen::VectorXd &zeta,
127 const double line_process_weight,
129 int n_edges = (int)pose_graph.
edges_.size();
130 int valid_edges_num = 0;
131 for (
int iter_edge = 0; iter_edge < n_edges; iter_edge++) {
135 double residual_square = e.transpose() * t.
information_ * e;
136 double temp = line_process_weight /
137 (line_process_weight + residual_square);
138 double temp2 = temp * temp;
143 return valid_edges_num;
148 const Eigen::VectorXd &zeta,
149 const double line_process_weight,
151 int n_edges = (int)pose_graph.
edges_.size();
152 double residual = 0.0;
153 for (
int iter_edge = 0; iter_edge < n_edges; iter_edge++) {
157 residual += line_process_iter * e.transpose() * te.
information_ * e +
158 line_process_weight * pow(sqrt(line_process_iter) - 1, 2.0);
165 int n_edges = (int)pose_graph.
edges_.size();
166 Eigen::VectorXd output(n_edges * 6);
167 for (
int iter_edge = 0; iter_edge < n_edges; iter_edge++) {
168 Eigen::Matrix4d X_inv, Ts, Tt_inv;
171 output.block<6, 1>(iter_edge * 6, 0) = e;
190 const PoseGraph &pose_graph,
const Eigen::VectorXd &zeta) {
191 int n_nodes = (int)pose_graph.
nodes_.size();
192 int n_edges = (int)pose_graph.
edges_.size();
193 Eigen::MatrixXd H(n_nodes * 6, n_nodes * 6);
194 Eigen::VectorXd b(n_nodes * 6);
198 for (
int iter_edge = 0; iter_edge < n_edges; iter_edge++) {
202 Eigen::Matrix4d X_inv, Ts, Tt_inv;
214 H.block<6, 6>(id_i, id_i).noalias() +=
215 line_process_iter * JsT_Info * Js;
216 H.block<6, 6>(id_i, id_j).noalias() +=
217 line_process_iter * JsT_Info * Jt;
218 H.block<6, 6>(id_j, id_i).noalias() +=
219 line_process_iter * JtT_Info * Js;
220 H.block<6, 6>(id_j, id_j).noalias() +=
221 line_process_iter * JtT_Info * Jt;
222 b.block<6, 1>(id_i, 0).noalias() -=
223 line_process_iter * eT_Info.transpose() * Js;
224 b.block<6, 1>(id_j, 0).noalias() -=
225 line_process_iter * eT_Info.transpose() * Jt;
227 return std::make_tuple(std::move(H), std::move(b));
231 int n_nodes = (int)pose_graph.
nodes_.size();
232 Eigen::VectorXd output(n_nodes * 6);
233 for (
int iter_node = 0; iter_node < n_nodes; iter_node++) {
235 pose_graph.
nodes_[iter_node].pose_);
236 output.block<6, 1>(iter_node * 6, 0) = output_iter;
242 const Eigen::VectorXd delta) {
243 std::shared_ptr<PoseGraph> pose_graph_updated =
244 std::make_shared<PoseGraph>();
245 *pose_graph_updated = pose_graph;
246 int n_nodes = (int)pose_graph.
nodes_.size();
247 for (
int iter_node = 0; iter_node < n_nodes; iter_node++) {
249 pose_graph_updated->nodes_[iter_node].pose_ =
251 pose_graph_updated->nodes_[iter_node].pose_;
253 return pose_graph_updated;
267 const Eigen::VectorXd &delta,
268 const Eigen::VectorXd &x,
281 double current_residual,
284 if (current_residual - new_residual <
287 "Current_residual - new_residual < {:e} * current_residual",
325 int n_edges = (int)pose_graph.
edges_.size();
326 double average_number_of_correspondences = 0.0;
327 for (
int iter_edge = 0; iter_edge < n_edges; iter_edge++) {
328 double number_of_correspondences =
329 pose_graph.
edges_[iter_edge].information_(5, 5);
330 average_number_of_correspondences += number_of_correspondences;
334 average_number_of_correspondences /= (double)n_edges;
335 double line_process_weight =
338 average_number_of_correspondences;
339 return line_process_weight;
347 int reference_node) {
350 int n_nodes = (int)pose_graph_new.
nodes_.size();
351 if (reference_node < 0 || reference_node >= n_nodes) {
354 Eigen::Matrix4d compensation =
355 pose_graph_orig.
nodes_[reference_node].pose_ *
356 pose_graph_new.
nodes_[reference_node].pose_.inverse();
357 for (
int i = 0; i < n_nodes; i++) {
358 pose_graph_new.
nodes_[i].pose_ =
359 compensation * pose_graph_new.
nodes_[i].pose_;
365 bool ignore_uncertain_edges =
false) {
366 size_t n_nodes = pose_graph.
nodes_.size();
367 size_t n_edges = pose_graph.
edges_.size();
371 std::vector<int> nodes_to_explore{};
372 std::vector<int> component{};
374 nodes_to_explore.push_back(0);
375 component.push_back(0);
377 while (!nodes_to_explore.empty()) {
378 int i = nodes_to_explore.back();
379 nodes_to_explore.pop_back();
380 for (
size_t j = 0; j < n_edges; j++) {
385 int adjacent_node{-1};
391 if (adjacent_node != -1) {
392 auto find_result = std::find(component.begin(), component.end(),
394 if (find_result == component.end()) {
395 nodes_to_explore.push_back(adjacent_node);
396 component.push_back(adjacent_node);
401 return component.size() == n_nodes;
405 int n_nodes = (int)pose_graph.
nodes_.size();
406 int n_edges = (int)pose_graph.
edges_.size();
415 "Certain-edge subset of PoseGraph is not connected.");
418 for (
int j = 0; j < n_edges; j++) {
426 "Invalid PoseGraph - an edge references an invalide "
431 for (
int j = 0; j < n_edges; j++) {
435 "Invalid PoseGraph - the certain edge does not have 1.0 as "
446 std::shared_ptr<PoseGraph> pose_graph_pruned =
447 std::make_shared<PoseGraph>();
449 int n_nodes = (int)pose_graph.
nodes_.size();
450 for (
int iter_node = 0; iter_node < n_nodes; iter_node++) {
452 pose_graph_pruned->nodes_.push_back(t);
454 int n_edges = (int)pose_graph.
edges_.size();
455 for (
int iter_edge = 0; iter_edge < n_edges; iter_edge++) {
459 pose_graph_pruned->edges_.push_back(t);
462 pose_graph_pruned->edges_.push_back(t);
465 return pose_graph_pruned;
472 int n_nodes = (int)pose_graph.
nodes_.size();
473 int n_edges = (int)pose_graph.
edges_.size();
477 "[GlobalOptimizationGaussNewton] Optimizing PoseGraph having {:d} "
478 "nodes and {:d} edges.",
483 double current_residual, new_residual;
486 current_residual = new_residual;
504 timer_overall.
Start();
506 for (iter = 0; !stop; iter++) {
510 Eigen::VectorXd delta(H.cols());
511 bool solver_success =
false;
522 std::shared_ptr<PoseGraph> pose_graph_new =
525 Eigen::VectorXd zeta_new;
528 line_process_weight, option);
530 current_residual, new_residual, criteria);
532 current_residual = new_residual;
535 pose_graph = *pose_graph_new;
538 line_process_weight, option);
546 "[Iteration {:02d}] residual : {:e}, valid edges : {:d}, time "
549 iter, current_residual, valid_edges_num,
554 timer_overall.
Stop();
556 "[GlobalOptimizationGaussNewton] total time : {:.3f} sec.",
564 int n_nodes = (int)pose_graph.
nodes_.size();
565 int n_edges = (int)pose_graph.
edges_.size();
569 "[GlobalOptimizationLM] Optimizing PoseGraph having {:d} nodes and "
575 double current_residual, new_residual;
578 current_residual = new_residual;
580 int valid_edges_num =
583 Eigen::MatrixXd H_I = Eigen::MatrixXd::Identity(n_nodes * 6, n_nodes * 6);
590 Eigen::VectorXd H_diag = H.diagonal();
592 double current_lambda = tau * H_diag.maxCoeff();
597 current_residual, current_lambda);
604 timer_overall.
Start();
605 for (
int iter = 0; !stop; iter++) {
610 Eigen::MatrixXd H_LM = H + current_lambda * H_I;
611 Eigen::VectorXd delta(H_LM.cols());
612 bool solver_success =
false;
616 H_LM, b,
true,
false,
621 std::shared_ptr<PoseGraph> pose_graph_new =
624 Eigen::VectorXd zeta_new;
627 line_process_weight, option);
628 rho = (current_residual - new_residual) /
629 (delta.dot(current_lambda * delta + b) + 1e-3);
633 current_residual, new_residual, criteria);
635 double alpha = 1. - pow((2 * rho - 1), 3);
639 current_lambda *= scaleFactor;
641 current_residual = new_residual;
644 pose_graph = *pose_graph_new;
647 pose_graph, zeta, line_process_weight, option);
653 current_lambda *= ni;
659 }
while (!((rho > 0) || stop));
663 "[Iteration {:02d}] residual : {:e}, valid edges : {:d}, "
666 iter, current_residual, valid_edges_num,
672 timer_overall.
Stop();
685 std::shared_ptr<PoseGraph> pose_graph_pre = std::make_shared<PoseGraph>();
686 *pose_graph_pre = pose_graph;
688 auto pose_graph_pre_pruned =
691 auto pose_graph_pre_pruned_2 =
695 pose_graph = *pose_graph_pre_pruned_2;
Convergence criteria of GlobalOptimization.
int max_iteration_lm_
Maximum iteration number for Levenberg Marquardt method.
double upper_scale_factor_
Upper scale factor value.
int max_iteration_
Maximum iteration number for iterative optimization module.
double lower_scale_factor_
Lower scale factor value.
double min_relative_residual_increment_
Minimum relative residual increments.
double min_right_term_
Minimum right term value.
double min_residual_
Minimum residual value.
double min_relative_increment_
Minimum relative increments.
void OptimizePoseGraph(PoseGraph &pose_graph, const GlobalOptimizationConvergenceCriteria &criteria, const GlobalOptimizationOption &option) const override
Run pose graph optimization method.
void OptimizePoseGraph(PoseGraph &pose_graph, const GlobalOptimizationConvergenceCriteria &criteria, const GlobalOptimizationOption &option) const override
Run pose graph optimization method.
Base class for global optimization method.
virtual void OptimizePoseGraph(PoseGraph &pose_graph, const GlobalOptimizationConvergenceCriteria &criteria, const GlobalOptimizationOption &option) const =0
Run pose graph optimization method.
Option for GlobalOptimization.
double preference_loop_closure_
int reference_node_
The pose of this node is unchanged after optimization.
double edge_prune_threshold_
double max_correspondence_distance_
Eigen::Matrix4d_u transformation_
Transformation matrix.
int target_node_id_
Target PoseGraphNode id.
int source_node_id_
Source PoseGraphNode id.
bool uncertain_
Whether the edge is uncertain.
Eigen::Matrix6d_u information_
Information matrix.
double confidence_
Confidence value of the edge.
Data structure defining the pose graph.
std::vector< PoseGraphNode > nodes_
List of PoseGraphNode.
std::vector< PoseGraphEdge > edges_
List of PoseGraphEdge.
double GetDurationInMillisecond() const
Eigen::Matrix< double, 6, 1 > Vector6d
Eigen::Matrix< double, 6, 6 > Matrix6d
Extending Eigen namespace by adding frequently used matrix type.
Eigen::Vector6d GetLinearized6DVector(const Eigen::Matrix4d &input)
bool ValidatePoseGraphConnectivity(const PoseGraph &pose_graph, bool ignore_uncertain_edges=false)
std::tuple< Eigen::Matrix6d, Eigen::Matrix6d > GetJacobian(const Eigen::Matrix4d &X_inv, const Eigen::Matrix4d &Ts, const Eigen::Matrix4d &Tt_inv)
Eigen::VectorXd UpdatePoseVector(const PoseGraph &pose_graph)
std::tuple< Eigen::Matrix4d, Eigen::Matrix4d, Eigen::Matrix4d > GetRelativePoses(const PoseGraph &pose_graph, int edge_id)
bool CheckResidual(double residual, const GlobalOptimizationConvergenceCriteria &criteria)
bool CheckRightTerm(const Eigen::VectorXd &right_term, const GlobalOptimizationConvergenceCriteria &criteria)
double ComputeResidual(const PoseGraph &pose_graph, const Eigen::VectorXd &zeta, const double line_process_weight, const GlobalOptimizationOption &option)
Function to compute residual defined in [Choi et al 2015] See Eq (9).
bool CheckRelativeIncrement(const Eigen::VectorXd &delta, const Eigen::VectorXd &x, const GlobalOptimizationConvergenceCriteria &criteria)
std::shared_ptr< PoseGraph > UpdatePoseGraph(const PoseGraph &pose_graph, const Eigen::VectorXd delta)
const std::vector< Eigen::Matrix4d, utility::Matrix4d_allocator > jacobian_operator
bool CheckMaxIteration(int iteration, const GlobalOptimizationConvergenceCriteria &criteria)
void GlobalOptimization(PoseGraph &pose_graph, const GlobalOptimizationMethod &method, const GlobalOptimizationConvergenceCriteria &criteria, const GlobalOptimizationOption &option)
bool ValidatePoseGraph(const PoseGraph &pose_graph)
std::shared_ptr< PoseGraph > CreatePoseGraphWithoutInvalidEdges(const PoseGraph &pose_graph, const GlobalOptimizationOption &option)
void CompensateReferencePoseGraphNode(PoseGraph &pose_graph_new, const PoseGraph &pose_graph_orig, int reference_node)
double ComputeLineProcessWeight(const PoseGraph &pose_graph, const GlobalOptimizationOption &option)
Eigen::VectorXd ComputeZeta(const PoseGraph &pose_graph)
Function to compute residual defined in [Choi et al 2015] See Eq (6).
bool CheckMaxIterationLM(int iteration, const GlobalOptimizationConvergenceCriteria &criteria)
Eigen::Vector6d GetMisalignmentVector(const Eigen::Matrix4d &X_inv, const Eigen::Matrix4d &Ts, const Eigen::Matrix4d &Tt_inv)
std::tuple< Eigen::MatrixXd, Eigen::VectorXd > ComputeLinearSystem(const PoseGraph &pose_graph, const Eigen::VectorXd &zeta)
int UpdateConfidence(PoseGraph &pose_graph, const Eigen::VectorXd &zeta, const double line_process_weight, const GlobalOptimizationOption &option)
bool CheckRelativeResidualIncrement(double current_residual, double new_residual, const GlobalOptimizationConvergenceCriteria &criteria)
std::tuple< bool, Eigen::VectorXd > SolveLinearSystemPSD(const Eigen::MatrixXd &A, const Eigen::VectorXd &b, bool prefer_sparse=false, bool check_symmetric=false, bool check_det=false, bool check_psd=false)
Function to solve Ax=b.
Eigen::Vector6d TransformMatrix4dToVector6d(const Eigen::Matrix4d &input)
Eigen::Matrix4d TransformVector6dToMatrix4d(const Eigen::Vector6d &input)
Generic file read and write utility for python interface.