249 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			C++
		
	
	
			
		
		
	
	
			249 lines
		
	
	
		
			8.5 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 testReferenceFrameFactor.cpp
 | |
|  * @author Alex Cunningham
 | |
|  */
 | |
| 
 | |
| #include <iostream>
 | |
| 
 | |
| #include <boost/bind.hpp>
 | |
| 
 | |
| #include <CppUnitLite/TestHarness.h>
 | |
| 
 | |
| #include <gtsam/base/Testable.h>
 | |
| #include <gtsam/base/numericalDerivative.h>
 | |
| #include <gtsam/geometry/Pose2.h>
 | |
| #include <gtsam/nonlinear/Symbol.h>
 | |
| #include <gtsam/nonlinear/NonlinearFactorGraph.h>
 | |
| #include <gtsam/nonlinear/NonlinearEquality.h>
 | |
| #include <gtsam/nonlinear/LevenbergMarquardtOptimizer.h>
 | |
| 
 | |
| #include <gtsam_unstable/slam/ReferenceFrameFactor.h>
 | |
| 
 | |
| using namespace std;
 | |
| using namespace boost;
 | |
| using namespace gtsam;
 | |
| 
 | |
| typedef gtsam::ReferenceFrameFactor<gtsam::Point2, gtsam::Pose2> PointReferenceFrameFactor;
 | |
| typedef gtsam::ReferenceFrameFactor<gtsam::Pose2, gtsam::Pose2> PoseReferenceFrameFactor;
 | |
| 
 | |
| Key lA1 = symbol_shorthand::L(1), lA2 = symbol_shorthand::L(2), lB1 = symbol_shorthand::L(11), lB2 = symbol_shorthand::L(12);
 | |
| Key tA1 = symbol_shorthand::T(1), tB1 = symbol_shorthand::T(2);
 | |
| 
 | |
| /* ************************************************************************* */
 | |
| TEST( ReferenceFrameFactor, equals ) {
 | |
|   PointReferenceFrameFactor
 | |
|     c1(lB1, tA1, lA1),
 | |
|     c2(lB1, tA1, lA1),
 | |
|     c3(lB1, tA1, lA2);
 | |
| 
 | |
|   EXPECT(assert_equal(c1, c1));
 | |
|   EXPECT(assert_equal(c1, c2));
 | |
|   EXPECT(!c1.equals(c3));
 | |
| }
 | |
| 
 | |
| /* ************************************************************************* */
 | |
| LieVector evaluateError_(const PointReferenceFrameFactor& c,
 | |
|     const Point2& global, const Pose2& trans, const Point2& local) {
 | |
|   return LieVector(c.evaluateError(global, trans, local));
 | |
| }
 | |
| TEST( ReferenceFrameFactor, jacobians ) {
 | |
| 
 | |
|   // from examples below
 | |
|   Point2 local(2.0, 3.0), global(-1.0, 2.0);
 | |
|   Pose2 trans(1.5, 2.5, 0.3);
 | |
| 
 | |
|   PointReferenceFrameFactor tc(lA1, tA1, lB1);
 | |
|   Matrix actualDT, actualDL, actualDF;
 | |
|   tc.evaluateError(global, trans, local, actualDF, actualDT, actualDL);
 | |
| 
 | |
|   Matrix numericalDT, numericalDL, numericalDF;
 | |
|   numericalDF = numericalDerivative31<LieVector,Point2,Pose2,Point2>(
 | |
|       boost::bind(evaluateError_, tc, _1, _2, _3),
 | |
|       global, trans, local, 1e-5);
 | |
|   numericalDT = numericalDerivative32<LieVector,Point2,Pose2,Point2>(
 | |
|       boost::bind(evaluateError_, tc, _1, _2, _3),
 | |
|       global, trans, local, 1e-5);
 | |
|   numericalDL = numericalDerivative33<LieVector,Point2,Pose2,Point2>(
 | |
|       boost::bind(evaluateError_, tc, _1, _2, _3),
 | |
|       global, trans, local, 1e-5);
 | |
| 
 | |
|   EXPECT(assert_equal(numericalDF, actualDF));
 | |
|   EXPECT(assert_equal(numericalDL, actualDL));
 | |
|   EXPECT(assert_equal(numericalDT, actualDT));
 | |
| }
 | |
| 
 | |
| /* ************************************************************************* */
 | |
| TEST( ReferenceFrameFactor, jacobians_zero ) {
 | |
| 
 | |
|   // get values that are ideal
 | |
|   Pose2 trans(2.0, 3.0, 0.0);
 | |
|   Point2 global(5.0, 6.0);
 | |
|   Point2 local = trans.transform_from(global);
 | |
| 
 | |
|   PointReferenceFrameFactor tc(lA1, tA1, lB1);
 | |
|   Vector actCost = tc.evaluateError(global, trans, local),
 | |
|       expCost = zero(2);
 | |
|   EXPECT(assert_equal(expCost, actCost, 1e-5));
 | |
| 
 | |
|   Matrix actualDT, actualDL, actualDF;
 | |
|   tc.evaluateError(global, trans, local, actualDF, actualDT, actualDL);
 | |
| 
 | |
|   Matrix numericalDT, numericalDL, numericalDF;
 | |
|   numericalDF = numericalDerivative31<LieVector,Point2,Pose2,Point2>(
 | |
|       boost::bind(evaluateError_, tc, _1, _2, _3),
 | |
|       global, trans, local, 1e-5);
 | |
|   numericalDT = numericalDerivative32<LieVector,Point2,Pose2,Point2>(
 | |
|       boost::bind(evaluateError_, tc, _1, _2, _3),
 | |
|       global, trans, local, 1e-5);
 | |
|   numericalDL = numericalDerivative33<LieVector,Point2,Pose2,Point2>(
 | |
|       boost::bind(evaluateError_, tc, _1, _2, _3),
 | |
|       global, trans, local, 1e-5);
 | |
| 
 | |
|   EXPECT(assert_equal(numericalDF, actualDF));
 | |
|   EXPECT(assert_equal(numericalDL, actualDL));
 | |
|   EXPECT(assert_equal(numericalDT, actualDT));
 | |
| }
 | |
| 
 | |
| /* ************************************************************************* */
 | |
| TEST_UNSAFE( ReferenceFrameFactor, converge_trans ) {
 | |
| 
 | |
|   // initial points
 | |
|   Point2 local1(2.0, 2.0), local2(4.0, 5.0),
 | |
|       global1(-1.0, 5.0), global2(2.0, 3.0);
 | |
|   Pose2 transIdeal(7.0, 3.0, M_PI/2);
 | |
| 
 | |
|   // verify direction
 | |
|   EXPECT(assert_equal(local1, transIdeal.transform_from(global1)));
 | |
|   EXPECT(assert_equal(local2, transIdeal.transform_from(global2)));
 | |
| 
 | |
|   // choose transform
 | |
|   //  Pose2 trans = transIdeal; // ideal - works
 | |
|   //  Pose2 trans = transIdeal * Pose2(0.1, 1.0, 0.00);  // translation - works
 | |
|   //  Pose2 trans = transIdeal * Pose2(10.1, 1.0, 0.00);  // large translation - works
 | |
|   //  Pose2 trans = transIdeal * Pose2(0.0, 0.0, 0.1);   // small rotation - works
 | |
|   Pose2 trans = transIdeal * Pose2(-200.0, 100.0, 1.3); // combined - works
 | |
|   //  Pose2 trans = transIdeal * Pose2(-200.0, 100.0, 2.0); // beyond pi/2 - fails
 | |
| 
 | |
|   NonlinearFactorGraph graph;
 | |
|   graph.add(PointReferenceFrameFactor(lB1, tA1, lA1));
 | |
|   graph.add(PointReferenceFrameFactor(lB2, tA1, lA2));
 | |
| 
 | |
|   // hard constraints on points
 | |
|   double error_gain = 1000.0;
 | |
|   graph.add(NonlinearEquality<gtsam::Point2>(lA1, local1, error_gain));
 | |
|   graph.add(NonlinearEquality<gtsam::Point2>(lA2, local2, error_gain));
 | |
|   graph.add(NonlinearEquality<gtsam::Point2>(lB1, global1, error_gain));
 | |
|   graph.add(NonlinearEquality<gtsam::Point2>(lB2, global2, error_gain));
 | |
| 
 | |
|   // create initial estimate
 | |
|   Values init;
 | |
|   init.insert(lA1, local1);
 | |
|   init.insert(lA2, local2);
 | |
|   init.insert(lB1, global1);
 | |
|   init.insert(lB2, global2);
 | |
|   init.insert(tA1, trans);
 | |
| 
 | |
|   // optimize
 | |
|   LevenbergMarquardtOptimizer solver(graph, init);
 | |
|   Values actual = solver.optimize();
 | |
| 
 | |
|   Values expected;
 | |
|   expected.insert(lA1, local1);
 | |
|   expected.insert(lA2, local2);
 | |
|   expected.insert(lB1, global1);
 | |
|   expected.insert(lB2, global2);
 | |
|   expected.insert(tA1, transIdeal);
 | |
| 
 | |
|   EXPECT(assert_equal(expected, actual, 1e-4));
 | |
| }
 | |
| 
 | |
| /* ************************************************************************* */
 | |
| TEST( ReferenceFrameFactor, converge_local ) {
 | |
| 
 | |
|   // initial points
 | |
|   Point2 global(-1.0, 2.0);
 | |
|   //  Pose2 trans(1.5, 2.5, 0.3); // original
 | |
|   //  Pose2 trans(1.5, 2.5, 1.0); // larger rotation
 | |
|   Pose2 trans(1.5, 2.5, 3.1); // significant rotation
 | |
| 
 | |
|   Point2 idealLocal = trans.transform_from(global);
 | |
| 
 | |
|   // perturb the initial estimate
 | |
|   //  Point2 local = idealLocal; // Ideal case - works
 | |
|   //  Point2 local = idealLocal + Point2(1.0, 0.0); // works
 | |
|   Point2 local = idealLocal + Point2(-10.0, 10.0); // works
 | |
| 
 | |
|   NonlinearFactorGraph graph;
 | |
|   double error_gain = 1000.0;
 | |
|   graph.add(PointReferenceFrameFactor(lB1, tA1, lA1));
 | |
|   graph.add(NonlinearEquality<gtsam::Point2>(lB1, global, error_gain));
 | |
|   graph.add(NonlinearEquality<gtsam::Pose2>(tA1, trans, error_gain));
 | |
| 
 | |
|   // create initial estimate
 | |
|   Values init;
 | |
|   init.insert(lA1, local);
 | |
|   init.insert(lB1, global);
 | |
|   init.insert(tA1, trans);
 | |
| 
 | |
|   // optimize
 | |
|   LevenbergMarquardtOptimizer solver(graph, init);
 | |
|   Values actual = solver.optimize();
 | |
| 
 | |
|   CHECK(actual.exists(lA1));
 | |
|   EXPECT(assert_equal(idealLocal, actual.at<Point2>(lA1), 1e-5));
 | |
| }
 | |
| 
 | |
| /* ************************************************************************* */
 | |
| TEST( ReferenceFrameFactor, converge_global ) {
 | |
| 
 | |
|   // initial points
 | |
|   Point2 local(2.0, 3.0);
 | |
|   //  Pose2 trans(1.5, 2.5, 0.3); // original
 | |
|   //  Pose2 trans(1.5, 2.5, 1.0); // larger rotation
 | |
|   Pose2 trans(1.5, 2.5, 3.1); // significant rotation
 | |
| 
 | |
|   Point2 idealForeign = trans.inverse().transform_from(local);
 | |
| 
 | |
|   // perturb the initial estimate
 | |
|   //  Point2 global = idealForeign; // Ideal - works
 | |
|   //  Point2 global = idealForeign + Point2(1.0, 0.0); // simple - works
 | |
|   Point2 global = idealForeign + Point2(10.0, -10.0); // larger - works
 | |
| 
 | |
|   NonlinearFactorGraph graph;
 | |
|   double error_gain = 1000.0;
 | |
|   graph.add(PointReferenceFrameFactor(lB1, tA1, lA1));
 | |
|   graph.add(NonlinearEquality<gtsam::Point2>(lA1, local, error_gain));
 | |
|   graph.add(NonlinearEquality<gtsam::Pose2>(tA1, trans, error_gain));
 | |
| 
 | |
|   // create initial estimate
 | |
|   Values init;
 | |
|   init.insert(lA1, local);
 | |
|   init.insert(lB1, global);
 | |
|   init.insert(tA1, trans);
 | |
| 
 | |
|   // optimize
 | |
|   LevenbergMarquardtOptimizer solver(graph, init);
 | |
|   Values actual = solver.optimize();
 | |
| 
 | |
|   // verify
 | |
|   CHECK(actual.exists(lB1));
 | |
|   EXPECT(assert_equal(idealForeign, actual.at<Point2>(lB1), 1e-5));
 | |
| }
 | |
| 
 | |
| /* ************************************************************************* */
 | |
| int main() { TestResult tr; return TestRegistry::runAllTests(tr); }
 | |
| /* ************************************************************************* */
 | |
| 
 | |
| 
 |