2010-10-14 12:54:38 +08:00
|
|
|
/* ----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
* GTSAM Copyright 2010, Georgia Tech Research Corporation,
|
|
|
|
* Atlanta, Georgia 30332-0415
|
|
|
|
* All Rights Reserved
|
|
|
|
* Authors: Frank Dellaert, et al. (see THANKS for the full author list)
|
|
|
|
|
|
|
|
* See LICENSE for the license information
|
|
|
|
|
|
|
|
* -------------------------------------------------------------------------- */
|
|
|
|
|
2009-09-09 12:43:04 +08:00
|
|
|
/**
|
|
|
|
* @file testNonlinearOptimizer.cpp
|
|
|
|
* @brief Unit tests for NonlinearOptimizer class
|
|
|
|
* @author Frank Dellaert
|
|
|
|
*/
|
|
|
|
|
2012-06-10 04:15:44 +08:00
|
|
|
#include <tests/smallExample.h>
|
2012-07-24 06:47:31 +08:00
|
|
|
#include <gtsam/slam/PriorFactor.h>
|
|
|
|
#include <gtsam/slam/BetweenFactor.h>
|
|
|
|
#include <gtsam/nonlinear/NonlinearFactorGraph.h>
|
|
|
|
#include <gtsam/nonlinear/Values.h>
|
2012-06-03 03:05:38 +08:00
|
|
|
#include <gtsam/nonlinear/Symbol.h>
|
2011-12-21 07:25:43 +08:00
|
|
|
#include <gtsam/nonlinear/NonlinearFactorGraph.h>
|
2012-03-23 01:46:43 +08:00
|
|
|
#include <gtsam/nonlinear/GaussNewtonOptimizer.h>
|
2012-03-23 11:43:28 +08:00
|
|
|
#include <gtsam/nonlinear/DoglegOptimizer.h>
|
2012-05-22 20:27:34 +08:00
|
|
|
#include <gtsam/nonlinear/LevenbergMarquardtOptimizer.h>
|
|
|
|
#include <gtsam/linear/GaussianFactorGraph.h>
|
|
|
|
#include <gtsam/linear/NoiseModel.h>
|
2012-07-24 06:47:31 +08:00
|
|
|
#include <gtsam/geometry/Pose2.h>
|
2012-05-22 20:27:34 +08:00
|
|
|
#include <gtsam/base/Matrix.h>
|
|
|
|
|
|
|
|
#include <CppUnitLite/TestHarness.h>
|
|
|
|
|
|
|
|
#include <boost/shared_ptr.hpp>
|
|
|
|
#include <boost/assign/std/list.hpp> // for operator +=
|
|
|
|
using namespace boost::assign;
|
2009-09-09 12:43:04 +08:00
|
|
|
|
2012-05-22 20:27:34 +08:00
|
|
|
#include <iostream>
|
|
|
|
|
|
|
|
using namespace std;
|
2009-09-09 12:43:04 +08:00
|
|
|
using namespace gtsam;
|
|
|
|
|
2010-03-05 06:46:27 +08:00
|
|
|
const double tol = 1e-5;
|
2010-01-27 12:39:35 +08:00
|
|
|
|
2012-06-03 03:28:21 +08:00
|
|
|
using symbol_shorthand::X;
|
|
|
|
using symbol_shorthand::L;
|
2012-02-21 08:53:35 +08:00
|
|
|
|
2010-01-18 13:51:19 +08:00
|
|
|
/* ************************************************************************* */
|
2010-01-20 10:28:23 +08:00
|
|
|
TEST( NonlinearOptimizer, iterateLM )
|
2010-01-18 13:51:19 +08:00
|
|
|
{
|
2012-10-02 22:40:07 +08:00
|
|
|
// really non-linear factor graph
|
2012-05-15 05:07:56 +08:00
|
|
|
example::Graph fg(example::createReallyNonlinearFactorGraph());
|
2010-01-20 10:28:23 +08:00
|
|
|
|
2012-10-02 22:40:07 +08:00
|
|
|
// config far from minimum
|
|
|
|
Point2 x0(3,0);
|
|
|
|
Values config;
|
|
|
|
config.insert(X(1), x0);
|
2010-01-20 10:28:23 +08:00
|
|
|
|
2012-10-02 22:40:07 +08:00
|
|
|
// normal iterate
|
|
|
|
GaussNewtonParams gnParams;
|
|
|
|
GaussNewtonOptimizer gnOptimizer(fg, config, gnParams);
|
|
|
|
gnOptimizer.iterate();
|
2010-01-20 10:28:23 +08:00
|
|
|
|
2012-10-02 22:40:07 +08:00
|
|
|
// LM iterate with lambda 0 should be the same
|
|
|
|
LevenbergMarquardtParams lmParams;
|
|
|
|
lmParams.lambdaInitial = 0.0;
|
|
|
|
LevenbergMarquardtOptimizer lmOptimizer(fg, config, lmParams);
|
|
|
|
lmOptimizer.iterate();
|
2010-01-20 10:28:23 +08:00
|
|
|
|
2012-10-02 22:40:07 +08:00
|
|
|
CHECK(assert_equal(gnOptimizer.values(), lmOptimizer.values(), 1e-9));
|
2010-01-20 10:28:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ************************************************************************* */
|
|
|
|
TEST( NonlinearOptimizer, optimize )
|
|
|
|
{
|
2012-05-15 05:07:56 +08:00
|
|
|
example::Graph fg(example::createReallyNonlinearFactorGraph());
|
2010-01-20 10:28:23 +08:00
|
|
|
|
2012-10-02 22:40:07 +08:00
|
|
|
// test error at minimum
|
|
|
|
Point2 xstar(0,0);
|
|
|
|
Values cstar;
|
|
|
|
cstar.insert(X(1), xstar);
|
|
|
|
DOUBLES_EQUAL(0.0,fg.error(cstar),0.0);
|
|
|
|
|
|
|
|
// test error at initial = [(1-cos(3))^2 + (sin(3))^2]*50 =
|
|
|
|
Point2 x0(3,3);
|
|
|
|
Values c0;
|
|
|
|
c0.insert(X(1), x0);
|
|
|
|
DOUBLES_EQUAL(199.0,fg.error(c0),1e-3);
|
|
|
|
|
|
|
|
// optimize parameters
|
|
|
|
Ordering ord;
|
|
|
|
ord.push_back(X(1));
|
|
|
|
|
|
|
|
// Gauss-Newton
|
|
|
|
GaussNewtonParams gnParams;
|
|
|
|
gnParams.ordering = ord;
|
|
|
|
Values actual1 = GaussNewtonOptimizer(fg, c0, gnParams).optimize();
|
|
|
|
DOUBLES_EQUAL(0,fg.error(actual1),tol);
|
|
|
|
|
|
|
|
// Levenberg-Marquardt
|
|
|
|
LevenbergMarquardtParams lmParams;
|
|
|
|
lmParams.ordering = ord;
|
2012-05-15 06:31:42 +08:00
|
|
|
Values actual2 = LevenbergMarquardtOptimizer(fg, c0, lmParams).optimize();
|
2012-05-15 05:07:56 +08:00
|
|
|
DOUBLES_EQUAL(0,fg.error(actual2),tol);
|
2012-03-23 11:43:28 +08:00
|
|
|
|
|
|
|
// Dogleg
|
2012-05-15 06:31:42 +08:00
|
|
|
DoglegParams dlParams;
|
|
|
|
dlParams.ordering = ord;
|
|
|
|
Values actual3 = DoglegOptimizer(fg, c0, dlParams).optimize();
|
2012-05-15 05:07:56 +08:00
|
|
|
DOUBLES_EQUAL(0,fg.error(actual3),tol);
|
2011-02-06 12:13:32 +08:00
|
|
|
}
|
|
|
|
|
2010-07-23 05:17:02 +08:00
|
|
|
/* ************************************************************************* */
|
2010-08-06 23:10:19 +08:00
|
|
|
TEST( NonlinearOptimizer, SimpleLMOptimizer )
|
2010-07-23 05:17:02 +08:00
|
|
|
{
|
2012-10-02 22:40:07 +08:00
|
|
|
example::Graph fg(example::createReallyNonlinearFactorGraph());
|
2010-08-10 01:20:03 +08:00
|
|
|
|
2012-10-02 22:40:07 +08:00
|
|
|
Point2 x0(3,3);
|
|
|
|
Values c0;
|
|
|
|
c0.insert(X(1), x0);
|
2010-07-23 05:17:02 +08:00
|
|
|
|
2012-10-02 22:40:07 +08:00
|
|
|
Values actual = LevenbergMarquardtOptimizer(fg, c0).optimize();
|
|
|
|
DOUBLES_EQUAL(0,fg.error(actual),tol);
|
2010-08-10 01:20:03 +08:00
|
|
|
}
|
2010-07-23 05:17:02 +08:00
|
|
|
|
2010-08-06 23:10:19 +08:00
|
|
|
/* ************************************************************************* */
|
|
|
|
TEST( NonlinearOptimizer, SimpleGNOptimizer )
|
|
|
|
{
|
2012-05-15 05:07:56 +08:00
|
|
|
example::Graph fg(example::createReallyNonlinearFactorGraph());
|
2010-08-10 01:20:03 +08:00
|
|
|
|
2012-05-15 05:07:56 +08:00
|
|
|
Point2 x0(3,3);
|
|
|
|
Values c0;
|
2012-06-03 03:28:21 +08:00
|
|
|
c0.insert(X(1), x0);
|
2010-08-06 23:10:19 +08:00
|
|
|
|
2012-05-15 05:07:56 +08:00
|
|
|
Values actual = GaussNewtonOptimizer(fg, c0).optimize();
|
2012-10-02 22:40:07 +08:00
|
|
|
DOUBLES_EQUAL(0,fg.error(actual),tol);
|
2010-08-10 01:20:03 +08:00
|
|
|
}
|
2010-08-06 23:10:19 +08:00
|
|
|
|
2010-08-10 01:20:03 +08:00
|
|
|
/* ************************************************************************* */
|
2012-03-23 11:43:28 +08:00
|
|
|
TEST( NonlinearOptimizer, SimpleDLOptimizer )
|
2010-08-10 01:20:03 +08:00
|
|
|
{
|
2012-05-15 05:07:56 +08:00
|
|
|
example::Graph fg(example::createReallyNonlinearFactorGraph());
|
2010-08-10 01:20:03 +08:00
|
|
|
|
2012-03-23 11:43:28 +08:00
|
|
|
Point2 x0(3,3);
|
2012-05-15 05:07:56 +08:00
|
|
|
Values c0;
|
2012-06-03 03:28:21 +08:00
|
|
|
c0.insert(X(1), x0);
|
2010-08-06 23:10:19 +08:00
|
|
|
|
2012-05-15 05:07:56 +08:00
|
|
|
Values actual = DoglegOptimizer(fg, c0).optimize();
|
|
|
|
DOUBLES_EQUAL(0,fg.error(actual),tol);
|
2010-08-06 23:10:19 +08:00
|
|
|
}
|
|
|
|
|
2011-06-14 00:55:31 +08:00
|
|
|
/* ************************************************************************* */
|
|
|
|
TEST( NonlinearOptimizer, optimization_method )
|
|
|
|
{
|
2012-03-23 01:46:43 +08:00
|
|
|
LevenbergMarquardtParams paramsQR;
|
2012-05-25 23:26:30 +08:00
|
|
|
paramsQR.linearSolverType = LevenbergMarquardtParams::MULTIFRONTAL_QR;
|
2012-05-15 13:08:57 +08:00
|
|
|
LevenbergMarquardtParams paramsChol;
|
2012-05-25 23:26:30 +08:00
|
|
|
paramsChol.linearSolverType = LevenbergMarquardtParams::MULTIFRONTAL_CHOLESKY;
|
2012-02-27 09:18:36 +08:00
|
|
|
|
2012-10-02 22:40:07 +08:00
|
|
|
example::Graph fg = example::createReallyNonlinearFactorGraph();
|
2011-06-14 00:55:31 +08:00
|
|
|
|
2012-10-02 22:40:07 +08:00
|
|
|
Point2 x0(3,3);
|
|
|
|
Values c0;
|
|
|
|
c0.insert(X(1), x0);
|
2011-06-14 00:55:31 +08:00
|
|
|
|
2012-10-02 22:40:07 +08:00
|
|
|
Values actualMFQR = LevenbergMarquardtOptimizer(fg, c0, paramsQR).optimize();
|
|
|
|
DOUBLES_EQUAL(0,fg.error(actualMFQR),tol);
|
2011-06-14 00:55:31 +08:00
|
|
|
|
2012-05-15 13:08:57 +08:00
|
|
|
Values actualMFChol = LevenbergMarquardtOptimizer(fg, c0, paramsChol).optimize();
|
|
|
|
DOUBLES_EQUAL(0,fg.error(actualMFChol),tol);
|
2011-06-14 00:55:31 +08:00
|
|
|
}
|
|
|
|
|
2010-01-20 10:28:23 +08:00
|
|
|
/* ************************************************************************* */
|
|
|
|
TEST( NonlinearOptimizer, Factorization )
|
|
|
|
{
|
2012-10-02 22:40:07 +08:00
|
|
|
Values config;
|
|
|
|
config.insert(X(1), Pose2(0.,0.,0.));
|
|
|
|
config.insert(X(2), Pose2(1.5,0.,0.));
|
2010-01-18 13:51:19 +08:00
|
|
|
|
2012-10-02 22:40:07 +08:00
|
|
|
NonlinearFactorGraph graph;
|
|
|
|
graph.add(PriorFactor<Pose2>(X(1), Pose2(0.,0.,0.), noiseModel::Isotropic::Sigma(3, 1e-10)));
|
|
|
|
graph.add(BetweenFactor<Pose2>(X(1),X(2), Pose2(1.,0.,0.), noiseModel::Isotropic::Sigma(3, 1)));
|
2010-01-18 13:51:19 +08:00
|
|
|
|
2012-10-02 22:40:07 +08:00
|
|
|
Ordering ordering;
|
|
|
|
ordering.push_back(X(1));
|
|
|
|
ordering.push_back(X(2));
|
2010-01-20 10:28:23 +08:00
|
|
|
|
2012-10-02 22:40:07 +08:00
|
|
|
LevenbergMarquardtOptimizer optimizer(graph, config, ordering);
|
|
|
|
optimizer.iterate();
|
2010-01-20 10:28:23 +08:00
|
|
|
|
2012-10-02 22:40:07 +08:00
|
|
|
Values expected;
|
|
|
|
expected.insert(X(1), Pose2(0.,0.,0.));
|
|
|
|
expected.insert(X(2), Pose2(1.,0.,0.));
|
|
|
|
CHECK(assert_equal(expected, optimizer.values(), 1e-5));
|
2010-01-20 10:28:23 +08:00
|
|
|
}
|
|
|
|
|
2011-03-30 03:51:50 +08:00
|
|
|
/* ************************************************************************* */
|
2012-03-23 02:02:25 +08:00
|
|
|
TEST(NonlinearOptimizer, NullFactor) {
|
2011-03-30 03:51:50 +08:00
|
|
|
|
2012-03-23 01:46:43 +08:00
|
|
|
example::Graph fg = example::createReallyNonlinearFactorGraph();
|
2011-03-30 03:51:50 +08:00
|
|
|
|
|
|
|
// Add null factor
|
2012-03-23 01:46:43 +08:00
|
|
|
fg.push_back(example::Graph::sharedFactor());
|
2011-03-30 03:51:50 +08:00
|
|
|
|
|
|
|
// test error at minimum
|
|
|
|
Point2 xstar(0,0);
|
2012-02-03 00:16:46 +08:00
|
|
|
Values cstar;
|
2012-06-03 03:28:21 +08:00
|
|
|
cstar.insert(X(1), xstar);
|
2012-03-23 01:46:43 +08:00
|
|
|
DOUBLES_EQUAL(0.0,fg.error(cstar),0.0);
|
2011-03-30 03:51:50 +08:00
|
|
|
|
|
|
|
// test error at initial = [(1-cos(3))^2 + (sin(3))^2]*50 =
|
|
|
|
Point2 x0(3,3);
|
2012-03-23 01:46:43 +08:00
|
|
|
Values c0;
|
2012-06-03 03:28:21 +08:00
|
|
|
c0.insert(X(1), x0);
|
2012-03-23 01:46:43 +08:00
|
|
|
DOUBLES_EQUAL(199.0,fg.error(c0),1e-3);
|
2011-03-30 03:51:50 +08:00
|
|
|
|
|
|
|
// optimize parameters
|
2012-03-23 01:46:43 +08:00
|
|
|
Ordering ord;
|
2012-06-03 03:28:21 +08:00
|
|
|
ord.push_back(X(1));
|
2011-03-30 03:51:50 +08:00
|
|
|
|
|
|
|
// Gauss-Newton
|
2012-05-15 05:07:56 +08:00
|
|
|
Values actual1 = GaussNewtonOptimizer(fg, c0, ord).optimize();
|
|
|
|
DOUBLES_EQUAL(0,fg.error(actual1),tol);
|
2011-03-30 03:51:50 +08:00
|
|
|
|
|
|
|
// Levenberg-Marquardt
|
2012-05-15 05:07:56 +08:00
|
|
|
Values actual2 = LevenbergMarquardtOptimizer(fg, c0, ord).optimize();
|
|
|
|
DOUBLES_EQUAL(0,fg.error(actual2),tol);
|
2012-03-23 11:43:28 +08:00
|
|
|
|
|
|
|
// Dogleg
|
2012-05-15 05:07:56 +08:00
|
|
|
Values actual3 = DoglegOptimizer(fg, c0, ord).optimize();
|
|
|
|
DOUBLES_EQUAL(0,fg.error(actual3),tol);
|
2011-03-30 03:51:50 +08:00
|
|
|
}
|
|
|
|
|
2012-05-15 21:23:43 +08:00
|
|
|
/* ************************************************************************* */
|
|
|
|
TEST(NonlinearOptimizer, MoreOptimization) {
|
|
|
|
|
|
|
|
NonlinearFactorGraph fg;
|
2012-06-23 03:36:49 +08:00
|
|
|
fg.add(PriorFactor<Pose2>(0, Pose2(0,0,0), noiseModel::Isotropic::Sigma(3,1)));
|
|
|
|
fg.add(BetweenFactor<Pose2>(0, 1, Pose2(1,0,M_PI/2), noiseModel::Isotropic::Sigma(3,1)));
|
|
|
|
fg.add(BetweenFactor<Pose2>(1, 2, Pose2(1,0,M_PI/2), noiseModel::Isotropic::Sigma(3,1)));
|
2012-05-15 21:23:43 +08:00
|
|
|
|
|
|
|
Values init;
|
|
|
|
init.insert(0, Pose2(3,4,-M_PI));
|
|
|
|
init.insert(1, Pose2(10,2,-M_PI));
|
|
|
|
init.insert(2, Pose2(11,7,-M_PI));
|
|
|
|
|
|
|
|
Values expected;
|
|
|
|
expected.insert(0, Pose2(0,0,0));
|
|
|
|
expected.insert(1, Pose2(1,0,M_PI/2));
|
|
|
|
expected.insert(2, Pose2(1,1,M_PI));
|
|
|
|
|
|
|
|
// Try LM and Dogleg
|
|
|
|
EXPECT(assert_equal(expected, LevenbergMarquardtOptimizer(fg, init).optimize()));
|
|
|
|
EXPECT(assert_equal(expected, DoglegOptimizer(fg, init).optimize()));
|
|
|
|
}
|
|
|
|
|
2012-11-07 06:42:01 +08:00
|
|
|
/* ************************************************************************* */
|
|
|
|
TEST(NonlinearOptimizer, MoreOptimizationWithHuber) {
|
|
|
|
|
|
|
|
NonlinearFactorGraph fg;
|
|
|
|
fg.add(PriorFactor<Pose2>(0, Pose2(0,0,0), noiseModel::Isotropic::Sigma(3,1)));
|
|
|
|
fg.add(BetweenFactor<Pose2>(0, 1, Pose2(1,0,M_PI/2),
|
2013-02-08 08:47:52 +08:00
|
|
|
noiseModel::Robust::Create(noiseModel::mEstimator::Huber::Create(2.0),
|
2012-11-07 06:42:01 +08:00
|
|
|
noiseModel::Isotropic::Sigma(3,1))));
|
|
|
|
fg.add(BetweenFactor<Pose2>(1, 2, Pose2(1,0,M_PI/2),
|
2013-02-08 08:47:52 +08:00
|
|
|
noiseModel::Robust::Create(noiseModel::mEstimator::Huber::Create(3.0),
|
2012-11-07 06:42:01 +08:00
|
|
|
noiseModel::Isotropic::Sigma(3,1))));
|
|
|
|
|
|
|
|
Values init;
|
|
|
|
init.insert(0, Pose2(10,10,0));
|
|
|
|
init.insert(1, Pose2(1,0,M_PI));
|
|
|
|
init.insert(2, Pose2(1,1,-M_PI));
|
|
|
|
|
|
|
|
Values expected;
|
|
|
|
expected.insert(0, Pose2(0,0,0));
|
|
|
|
expected.insert(1, Pose2(1,0,M_PI/2));
|
|
|
|
expected.insert(2, Pose2(1,1,M_PI));
|
|
|
|
|
|
|
|
EXPECT(assert_equal(expected, GaussNewtonOptimizer(fg, init).optimize()));
|
|
|
|
EXPECT(assert_equal(expected, LevenbergMarquardtOptimizer(fg, init).optimize()));
|
|
|
|
EXPECT(assert_equal(expected, DoglegOptimizer(fg, init).optimize()));
|
|
|
|
}
|
|
|
|
|
2009-09-09 12:43:04 +08:00
|
|
|
/* ************************************************************************* */
|
|
|
|
int main() {
|
2012-10-02 22:40:07 +08:00
|
|
|
TestResult tr;
|
|
|
|
return TestRegistry::runAllTests(tr);
|
2009-09-09 12:43:04 +08:00
|
|
|
}
|
|
|
|
/* ************************************************************************* */
|