| /* |
| * This module contains the definition of a Levenberg-Marquardt solver for |
| * non-linear least squares problems of the following form: |
| * |
| * arg min ||f(X)|| = arg min f(X)^T f(X) |
| * X X |
| * |
| * where X is an Nx1 state vector and f(X) is an Mx1 nonlinear measurement error |
| * function of X which we wish to minimize the norm of. |
| * |
| * Levenberg-Marquardt solves the above problem through a damped Gauss-Newton |
| * method where the solver step on each iteration is chosen such that: |
| * (J' J + uI) * step = - J' f(x) |
| * where J = df(x)/dx is the Jacobian of the error function, u is a damping |
| * factor, and I is the identity matrix. |
| * |
| * The algorithm implemented here follows Algorithm 3.16 outlined in this paper: |
| * Madsen, Kaj, Hans Bruun Nielsen, and Ole Tingleff. |
| * "Methods for non-linear least squares problems." (2004). |
| * |
| * This algorithm uses a variant of the Marquardt method for updating the |
| * damping factor which ensures that changes in the factor have no |
| * discontinuities or fluttering behavior between solver steps. |
| */ |
| #ifndef LOCATION_LBS_CONTEXTHUB_NANOAPPS_COMMON_MATH_LEVENBERG_MARQUARDT_H_ |
| #define LOCATION_LBS_CONTEXTHUB_NANOAPPS_COMMON_MATH_LEVENBERG_MARQUARDT_H_ |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #ifdef __cplusplus |
| extern "C" { |
| #endif |
| |
| // Function pointer for computing residual, f(X), and Jacobian, J = df(X)/dX |
| // for a given state value, X, and other parameter data needed for computing |
| // these terms, f_data. |
| // |
| // Note if f_data is not needed, it is allowable for f_data to be passed in |
| // as NULL. |
| // |
| // jacobian is also allowed to be passed in as NULL, and in this case only the |
| // residual will be computed and returned. |
| typedef void (*ResidualAndJacobianFunction)(const float *state, |
| const void *f_data, |
| float *residual, float *jacobian); |
| |
| |
| #define MAX_LM_STATE_DIMENSION (10) |
| #define MAX_LM_MEAS_DIMENSION (50) |
| |
| // Structure containing fixed parameters for a single LM solve. |
| struct LmParams { |
| // maximum number of iterations allowed by the solver. |
| uint32_t max_iterations; |
| |
| // initial scaling factor for setting the damping factor, i.e.: |
| // damping_factor = initial_u_scale * max(J'J). |
| float initial_u_scale; |
| |
| // magnitude of the cost function gradient required for solution, i.e. |
| // max(gradient) = max(J'f(x)) < gradient_threshold. |
| float gradient_threshold; |
| |
| // magnitude of relative error required for solution step, i.e. |
| // ||step|| / ||state|| < relative_step_thresold. |
| float relative_step_threshold; |
| }; |
| |
| // Structure containing data needed for a single LM solve. |
| // Defining the data here allows flexibility in how the memory is allocated |
| // for the solver. |
| struct LmData { |
| float step[MAX_LM_STATE_DIMENSION]; |
| float residual[MAX_LM_MEAS_DIMENSION]; |
| float residual_new[MAX_LM_MEAS_DIMENSION]; |
| float gradient[MAX_LM_MEAS_DIMENSION]; |
| float hessian[MAX_LM_STATE_DIMENSION * MAX_LM_STATE_DIMENSION]; |
| float temp[MAX_LM_STATE_DIMENSION * MAX_LM_MEAS_DIMENSION]; |
| }; |
| |
| // Enumeration indicating status of the LM solver. |
| enum LmStatus { |
| RUNNING = 0, |
| // Successful solve conditions: |
| RELATIVE_STEP_SUFFICIENTLY_SMALL, // solver step is sufficiently small. |
| GRADIENT_SUFFICIENTLY_SMALL, // cost function gradient is below threshold. |
| HIT_MAX_ITERATIONS, |
| |
| // Solver failure modes: |
| CHOLESKY_FAIL, |
| INVALID_DATA_DIMENSIONS, |
| }; |
| |
| // Structure for containing variables needed for a Levenberg-Marquardt solver. |
| struct LmSolver { |
| // Solver parameters. |
| struct LmParams params; |
| |
| // Pointer to solver data. |
| struct LmData *data; |
| |
| // Function for computing residual (f(x)) and jacobian (df(x)/dx). |
| ResidualAndJacobianFunction func; |
| |
| // Number of iterations in current solution. |
| uint32_t num_iter; |
| }; |
| |
| // Initializes LM solver with provided parameters and error function. |
| void lmSolverInit(struct LmSolver *solver, const struct LmParams *params, |
| ResidualAndJacobianFunction func); |
| |
| // Sets pointer for temporary data needed for an individual LM solve. |
| // Note, this must be called prior to calling lmSolverSolve(). |
| void lmSolverSetData(struct LmSolver *solver, struct LmData *data); |
| |
| /* |
| * Runs the LM solver for a given set of data and error function. |
| * |
| * INPUTS: |
| * solver : pointer to an struct LmSolver structure. |
| * initial_state : initial guess of the estimation state. |
| * f_data : pointer to additional data needed by the error function. |
| * state_dim : dimension of X. |
| * meas_dim : dimension of f(X), defined in the error function. |
| * |
| * OUTPUTS: |
| * LmStatus : enum indicating state of the solver on completion. |
| * est_state : estimated state. |
| */ |
| enum LmStatus lmSolverSolve(struct LmSolver *solver, const float *initial_state, |
| void *f_data, size_t state_dim, size_t meas_dim, |
| float *state); |
| |
| ////////////////////////// TEST UTILITIES //////////////////////////////////// |
| // This function is exposed here for testing purposes only. |
| /* |
| * Computes the ratio of actual cost function gain over expected cost |
| * function gain for the given solver step. This ratio is used to adjust |
| * the solver step size for the next solver iteration. |
| * |
| * INPUTS: |
| * residual: f(x) for the current state x. |
| * residual_new: f(x + step) for the new state after the solver step. |
| * step: the solver step. |
| * gradient: gradient of the cost function F(x). |
| * damping_factor: the current damping factor used in the solver. |
| * state_dim: dimension of the state, x. |
| * meas_dim: dimension of f(x). |
| */ |
| float computeGainRatio(const float *residual, const float *residual_new, |
| const float *step, const float *gradient, |
| float damping_factor, size_t state_dim, |
| size_t meas_dim); |
| |
| #ifdef __cplusplus |
| } |
| #endif |
| |
| #endif // LOCATION_LBS_CONTEXTHUB_NANOAPPS_COMMON_MATH_LEVENBERG_MARQUARDT_H_ |