gtsam/gtsam_unstable/nonlinear/tests/testLCNLPSolver2.cpp

336 lines
12 KiB
C++

/* ----------------------------------------------------------------------------
* 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
* -------------------------------------------------------------------------- */
/**
* @file testQPSimple.cpp
* @brief Unit tests for testQPSimple
* @author Duy-Nguyen Ta
* @author Krunal Chande
* @author Luca Carlone
* @date Dec 15, 2014
*/
#include <gtsam/base/numericalDerivative.h>
#include <gtsam/geometry/Pose3.h>
#include <gtsam/inference/Symbol.h>
#include <gtsam/nonlinear/LinearContainerFactor.h>
#include <gtsam/slam/PriorFactor.h>
#include <gtsam/slam/BetweenFactor.h>
#include <gtsam_unstable/linear/QPSolver.h>
#include <gtsam_unstable/nonlinear/LCNLPSolver.h>
#include <gtsam_unstable/nonlinear/NonlinearInequality.h>
#include <CppUnitLite/TestHarness.h>
#include <iostream>
using namespace std;
using namespace gtsam::symbol_shorthand;
using namespace gtsam;
const double tol = 1e-10;
//******************************************************************************
const size_t X_AXIS = 0;
const size_t Y_AXIS = 1;
const size_t Z_AXIS = 2;
/**
* Inequality boundary constraint on one axis (x, y or z)
* axis <= bound
*/
class AxisUpperBound : public NonlinearInequality1<Pose3> {
typedef NonlinearInequality1<Pose3> Base;
size_t axis_;
double bound_;
public:
AxisUpperBound(Key key, size_t axis, double bound, Key dualKey) : Base(key, dualKey), axis_(axis), bound_(bound) {
}
double computeError(const Pose3& pose, boost::optional<Matrix&> H = boost::none) const {
if (H)
*H = (Matrix(1,6) << zeros(1,3), pose.rotation().matrix().row(axis_)).finished();
return pose.translation().vector()[axis_] - bound_;
}
};
/**
* Inequality boundary constraint on one axis (x, y or z)
* bound <= axis
*/
class AxisLowerBound : public NonlinearInequality1<Pose3> {
typedef NonlinearInequality1<Pose3> Base;
size_t axis_;
double bound_;
public:
AxisLowerBound(Key key, size_t axis, double bound, Key dualKey) : Base(key, dualKey), axis_(axis), bound_(bound) {
}
double computeError(const Pose3& pose, boost::optional<Matrix&> H = boost::none) const {
if (H)
*H = (Matrix(1,6) << zeros(1,3), -pose.rotation().matrix().row(axis_)).finished();
return -pose.translation().vector()[axis_] + bound_;
}
};
//******************************************************************************
TEST(testlcnlpSolver, poseWithABoundary) {
const Key dualKey = 0;
//Instantiate LCNLP
LCNLP lcnlp;
lcnlp.cost.add(PriorFactor<Pose3>(X(1), Pose3(Rot3::ypr(0.1, 0.2, 0.3), Point3(1, 0, 0)), noiseModel::Unit::Create(6)));
AxisUpperBound constraint(X(1), X_AXIS, 0, dualKey);
lcnlp.linearInequalities.add(constraint);
Values initialValues;
initialValues.insert(X(1), Pose3(Rot3::ypr(0.3, 0.2, 0.3), Point3(1, 0, 0)));
Values expectedSolution;
expectedSolution.insert(X(1), Pose3(Rot3::ypr(0.1, 0.2, 0.3), Point3(0, 0, 0)));
// Instantiate LCNLPSolver
LCNLPSolver lcnlpSolver(lcnlp);
Values actualSolution = lcnlpSolver.optimize(initialValues).first;
CHECK(assert_equal(expectedSolution, actualSolution, 1e-10));
}
//******************************************************************************
TEST(testlcnlpSolver, poseWithNoConstraints) {
//Instantiate LCNLP
LCNLP lcnlp;
lcnlp.cost.add(PriorFactor<Pose3>(X(1), Pose3(Rot3::ypr(0.1, 0.2, 0.3), Point3(1, 0, 0)), noiseModel::Unit::Create(6)));
Values initialValues;
initialValues.insert(X(1), Pose3(Rot3::ypr(0.3, 0.2, 0.3), Point3(1, 0, 0)));
Values expectedSolution;
expectedSolution.insert(X(1), Pose3(Rot3::ypr(0.1, 0.2, 0.3), Point3(1, 0, 0)));
// Instantiate LCNLPSolver
LCNLPSolver lcnlpSolver(lcnlp);
Values actualSolution = lcnlpSolver.optimize(initialValues, true, false).first; // TODO: Fails without warmstart
CHECK(assert_equal(expectedSolution, actualSolution, 1e-10));
}
//******************************************************************************
TEST(testlcnlpSolver, poseWithinA2DBox) {
const Key dualKey = 0;
//Instantiate LCNLP
LCNLP lcnlp;
lcnlp.cost.add(PriorFactor<Pose3>(X(1), Pose3(Rot3::ypr(0.1, 0.2, 0.3), Point3(10, 0.5, 0)), noiseModel::Unit::Create(6)));
lcnlp.linearInequalities.add(AxisLowerBound(X(1), X_AXIS, -1, dualKey)); // -1 <= x
lcnlp.linearInequalities.add(AxisUpperBound(X(1), X_AXIS, 1, dualKey+1)); // x <= 1
lcnlp.linearInequalities.add(AxisLowerBound(X(1), Y_AXIS, -1, dualKey+2)); // -1 <= y
lcnlp.linearInequalities.add(AxisUpperBound(X(1), Y_AXIS, 1, dualKey+3));// y <= 1
Values initialValues;
initialValues.insert(X(1), Pose3(Rot3::ypr(1, -1, 2), Point3(3, -5, 0)));
Values expectedSolution;
expectedSolution.insert(X(1), Pose3(Rot3::ypr(0.1, 0.2, 0.3), Point3(1, 0.5, 0)));
// Instantiate LCNLPSolver
LCNLPSolver lcnlpSolver(lcnlp);
Values actualSolution = lcnlpSolver.optimize(initialValues).first;
CHECK(assert_equal(expectedSolution, actualSolution, 1e-10));
}
//******************************************************************************
TEST(testlcnlpSolver, posesInA2DBox) {
const double xLowerBound = -3.0,
xUpperBound = 5.0,
yLowerBound = -1.0,
yUpperBound = 2.0,
zLowerBound = 0.0,
zUpperBound = 2.0;
//Instantiate LCNLP
LCNLP lcnlp;
// prior on the first pose
SharedDiagonal priorNoise = noiseModel::Diagonal::Sigmas(
(Vector(6) << 0.001, 0.001, 0.001, 0.0001, 0.0001, 0.0001).finished());
lcnlp.cost.add(PriorFactor<Pose3>(X(1), Pose3(), priorNoise));
// odometry between factor for subsequent poses
SharedDiagonal odoNoise = noiseModel::Diagonal::Sigmas(
(Vector(6) << 0.001, 0.001, 0.001, 0.1, 0.1, 0.1).finished());
Pose3 odo12(Rot3::ypr(M_PI/2.0, 0, 0), Point3(10, 0, 0));
lcnlp.cost.add(BetweenFactor<Pose3>(X(1), X(2), odo12, odoNoise));
Pose3 odo23(Rot3::ypr(M_PI/2.0, 0, 0), Point3(2, 0, 2));
lcnlp.cost.add(BetweenFactor<Pose3>(X(2), X(3), odo23, odoNoise));
// Box constraints
Key dualKey = 0;
for (size_t i=1; i<=3; ++i) {
lcnlp.linearInequalities.add(AxisLowerBound(X(i), X_AXIS, xLowerBound, dualKey++));
lcnlp.linearInequalities.add(AxisUpperBound(X(i), X_AXIS, xUpperBound, dualKey++));
lcnlp.linearInequalities.add(AxisLowerBound(X(i), Y_AXIS, yLowerBound, dualKey++));
lcnlp.linearInequalities.add(AxisUpperBound(X(i), Y_AXIS, yUpperBound, dualKey++));
lcnlp.linearInequalities.add(AxisLowerBound(X(i), Z_AXIS, zLowerBound, dualKey++));
lcnlp.linearInequalities.add(AxisUpperBound(X(i), Z_AXIS, zUpperBound, dualKey++));
}
Values initialValues;
initialValues.insert(X(1), Pose3(Rot3(), Point3(0, 0, 0)));
initialValues.insert(X(2), Pose3(Rot3(), Point3(0, 0, 0)));
initialValues.insert(X(3), Pose3(Rot3(), Point3(0, 0, 0)));
Values expectedSolution;
expectedSolution.insert(X(1), Pose3());
expectedSolution.insert(X(2), Pose3(Rot3::ypr(M_PI/2.0, 0, 0), Point3(5, 0, 0)));
expectedSolution.insert(X(3), Pose3(Rot3::ypr(M_PI, 0, 0), Point3(5, 2, 2)));
// Instantiate LCNLPSolver
LCNLPSolver lcnlpSolver(lcnlp);
bool useWarmStart = true;
Values actualSolution = lcnlpSolver.optimize(initialValues, useWarmStart).first;
// cout << "Rotation angles: " << endl;
// for (size_t i = 1; i<=3; i++) {
// cout << actualSolution.at<Pose3>(X(i)).rotation().ypr().transpose()*180/M_PI << endl;
// }
// cout << "Actual Error: " << lcnlp.cost.error(actualSolution) << endl;
// cout << "Expected Error: " << lcnlp.cost.error(expectedSolution) << endl;
// actualSolution.print("actualSolution: ");
AxisLowerBound factor(X(1), X_AXIS, xLowerBound, dualKey++);
Matrix hessian = numericalHessian<Pose3>(boost::bind(&AxisLowerBound::computeError, factor, _1, boost::none), Pose3(), 1e-3);
cout << "Hessian: \n" << hessian << endl;
CHECK(assert_equal(expectedSolution, actualSolution, 1e-5));
}
//******************************************************************************
TEST(testlcnlpSolver, iterativePoseinBox) {
const double xLowerBound = -1.0,
xUpperBound = 1.0,
yLowerBound = -1.0,
yUpperBound = 1.0,
zLowerBound = -1.0,
zUpperBound = 1.0;
//Instantiate LCNLP
LCNLP lcnlp;
// prior on the first pose
SharedDiagonal priorNoise = noiseModel::Diagonal::Sigmas(
(Vector(6) << 0.001, 0.001, 0.001, 0.0001, 0.0001, 0.0001).finished());
lcnlp.cost.add(PriorFactor<Pose3>(X(0), Pose3(), priorNoise));
// odometry between factor for subsequent poses
SharedDiagonal odoNoise = noiseModel::Diagonal::Sigmas(
(Vector(6) << 0.001, 0.001, 0.001, 0.1, 0.1, 0.1).finished());
Pose3 odo(Rot3::ypr(0, 0, 0), Point3(0.4, 0, 0));
Values initialValues;
Values isamValues;
initialValues.insert(X(0), Pose3(Rot3(), Point3(0, 0, 0)));
isamValues.insert(X(0), Pose3(Rot3(), Point3(0, 0, 0)));
std::pair<Values, VectorValues> solutionAndDuals;
solutionAndDuals.first.insert(X(0), Pose3(Rot3(), Point3(0, 0, 0)));
Values actualSolution;
bool useWarmStart = true;
bool firstTime = true;
// Box constraints
Key dualKey = 0;
for (size_t i=1; i<=4; ++i) {
lcnlp.cost.add(BetweenFactor<Pose3>(X(i-1), X(i), odo, odoNoise));
lcnlp.linearInequalities.add(AxisLowerBound(X(i), X_AXIS, xLowerBound, dualKey++));
lcnlp.linearInequalities.add(AxisUpperBound(X(i), X_AXIS, xUpperBound, dualKey++));
lcnlp.linearInequalities.add(AxisLowerBound(X(i), Y_AXIS, yLowerBound, dualKey++));
lcnlp.linearInequalities.add(AxisUpperBound(X(i), Y_AXIS, yUpperBound, dualKey++));
lcnlp.linearInequalities.add(AxisLowerBound(X(i), Z_AXIS, zLowerBound, dualKey++));
lcnlp.linearInequalities.add(AxisUpperBound(X(i), Z_AXIS, zUpperBound, dualKey++));
initialValues.insert(X(i), Pose3(Rot3(), Point3(0, 0, 0)));
isamValues = solutionAndDuals.first;
isamValues.insert(X(i), solutionAndDuals.first.at(X(i-1)));
isamValues.print("iSAM values: \n");
// Instantiate LCNLPSolver
LCNLPSolver lcnlpSolver(lcnlp);
// if (firstTime) {
solutionAndDuals = lcnlpSolver.optimize(isamValues, useWarmStart);
// firstTime = false;
// }
// else {
// cout << " using this \n ";
// solutionAndDuals = lcnlpSolver.optimize(isamValues, solutionAndDuals.second, useWarmStart);
//
// }
cout << " ************************** \n";
}
actualSolution = solutionAndDuals.first;
cout << "out of loop" << endl;
Values expectedSolution;
expectedSolution.insert(X(0), Pose3());
expectedSolution.insert(X(1), Pose3(Rot3::ypr(0, 0, 0), Point3(0.25, 0, 0)));
expectedSolution.insert(X(2), Pose3(Rot3::ypr(0, 0, 0), Point3(0.50, 0, 0)));
expectedSolution.insert(X(3), Pose3(Rot3::ypr(0, 0, 0), Point3(0.75, 0, 0)));
expectedSolution.insert(X(4), Pose3(Rot3::ypr(0, 0, 0), Point3(1, 0, 0)));
// cout << "Rotation angles: " << endl;
// for (size_t i = 1; i<=3; i++) {
// cout << actualSolution.at<Pose3>(X(i)).rotation().ypr().transpose()*180/M_PI << endl;
// }
cout << "Actual Error: " << lcnlp.cost.error(actualSolution) << endl;
cout << "Expected Error: " << lcnlp.cost.error(expectedSolution) << endl;
actualSolution.print("actualSolution: ");
CHECK(assert_equal(expectedSolution, actualSolution, 1e-5));
}
//******************************************************************************
double testHessian(const Pose3& X) {
return X.transform_from(Point3(0,0,0)).x();
}
Pose3 pose(Rot3::ypr(0.1, 0.4, 0.7), Point3(1, 9.5, 7.3));
TEST(testlcnlpSolver, testingHessian) {
Matrix H = numericalHessian(testHessian, pose, 1e-5);
}
double h3(const Vector6& v) {
return pose.retract(v).translation().x();
}
TEST(testlcnlpSolver, NumericalHessian3) {
Matrix6 expected;
expected.setZero();
Vector6 z;
z.setZero();
EXPECT(assert_equal(expected, numericalHessian(h3, z, 1e-5), 1e-5));
}
//******************************************************************************
int main() {
cout<<"here: "<<endl;
TestResult tr;
return TestRegistry::runAllTests(tr);
}
//******************************************************************************