Moved common code to InitializePose
							parent
							
								
									a3ac89b0fb
								
							
						
					
					
						commit
						0d655e35b4
					
				| 
						 | 
				
			
			@ -0,0 +1,99 @@
 | 
			
		|||
/* ----------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
 * 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  InitializePose.h
 | 
			
		||||
 *  @author Frank Dellaert
 | 
			
		||||
 *  @date   August, 2020
 | 
			
		||||
 *  @brief common code between lago.* (2D) and InitializePose3.* (3D)
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <gtsam/inference/Symbol.h>
 | 
			
		||||
#include <gtsam/nonlinear/GaussNewtonOptimizer.h>
 | 
			
		||||
#include <gtsam/nonlinear/NonlinearFactorGraph.h>
 | 
			
		||||
#include <gtsam/nonlinear/PriorFactor.h>
 | 
			
		||||
#include <gtsam/slam/BetweenFactor.h>
 | 
			
		||||
 | 
			
		||||
namespace gtsam {
 | 
			
		||||
namespace initialize {
 | 
			
		||||
 | 
			
		||||
static constexpr Key kAnchorKey = 99999999;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Select the subgraph of betweenFactors and transforms priors into between
 | 
			
		||||
 * wrt a fictitious node
 | 
			
		||||
 */
 | 
			
		||||
template <class Pose>
 | 
			
		||||
static NonlinearFactorGraph buildPoseGraph(const NonlinearFactorGraph& graph) {
 | 
			
		||||
  NonlinearFactorGraph poseGraph;
 | 
			
		||||
 | 
			
		||||
  for (const auto& factor : graph) {
 | 
			
		||||
    // recast to a between on Pose
 | 
			
		||||
    if (auto between =
 | 
			
		||||
            boost::dynamic_pointer_cast<BetweenFactor<Pose> >(factor))
 | 
			
		||||
      poseGraph.add(between);
 | 
			
		||||
 | 
			
		||||
    // recast PriorFactor<Pose> to BetweenFactor<Pose>
 | 
			
		||||
    if (auto prior = boost::dynamic_pointer_cast<PriorFactor<Pose> >(factor))
 | 
			
		||||
      poseGraph.emplace_shared<BetweenFactor<Pose> >(
 | 
			
		||||
          kAnchorKey, prior->keys()[0], prior->prior(), prior->noiseModel());
 | 
			
		||||
  }
 | 
			
		||||
  return poseGraph;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Use Gauss-Newton optimizer to optimize for poses given rotation estimates
 | 
			
		||||
 */
 | 
			
		||||
template <class Pose>
 | 
			
		||||
static Values computePoses(const Values& initialRot,
 | 
			
		||||
                           NonlinearFactorGraph* posegraph,
 | 
			
		||||
                           bool singleIter = true) {
 | 
			
		||||
  const auto origin = Pose().translation();
 | 
			
		||||
 | 
			
		||||
  // Upgrade rotations to full poses
 | 
			
		||||
  Values initialPose;
 | 
			
		||||
  for (const auto& key_value : initialRot) {
 | 
			
		||||
    Key key = key_value.key;
 | 
			
		||||
    const auto& rot = initialRot.at<typename Pose::Rotation>(key);
 | 
			
		||||
    Pose initializedPose = Pose(rot, origin);
 | 
			
		||||
    initialPose.insert(key, initializedPose);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // add prior on dummy node
 | 
			
		||||
  auto priorModel = noiseModel::Unit::Create(Pose::dimension);
 | 
			
		||||
  initialPose.insert(kAnchorKey, Pose());
 | 
			
		||||
  posegraph->emplace_shared<PriorFactor<Pose> >(kAnchorKey, Pose(), priorModel);
 | 
			
		||||
 | 
			
		||||
  // Create optimizer
 | 
			
		||||
  GaussNewtonParams params;
 | 
			
		||||
  if (singleIter) {
 | 
			
		||||
    params.maxIterations = 1;
 | 
			
		||||
  } else {
 | 
			
		||||
    params.setVerbosity("TERMINATION");
 | 
			
		||||
  }
 | 
			
		||||
  GaussNewtonOptimizer optimizer(*posegraph, initialPose, params);
 | 
			
		||||
  Values GNresult = optimizer.optimize();
 | 
			
		||||
 | 
			
		||||
  // put into Values structure
 | 
			
		||||
  Values estimate;
 | 
			
		||||
  for (const auto& key_value : GNresult) {
 | 
			
		||||
    Key key = key_value.key;
 | 
			
		||||
    if (key != kAnchorKey) {
 | 
			
		||||
      const Pose& pose = GNresult.at<Pose>(key);
 | 
			
		||||
      estimate.insert(key, pose);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return estimate;
 | 
			
		||||
}
 | 
			
		||||
}  // namespace initialize
 | 
			
		||||
}  // namespace gtsam
 | 
			
		||||
| 
						 | 
				
			
			@ -10,28 +10,31 @@
 | 
			
		|||
 * -------------------------------------------------------------------------- */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 *  @file  InitializePose3.h
 | 
			
		||||
 *  @file  InitializePose3.cpp
 | 
			
		||||
 *  @author Luca Carlone
 | 
			
		||||
 *  @author Frank Dellaert
 | 
			
		||||
 *  @date   August, 2014
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <gtsam/slam/InitializePose3.h> 
 | 
			
		||||
 | 
			
		||||
#include <gtsam/slam/InitializePose.h> 
 | 
			
		||||
#include <gtsam/nonlinear/PriorFactor.h>
 | 
			
		||||
#include <gtsam/slam/BetweenFactor.h>
 | 
			
		||||
#include <gtsam/nonlinear/GaussNewtonOptimizer.h>
 | 
			
		||||
#include <gtsam/inference/Symbol.h>
 | 
			
		||||
#include <gtsam/geometry/Pose2.h>
 | 
			
		||||
#include <gtsam/geometry/Pose3.h>
 | 
			
		||||
#include <gtsam/base/timing.h>
 | 
			
		||||
 | 
			
		||||
#include <boost/math/special_functions.hpp>
 | 
			
		||||
 | 
			
		||||
#include <utility>
 | 
			
		||||
 | 
			
		||||
using namespace std;
 | 
			
		||||
 | 
			
		||||
namespace gtsam {
 | 
			
		||||
 | 
			
		||||
static const Key kAnchorKey = symbol('Z', 9999999);
 | 
			
		||||
 | 
			
		||||
/* ************************************************************************* */
 | 
			
		||||
GaussianFactorGraph InitializePose3::buildLinearOrientationGraph(const NonlinearFactorGraph& g) {
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -62,7 +65,7 @@ GaussianFactorGraph InitializePose3::buildLinearOrientationGraph(const Nonlinear
 | 
			
		|||
  }
 | 
			
		||||
  // prior on the anchor orientation
 | 
			
		||||
  linearGraph.add(
 | 
			
		||||
      kAnchorKey, I_9x9,
 | 
			
		||||
      initialize::kAnchorKey, I_9x9,
 | 
			
		||||
      (Vector(9) << 1.0, 0.0, 0.0, /*  */ 0.0, 1.0, 0.0, /*  */ 0.0, 0.0, 1.0)
 | 
			
		||||
          .finished(),
 | 
			
		||||
          noiseModel::Isotropic::Precision(9, 1));
 | 
			
		||||
| 
						 | 
				
			
			@ -78,7 +81,7 @@ Values InitializePose3::normalizeRelaxedRotations(
 | 
			
		|||
  Values validRot3;
 | 
			
		||||
  for(const auto& it: relaxedRot3) {
 | 
			
		||||
    Key key = it.first;
 | 
			
		||||
    if (key != kAnchorKey) {
 | 
			
		||||
    if (key != initialize::kAnchorKey) {
 | 
			
		||||
      Matrix3 M;
 | 
			
		||||
      M << Eigen::Map<const Matrix3>(it.second.data()); // Recover M from vectorized
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -91,24 +94,10 @@ Values InitializePose3::normalizeRelaxedRotations(
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
/* ************************************************************************* */
 | 
			
		||||
NonlinearFactorGraph InitializePose3::buildPose3graph(const NonlinearFactorGraph& graph) {
 | 
			
		||||
NonlinearFactorGraph InitializePose3::buildPose3graph(
 | 
			
		||||
    const NonlinearFactorGraph& graph) {
 | 
			
		||||
  gttic(InitializePose3_buildPose3graph);
 | 
			
		||||
  NonlinearFactorGraph pose3Graph;
 | 
			
		||||
 | 
			
		||||
  for(const auto& factor: graph) {
 | 
			
		||||
 | 
			
		||||
    // recast to a between on Pose3
 | 
			
		||||
    const auto pose3Between = boost::dynamic_pointer_cast<BetweenFactor<Pose3> >(factor);
 | 
			
		||||
    if (pose3Between)
 | 
			
		||||
      pose3Graph.add(pose3Between);
 | 
			
		||||
 | 
			
		||||
    // recast PriorFactor<Pose3> to BetweenFactor<Pose3>
 | 
			
		||||
    const auto pose3Prior = boost::dynamic_pointer_cast<PriorFactor<Pose3> >(factor);
 | 
			
		||||
    if (pose3Prior)
 | 
			
		||||
      pose3Graph.emplace_shared<BetweenFactor<Pose3> >(kAnchorKey, pose3Prior->keys()[0],
 | 
			
		||||
              pose3Prior->prior(), pose3Prior->noiseModel());
 | 
			
		||||
  }
 | 
			
		||||
  return pose3Graph;
 | 
			
		||||
  return initialize::buildPoseGraph<Pose3>(graph);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ************************************************************************* */
 | 
			
		||||
| 
						 | 
				
			
			@ -134,7 +123,7 @@ Values InitializePose3::computeOrientationsGradient(
 | 
			
		|||
 | 
			
		||||
  // this works on the inverse rotations, according to Tron&Vidal,2011
 | 
			
		||||
  Values inverseRot;
 | 
			
		||||
  inverseRot.insert(kAnchorKey, Rot3());
 | 
			
		||||
  inverseRot.insert(initialize::kAnchorKey, Rot3());
 | 
			
		||||
  for(const auto& key_value: givenGuess) {
 | 
			
		||||
    Key key = key_value.key;
 | 
			
		||||
    const Pose3& pose = givenGuess.at<Pose3>(key);
 | 
			
		||||
| 
						 | 
				
			
			@ -145,7 +134,7 @@ Values InitializePose3::computeOrientationsGradient(
 | 
			
		|||
  KeyVectorMap adjEdgesMap;
 | 
			
		||||
  KeyRotMap factorId2RotMap;
 | 
			
		||||
 | 
			
		||||
  createSymbolicGraph(adjEdgesMap, factorId2RotMap, pose3Graph);
 | 
			
		||||
  createSymbolicGraph(pose3Graph, &adjEdgesMap, &factorId2RotMap);
 | 
			
		||||
 | 
			
		||||
  // calculate max node degree & allocate gradient
 | 
			
		||||
  size_t maxNodeDeg = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -212,11 +201,11 @@ Values InitializePose3::computeOrientationsGradient(
 | 
			
		|||
  } // enf of gradient iterations
 | 
			
		||||
 | 
			
		||||
  // Return correct rotations
 | 
			
		||||
  const Rot3& Rref = inverseRot.at<Rot3>(kAnchorKey); // This will be set to the identity as so far we included no prior
 | 
			
		||||
  const Rot3& Rref = inverseRot.at<Rot3>(initialize::kAnchorKey); // This will be set to the identity as so far we included no prior
 | 
			
		||||
  Values estimateRot;
 | 
			
		||||
  for(const auto& key_value: inverseRot) {
 | 
			
		||||
    Key key = key_value.key;
 | 
			
		||||
    if (key != kAnchorKey) {
 | 
			
		||||
    if (key != initialize::kAnchorKey) {
 | 
			
		||||
      const Rot3& R = inverseRot.at<Rot3>(key);
 | 
			
		||||
      if(setRefFrame)
 | 
			
		||||
        estimateRot.insert(key, Rref.compose(R.inverse()));
 | 
			
		||||
| 
						 | 
				
			
			@ -228,32 +217,34 @@ Values InitializePose3::computeOrientationsGradient(
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
/* ************************************************************************* */
 | 
			
		||||
void InitializePose3::createSymbolicGraph(KeyVectorMap& adjEdgesMap, KeyRotMap& factorId2RotMap,
 | 
			
		||||
                         const NonlinearFactorGraph& pose3Graph) {
 | 
			
		||||
void InitializePose3::createSymbolicGraph(
 | 
			
		||||
    const NonlinearFactorGraph& pose3Graph, KeyVectorMap* adjEdgesMap,
 | 
			
		||||
    KeyRotMap* factorId2RotMap) {
 | 
			
		||||
  size_t factorId = 0;
 | 
			
		||||
  for(const auto& factor: pose3Graph) {
 | 
			
		||||
    auto pose3Between = boost::dynamic_pointer_cast<BetweenFactor<Pose3> >(factor);
 | 
			
		||||
    if (pose3Between){
 | 
			
		||||
  for (const auto& factor : pose3Graph) {
 | 
			
		||||
    auto pose3Between =
 | 
			
		||||
        boost::dynamic_pointer_cast<BetweenFactor<Pose3> >(factor);
 | 
			
		||||
    if (pose3Between) {
 | 
			
		||||
      Rot3 Rij = pose3Between->measured().rotation();
 | 
			
		||||
      factorId2RotMap.insert(pair<Key, Rot3 >(factorId,Rij));
 | 
			
		||||
      factorId2RotMap->emplace(factorId, Rij);
 | 
			
		||||
 | 
			
		||||
      Key key1 = pose3Between->key1();
 | 
			
		||||
      if (adjEdgesMap.find(key1) != adjEdgesMap.end()){  // key is already in
 | 
			
		||||
        adjEdgesMap.at(key1).push_back(factorId);
 | 
			
		||||
      }else{
 | 
			
		||||
      if (adjEdgesMap->find(key1) != adjEdgesMap->end()) {  // key is already in
 | 
			
		||||
        adjEdgesMap->at(key1).push_back(factorId);
 | 
			
		||||
      } else {
 | 
			
		||||
        vector<size_t> edge_id;
 | 
			
		||||
        edge_id.push_back(factorId);
 | 
			
		||||
        adjEdgesMap.insert(pair<Key, vector<size_t> >(key1, edge_id));
 | 
			
		||||
        adjEdgesMap->emplace(key1, edge_id);
 | 
			
		||||
      }
 | 
			
		||||
      Key key2 = pose3Between->key2();
 | 
			
		||||
      if (adjEdgesMap.find(key2) != adjEdgesMap.end()){  // key is already in
 | 
			
		||||
        adjEdgesMap.at(key2).push_back(factorId);
 | 
			
		||||
      }else{
 | 
			
		||||
      if (adjEdgesMap->find(key2) != adjEdgesMap->end()) {  // key is already in
 | 
			
		||||
        adjEdgesMap->at(key2).push_back(factorId);
 | 
			
		||||
      } else {
 | 
			
		||||
        vector<size_t> edge_id;
 | 
			
		||||
        edge_id.push_back(factorId);
 | 
			
		||||
        adjEdgesMap.insert(pair<Key, vector<size_t> >(key2, edge_id));
 | 
			
		||||
        adjEdgesMap->emplace(key2, edge_id);
 | 
			
		||||
      }
 | 
			
		||||
    }else{
 | 
			
		||||
    } else {
 | 
			
		||||
      cout << "Error in createSymbolicGraph" << endl;
 | 
			
		||||
    }
 | 
			
		||||
    factorId++;
 | 
			
		||||
| 
						 | 
				
			
			@ -293,50 +284,16 @@ Values InitializePose3::initializeOrientations(const NonlinearFactorGraph& graph
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
///* ************************************************************************* */
 | 
			
		||||
Values InitializePose3::computePoses(NonlinearFactorGraph& pose3graph,  Values& initialRot) {
 | 
			
		||||
Values InitializePose3::computePoses(const Values& initialRot,
 | 
			
		||||
                                      NonlinearFactorGraph* posegraph,
 | 
			
		||||
                                      bool singleIter) {
 | 
			
		||||
  gttic(InitializePose3_computePoses);
 | 
			
		||||
 | 
			
		||||
  // put into Values structure
 | 
			
		||||
  Values initialPose;
 | 
			
		||||
  for (const auto& key_value : initialRot) {
 | 
			
		||||
    Key key = key_value.key;
 | 
			
		||||
    const Rot3& rot = initialRot.at<Rot3>(key);
 | 
			
		||||
    Pose3 initializedPose = Pose3(rot, Point3(0, 0, 0));
 | 
			
		||||
    initialPose.insert(key, initializedPose);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // add prior
 | 
			
		||||
  noiseModel::Unit::shared_ptr priorModel = noiseModel::Unit::Create(6);
 | 
			
		||||
  initialPose.insert(kAnchorKey, Pose3());
 | 
			
		||||
  pose3graph.emplace_shared<PriorFactor<Pose3> >(kAnchorKey, Pose3(), priorModel);
 | 
			
		||||
 | 
			
		||||
  // Create optimizer
 | 
			
		||||
  GaussNewtonParams params;
 | 
			
		||||
  bool singleIter = true;
 | 
			
		||||
  if (singleIter) {
 | 
			
		||||
    params.maxIterations = 1;
 | 
			
		||||
  } else {
 | 
			
		||||
    cout << " \n\n\n\n  performing more than 1 GN iterations \n\n\n" << endl;
 | 
			
		||||
    params.setVerbosity("TERMINATION");
 | 
			
		||||
  }
 | 
			
		||||
  GaussNewtonOptimizer optimizer(pose3graph, initialPose, params);
 | 
			
		||||
  Values GNresult = optimizer.optimize();
 | 
			
		||||
 | 
			
		||||
  // put into Values structure
 | 
			
		||||
  Values estimate;
 | 
			
		||||
  for (const auto& key_value : GNresult) {
 | 
			
		||||
    Key key = key_value.key;
 | 
			
		||||
    if (key != kAnchorKey) {
 | 
			
		||||
      const Pose3& pose = GNresult.at<Pose3>(key);
 | 
			
		||||
      estimate.insert(key, pose);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return estimate;
 | 
			
		||||
  return initialize::computePoses<Pose3>(initialRot, posegraph, singleIter);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ************************************************************************* */
 | 
			
		||||
Values InitializePose3::initialize(const NonlinearFactorGraph& graph, const Values& givenGuess,
 | 
			
		||||
                  bool useGradient) {
 | 
			
		||||
Values InitializePose3::initialize(const NonlinearFactorGraph& graph,
 | 
			
		||||
                                   const Values& givenGuess, bool useGradient) {
 | 
			
		||||
  gttic(InitializePose3_initialize);
 | 
			
		||||
  Values initialValues;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -352,7 +309,7 @@ Values InitializePose3::initialize(const NonlinearFactorGraph& graph, const Valu
 | 
			
		|||
    orientations = computeOrientationsChordal(pose3Graph);
 | 
			
		||||
 | 
			
		||||
  // Compute the full poses (1 GN iteration on full poses)
 | 
			
		||||
  return computePoses(pose3Graph, orientations);
 | 
			
		||||
  return computePoses(orientations, &pose3Graph);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ************************************************************************* */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,6 +26,9 @@
 | 
			
		|||
#include <gtsam/linear/VectorValues.h>
 | 
			
		||||
#include <gtsam/nonlinear/NonlinearFactorGraph.h>
 | 
			
		||||
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
namespace gtsam {
 | 
			
		||||
 | 
			
		||||
typedef std::map<Key, std::vector<size_t> > KeyVectorMap;
 | 
			
		||||
| 
						 | 
				
			
			@ -50,9 +53,9 @@ struct GTSAM_EXPORT InitializePose3 {
 | 
			
		|||
      const NonlinearFactorGraph& pose3Graph, const Values& givenGuess,
 | 
			
		||||
      size_t maxIter = 10000, const bool setRefFrame = true);
 | 
			
		||||
 | 
			
		||||
  static void createSymbolicGraph(KeyVectorMap& adjEdgesMap,
 | 
			
		||||
                                  KeyRotMap& factorId2RotMap,
 | 
			
		||||
                                  const NonlinearFactorGraph& pose3Graph);
 | 
			
		||||
  static void createSymbolicGraph(const NonlinearFactorGraph& pose3Graph,
 | 
			
		||||
                                  KeyVectorMap* adjEdgesMap,
 | 
			
		||||
                                  KeyRotMap* factorId2RotMap);
 | 
			
		||||
 | 
			
		||||
  static Vector3 gradientTron(const Rot3& R1, const Rot3& R2, const double a,
 | 
			
		||||
                              const double b);
 | 
			
		||||
| 
						 | 
				
			
			@ -64,8 +67,12 @@ struct GTSAM_EXPORT InitializePose3 {
 | 
			
		|||
  static NonlinearFactorGraph buildPose3graph(
 | 
			
		||||
      const NonlinearFactorGraph& graph);
 | 
			
		||||
 | 
			
		||||
  static Values computePoses(NonlinearFactorGraph& pose3graph,
 | 
			
		||||
                             Values& initialRot);
 | 
			
		||||
  /**
 | 
			
		||||
   * Use Gauss-Newton optimizer to optimize for poses given rotation estimates
 | 
			
		||||
   */
 | 
			
		||||
  static Values computePoses(const Values& initialRot,
 | 
			
		||||
                             NonlinearFactorGraph* poseGraph,
 | 
			
		||||
                             bool singleIter = true);
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * "extract" the Pose3 subgraph of the original graph, get orientations from
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,6 +17,8 @@
 | 
			
		|||
 */
 | 
			
		||||
 | 
			
		||||
#include <gtsam/slam/lago.h>
 | 
			
		||||
 | 
			
		||||
#include <gtsam/slam/InitializePose.h>
 | 
			
		||||
#include <gtsam/nonlinear/PriorFactor.h>
 | 
			
		||||
#include <gtsam/slam/BetweenFactor.h>
 | 
			
		||||
#include <gtsam/inference/Symbol.h>
 | 
			
		||||
| 
						 | 
				
			
			@ -33,7 +35,6 @@ namespace lago {
 | 
			
		|||
static const Matrix I = I_1x1;
 | 
			
		||||
static const Matrix I3 = I_3x3;
 | 
			
		||||
 | 
			
		||||
static const Key keyAnchor = symbol('Z', 9999999);
 | 
			
		||||
static const noiseModel::Diagonal::shared_ptr priorOrientationNoise =
 | 
			
		||||
    noiseModel::Diagonal::Sigmas((Vector(1) << 0).finished());
 | 
			
		||||
static const noiseModel::Diagonal::shared_ptr priorPose2Noise =
 | 
			
		||||
| 
						 | 
				
			
			@ -79,7 +80,7 @@ key2doubleMap computeThetasToRoot(const key2doubleMap& deltaThetaMap,
 | 
			
		|||
  key2doubleMap thetaToRootMap;
 | 
			
		||||
 | 
			
		||||
  // Orientation of the roo
 | 
			
		||||
  thetaToRootMap.insert(pair<Key, double>(keyAnchor, 0.0));
 | 
			
		||||
  thetaToRootMap.insert(pair<Key, double>(initialize::kAnchorKey, 0.0));
 | 
			
		||||
 | 
			
		||||
  // for all nodes in the tree
 | 
			
		||||
  for(const key2doubleMap::value_type& it: deltaThetaMap) {
 | 
			
		||||
| 
						 | 
				
			
			@ -189,41 +190,16 @@ GaussianFactorGraph buildLinearOrientationGraph(
 | 
			
		|||
    lagoGraph.add(key1, -I, key2, I, deltaThetaRegularized, model_deltaTheta);
 | 
			
		||||
  }
 | 
			
		||||
  // prior on the anchor orientation
 | 
			
		||||
  lagoGraph.add(keyAnchor, I, (Vector(1) << 0.0).finished(), priorOrientationNoise);
 | 
			
		||||
  lagoGraph.add(initialize::kAnchorKey, I, (Vector(1) << 0.0).finished(), priorOrientationNoise);
 | 
			
		||||
  return lagoGraph;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ************************************************************************* */
 | 
			
		||||
// Select the subgraph of betweenFactors and transforms priors into between wrt a fictitious node
 | 
			
		||||
static NonlinearFactorGraph buildPose2graph(const NonlinearFactorGraph& graph) {
 | 
			
		||||
  gttic(lago_buildPose2graph);
 | 
			
		||||
  NonlinearFactorGraph pose2Graph;
 | 
			
		||||
 | 
			
		||||
  for(const boost::shared_ptr<NonlinearFactor>& factor: graph) {
 | 
			
		||||
 | 
			
		||||
    // recast to a between on Pose2
 | 
			
		||||
    boost::shared_ptr<BetweenFactor<Pose2> > pose2Between =
 | 
			
		||||
        boost::dynamic_pointer_cast<BetweenFactor<Pose2> >(factor);
 | 
			
		||||
    if (pose2Between)
 | 
			
		||||
      pose2Graph.add(pose2Between);
 | 
			
		||||
 | 
			
		||||
    // recast PriorFactor<Pose2> to BetweenFactor<Pose2>
 | 
			
		||||
    boost::shared_ptr<PriorFactor<Pose2> > pose2Prior =
 | 
			
		||||
        boost::dynamic_pointer_cast<PriorFactor<Pose2> >(factor);
 | 
			
		||||
    if (pose2Prior)
 | 
			
		||||
      pose2Graph.add(
 | 
			
		||||
          BetweenFactor<Pose2>(keyAnchor, pose2Prior->keys()[0],
 | 
			
		||||
              pose2Prior->prior(), pose2Prior->noiseModel()));
 | 
			
		||||
  }
 | 
			
		||||
  return pose2Graph;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ************************************************************************* */
 | 
			
		||||
static PredecessorMap<Key> findOdometricPath(
 | 
			
		||||
    const NonlinearFactorGraph& pose2Graph) {
 | 
			
		||||
 | 
			
		||||
  PredecessorMap<Key> tree;
 | 
			
		||||
  Key minKey = keyAnchor; // this initialization does not matter
 | 
			
		||||
  Key minKey = initialize::kAnchorKey; // this initialization does not matter
 | 
			
		||||
  bool minUnassigned = true;
 | 
			
		||||
 | 
			
		||||
  for(const boost::shared_ptr<NonlinearFactor>& factor: pose2Graph) {
 | 
			
		||||
| 
						 | 
				
			
			@ -240,8 +216,8 @@ static PredecessorMap<Key> findOdometricPath(
 | 
			
		|||
        minKey = key1;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  tree.insert(minKey, keyAnchor);
 | 
			
		||||
  tree.insert(keyAnchor, keyAnchor); // root
 | 
			
		||||
  tree.insert(minKey, initialize::kAnchorKey);
 | 
			
		||||
  tree.insert(initialize::kAnchorKey, initialize::kAnchorKey); // root
 | 
			
		||||
  return tree;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -284,7 +260,7 @@ VectorValues initializeOrientations(const NonlinearFactorGraph& graph,
 | 
			
		|||
 | 
			
		||||
  // 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);
 | 
			
		||||
  NonlinearFactorGraph pose2Graph = initialize::buildPoseGraph<Pose2>(graph);
 | 
			
		||||
 | 
			
		||||
  // Get orientations from relative orientation measurements
 | 
			
		||||
  return computeOrientations(pose2Graph, useOdometricPath);
 | 
			
		||||
| 
						 | 
				
			
			@ -338,7 +314,7 @@ Values computePoses(const NonlinearFactorGraph& pose2graph,
 | 
			
		|||
    }
 | 
			
		||||
  }
 | 
			
		||||
  // add prior
 | 
			
		||||
  linearPose2graph.add(keyAnchor, I3, Vector3(0.0, 0.0, 0.0),
 | 
			
		||||
  linearPose2graph.add(initialize::kAnchorKey, I3, Vector3(0.0, 0.0, 0.0),
 | 
			
		||||
      priorPose2Noise);
 | 
			
		||||
 | 
			
		||||
  // optimize
 | 
			
		||||
| 
						 | 
				
			
			@ -348,7 +324,7 @@ Values computePoses(const NonlinearFactorGraph& pose2graph,
 | 
			
		|||
  Values initialGuessLago;
 | 
			
		||||
  for(const VectorValues::value_type& it: posesLago) {
 | 
			
		||||
    Key key = it.first;
 | 
			
		||||
    if (key != keyAnchor) {
 | 
			
		||||
    if (key != initialize::kAnchorKey) {
 | 
			
		||||
      const Vector& poseVector = it.second;
 | 
			
		||||
      Pose2 poseLago = Pose2(poseVector(0), poseVector(1),
 | 
			
		||||
          orientationsLago.at(key)(0) + poseVector(2));
 | 
			
		||||
| 
						 | 
				
			
			@ -364,7 +340,7 @@ Values initialize(const NonlinearFactorGraph& graph, bool useOdometricPath) {
 | 
			
		|||
 | 
			
		||||
  // 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);
 | 
			
		||||
  NonlinearFactorGraph pose2Graph = initialize::buildPoseGraph<Pose2>(graph);
 | 
			
		||||
 | 
			
		||||
  // Get orientations from relative orientation measurements
 | 
			
		||||
  VectorValues orientationsLago = computeOrientations(pose2Graph,
 | 
			
		||||
| 
						 | 
				
			
			@ -385,7 +361,7 @@ Values initialize(const NonlinearFactorGraph& graph,
 | 
			
		|||
  // for all nodes in the tree
 | 
			
		||||
  for(const VectorValues::value_type& it: orientations) {
 | 
			
		||||
    Key key = it.first;
 | 
			
		||||
    if (key != keyAnchor) {
 | 
			
		||||
    if (key != initialize::kAnchorKey) {
 | 
			
		||||
      const Pose2& pose = initialGuess.at<Pose2>(key);
 | 
			
		||||
      const Vector& orientation = it.second;
 | 
			
		||||
      Pose2 poseLago = Pose2(pose.x(), pose.y(), orientation(0));
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,82 @@
 | 
			
		|||
/* ----------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
 * 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/InitializePose.h>
 | 
			
		||||
 | 
			
		||||
#include <CppUnitLite/TestHarness.h>
 | 
			
		||||
#include <gtsam/geometry/Pose2.h>
 | 
			
		||||
#include <gtsam/geometry/Pose3.h>
 | 
			
		||||
#include <gtsam/slam/dataset.h>
 | 
			
		||||
 | 
			
		||||
#include <cmath>
 | 
			
		||||
 | 
			
		||||
using namespace std;
 | 
			
		||||
using namespace gtsam;
 | 
			
		||||
 | 
			
		||||
/* ************************************************************************* */
 | 
			
		||||
TEST(InitializePose3, computePoses2D) {
 | 
			
		||||
  const string g2oFile = findExampleDataFile("noisyToyGraph.txt");
 | 
			
		||||
  NonlinearFactorGraph::shared_ptr inputGraph;
 | 
			
		||||
  Values::shared_ptr posesInFile;
 | 
			
		||||
  bool is3D = false;
 | 
			
		||||
  boost::tie(inputGraph, posesInFile) = readG2o(g2oFile, is3D);
 | 
			
		||||
 | 
			
		||||
  auto priorModel = noiseModel::Unit::Create(3);
 | 
			
		||||
  inputGraph->addPrior(0, posesInFile->at<Pose2>(0), priorModel);
 | 
			
		||||
 | 
			
		||||
  auto poseGraph = initialize::buildPoseGraph<Pose2>(*inputGraph);
 | 
			
		||||
 | 
			
		||||
  auto I = genericValue(Rot3());
 | 
			
		||||
  Values orientations;
 | 
			
		||||
  for (size_t i : {0, 1, 2, 3})
 | 
			
		||||
    orientations.insert(i, posesInFile->at<Pose2>(i).rotation());
 | 
			
		||||
  const Values poses = initialize::computePoses<Pose2>(orientations, &poseGraph);
 | 
			
		||||
 | 
			
		||||
  // posesInFile is seriously noisy, so we check error of recovered poses
 | 
			
		||||
  EXPECT_DOUBLES_EQUAL(0.0810283, inputGraph->error(poses), 1e-6);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ************************************************************************* */
 | 
			
		||||
TEST(InitializePose3, computePoses3D) {
 | 
			
		||||
  const string g2oFile = findExampleDataFile("Klaus3");
 | 
			
		||||
  NonlinearFactorGraph::shared_ptr inputGraph;
 | 
			
		||||
  Values::shared_ptr posesInFile;
 | 
			
		||||
  bool is3D = true;
 | 
			
		||||
  boost::tie(inputGraph, posesInFile) = readG2o(g2oFile, is3D);
 | 
			
		||||
 | 
			
		||||
  auto priorModel = noiseModel::Unit::Create(6);
 | 
			
		||||
  inputGraph->addPrior(0, posesInFile->at<Pose3>(0), priorModel);
 | 
			
		||||
 | 
			
		||||
  auto poseGraph = initialize::buildPoseGraph<Pose3>(*inputGraph);
 | 
			
		||||
 | 
			
		||||
  auto I = genericValue(Rot3());
 | 
			
		||||
  Values orientations;
 | 
			
		||||
  for (size_t i : {0, 1, 2})
 | 
			
		||||
    orientations.insert(i, posesInFile->at<Pose3>(i).rotation());
 | 
			
		||||
  Values poses = initialize::computePoses<Pose3>(orientations, &poseGraph);
 | 
			
		||||
  EXPECT(assert_equal(*posesInFile, poses, 0.1));  // TODO(frank): very loose !!
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ************************************************************************* */
 | 
			
		||||
int main() {
 | 
			
		||||
  TestResult tr;
 | 
			
		||||
  return TestRegistry::runAllTests(tr);
 | 
			
		||||
}
 | 
			
		||||
/* ************************************************************************* */
 | 
			
		||||
| 
						 | 
				
			
			@ -121,7 +121,7 @@ TEST( InitializePose3, orientationsGradientSymbolicGraph ) {
 | 
			
		|||
  KeyVectorMap adjEdgesMap;
 | 
			
		||||
  KeyRotMap factorId2RotMap;
 | 
			
		||||
 | 
			
		||||
  InitializePose3::createSymbolicGraph(adjEdgesMap, factorId2RotMap, pose3Graph);
 | 
			
		||||
  InitializePose3::createSymbolicGraph(pose3Graph, &adjEdgesMap, &factorId2RotMap);
 | 
			
		||||
 | 
			
		||||
  EXPECT_DOUBLES_EQUAL(adjEdgesMap.at(x0)[0], 0, 1e-9);
 | 
			
		||||
  EXPECT_DOUBLES_EQUAL(adjEdgesMap.at(x0)[1], 3, 1e-9);
 | 
			
		||||
| 
						 | 
				
			
			@ -258,21 +258,21 @@ TEST( InitializePose3, posesWithGivenGuess ) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
/* ************************************************************************* */
 | 
			
		||||
TEST( InitializePose3, initializePoses )
 | 
			
		||||
{
 | 
			
		||||
TEST(InitializePose3, initializePoses) {
 | 
			
		||||
  const string g2oFile = findExampleDataFile("pose3example-grid");
 | 
			
		||||
  NonlinearFactorGraph::shared_ptr inputGraph;
 | 
			
		||||
  Values::shared_ptr expectedValues;
 | 
			
		||||
  Values::shared_ptr posesInFile;
 | 
			
		||||
  bool is3D = true;
 | 
			
		||||
  boost::tie(inputGraph, expectedValues) = readG2o(g2oFile, is3D);
 | 
			
		||||
  noiseModel::Unit::shared_ptr priorModel = noiseModel::Unit::Create(6);
 | 
			
		||||
  boost::tie(inputGraph, posesInFile) = readG2o(g2oFile, is3D);
 | 
			
		||||
 | 
			
		||||
  auto priorModel = noiseModel::Unit::Create(6);
 | 
			
		||||
  inputGraph->addPrior(0, Pose3(), priorModel);
 | 
			
		||||
 | 
			
		||||
  Values initial = InitializePose3::initialize(*inputGraph);
 | 
			
		||||
  EXPECT(assert_equal(*expectedValues,initial,0.1));  // TODO(frank): very loose !!
 | 
			
		||||
  EXPECT(assert_equal(*posesInFile, initial,
 | 
			
		||||
                      0.1));  // TODO(frank): very loose !!
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* ************************************************************************* */
 | 
			
		||||
int main() {
 | 
			
		||||
  TestResult tr;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue