ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
global_optimization.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
12 #include "pybind/docstring.h"
14 
15 namespace cloudViewer {
16 namespace pipelines {
17 namespace registration {
18 
19 template <class GlobalOptimizationMethodBase = GlobalOptimizationMethod>
20 class PyGlobalOptimizationMethod : public GlobalOptimizationMethodBase {
21 public:
22  using GlobalOptimizationMethodBase::GlobalOptimizationMethodBase;
24  PoseGraph &pose_graph,
26  const GlobalOptimizationOption &option) const override {
27  PYBIND11_OVERLOAD_PURE(void, GlobalOptimizationMethodBase, pose_graph,
28  criteria, option);
29  }
30 };
31 
32 void pybind_global_optimization(py::module &m) {
33  // cloudViewer.registration.PoseGraphNode
34  py::class_<PoseGraphNode, std::shared_ptr<PoseGraphNode>> pose_graph_node(
35  m, "PoseGraphNode", "Node of ``PoseGraph``.");
36  py::detail::bind_default_constructor<PoseGraphNode>(pose_graph_node);
37  py::detail::bind_copy_functions<PoseGraphNode>(pose_graph_node);
38  pose_graph_node.def_readwrite("pose", &PoseGraphNode::pose_)
39  .def(py::init([](Eigen::Matrix4d pose =
40  Eigen::Matrix4d::Identity()) {
41  return new PoseGraphNode(pose);
42  }),
43  "pose"_a)
44  .def("__repr__", [](const PoseGraphNode &rr) {
45  return std::string(
46  "PoseGraphNode, access pose to get its "
47  "current pose.");
48  });
49 
50  // cloudViewer.registration.PoseGraphNodeVector
51  auto pose_graph_node_vector = py::bind_vector<std::vector<PoseGraphNode>>(
52  m, "PoseGraphNodeVector");
53  pose_graph_node_vector.attr("__doc__") = docstring::static_property(
54  py::cpp_function([](py::handle arg) -> std::string {
55  return "Vector of PoseGraphNode";
56  }),
57  py::none(), py::none(), "");
58 
59  // cloudViewer.registration.PoseGraphEdge
60  py::class_<PoseGraphEdge, std::shared_ptr<PoseGraphEdge>> pose_graph_edge(
61  m, "PoseGraphEdge", "Edge of ``PoseGraph``.");
62  py::detail::bind_default_constructor<PoseGraphEdge>(pose_graph_edge);
63  py::detail::bind_copy_functions<PoseGraphEdge>(pose_graph_edge);
64  pose_graph_edge
65  .def_readwrite("source_node_id", &PoseGraphEdge::source_node_id_,
66  "int: Source ``PoseGraphNode`` id.")
67  .def_readwrite("target_node_id", &PoseGraphEdge::target_node_id_,
68  "int: Target ``PoseGraphNode`` id.")
69  .def_readwrite(
70  "transformation", &PoseGraphEdge::transformation_,
71  "``4 x 4`` float64 numpy array: Transformation matrix.")
72  .def_readwrite("information", &PoseGraphEdge::information_,
73  "``6 x 6`` float64 numpy array: Information matrix.")
74  .def_readwrite("uncertain", &PoseGraphEdge::uncertain_,
75  "bool: Whether the edge is uncertain. Odometry edge "
76  "has uncertain == false, loop closure edges has "
77  "uncertain == true")
78  .def_readwrite(
79  "confidence", &PoseGraphEdge::confidence_,
80  "float from 0 to 1: Confidence value of the edge. if "
81  "uncertain is true, it has confidence bounded in [0,1]. "
82  "1 means reliable, and 0 means "
83  "unreliable edge. This correspondence to "
84  "line process value in [Choi et al 2015] See "
85  "core/registration/globaloptimization.h for more details.")
86  .def(py::init([](int source_node_id, int target_node_id,
87  Eigen::Matrix4d transformation,
88  Eigen::Matrix6d information, bool uncertain,
89  double confidence) {
90  return new PoseGraphEdge(source_node_id, target_node_id,
91  transformation, information,
92  uncertain, confidence);
93  }),
94  "source_node_id"_a = -1, "target_node_id"_a = -1,
95  "transformation"_a = Eigen::Matrix4d::Identity(),
96  "information"_a = Eigen::Matrix6d::Identity(),
97  "uncertain"_a = false, "confidence"_a = 1.0)
98  .def("__repr__", [](const PoseGraphEdge &rr) {
99  return std::string("PoseGraphEdge from nodes ") +
100  std::to_string(rr.source_node_id_) +
101  std::string(" to ") +
102  std::to_string(rr.target_node_id_) +
103  std::string(
104  ", access transformation to get relative "
105  "transformation");
106  });
107 
108  // cloudViewer.registration.PoseGraphEdgeVector
109  auto pose_graph_edge_vector = py::bind_vector<std::vector<PoseGraphEdge>>(
110  m, "PoseGraphEdgeVector");
111  pose_graph_edge_vector.attr("__doc__") = docstring::static_property(
112  py::cpp_function([](py::handle arg) -> std::string {
113  return "Vector of PoseGraphEdge";
114  }),
115  py::none(), py::none(), "");
116 
117  // cloudViewer.registration.PoseGraph
118  py::class_<PoseGraph, std::shared_ptr<PoseGraph>> pose_graph(
119  m, "PoseGraph", "Data structure defining the pose graph.");
120  py::detail::bind_default_constructor<PoseGraph>(pose_graph);
121  py::detail::bind_copy_functions<PoseGraph>(pose_graph);
122  pose_graph
123  .def_readwrite(
124  "nodes", &PoseGraph::nodes_,
125  "``List(PoseGraphNode)``: List of ``PoseGraphNode``.")
126  .def_readwrite(
127  "edges", &PoseGraph::edges_,
128  "``List(PoseGraphEdge)``: List of ``PoseGraphEdge``.")
129  .def("__repr__", [](const PoseGraph &rr) {
130  return std::string("PoseGraph with ") +
131  std::to_string(rr.nodes_.size()) +
132  std::string(" nodes and ") +
133  std::to_string(rr.edges_.size()) +
134  std::string(" edges.");
135  });
136 
137  // cloudViewer.registration.GlobalOptimizationMethod
138  py::class_<GlobalOptimizationMethod,
140  global_optimization_method(
141  m, "GlobalOptimizationMethod",
142  "Base class for global optimization method.");
143  global_optimization_method.def("OptimizePoseGraph",
145  "pose_graph"_a, "criteria"_a, "option"_a,
146  "Run pose graph optimization.");
148  m, "GlobalOptimizationMethod", "OptimizePoseGraph",
149  {{"pose_graph", "The pose graph to be optimized (in-place)."},
150  {"criteria", "Convergence criteria."},
151  {"option", "Global optimization options."}});
152 
156  global_optimization_method_lm(
157  m, "GlobalOptimizationLevenbergMarquardt",
158  "Global optimization with Levenberg-Marquardt algorithm. "
159  "Recommended over the Gauss-Newton method since the LM has "
160  "better convergence characteristics.");
161  py::detail::bind_default_constructor<GlobalOptimizationLevenbergMarquardt>(
162  global_optimization_method_lm);
163  py::detail::bind_copy_functions<GlobalOptimizationLevenbergMarquardt>(
164  global_optimization_method_lm);
165  global_optimization_method_lm.def(
166  "__repr__", [](const GlobalOptimizationLevenbergMarquardt &te) {
167  return std::string("GlobalOptimizationLevenbergMarquardt");
168  });
169 
173  global_optimization_method_gn(
174  m, "GlobalOptimizationGaussNewton",
175  "Global optimization with Gauss-Newton algorithm.");
176  py::detail::bind_default_constructor<GlobalOptimizationGaussNewton>(
177  global_optimization_method_gn);
178  py::detail::bind_copy_functions<GlobalOptimizationGaussNewton>(
179  global_optimization_method_gn);
180  global_optimization_method_gn.def(
181  "__repr__", [](const GlobalOptimizationGaussNewton &te) {
182  return std::string("GlobalOptimizationGaussNewton");
183  });
184 
185  py::class_<GlobalOptimizationConvergenceCriteria> criteria(
186  m, "GlobalOptimizationConvergenceCriteria",
187  "Convergence criteria of GlobalOptimization.");
188  py::detail::bind_default_constructor<GlobalOptimizationConvergenceCriteria>(
189  criteria);
190  py::detail::bind_copy_functions<GlobalOptimizationConvergenceCriteria>(
191  criteria);
192  criteria.def_readwrite(
193  "max_iteration",
195  "int: Maximum iteration number for iterative optimization "
196  "module.")
197  .def_readwrite("min_relative_increment",
199  min_relative_increment_,
200  "float: Minimum relative increments.")
201  .def_readwrite("min_relative_residual_increment",
203  min_relative_residual_increment_,
204  "float: Minimum relative residual increments.")
205  .def_readwrite(
206  "min_right_term",
208  "float: Minimum right term value.")
209  .def_readwrite(
210  "min_residual",
212  "float: Minimum residual value.")
213  .def_readwrite(
214  "max_iteration_lm",
216  "int: Maximum iteration number for Levenberg Marquardt "
217  "method. max_iteration_lm is used for additional "
218  "Levenberg-Marquardt inner loop that automatically changes "
219  "steepest gradient gain.")
220  .def_readwrite(
221  "upper_scale_factor",
223  "float: Upper scale factor value. Scaling factors are used "
224  "for levenberg marquardt algorithm these are scaling "
225  "factors that increase/decrease lambda used in H_LM = H + "
226  "lambda * I")
227  .def_readwrite(
228  "lower_scale_factor",
230  "float: Lower scale factor value.")
231  .def("__repr__", [](const GlobalOptimizationConvergenceCriteria
232  &cr) {
233  return std::string("GlobalOptimizationConvergenceCriteria") +
234  std::string("\n> max_iteration : ") +
236  std::string("\n> min_relative_increment : ") +
238  std::string("\n> min_relative_residual_increment : ") +
240  std::string("\n> min_right_term : ") +
242  std::string("\n> min_residual : ") +
244  std::string("\n> max_iteration_lm : ") +
246  std::string("\n> upper_scale_factor : ") +
248  std::string("\n> lower_scale_factor : ") +
250  });
251 
252  py::class_<GlobalOptimizationOption> option(
253  m, "GlobalOptimizationOption", "Option for GlobalOptimization.");
254  py::detail::bind_default_constructor<GlobalOptimizationOption>(option);
255  py::detail::bind_copy_functions<GlobalOptimizationOption>(option);
256  option.def_readwrite(
257  "max_correspondence_distance",
259  "float: Identifies which distance value is used for "
260  "finding neighboring points when making information "
261  "matrix. According to [Choi et al 2015], this "
262  "distance is used for determining $mu, a line process "
263  "weight.")
264  .def_readwrite("edge_prune_threshold",
266  "float: According to [Choi et al 2015], "
267  "line_process weight < edge_prune_threshold (0.25) "
268  "is pruned.")
269  .def_readwrite("preference_loop_closure",
271  "float: Balancing parameter to decide which one is "
272  "more reliable: odometry vs loop-closure. [0,1] -> "
273  "try to unchange odometry edges, [1) -> try to "
274  "utilize loop-closure. Recommendation: 0.1 for RGBD "
275  "Odometry, 2.0 for fragment registration.")
276  .def_readwrite("reference_node",
278  "int: The pose of this node is unchanged after "
279  "optimization.")
280  .def(py::init([](double max_correspondence_distance,
281  double edge_prune_threshold,
282  double preference_loop_closure,
283  int reference_node) {
284  return new GlobalOptimizationOption(
285  max_correspondence_distance, edge_prune_threshold,
286  preference_loop_closure, reference_node);
287  }),
288  "max_correspondence_distance"_a = 0.03,
289  "edge_prune_threshold"_a = 0.25,
290  "preference_loop_closure"_a = 1.0, "reference_node"_a = -1)
291  .def("__repr__", [](const GlobalOptimizationOption &goo) {
292  return std::string("GlobalOptimizationOption") +
293  std::string("\n> max_correspondence_distance : ") +
295  std::string("\n> edge_prune_threshold : ") +
297  std::string("\n> preference_loop_closure : ") +
299  std::string("\n> reference_node : ") +
301  });
302 }
303 
305  m.def(
306  "global_optimization",
307  [](PoseGraph &pose_graph, const GlobalOptimizationMethod &method,
309  const GlobalOptimizationOption &option) {
310  GlobalOptimization(pose_graph, method, criteria, option);
311  },
312  "Function to optimize PoseGraph", "pose_graph"_a, "method"_a,
313  "criteria"_a, "option"_a);
315  m, "global_optimization",
316  {{"pose_graph", "The pose_graph to be optimized (in-place)."},
317  {"method",
318  "Global optimization method. Either "
319  "``GlobalOptimizationGaussNewton()`` or "
320  "``GlobalOptimizationLevenbergMarquardt()``."},
321  {"criteria", "Global optimization convergence criteria."},
322  {"option", "Global optimization option."}});
323 }
324 
325 } // namespace registration
326 } // namespace pipelines
327 } // namespace cloudViewer
virtual void OptimizePoseGraph(PoseGraph &pose_graph, const GlobalOptimizationConvergenceCriteria &criteria, const GlobalOptimizationOption &option) const =0
Run pose graph optimization method.
int reference_node_
The pose of this node is unchanged after optimization.
Eigen::Matrix4d_u transformation_
Transformation matrix.
Definition: PoseGraph.h:76
int target_node_id_
Target PoseGraphNode id.
Definition: PoseGraph.h:74
int source_node_id_
Source PoseGraphNode id.
Definition: PoseGraph.h:72
bool uncertain_
Whether the edge is uncertain.
Definition: PoseGraph.h:83
Eigen::Matrix6d_u information_
Information matrix.
Definition: PoseGraph.h:78
double confidence_
Confidence value of the edge.
Definition: PoseGraph.h:90
Data structure defining the pose graph.
Definition: PoseGraph.h:96
std::vector< PoseGraphNode > nodes_
List of PoseGraphNode.
Definition: PoseGraph.h:108
std::vector< PoseGraphEdge > edges_
List of PoseGraphEdge.
Definition: PoseGraph.h:111
void OptimizePoseGraph(PoseGraph &pose_graph, const GlobalOptimizationConvergenceCriteria &criteria, const GlobalOptimizationOption &option) const override
Run pose graph optimization method.
Eigen::Matrix< double, 6, 6 > Matrix6d
Extending Eigen namespace by adding frequently used matrix type.
Definition: Eigen.h:106
void ClassMethodDocInject(py::module &pybind_module, const std::string &class_name, const std::string &function_name, const std::unordered_map< std::string, std::string > &map_parameter_body_docs)
Definition: docstring.cpp:27
py::handle static_property
Definition: docstring.cpp:24
void FunctionDocInject(py::module &pybind_module, const std::string &function_name, const std::unordered_map< std::string, std::string > &map_parameter_body_docs)
Definition: docstring.cpp:76
void pybind_global_optimization_methods(py::module &m)
void GlobalOptimization(PoseGraph &pose_graph, const GlobalOptimizationMethod &method, const GlobalOptimizationConvergenceCriteria &criteria, const GlobalOptimizationOption &option)
static const double max_correspondence_distance
Generic file read and write utility for python interface.
std::string to_string(const T &n)
Definition: Common.h:20