|
MROB
|
#include <factor_graph_solve.hpp>


Public Types | |
| enum | matrixMethod { ADJ =0, SCHUR } |
| enum | optimMethod { GN =0, LM, LM_ELLIPS } |
Public Member Functions | |
| FGraphSolve (matrixMethod method=ADJ) | |
| uint_t | solve (optimMethod method=GN, uint_t maxIters=20, matData_t lambda=1e-6, matData_t solutionTolerance=1e-2) |
| matData_t | chi2 (bool evaluateResidualsFlag=true) |
| std::vector< MatX > | get_estimated_state () |
| void | set_build_matrix_method (matrixMethod method) |
| matrixMethod | get_build_matrix_method () |
| SMatCol | get_information_matrix () |
| SMatCol | get_adjacency_matrix () |
| SMatCol | get_W_matrix () |
| MatX1 | get_vector_b () |
| MatX1 | get_chi2_array () |
Public Member Functions inherited from mrob::FGraph | |
| factor_id_t | add_factor (std::shared_ptr< Factor > &factor) |
| factor_id_t | add_eigen_factor (std::shared_ptr< EigenFactor > &factor) |
| factor_id_t | add_node (std::shared_ptr< Node > &node) |
| std::shared_ptr< Node > & | get_node (factor_id_t key) |
| std::shared_ptr< Factor > & | get_factor (factor_id_t key) |
| std::shared_ptr< EigenFactor > & | get_eigen_factor (factor_id_t key) |
| void | print (bool complete=false) const |
| factor_id_t | number_nodes () |
| factor_id_t | number_factors () |
| uint_t | get_dimension_state () |
| uint_t | get_dimension_obs () |
| void | save_graph () const |
| void | load_graph () |
Protected Member Functions | |
| void | build_problem (bool useLambda=false) |
| void | build_adjacency () |
| void | build_info_adjacency () |
| void | build_info_EF () |
| void | build_schur () |
| void | optimize_gauss_newton (bool useLambda=false) |
| uint_t | optimize_levenberg_marquardt (uint_t maxIters) |
| void | update_nodes () |
| void | synchronize_nodes_state () |
| void | synchronize_nodes_auxiliary_state () |
| void | build_index_nodes_matrix () |
Protected Attributes | |
| matrixMethod | matrixMethod_ |
| optimMethod | optimMethod_ |
| factor_id_t | N_ |
| factor_id_t | M_ |
| std::unordered_map< factor_id_t, factor_id_t > | indNodesMatrix_ |
| SMatRow | A_ |
| SMatRow | W_ |
| MatX1 | r_ |
| SMatCol | L_ |
| MatX1 | b_ |
| MatX1 | dx_ |
| matData_t | lambda_ |
| matData_t | solutionTolerance_ |
| MatX1 | diagL_ |
| SMatCol | hessianEF_ |
| MatX1 | gradientEF_ |
| bool | buildAdjacencyFlag_ |
| TimeProfiling | time_profiles_ |
Protected Attributes inherited from mrob::FGraph | |
| std::deque< std::shared_ptr< Node > > | nodes_ |
| std::deque< std::shared_ptr< Node > > | active_nodes_ |
| std::deque< std::shared_ptr< Factor > > | factors_ |
| std::deque< std::shared_ptr< EigenFactor > > | eigen_factors_ |
| uint_t | stateDim_ |
| uint_t | obsDim_ |
Class FGraphSolve creates all the required matrices for solving the LSQ problem. The problem takes the following form:
x* = argmin {C(x)} = argmin {1/2 sum ||r_i(x,z)||2_W} = argmin 1/2||r||2_W.
last term in vectorized form.
With this arrangement, the linearized factor substracts the residual (r) to the first order term of the nonlinear observation function: ||h(x)-z||2_W = ||h(x0) + J dx - z ||2_W = ||J dx + r||2_W
When optimizing the linearized LSQ: dC 1 d – = - —(sum r' W r) = J' W (J dx + r) = 0 dx 2 dx
=> dx = -(J'WJ)^(-1) J'W r
This convention will be followed by all factors in this library, otherwise the optimization will not work properly.
Different options are provided:
Routines provide different optimization methods:
This enums all matrix building methods available For now we only do build adjacency
This enums optimization methods available:
|
protected |
This protected method creates an Adjacency matrix, iterating over all factors in the FG and creates a block diagonal matrix W with each factors information. As a result, residuals, Jacobians and chi2 values are up to date
|
protected |
This function build the node index that will be used for creating the adjacency matrix and the information matrix.
It maps the node Id to the column index in the matrix, such that non-consecutive nodes can be bookkeep for a fast access
|
protected |
From the adjacency matrix it creates the information matrix as L = A^T * W * A The residuals are also calculated as b = A^T * W *r
L_ dx = b_ corresponds to the normal equation A'*W*A dx = A'*W*r only store the lower part of the information matrix (symmetric)
XXX: In terms of speed, using the selfadjointview does not improve, Eigen stores a temporary object and then copy only the upper part.
|
protected |
Builds the information matrix directly from Eigen Factors. It follows a different approach than build adjacency, it will only create a Hessian and Jacobian when at least one EF is present.
|
protected |
build problem creates an information matrix L, W and a vector b
It chooses from building the information from the adjacency matrix, directly building info or schur (TODO)
If bool useLambda is true, it also stores a vector D2 containing the diagonal of the information matrix L
| matData_t FGraphSolve::chi2 | ( | bool | evaluateResidualsFlag = true | ) |
Evaluates the current solution chi2.
Variable relinearizeProblemFlag:
|
inline |
Returns a copy to the Adjacency matrix. There is a conversion (implies copy) from Row to Col-convention (which is what np.array needs) TODO If true, it re-evaluates the problem
| MatX1 FGraphSolve::get_chi2_array | ( | ) |
Returns a vector of chi2 values for each of the factors.
| std::vector< MatX > FGraphSolve::get_estimated_state | ( | ) |
Returns a Reference to the solution vector of all variables, vectors, matrices, etc.
|
inline |
Returns a copy to the information matrix. pybind does not allow to pass by reference, so there is a copy anyway CHeck out more here: https://pybind11.readthedocs.io/en/stable/advanced/cast/eigen.html TODO If true, it re-evaluates the problem
|
inline |
Returns a copy to the processed residuals in state space b = A'Wr. TODO If true, it re-evaluates the problem
|
inline |
Returns a copy to the W matrix. There is a conversion (implies copy) from Row to Col-convention (which is what np.array needs) TODO If true, it re-evaluates the problem
|
protected |
Once the matrix L is generated, it solves the linearized LSQ by using the Gauss-Newton algorithm
Input useLambda (default false) builds the GN problem with lambda factor on the diagonal L = A'*A + lambda * I
|
protected |
It generates the information matrix as L' = L + lambda * I
TODO, the preconditioning could be adapted to expected values, such as w < pi and v < avg L' = L + lambda * D2
Iteratively updates the solution given the right estimation of lambda Parameters are necessary to be specified in advance, o.w. it would use default values.
input maxIters, before returning a result
output: number of iterations it took to converge. 0 when incorrect solution
|
inline |
Functions to set the matrix method building
| uint_t FGraphSolve::solve | ( | optimMethod | method = GN, |
| uint_t | maxIters = 20, |
||
| matData_t | lambda = 1e-6, |
||
| matData_t | solutionTolerance = 1e-2 |
||
| ) |
Solve call the corresponding routine on the class parameters or ultimately on the function input, by default optim method is Gauss Newton
Return: number of iterations Failed to converge = 0 iterations
2800 2D nodes on M3500 Time profile :13.902 % build Adjacency matrix, 34.344 % build Information, 48.0506 % build Cholesky, 2.3075 % solve forward and back substitution, 1.3959 % update values,
|
protected |
Synchronize auxiliary state variables in all nodes exactly value the current state. This function is used when we will update a solution but it needs verification, so we book-keep at auxiliary.
|
protected |
Synchronize state variable in all nodes exactly value the current state.
Usually this function un-does an incorrect update of the state.
|
protected |
Function that updates all nodes with the current solution, this must be called after solving the problem
1.8.13