119 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			C++
		
	
	
		
		
			
		
	
	
			119 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			C++
		
	
	
|  | /*
 | ||
|  |  * testInvDepthFactorVariant1.cpp | ||
|  |  * | ||
|  |  *  Created on: Apr 13, 2012 | ||
|  |  *      Author: cbeall3 | ||
|  |  */ | ||
|  | 
 | ||
|  | #include <CppUnitLite/TestHarness.h>
 | ||
|  | #include <gtsam_unstable/slam/InvDepthFactorVariant2.h>
 | ||
|  | #include <gtsam/nonlinear/NonlinearEquality.h>
 | ||
|  | #include <gtsam/nonlinear/LevenbergMarquardtOptimizer.h>
 | ||
|  | #include <gtsam/nonlinear/NonlinearFactorGraph.h>
 | ||
|  | #include <gtsam/nonlinear/Symbol.h>
 | ||
|  | #include <gtsam/geometry/PinholeCamera.h>
 | ||
|  | #include <gtsam/geometry/Cal3_S2.h>
 | ||
|  | #include <gtsam/geometry/Pose3.h>
 | ||
|  | #include <gtsam/geometry/Point3.h>
 | ||
|  | #include <gtsam/geometry/Point2.h>
 | ||
|  | 
 | ||
|  | using namespace std; | ||
|  | using namespace gtsam; | ||
|  | 
 | ||
|  | /* ************************************************************************* */ | ||
|  | TEST( InvDepthFactorVariant2, optimize) { | ||
|  | 
 | ||
|  |   // Create two poses looking in the x-direction
 | ||
|  |   Pose3 pose1(Rot3::ypr(-M_PI/2, 0., -M_PI/2), gtsam::Point3(0,0,1.0)); | ||
|  |   Pose3 pose2(Rot3::ypr(-M_PI/2, 0., -M_PI/2), gtsam::Point3(0,0,1.5)); | ||
|  | 
 | ||
|  |   // Create a landmark 5 meters in front of pose1 (camera center at (0,0,1))
 | ||
|  |   Point3 landmark(5, 0, 1); | ||
|  | 
 | ||
|  |   // Create image observations
 | ||
|  |   Cal3_S2::shared_ptr K(new Cal3_S2(1500, 1200, 0, 640, 480)); | ||
|  |   PinholeCamera<Cal3_S2> camera1(pose1, *K); | ||
|  |   PinholeCamera<Cal3_S2> camera2(pose2, *K); | ||
|  |   Point2 pixel1 = camera1.project(landmark); | ||
|  |   Point2 pixel2 = camera2.project(landmark); | ||
|  | 
 | ||
|  |   // Create expected landmark
 | ||
|  |   Point3 referencePoint = pose1.translation(); | ||
|  |   Point3 ray = landmark - referencePoint; | ||
|  |   double theta = atan2(ray.y(), ray.x()); | ||
|  |   double phi = atan2(ray.z(), sqrt(ray.x()*ray.x()+ray.y()*ray.y())); | ||
|  |   double rho = 1./ray.norm(); | ||
|  |   LieVector expected(3, theta, phi, rho); | ||
|  | 
 | ||
|  | 
 | ||
|  |    | ||
|  |   // Create a factor graph with two inverse depth factors and two pose priors
 | ||
|  |   Key poseKey1(1); | ||
|  |   Key poseKey2(2); | ||
|  |   Key landmarkKey(100); | ||
|  |   SharedNoiseModel sigma(noiseModel::Unit::Create(2)); | ||
|  |   NonlinearFactor::shared_ptr factor1(new NonlinearEquality<Pose3>(poseKey1, pose1, 100000)); | ||
|  |   NonlinearFactor::shared_ptr factor2(new NonlinearEquality<Pose3>(poseKey2, pose2, 100000)); | ||
|  |   NonlinearFactor::shared_ptr factor3(new InvDepthFactorVariant2(poseKey1, landmarkKey, pixel1, K, referencePoint, sigma)); | ||
|  |   NonlinearFactor::shared_ptr factor4(new InvDepthFactorVariant2(poseKey2, landmarkKey, pixel2, K, referencePoint, sigma)); | ||
|  |   NonlinearFactorGraph graph; | ||
|  |   graph.push_back(factor1); | ||
|  |   graph.push_back(factor2); | ||
|  |   graph.push_back(factor3); | ||
|  |   graph.push_back(factor4); | ||
|  | 
 | ||
|  |   // Create a values with slightly incorrect initial conditions
 | ||
|  |   Values values; | ||
|  |   values.insert(poseKey1, pose1.retract(Vector_(6, +0.01, -0.02, +0.03, -0.10, +0.20, -0.30))); | ||
|  |   values.insert(poseKey2, pose2.retract(Vector_(6, +0.01, +0.02, -0.03, -0.10, +0.20, +0.30))); | ||
|  |   values.insert(landmarkKey, expected.retract(Vector_(3, +0.02, -0.04, +0.05))); | ||
|  | 
 | ||
|  |   // Optimize the graph to recover the actual landmark position
 | ||
|  |   LevenbergMarquardtParams params; | ||
|  |   Values result = LevenbergMarquardtOptimizer(graph, values, params).optimize(); | ||
|  |   LieVector actual = result.at<LieVector>(landmarkKey); | ||
|  |    | ||
|  | 
 | ||
|  |   values.at<Pose3>(poseKey1).print("Pose1 Before:\n"); | ||
|  |   result.at<Pose3>(poseKey1).print("Pose1 After:\n"); | ||
|  |   pose1.print("Pose1 Truth:\n"); | ||
|  |   std::cout << std::endl << std::endl; | ||
|  |   values.at<Pose3>(poseKey2).print("Pose2 Before:\n"); | ||
|  |   result.at<Pose3>(poseKey2).print("Pose2 After:\n"); | ||
|  |   pose2.print("Pose2 Truth:\n"); | ||
|  |   std::cout << std::endl << std::endl; | ||
|  |   values.at<LieVector>(landmarkKey).print("Landmark Before:\n"); | ||
|  |   result.at<LieVector>(landmarkKey).print("Landmark After:\n"); | ||
|  |   expected.print("Landmark Truth:\n"); | ||
|  |   std::cout << std::endl << std::endl; | ||
|  | 
 | ||
|  |   // Calculate world coordinates of landmark versions
 | ||
|  |   Point3 world_landmarkBefore; | ||
|  |   { | ||
|  |     LieVector landmarkBefore = values.at<LieVector>(landmarkKey); | ||
|  |     double theta = landmarkBefore(0), phi = landmarkBefore(1), rho = landmarkBefore(2); | ||
|  |     world_landmarkBefore = referencePoint + Point3(cos(theta)*cos(phi)/rho, sin(theta)*cos(phi)/rho, sin(phi)/rho); | ||
|  |   } | ||
|  |   Point3 world_landmarkAfter; | ||
|  |   { | ||
|  |     LieVector landmarkAfter = result.at<LieVector>(landmarkKey); | ||
|  |     double theta = landmarkAfter(0), phi = landmarkAfter(1), rho = landmarkAfter(2); | ||
|  |     world_landmarkAfter = referencePoint + Point3(cos(theta)*cos(phi)/rho, sin(theta)*cos(phi)/rho, sin(phi)/rho); | ||
|  |   } | ||
|  | 
 | ||
|  |   world_landmarkBefore.print("World Landmark Before:\n"); | ||
|  |   world_landmarkAfter.print("World Landmark After:\n"); | ||
|  |   landmark.print("World Landmark Truth:\n"); | ||
|  |   std::cout << std::endl << std::endl; | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  |   // Test that the correct landmark parameters have been recovered
 | ||
|  |   EXPECT(assert_equal(expected, actual, 1e-9)); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | /* ************************************************************************* */ | ||
|  | int main() { TestResult tr; return TestRegistry::runAllTests(tr);} | ||
|  | /* ************************************************************************* */ |