levmarq.h
Go to the documentation of this file.
1 
17 #ifndef ARUCO_MM__LevMarq_H
18 #define ARUCO_MM__LevMarq_H
19 #include <Eigen/Core>
20 #include <Eigen/Cholesky>
21 #include <functional>
22 #include <iostream>
23 #include <cmath>
24 #include <ctime>
25 #include <cstring>
26 #include <vector>
27 #include <chrono>
28 #include <iomanip>
29 namespace aruco
30 {
31 // Levenberg-Marquardt method for general problems Inspired in
32 //@MISC\{IMM2004-03215,
33 // author = "K. Madsen and H. B. Nielsen and O. Tingleff",
34 // title = "Methods for Non-Linear Least Squares Problems (2nd ed.)",
35 // year = "2004",
36 // pages = "60",
37 // publisher = "Informatics and Mathematical Modelling, Technical University of Denmark, {DTU}",
38 // address = "Richard Petersens Plads, Building 321, {DK-}2800 Kgs. Lyngby",
39 // url = "http://www.ltu.se/cms_fs/1.51590!/nonlinear_least_squares.pdf"
40 //}
41 template <typename T>
42 class LevMarq
43 {
44 public:
45  typedef Eigen::Matrix<T, Eigen::Dynamic, 1> eVector;
46  typedef std::function<void(const eVector &, eVector &)> F_z_x;
47  typedef std::function<void(const eVector &, Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> &)> F_z_J;
48  LevMarq();
61  LevMarq(int maxIters, double minError, double min_step_error_diff = 0, double tau = 1,
62  double der_epsilon = 1e-3);
63 
76  void setParams(int maxIters, double minError, double min_step_error_diff = 0,
77  double tau = 1, double der_epsilon = 1e-3);
78 
91  double solve(eVector &z, F_z_x, F_z_J);
93 
94 
99  void init(eVector &z, F_z_x);
106  bool step(F_z_x f_z_x, F_z_J f_z_J);
107  bool step(F_z_x f_z_x);
113  double getCurrentSolution(eVector &z);
119  double getBestSolution(eVector &z);
120 
130  double solve(eVector &z, F_z_x);
131  // to enable verbose mode
132  bool &verbose()
133  {
134  return _verbose;
135  }
136 
137  // sets a callback func call at each step
138  void setStepCallBackFunc(std::function<void(const eVector &)> callback)
139  {
140  _step_callback = callback;
141  }
142  // sets a function that indicates when the algorithm must be stop. returns true if must stop and false otherwise
143  void setStopFunction(std::function<bool(const eVector &)> stop_function)
144  {
145  _stopFunction = stop_function;
146  }
147 
148  void calcDerivates(const eVector &z, Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> &, F_z_x);
149 
150 private:
153  bool _verbose;
154  //--------
157  Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> I, J;
158  double mu, v;
159  std::function<void(const eVector &)> _step_callback;
160  std::function<bool(const eVector &)> _stopFunction;
161 };
162 
163 
164 
165 template <typename T>
167 {
168  _maxIters = 1000;
169  _minErrorAllowed = 0;
170  _der_epsilon = 1e-3;
171  _verbose = false;
172  _tau = 1;
173  v = 5;
174  _min_step_error_diff = 0;
175 }
188 template <typename T>
189 LevMarq<T>::LevMarq(int maxIters, double minError, double min_step_error_diff, double tau,
190  double der_epsilon)
191 {
192  _maxIters = maxIters;
193  _minErrorAllowed = minError;
194  _der_epsilon = der_epsilon;
195  _verbose = false;
196  _tau = tau;
197  v = 5;
198  _min_step_error_diff = min_step_error_diff;
199 }
200 
213 template <typename T>
214 void LevMarq<T>::setParams(int maxIters, double minError, double min_step_error_diff,
215  double tau, double der_epsilon)
216 {
217  _maxIters = maxIters;
218  _minErrorAllowed = minError;
219  _der_epsilon = der_epsilon;
220  _tau = tau;
221  _min_step_error_diff = min_step_error_diff;
222 }
223 
224 
225 template <typename T>
227  Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> &j, F_z_x f_z_x)
228 {
229  for (int i = 0; i < z.rows(); i++)
230  {
231  eVector zp(z), zm(z);
232  zp(i) += _der_epsilon;
233  zm(i) -= _der_epsilon;
234  eVector xp, xm;
235  f_z_x(zp, xp);
236  f_z_x(zm, xm);
237  eVector dif = (xp - xm) / (2.f * _der_epsilon);
238  j.middleCols(i, 1) = dif;
239  }
240 }
241 
242 template <typename T>
243 double LevMarq<T>::solve(eVector &z, F_z_x f_z_x)
244 {
245  return solve(z, f_z_x,
246  std::bind(&LevMarq::calcDerivates, this, std::placeholders::_1,
247  std::placeholders::_2, f_z_x));
248 }
249 template <typename T>
251 {
252  return step(f_z_x, std::bind(&LevMarq::calcDerivates, this, std::placeholders::_1,
253  std::placeholders::_2, f_z_x));
254 }
255 
256 template <typename T>
258 {
259  curr_z = z;
260  I.resize(z.rows(), z.rows());
261  I.setIdentity();
262  f_z_x(curr_z, x64);
263  minErr = currErr = prevErr = x64.cwiseProduct(x64).sum();
264  J.resize(x64.rows(), z.rows());
265  mu = -1;
266 }
267 
268 
269 #define splm_get_time(a, b) \
270  std::chrono::duration_cast<std::chrono::duration<double>>(a - b).count()
271 
272 
273 template <typename T>
274 bool LevMarq<T>::step(F_z_x f_z_x, F_z_J f_J)
275 {
276  f_J(curr_z, J);
277  Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> Jt = J.transpose();
278  Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> JtJ = (Jt * J);
279 
280  eVector B = -Jt * x64;
281  if (mu < 0)
282  { // first time only
283  int max = 0;
284  for (int j = 1; j < JtJ.cols(); j++)
285  if (JtJ(j, j) > JtJ(max, max))
286  max = j;
287  mu = JtJ(max, max) * _tau;
288  }
289 
290  double gain = 0, prev_mu = 0;
291  int ntries = 0;
292  bool isStepAccepted = false;
293  do
294  {
295  // add/update dumping factor to JtJ.
296  // very efficient in any case, but particularly if initial dump does not produce improvement and must reenter
297  for (int j = 0; j < JtJ.cols(); j++)
298  JtJ(j, j) += mu - prev_mu; // update mu
299  prev_mu = mu;
300  eVector delta = JtJ.ldlt().solve(B);
301  eVector estimated_z = curr_z + delta;
302  // compute error
303  f_z_x(estimated_z, x64);
304  auto err = x64.cwiseProduct(x64).sum();
305  auto L = 0.5 * delta.transpose() * ((mu * delta) - B);
306  gain = (err - prevErr) / L(0, 0);
307  // get gain
308  if (gain > 0)
309  {
310  mu = mu * std::max(double(0.33), 1. - pow(2 * gain - 1, 3));
311  v = 5.f;
312  currErr = err;
313  curr_z = estimated_z;
314  isStepAccepted = true;
315  }
316  else
317  {
318  mu = mu * v;
319  v = v * 5;
320  }
321 
322  } while (gain <= 0 && ntries++ < 5);
323 
324  if (_verbose)
325  std::cout << std::setprecision(5) << "Curr Error=" << currErr
326  << " AErr(prev-curr)=" << prevErr - currErr << " gain=" << gain
327  << " dumping factor=" << mu << std::endl;
328  // //check if we must move to the new position or exit
329  if (currErr < prevErr)
330  std::swap(currErr, prevErr);
331 
332  return isStepAccepted;
333 }
334 
335 
336 template <typename T>
338 {
339  z = curr_z;
340  return currErr;
341 }
342 template <typename T>
343 double LevMarq<T>::solve(eVector &z, F_z_x f_z_x, F_z_J f_J)
344 {
345  init(z, f_z_x);
346 
347  if (_stopFunction)
348  {
349  do
350  {
351  step(f_z_x, f_J);
352  if (_step_callback)
353  _step_callback(curr_z);
354  } while (!_stopFunction(curr_z));
355  }
356  else
357  {
358  // intial error estimation
359  int mustExit = 0;
360  for (int i = 0; i < _maxIters && !mustExit; i++)
361  {
362  if (_verbose)
363  std::cerr << "iteration " << i << "/" << _maxIters << " ";
364  bool isStepAccepted = step(f_z_x, f_J);
365  // check if we must exit
366  if (currErr < _minErrorAllowed)
367  mustExit = 1;
368  if (fabs(prevErr - currErr) <= _min_step_error_diff || !isStepAccepted)
369  mustExit = 2;
370  // exit if error increment
371  if (currErr < prevErr)
372  mustExit = 3;
373  // if ( (prevErr-currErr) < 1e-5 ) mustExit=true;
374  if (_step_callback)
375  _step_callback(curr_z);
376  }
377 
378  // std::cout<<"Exit code="<<mustExit<<std::endl;
379  }
380  z = curr_z;
381  return currErr;
382 }
383 
384 } // namespace aruco
385 
386 #endif
aruco::LevMarq::setStepCallBackFunc
void setStepCallBackFunc(std::function< void(const eVector &)> callback)
Definition: levmarq.h:138
aruco::LevMarq::eVector
Eigen::Matrix< T, Eigen::Dynamic, 1 > eVector
Definition: levmarq.h:45
aruco::LevMarq::setParams
void setParams(int maxIters, double minError, double min_step_error_diff=0, double tau=1, double der_epsilon=1e-3)
setParams
Definition: levmarq.h:214
aruco::LevMarq::F_z_x
std::function< void(const eVector &, eVector &)> F_z_x
Definition: levmarq.h:46
aruco::LevMarq::getBestSolution
double getBestSolution(eVector &z)
getBestSolution sets in z the best solution up to this moment
aruco::LevMarq::_tau
double _tau
Definition: levmarq.h:152
aruco::LevMarq::init
void init(eVector &z, F_z_x)
Step by step solve mode.
Definition: levmarq.h:257
aruco::LevMarq::calcDerivates
void calcDerivates(const eVector &z, Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic > &, F_z_x)
Definition: levmarq.h:226
aruco::LevMarq::minErr
double minErr
Definition: levmarq.h:156
aruco::LevMarq::_step_callback
std::function< void(const eVector &)> _step_callback
Definition: levmarq.h:159
aruco::LevMarq::J
Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic > J
Definition: levmarq.h:157
aruco::LevMarq::I
Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic > I
Definition: levmarq.h:157
aruco::LevMarq::prevErr
double prevErr
Definition: levmarq.h:156
aruco::LevMarq::curr_z
eVector curr_z
Definition: levmarq.h:155
aruco::LevMarq::setStopFunction
void setStopFunction(std::function< bool(const eVector &)> stop_function)
Definition: levmarq.h:143
aruco::LevMarq::_maxIters
int _maxIters
Definition: levmarq.h:151
aruco::LevMarq::_der_epsilon
double _der_epsilon
Definition: levmarq.h:152
f
f
aruco::LevMarq::_minErrorAllowed
double _minErrorAllowed
Definition: levmarq.h:152
aruco::LevMarq::getCurrentSolution
double getCurrentSolution(eVector &z)
getCurrentSolution returns the current solution
Definition: levmarq.h:337
aruco::LevMarq::currErr
double currErr
Definition: levmarq.h:156
aruco::LevMarq::solve
double solve(eVector &z, F_z_x, F_z_J)
solve non linear minimization problem ||F(z)||, where F(z)=f(z) f(z)^t
Definition: levmarq.h:343
aruco::LevMarq::_verbose
bool _verbose
Definition: levmarq.h:153
aruco::LevMarq::verbose
bool & verbose()
Definition: levmarq.h:132
aruco::LevMarq::step
bool step(F_z_x f_z_x, F_z_J f_z_J)
step gives a step of the search
Definition: levmarq.h:274
aruco
Definition: cameraparameters.h:24
aruco::LevMarq::_min_step_error_diff
double _min_step_error_diff
Definition: levmarq.h:152
aruco::LevMarq::x64
eVector x64
Definition: levmarq.h:155
aruco::LevMarq::F_z_J
std::function< void(const eVector &, Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic > &)> F_z_J
Definition: levmarq.h:47
aruco::LevMarq::LevMarq
LevMarq()
Definition: levmarq.h:166
aruco::LevMarq::_stopFunction
std::function< bool(const eVector &)> _stopFunction
Definition: levmarq.h:160
aruco::LevMarq::v
double v
Definition: levmarq.h:158
aruco::LevMarq
Definition: levmarq.h:42
aruco::LevMarq::mu
double mu
Definition: levmarq.h:158


aruco
Author(s): Rafael Muñoz Salinas , Bence Magyar
autogenerated on Sat Sep 23 2023 02:26:45