initial implementation of chordal relaxation
							parent
							
								
									bcab483574
								
							
						
					
					
						commit
						2f2a40a737
					
				|  | @ -0,0 +1,237 @@ | |||
| /* ----------------------------------------------------------------------------
 | ||||
| 
 | ||||
|  * 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  InitializePose3.h | ||||
|  *  @author Luca Carlone | ||||
|  *  @author Frank Dellaert | ||||
|  *  @date   August, 2014 | ||||
|  */ | ||||
| 
 | ||||
| #include <gtsam/slam/InitializePose3.h> | ||||
| #include <gtsam/slam/PriorFactor.h> | ||||
| #include <gtsam/slam/BetweenFactor.h> | ||||
| #include <gtsam/inference/Symbol.h> | ||||
| #include <gtsam/geometry/Pose3.h> | ||||
| #include <gtsam/base/timing.h> | ||||
| 
 | ||||
| #include <boost/math/special_functions.hpp> | ||||
| 
 | ||||
| using namespace std; | ||||
| 
 | ||||
| namespace gtsam { | ||||
| namespace InitializePose3 { | ||||
| 
 | ||||
| //static const Matrix I = eye(1);
 | ||||
| static const Matrix I9 = eye(9); | ||||
| static const Vector zero9 = Vector::Zero(9); | ||||
| static const Vector zero33= Matrix::Zero(3,3); | ||||
| 
 | ||||
| static const Key keyAnchor = symbol('Z', 9999999); | ||||
| 
 | ||||
| /* ************************************************************************* */ | ||||
| GaussianFactorGraph buildLinearOrientationGraph(const NonlinearFactorGraph& g) { | ||||
| 
 | ||||
|   GaussianFactorGraph linearGraph; | ||||
|   noiseModel::Unit::shared_ptr model = noiseModel::Unit::Create(9); | ||||
| 
 | ||||
|   BOOST_FOREACH(const boost::shared_ptr<NonlinearFactor>& factor, g) { | ||||
|     Matrix3 Rijt; | ||||
| 
 | ||||
|     boost::shared_ptr<BetweenFactor<Pose3> > pose3Between = | ||||
|         boost::dynamic_pointer_cast<BetweenFactor<Pose3> >(factor); | ||||
|     if (pose3Between) | ||||
|       Rijt = pose3Between->measured().rotation().matrix().transpose(); | ||||
|     else | ||||
|       std::cout << "Error in buildLinearOrientationGraph" << std::endl; | ||||
| 
 | ||||
|     const FastVector<Key>& keys = factor->keys(); | ||||
|     Key key1 = keys[0], key2 = keys[1]; | ||||
|     Matrix M9 = (Matrix(9,9) << Rijt,   zero33, zero33, | ||||
|                       zero33, Rijt,   zero33, | ||||
|                       zero33, zero33, Rijt); | ||||
|     linearGraph.add(key1, -I9, key2, M9, zero9, model); | ||||
|   } | ||||
|   // prior on the anchor orientation
 | ||||
|   linearGraph.add(keyAnchor, I9, (Vector(1) << 1.0, 0.0, 0.0,/*  */ 0.0, 1.0, 0.0, /*  */ 0.0, 0.0, 1.0), model); | ||||
|   return linearGraph; | ||||
| } | ||||
| 
 | ||||
| /* ************************************************************************* */ | ||||
| // Transform VectorValues into valid Rot3
 | ||||
| Values normalizeRelaxedRotations(const VectorValues& relaxedRot3) { | ||||
|   gttic(InitializePose3_computeOrientations); | ||||
| 
 | ||||
|   Values validRot3; | ||||
| 
 | ||||
|   return validRot3; | ||||
| } | ||||
| 
 | ||||
| /* ************************************************************************* */ | ||||
| // Select the subgraph of betweenFactors and transforms priors into between wrt a fictitious node
 | ||||
| NonlinearFactorGraph buildPose3graph(const NonlinearFactorGraph& graph) { | ||||
|   gttic(InitializePose3_buildPose3graph); | ||||
|   NonlinearFactorGraph pose3Graph; | ||||
| 
 | ||||
|   BOOST_FOREACH(const boost::shared_ptr<NonlinearFactor>& factor, graph) { | ||||
| 
 | ||||
|     // recast to a between on Pose3
 | ||||
|     boost::shared_ptr<BetweenFactor<Pose3> > pose3Between = | ||||
|         boost::dynamic_pointer_cast<BetweenFactor<Pose3> >(factor); | ||||
|     if (pose3Between) | ||||
|       pose3Graph.add(pose3Between); | ||||
| 
 | ||||
|     // recast PriorFactor<Pose3> to BetweenFactor<Pose3>
 | ||||
|     boost::shared_ptr<PriorFactor<Pose3> > pose3Prior = | ||||
|         boost::dynamic_pointer_cast<PriorFactor<Pose3> >(factor); | ||||
|     if (pose3Prior) | ||||
|       pose3Graph.add( | ||||
|           BetweenFactor<Pose3>(keyAnchor, pose3Prior->keys()[0], | ||||
|               pose3Prior->prior(), pose3Prior->get_noiseModel())); | ||||
|   } | ||||
|   return pose3Graph; | ||||
| } | ||||
| 
 | ||||
| /* ************************************************************************* */ | ||||
| // Return the orientations of a graph including only BetweenFactors<Pose2>
 | ||||
| Values computeOrientations(const NonlinearFactorGraph& pose3Graph) { | ||||
|   gttic(InitializePose3_computeOrientations); | ||||
| 
 | ||||
|   // regularize measurements and plug everything in a factor graph
 | ||||
|   GaussianFactorGraph relaxedGraph = buildLinearOrientationGraph(pose3Graph); | ||||
| 
 | ||||
|   // Solve the LFG
 | ||||
|   VectorValues relaxedRot3 = relaxedGraph.optimize(); | ||||
| 
 | ||||
|   // normalize and compute Rot3
 | ||||
|   Values initializedRot3 = normalizeRelaxedRotations(relaxedRot3); | ||||
| 
 | ||||
|   return initializedRot3; | ||||
| } | ||||
| 
 | ||||
| /* ************************************************************************* */ | ||||
| Values initializeOrientations(const NonlinearFactorGraph& graph) { | ||||
| 
 | ||||
|   // We "extract" the Pose3 subgraph of the original graph: this
 | ||||
|   // is done to properly model priors and avoiding operating on a larger graph
 | ||||
|   NonlinearFactorGraph pose3Graph = buildPose3graph(graph); | ||||
| 
 | ||||
|   // Get orientations from relative orientation measurements
 | ||||
|   return computeOrientations(pose3Graph); | ||||
| } | ||||
| 
 | ||||
| ///* ************************************************************************* */
 | ||||
| //Values computePoses(const NonlinearFactorGraph& pose2graph,
 | ||||
| //    VectorValues& orientationsLago) {
 | ||||
| //  gttic(lago_computePoses);
 | ||||
| //
 | ||||
| //  // Linearized graph on full poses
 | ||||
| //  GaussianFactorGraph linearPose2graph;
 | ||||
| //
 | ||||
| //  // We include the linear version of each between factor
 | ||||
| //  BOOST_FOREACH(const boost::shared_ptr<NonlinearFactor>& factor, pose2graph) {
 | ||||
| //
 | ||||
| //    boost::shared_ptr<BetweenFactor<Pose2> > pose2Between =
 | ||||
| //        boost::dynamic_pointer_cast<BetweenFactor<Pose2> >(factor);
 | ||||
| //
 | ||||
| //    if (pose2Between) {
 | ||||
| //      Key key1 = pose2Between->keys()[0];
 | ||||
| //      double theta1 = orientationsLago.at(key1)(0);
 | ||||
| //      double s1 = sin(theta1);
 | ||||
| //      double c1 = cos(theta1);
 | ||||
| //
 | ||||
| //      Key key2 = pose2Between->keys()[1];
 | ||||
| //      double theta2 = orientationsLago.at(key2)(0);
 | ||||
| //
 | ||||
| //      double linearDeltaRot = theta2 - theta1
 | ||||
| //          - pose2Between->measured().theta();
 | ||||
| //      linearDeltaRot = Rot2(linearDeltaRot).theta(); // to normalize
 | ||||
| //
 | ||||
| //      double dx = pose2Between->measured().x();
 | ||||
| //      double dy = pose2Between->measured().y();
 | ||||
| //
 | ||||
| //      Vector globalDeltaCart = //
 | ||||
| //          (Vector(2) << c1 * dx - s1 * dy, s1 * dx + c1 * dy);
 | ||||
| //      Vector b = (Vector(3) << globalDeltaCart, linearDeltaRot); // rhs
 | ||||
| //      Matrix J1 = -I3;
 | ||||
| //      J1(0, 2) = s1 * dx + c1 * dy;
 | ||||
| //      J1(1, 2) = -c1 * dx + s1 * dy;
 | ||||
| //      // Retrieve the noise model for the relative rotation
 | ||||
| //      boost::shared_ptr<noiseModel::Diagonal> diagonalModel =
 | ||||
| //          boost::dynamic_pointer_cast<noiseModel::Diagonal>(
 | ||||
| //              pose2Between->get_noiseModel());
 | ||||
| //
 | ||||
| //      linearPose2graph.add(key1, J1, key2, I3, b, diagonalModel);
 | ||||
| //    } else {
 | ||||
| //      throw invalid_argument(
 | ||||
| //          "computeLagoPoses: cannot manage non between factor here!");
 | ||||
| //    }
 | ||||
| //  }
 | ||||
| //  // add prior
 | ||||
| //  linearPose2graph.add(keyAnchor, I3, (Vector(3) << 0.0, 0.0, 0.0),
 | ||||
| //      priorPose2Noise);
 | ||||
| //
 | ||||
| //  // optimize
 | ||||
| //  VectorValues posesLago = linearPose2graph.optimize();
 | ||||
| //
 | ||||
| //  // put into Values structure
 | ||||
| //  Values initialGuessLago;
 | ||||
| //  BOOST_FOREACH(const VectorValues::value_type& it, posesLago) {
 | ||||
| //    Key key = it.first;
 | ||||
| //    if (key != keyAnchor) {
 | ||||
| //      const Vector& poseVector = it.second;
 | ||||
| //      Pose2 poseLago = Pose2(poseVector(0), poseVector(1),
 | ||||
| //          orientationsLago.at(key)(0) + poseVector(2));
 | ||||
| //      initialGuessLago.insert(key, poseLago);
 | ||||
| //    }
 | ||||
| //  }
 | ||||
| //  return initialGuessLago;
 | ||||
| //}
 | ||||
| 
 | ||||
| ///* ************************************************************************* */
 | ||||
| //Values initialize(const NonlinearFactorGraph& graph, bool useOdometricPath) {
 | ||||
| //  gttic(lago_initialize);
 | ||||
| //
 | ||||
| //  // We "extract" the Pose2 subgraph of the original graph: this
 | ||||
| //  // is done to properly model priors and avoiding operating on a larger graph
 | ||||
| //  NonlinearFactorGraph pose2Graph = buildPose2graph(graph);
 | ||||
| //
 | ||||
| //  // Get orientations from relative orientation measurements
 | ||||
| //  VectorValues orientationsLago = computeOrientations(pose2Graph,
 | ||||
| //      useOdometricPath);
 | ||||
| //
 | ||||
| //  // Compute the full poses
 | ||||
| //  return computePoses(pose2Graph, orientationsLago);
 | ||||
| //}
 | ||||
| //
 | ||||
| ///* ************************************************************************* */
 | ||||
| //Values initialize(const NonlinearFactorGraph& graph,
 | ||||
| //    const Values& initialGuess) {
 | ||||
| //  Values initialGuessLago;
 | ||||
| //
 | ||||
| //  // get the orientation estimates from LAGO
 | ||||
| //  VectorValues orientations = initializeOrientations(graph);
 | ||||
| //
 | ||||
| //  // for all nodes in the tree
 | ||||
| //  BOOST_FOREACH(const VectorValues::value_type& it, orientations) {
 | ||||
| //    Key key = it.first;
 | ||||
| //    if (key != keyAnchor) {
 | ||||
| //      const Pose2& pose = initialGuess.at<Pose2>(key);
 | ||||
| //      const Vector& orientation = it.second;
 | ||||
| //      Pose2 poseLago = Pose2(pose.x(), pose.y(), orientation(0));
 | ||||
| //      initialGuessLago.insert(key, poseLago);
 | ||||
| //    }
 | ||||
| //  }
 | ||||
| //  return initialGuessLago;
 | ||||
| //}
 | ||||
| 
 | ||||
| } // end of namespace lago
 | ||||
| } // end of namespace gtsam
 | ||||
|  | @ -0,0 +1,60 @@ | |||
| /* ----------------------------------------------------------------------------
 | ||||
| 
 | ||||
|  * 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  InitializePose3.h | ||||
|  *  @brief Initialize Pose3 in a factor graph | ||||
|  * | ||||
|  *  @author Luca Carlone | ||||
|  *  @author Frank Dellaert | ||||
|  *  @date   August, 2014 | ||||
|  */ | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <gtsam/nonlinear/NonlinearFactorGraph.h> | ||||
| #include <gtsam/linear/GaussianFactorGraph.h> | ||||
| #include <gtsam/linear/VectorValues.h> | ||||
| #include <gtsam/inference/graph.h> | ||||
| 
 | ||||
| namespace gtsam { | ||||
| namespace InitializePose3 { | ||||
| 
 | ||||
| GTSAM_EXPORT GaussianFactorGraph buildLinearOrientationGraph(const NonlinearFactorGraph& g); | ||||
| 
 | ||||
| GTSAM_EXPORT Values normalizeRelaxedRotations(const VectorValues& relaxedRot3); | ||||
| 
 | ||||
| GTSAM_EXPORT Values computeOrientations(const NonlinearFactorGraph& pose3Graph); | ||||
| 
 | ||||
| GTSAM_EXPORT Values initializeOrientations(const NonlinearFactorGraph& graph); | ||||
| 
 | ||||
| GTSAM_EXPORT NonlinearFactorGraph buildPose3graph(const NonlinearFactorGraph& graph); | ||||
| 
 | ||||
| ///** Linear factor graph with regularized orientation measurements */
 | ||||
| //GTSAM_EXPORT GaussianFactorGraph buildLinearOrientationGraph(
 | ||||
| //    const std::vector<size_t>& spanningTreeIds,
 | ||||
| //    const std::vector<size_t>& chordsIds, const NonlinearFactorGraph& g,
 | ||||
| //    const key2doubleMap& orientationsToRoot, const PredecessorMap<Key>& tree);
 | ||||
| //
 | ||||
| ///** LAGO: Return the orientations of the Pose2 in a generic factor graph */
 | ||||
| //GTSAM_EXPORT VectorValues initializeOrientations(
 | ||||
| //    const NonlinearFactorGraph& graph, bool useOdometricPath = true);
 | ||||
| //
 | ||||
| ///** Return the values for the Pose2 in a generic factor graph */
 | ||||
| //GTSAM_EXPORT Values initialize(const NonlinearFactorGraph& graph,
 | ||||
| //    bool useOdometricPath = true);
 | ||||
| //
 | ||||
| ///** Only correct the orientation part in initialGuess */
 | ||||
| //GTSAM_EXPORT Values initialize(const NonlinearFactorGraph& graph,
 | ||||
| //    const Values& initialGuess);
 | ||||
| 
 | ||||
| } // end of namespace lago
 | ||||
| } // end of namespace gtsam
 | ||||
|  | @ -0,0 +1,96 @@ | |||
| /* ----------------------------------------------------------------------------
 | ||||
| 
 | ||||
|  * 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  testInitializePose3.cpp | ||||
|  *  @brief Unit tests for 3D SLAM initialization, using rotation relaxation | ||||
|  * | ||||
|  *  @author Luca Carlone | ||||
|  *  @author Frank Dellaert | ||||
|  *  @date   August, 2014 | ||||
|  */ | ||||
| 
 | ||||
| #include <gtsam/slam/InitializePose3.h> | ||||
| #include <gtsam/slam/dataset.h> | ||||
| #include <gtsam/slam/BetweenFactor.h> | ||||
| #include <gtsam/slam/PriorFactor.h> | ||||
| #include <gtsam/inference/Symbol.h> | ||||
| #include <gtsam/geometry/Pose3.h> | ||||
| #include <CppUnitLite/TestHarness.h> | ||||
| 
 | ||||
| #include <cmath> | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace gtsam; | ||||
| using namespace boost::assign; | ||||
| 
 | ||||
| static Symbol x0('x', 0), x1('x', 1), x2('x', 2), x3('x', 3); | ||||
| static SharedNoiseModel model(noiseModel::Isotropic::Sigma(6, 0.1)); | ||||
| 
 | ||||
| namespace simple { | ||||
| // We consider a small graph:
 | ||||
| //                            symbolic FG
 | ||||
| //               x2               0  1
 | ||||
| //             / | \              1  2
 | ||||
| //            /  |  \             2  3
 | ||||
| //          x3   |   x1           2  0
 | ||||
| //           \   |   /            0  3
 | ||||
| //            \  |  /
 | ||||
| //               x0
 | ||||
| //
 | ||||
| static Point3 p1 = Point3(0,0,0); | ||||
| static Rot3   R1 = Rot3::Expmap( ( Vector(3) << 0.0,0.0,0.0 ) ); | ||||
| static Point3 p2 = Point3(1,2,0); | ||||
| static Rot3   R2 = Rot3::Expmap( ( Vector(3) << 0.0,0.0,1.570796 ) ); | ||||
| static Point3 p3 = Point3(0,2,0); | ||||
| static Rot3   R3 = Rot3::Expmap( ( Vector(3) << 0.0,0.0,3.141593 ) ); | ||||
| static Point3 p4 = Point3(-1,1,0); | ||||
| static Rot3   R4 = Rot3::Expmap( ( Vector(3) << 0.0,0.0,4.712389 ) ); | ||||
| 
 | ||||
| static Pose3 pose0 = Pose3(R1,p1); | ||||
| static Pose3 pose1 = Pose3(R2,p2); | ||||
| static Pose3 pose2 = Pose3(R3,p3); | ||||
| static Pose3 pose3 = Pose3(R4,p4); | ||||
| 
 | ||||
| NonlinearFactorGraph graph() { | ||||
|   NonlinearFactorGraph g; | ||||
|   g.add(BetweenFactor<Pose3>(x0, x1, pose0.between(pose1), model)); | ||||
|   g.add(BetweenFactor<Pose3>(x1, x2, pose1.between(pose2), model)); | ||||
|   g.add(BetweenFactor<Pose3>(x2, x3, pose2.between(pose3), model)); | ||||
|   g.add(BetweenFactor<Pose3>(x2, x0, pose2.between(pose0), model)); | ||||
|   g.add(BetweenFactor<Pose3>(x0, x3, pose0.between(pose3), model)); | ||||
|   g.add(PriorFactor<Pose3>(x0, pose0, model)); | ||||
|   return g; | ||||
| } | ||||
| } | ||||
| 
 | ||||
| /* *************************************************************************** */ | ||||
| TEST( InitializePose3, buildPose3graph ) { | ||||
|   NonlinearFactorGraph pose3graph = InitializePose3::buildPose3graph(simple::graph()); | ||||
|   // pose3graph.print("");
 | ||||
| } | ||||
| 
 | ||||
| /* *************************************************************************** */ | ||||
| TEST( InitializePose3, orientations ) { | ||||
|   Values initial = InitializePose3::initializeOrientations(simple::graph()); | ||||
| 
 | ||||
|   // comparison is up to M_PI, that's why we add some multiples of 2*M_PI
 | ||||
|   EXPECT(assert_equal(initial.at<Pose3>(0), simple::pose0, 1e-6)); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* ************************************************************************* */ | ||||
| int main() { | ||||
|   TestResult tr; | ||||
|   return TestRegistry::runAllTests(tr); | ||||
| } | ||||
| /* ************************************************************************* */ | ||||
| 
 | ||||
		Loading…
	
		Reference in New Issue