Merged from branch 'trunk'
						commit
						2705d85fa7
					
				| 
						 | 
				
			
			@ -104,7 +104,7 @@ if(MSVC AND NOT Boost_USE_STATIC_LIBS)
 | 
			
		|||
	add_definitions(-DBOOST_ALL_DYN_LINK)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
find_package(Boost 1.43 COMPONENTS serialization system filesystem thread date_time regex timer chrono)
 | 
			
		||||
find_package(Boost 1.43 COMPONENTS serialization system filesystem thread program_options date_time regex timer chrono)
 | 
			
		||||
 | 
			
		||||
# Required components
 | 
			
		||||
if(NOT Boost_SERIALIZATION_LIBRARY OR NOT Boost_SYSTEM_LIBRARY OR NOT Boost_FILESYSTEM_LIBRARY OR
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,7 +18,7 @@ foreach(example_src ${example_srcs} )
 | 
			
		|||
        set_target_properties(${example_bin} PROPERTIES EXCLUDE_FROM_ALL ON)
 | 
			
		||||
    endif()
 | 
			
		||||
    
 | 
			
		||||
    target_link_libraries(${example_bin} ${gtsam-default})
 | 
			
		||||
    target_link_libraries(${example_bin} ${gtsam-default} ${Boost_PROGRAM_OPTIONS_LIBRARY})
 | 
			
		||||
	if(NOT MSVC)
 | 
			
		||||
      add_custom_target(${example_bin}.run ${EXECUTABLE_OUTPUT_PATH}${example_bin} ${ARGN})
 | 
			
		||||
	endif()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| 
						 | 
				
			
			@ -0,0 +1,516 @@
 | 
			
		|||
/* ----------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
* 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    timeIncremental.cpp
 | 
			
		||||
* @brief   Incremental and batch solving, timing, and accuracy comparisons
 | 
			
		||||
* @author  Richard Roberts
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#include <gtsam/base/timing.h>
 | 
			
		||||
#include <gtsam/slam/dataset.h>
 | 
			
		||||
#include <gtsam/geometry/Pose2.h>
 | 
			
		||||
#include <gtsam/slam/PriorFactor.h>
 | 
			
		||||
#include <gtsam/slam/BetweenFactor.h>
 | 
			
		||||
#include <gtsam/slam/BearingRangeFactor.h>
 | 
			
		||||
#include <gtsam/nonlinear/Symbol.h>
 | 
			
		||||
#include <gtsam/nonlinear/ISAM2.h>
 | 
			
		||||
#include <gtsam/nonlinear/LevenbergMarquardtOptimizer.h>
 | 
			
		||||
#include <gtsam/nonlinear/Marginals.h>
 | 
			
		||||
 | 
			
		||||
#include <fstream>
 | 
			
		||||
#include <boost/archive/binary_oarchive.hpp>
 | 
			
		||||
#include <boost/archive/binary_iarchive.hpp>
 | 
			
		||||
#include <boost/serialization/export.hpp>
 | 
			
		||||
#include <boost/program_options.hpp>
 | 
			
		||||
#include <boost/range/algorithm/set_algorithm.hpp>
 | 
			
		||||
#include <boost/random.hpp>
 | 
			
		||||
 | 
			
		||||
using namespace std;
 | 
			
		||||
using namespace gtsam;
 | 
			
		||||
using namespace gtsam::symbol_shorthand;
 | 
			
		||||
namespace po = boost::program_options;
 | 
			
		||||
namespace br = boost::range;
 | 
			
		||||
 | 
			
		||||
typedef Pose2 Pose;
 | 
			
		||||
 | 
			
		||||
typedef NoiseModelFactor1<Pose> NM1;
 | 
			
		||||
typedef NoiseModelFactor2<Pose,Pose> NM2;
 | 
			
		||||
typedef BearingRangeFactor<Pose,Point2> BR;
 | 
			
		||||
 | 
			
		||||
BOOST_CLASS_EXPORT(Value);
 | 
			
		||||
BOOST_CLASS_EXPORT(Pose);
 | 
			
		||||
BOOST_CLASS_EXPORT(Rot2);
 | 
			
		||||
BOOST_CLASS_EXPORT(Point2);
 | 
			
		||||
BOOST_CLASS_EXPORT(NonlinearFactor);
 | 
			
		||||
BOOST_CLASS_EXPORT(NoiseModelFactor);
 | 
			
		||||
BOOST_CLASS_EXPORT(NM1);
 | 
			
		||||
BOOST_CLASS_EXPORT(NM2);
 | 
			
		||||
BOOST_CLASS_EXPORT(BetweenFactor<Pose>);
 | 
			
		||||
BOOST_CLASS_EXPORT(PriorFactor<Pose>);
 | 
			
		||||
BOOST_CLASS_EXPORT(BR);
 | 
			
		||||
BOOST_CLASS_EXPORT(noiseModel::Base);
 | 
			
		||||
BOOST_CLASS_EXPORT(noiseModel::Isotropic);
 | 
			
		||||
BOOST_CLASS_EXPORT(noiseModel::Gaussian);
 | 
			
		||||
BOOST_CLASS_EXPORT(noiseModel::Diagonal);
 | 
			
		||||
BOOST_CLASS_EXPORT(noiseModel::Unit);
 | 
			
		||||
 | 
			
		||||
double chi2_red(const gtsam::NonlinearFactorGraph& graph, const gtsam::Values& config) {
 | 
			
		||||
  // Compute degrees of freedom (observations - variables)
 | 
			
		||||
  // In ocaml, +1 was added to the observations to account for the prior, but
 | 
			
		||||
  // the factor graph already includes a factor for the prior/equality constraint.
 | 
			
		||||
  //  double dof = graph.size() - config.size();
 | 
			
		||||
  int graph_dim = 0;
 | 
			
		||||
  BOOST_FOREACH(const boost::shared_ptr<gtsam::NonlinearFactor>& nlf, graph) {
 | 
			
		||||
    graph_dim += (int)nlf->dim();
 | 
			
		||||
  }
 | 
			
		||||
  double dof = double(graph_dim) - double(config.dim()); // kaess: changed to dim
 | 
			
		||||
  return 2. * graph.error(config) / dof; // kaess: added factor 2, graph.error returns half of actual error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Global variables (these are only set once at program startup and never modified after)
 | 
			
		||||
string outputFile;
 | 
			
		||||
string inputFile;
 | 
			
		||||
string datasetName;
 | 
			
		||||
int firstStep;
 | 
			
		||||
int lastStep;
 | 
			
		||||
bool incremental;
 | 
			
		||||
bool batch;
 | 
			
		||||
bool compare;
 | 
			
		||||
bool perturb;
 | 
			
		||||
double perturbationNoise;
 | 
			
		||||
string compareFile1, compareFile2;
 | 
			
		||||
 | 
			
		||||
NonlinearFactorGraph inputFileMeasurements;
 | 
			
		||||
Values initial;
 | 
			
		||||
 | 
			
		||||
// Run functions for each mode
 | 
			
		||||
void runIncremental();
 | 
			
		||||
void runBatch();
 | 
			
		||||
void runCompare();
 | 
			
		||||
void runPerturb();
 | 
			
		||||
 | 
			
		||||
/* ************************************************************************* */
 | 
			
		||||
int main(int argc, char *argv[]) {
 | 
			
		||||
 | 
			
		||||
  po::options_description desc("Available options");
 | 
			
		||||
  desc.add_options()
 | 
			
		||||
    ("help", "Print help message")
 | 
			
		||||
    ("write-solution,o", po::value<string>(&outputFile)->default_value(""), "Write graph and solution to the specified file")
 | 
			
		||||
    ("read-solution,i", po::value<string>(&inputFile)->default_value(""), "Read graph and solution from the specified file")
 | 
			
		||||
    ("dataset,d", po::value<string>(&datasetName)->default_value(""), "Read a dataset file (if and only if --incremental is used)")
 | 
			
		||||
    ("first-step,f", po::value<int>(&firstStep)->default_value(0), "First step to process from the dataset file")
 | 
			
		||||
    ("last-step,l", po::value<int>(&lastStep)->default_value(-1), "Last step to process, or -1 to process until the end of the dataset")
 | 
			
		||||
    ("incremental", "Run in incremental mode using ISAM2 (default)")
 | 
			
		||||
    ("batch", "Run in batch mode, requires an initialization from --read-solution")
 | 
			
		||||
    ("compare", po::value<vector<string> >()->multitoken(), "Compare two solution files")
 | 
			
		||||
    ("perturb", po::value<double>(&perturbationNoise), "Perturb a solution file with the specified noise")
 | 
			
		||||
    ;
 | 
			
		||||
  po::variables_map vm;
 | 
			
		||||
  po::store(po::command_line_parser(argc, argv).options(desc).run(), vm);
 | 
			
		||||
  po::notify(vm);
 | 
			
		||||
 | 
			
		||||
  batch = (vm.count("batch") > 0);
 | 
			
		||||
  compare = (vm.count("compare") > 0);
 | 
			
		||||
  perturb = (vm.count("perturb") > 0);
 | 
			
		||||
  incremental = (vm.count("incremental") > 0 || (!batch && !compare && !perturb));
 | 
			
		||||
  if(compare) {
 | 
			
		||||
    const vector<string>& compareFiles = vm["compare"].as<vector<string> >();
 | 
			
		||||
    if(compareFiles.size() != 2) {
 | 
			
		||||
      cout << "Must specify two files with --compare";
 | 
			
		||||
      exit(1);
 | 
			
		||||
    }
 | 
			
		||||
    compareFile1 = compareFiles[0];
 | 
			
		||||
    compareFile2 = compareFiles[1];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if((batch && incremental) || (batch && compare) || (incremental && compare)) {
 | 
			
		||||
    cout << "Only one of --incremental, --batch, and --compare may be specified\n" << desc << endl;
 | 
			
		||||
    exit(1);
 | 
			
		||||
  }
 | 
			
		||||
  if(incremental && datasetName.empty()) {
 | 
			
		||||
    cout << "In incremental mode, a dataset must be specified\n" << desc << endl;
 | 
			
		||||
    exit(1);
 | 
			
		||||
  }
 | 
			
		||||
  if(!incremental && !datasetName.empty()) {
 | 
			
		||||
    cout << "A dataset may only be specified in incremental mode\n" << desc << endl;
 | 
			
		||||
    exit(1);
 | 
			
		||||
  }
 | 
			
		||||
  if(batch && inputFile.empty()) {
 | 
			
		||||
    cout << "In batch model, an input file must be specified\n" << desc << endl;
 | 
			
		||||
    exit(1);
 | 
			
		||||
  }
 | 
			
		||||
  if(perturb && (inputFile.empty() || outputFile.empty())) {
 | 
			
		||||
    cout << "In perturb mode, specify input and output files\n" << desc << endl;
 | 
			
		||||
    exit(1);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Read input file
 | 
			
		||||
  if(!inputFile.empty())
 | 
			
		||||
  {
 | 
			
		||||
    cout << "Reading input file " << inputFile << endl;
 | 
			
		||||
    std::ifstream readerStream(inputFile, ios::binary);
 | 
			
		||||
    boost::archive::binary_iarchive reader(readerStream);
 | 
			
		||||
    reader >> inputFileMeasurements >> initial;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Run mode
 | 
			
		||||
  if(incremental)
 | 
			
		||||
    runIncremental();
 | 
			
		||||
  else if(batch)
 | 
			
		||||
    runBatch();
 | 
			
		||||
  else if(compare)
 | 
			
		||||
    runCompare();
 | 
			
		||||
  else if(perturb)
 | 
			
		||||
    runPerturb();
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ************************************************************************* */
 | 
			
		||||
void runIncremental()
 | 
			
		||||
{
 | 
			
		||||
  NonlinearFactorGraph datasetMeasurements;
 | 
			
		||||
  cout << "Loading dataset " << datasetName << endl;
 | 
			
		||||
  string datasetFile = findExampleDataFile(datasetName);
 | 
			
		||||
  std::pair<NonlinearFactorGraph::shared_ptr, Values::shared_ptr> data =
 | 
			
		||||
    load2D(datasetFile);
 | 
			
		||||
  datasetMeasurements = *data.first;
 | 
			
		||||
 | 
			
		||||
  ISAM2 isam2;
 | 
			
		||||
 | 
			
		||||
  if(!inputFile.empty())
 | 
			
		||||
  {
 | 
			
		||||
    cout << "Adding initial measurements and values" << endl;
 | 
			
		||||
    isam2.update(inputFileMeasurements, initial);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Look for the first measurement to use
 | 
			
		||||
  cout << "Looking for first measurement from step " << firstStep << endl;
 | 
			
		||||
  size_t nextMeasurement = 0;
 | 
			
		||||
  bool havePreviousPose = false;
 | 
			
		||||
  Key firstPose;
 | 
			
		||||
  while(nextMeasurement < datasetMeasurements.size())
 | 
			
		||||
  {
 | 
			
		||||
    if(BetweenFactor<Pose>::shared_ptr measurement =
 | 
			
		||||
      boost::dynamic_pointer_cast<BetweenFactor<Pose> >(datasetMeasurements[nextMeasurement]))
 | 
			
		||||
    {
 | 
			
		||||
      Key key1 = measurement->key1(), key2 = measurement->key2();
 | 
			
		||||
      if((key1 >= firstStep && key1 < key2) || (key2 >= firstStep && key2 < key1)) {
 | 
			
		||||
        // We found an odometry starting at firstStep
 | 
			
		||||
        firstPose = std::min(key1, key2);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      if((key2 >= firstStep && key1 < key2) || (key1 >= firstStep && key2 < key1)) {
 | 
			
		||||
        // We found an odometry joining firstStep with a previous pose
 | 
			
		||||
        havePreviousPose = true;
 | 
			
		||||
        firstPose = std::max(key1, key2);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    ++ nextMeasurement;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if(nextMeasurement == datasetMeasurements.size()) {
 | 
			
		||||
    cout << "The supplied first step is past the end of the dataset" << endl;
 | 
			
		||||
    exit(1);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // If we didn't find an odometry linking to a previous pose, create a first pose and a prior
 | 
			
		||||
  if(!havePreviousPose) {
 | 
			
		||||
    cout << "Looks like " << firstPose << " is the first time step, so adding a prior on it" << endl;
 | 
			
		||||
    NonlinearFactorGraph newFactors;
 | 
			
		||||
    Values newVariables;
 | 
			
		||||
 | 
			
		||||
    newFactors.push_back(boost::make_shared<PriorFactor<Pose> >(firstPose, Pose(), noiseModel::Unit::Create(Pose::Dim())));
 | 
			
		||||
    newVariables.insert(firstPose, Pose());
 | 
			
		||||
 | 
			
		||||
    isam2.update(newFactors, newVariables);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  cout << "Playing forward time steps..." << endl;
 | 
			
		||||
 | 
			
		||||
  for(size_t step = firstPose; nextMeasurement < datasetMeasurements.size() && (lastStep == -1 || step <= lastStep); ++step)
 | 
			
		||||
  {
 | 
			
		||||
    Values newVariables;
 | 
			
		||||
    NonlinearFactorGraph newFactors;
 | 
			
		||||
 | 
			
		||||
    // Collect measurements and new variables for the current step
 | 
			
		||||
    gttic_(Collect_measurements);
 | 
			
		||||
    while(nextMeasurement < datasetMeasurements.size()) {
 | 
			
		||||
 | 
			
		||||
      NonlinearFactor::shared_ptr measurementf = datasetMeasurements[nextMeasurement];
 | 
			
		||||
 | 
			
		||||
      if(BetweenFactor<Pose>::shared_ptr measurement =
 | 
			
		||||
        boost::dynamic_pointer_cast<BetweenFactor<Pose> >(measurementf))
 | 
			
		||||
      {
 | 
			
		||||
        // Stop collecting measurements that are for future steps
 | 
			
		||||
        if(measurement->key1() > step || measurement->key2() > step)
 | 
			
		||||
          break;
 | 
			
		||||
 | 
			
		||||
        // Require that one of the nodes is the current one
 | 
			
		||||
        if(measurement->key1() != step && measurement->key2() != step)
 | 
			
		||||
          throw runtime_error("Problem in data file, out-of-sequence measurements");
 | 
			
		||||
 | 
			
		||||
        // Add a new factor
 | 
			
		||||
        newFactors.push_back(measurement);
 | 
			
		||||
 | 
			
		||||
        // Initialize the new variable
 | 
			
		||||
        if(measurement->key1() > measurement->key2()) {
 | 
			
		||||
          if(step == 1)
 | 
			
		||||
            newVariables.insert(measurement->key1(), measurement->measured().inverse());
 | 
			
		||||
          else {
 | 
			
		||||
            Pose prevPose = isam2.calculateEstimate<Pose>(measurement->key2());
 | 
			
		||||
            newVariables.insert(measurement->key1(), prevPose * measurement->measured().inverse());
 | 
			
		||||
          }
 | 
			
		||||
          //        cout << "Initializing " << step << endl;
 | 
			
		||||
        } else {
 | 
			
		||||
          if(step == 1)
 | 
			
		||||
            newVariables.insert(measurement->key2(), measurement->measured());
 | 
			
		||||
          else {
 | 
			
		||||
            Pose prevPose = isam2.calculateEstimate<Pose>(measurement->key1());
 | 
			
		||||
            newVariables.insert(measurement->key2(), prevPose * measurement->measured());
 | 
			
		||||
          }
 | 
			
		||||
          //        cout << "Initializing " << step << endl;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      else if(BearingRangeFactor<Pose, Point2>::shared_ptr measurement =
 | 
			
		||||
        boost::dynamic_pointer_cast<BearingRangeFactor<Pose, Point2> >(measurementf))
 | 
			
		||||
      {
 | 
			
		||||
        Key poseKey = measurement->keys()[0], lmKey = measurement->keys()[1];
 | 
			
		||||
 | 
			
		||||
        // Stop collecting measurements that are for future steps
 | 
			
		||||
        if(poseKey > step)
 | 
			
		||||
          throw runtime_error("Problem in data file, out-of-sequence measurements");
 | 
			
		||||
 | 
			
		||||
        // Add new factor
 | 
			
		||||
        newFactors.push_back(measurement);
 | 
			
		||||
 | 
			
		||||
        // Initialize new landmark
 | 
			
		||||
        if(!isam2.getLinearizationPoint().exists(lmKey))
 | 
			
		||||
        {
 | 
			
		||||
          Pose pose;
 | 
			
		||||
          if(isam2.getLinearizationPoint().exists(poseKey))
 | 
			
		||||
            pose = isam2.calculateEstimate<Pose>(poseKey);
 | 
			
		||||
          else
 | 
			
		||||
            pose = newVariables.at<Pose>(poseKey);
 | 
			
		||||
          Rot2 measuredBearing = measurement->measured().first;
 | 
			
		||||
          double measuredRange = measurement->measured().second;
 | 
			
		||||
          newVariables.insert(lmKey, 
 | 
			
		||||
            pose.transform_from(measuredBearing.rotate(Point2(measuredRange, 0.0))));
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      else
 | 
			
		||||
      {
 | 
			
		||||
        throw std::runtime_error("Unknown factor type read from data file");
 | 
			
		||||
      }
 | 
			
		||||
      ++ nextMeasurement;
 | 
			
		||||
    }
 | 
			
		||||
    gttoc_(Collect_measurements);
 | 
			
		||||
 | 
			
		||||
    // Update iSAM2
 | 
			
		||||
    gttic_(Update_ISAM2);
 | 
			
		||||
    isam2.update(newFactors, newVariables);
 | 
			
		||||
    gttoc_(Update_ISAM2);
 | 
			
		||||
 | 
			
		||||
    if((step - firstPose) % 100 == 0) {
 | 
			
		||||
      gttic_(chi2);
 | 
			
		||||
      Values estimate(isam2.calculateEstimate());
 | 
			
		||||
      double chi2 = chi2_red(isam2.getFactorsUnsafe(), estimate);
 | 
			
		||||
      cout << "chi2 = " << chi2 << endl;
 | 
			
		||||
      gttoc_(chi2);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    tictoc_finishedIteration_();
 | 
			
		||||
 | 
			
		||||
    if((step - firstPose) % 1000 == 0) {
 | 
			
		||||
      cout << "Step " << step << endl;
 | 
			
		||||
      tictoc_print_();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if(!outputFile.empty())
 | 
			
		||||
  {
 | 
			
		||||
    try {
 | 
			
		||||
      cout << "Writing output file " << outputFile << endl;
 | 
			
		||||
      std::ofstream writerStream(outputFile, ios::binary);
 | 
			
		||||
      boost::archive::binary_oarchive writer(writerStream);
 | 
			
		||||
      writer << isam2.getFactorsUnsafe() << isam2.calculateEstimate();
 | 
			
		||||
    } catch(std::exception& e) {
 | 
			
		||||
      cout << e.what() << endl;
 | 
			
		||||
      exit(1);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Compute marginals
 | 
			
		||||
  //try {
 | 
			
		||||
  //  Marginals marginals(graph, values);
 | 
			
		||||
  //  int i=0;
 | 
			
		||||
  //  BOOST_REVERSE_FOREACH(Key key1, values.keys()) {
 | 
			
		||||
  //    int j=0;
 | 
			
		||||
  //    BOOST_REVERSE_FOREACH(Key key2, values.keys()) {
 | 
			
		||||
  //      if(i != j) {
 | 
			
		||||
  //        gttic_(jointMarginalInformation);
 | 
			
		||||
  //        std::vector<Key> keys(2);
 | 
			
		||||
  //        keys[0] = key1;
 | 
			
		||||
  //        keys[1] = key2;
 | 
			
		||||
  //        JointMarginal info = marginals.jointMarginalInformation(keys);
 | 
			
		||||
  //        gttoc_(jointMarginalInformation);
 | 
			
		||||
  //        tictoc_finishedIteration_();
 | 
			
		||||
  //      }
 | 
			
		||||
  //      ++j;
 | 
			
		||||
  //      if(j >= 50)
 | 
			
		||||
  //        break;
 | 
			
		||||
  //    }
 | 
			
		||||
  //    ++i;
 | 
			
		||||
  //    if(i >= 50)
 | 
			
		||||
  //      break;
 | 
			
		||||
  //  }
 | 
			
		||||
  //  tictoc_print_();
 | 
			
		||||
  //  BOOST_FOREACH(Key key, values.keys()) {
 | 
			
		||||
  //    gttic_(marginalInformation);
 | 
			
		||||
  //    Matrix info = marginals.marginalInformation(key);
 | 
			
		||||
  //    gttoc_(marginalInformation);
 | 
			
		||||
  //    tictoc_finishedIteration_();
 | 
			
		||||
  //    ++i;
 | 
			
		||||
  //  }
 | 
			
		||||
  //} catch(std::exception& e) {
 | 
			
		||||
  //  cout << e.what() << endl;
 | 
			
		||||
  //}
 | 
			
		||||
  //tictoc_print_();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ************************************************************************* */
 | 
			
		||||
void runBatch()
 | 
			
		||||
{
 | 
			
		||||
  cout << "Creating batch optimizer..." << endl;
 | 
			
		||||
 | 
			
		||||
  gttic_(Create_optimizer);
 | 
			
		||||
  LevenbergMarquardtOptimizer optimizer(inputFileMeasurements, initial);
 | 
			
		||||
  gttoc_(Create_optimizer);
 | 
			
		||||
  double lastError;
 | 
			
		||||
  do {
 | 
			
		||||
    lastError = optimizer.error();
 | 
			
		||||
    gttic_(Iterate_optimizer);
 | 
			
		||||
    optimizer.iterate();
 | 
			
		||||
    gttoc_(Iterate_optimizer);
 | 
			
		||||
    cout << "Error: " << lastError << " -> " << optimizer.error() << ", lambda: " << optimizer.lambda() << endl;
 | 
			
		||||
    gttic_(chi2);
 | 
			
		||||
    double chi2 = chi2_red(inputFileMeasurements, optimizer.values());
 | 
			
		||||
    cout << "chi2 = " << chi2 << endl;
 | 
			
		||||
    gttoc_(chi2);
 | 
			
		||||
  } while(!checkConvergence(optimizer.params().relativeErrorTol,
 | 
			
		||||
    optimizer.params().absoluteErrorTol, optimizer.params().errorTol,
 | 
			
		||||
    lastError, optimizer.error(), optimizer.params().verbosity));
 | 
			
		||||
 | 
			
		||||
  tictoc_finishedIteration_();
 | 
			
		||||
  tictoc_print_();
 | 
			
		||||
 | 
			
		||||
  if(!outputFile.empty())
 | 
			
		||||
  {
 | 
			
		||||
    try {
 | 
			
		||||
      cout << "Writing output file " << outputFile << endl;
 | 
			
		||||
      std::ofstream writerStream(outputFile, ios::binary);
 | 
			
		||||
      boost::archive::binary_oarchive writer(writerStream);
 | 
			
		||||
      writer << inputFileMeasurements << optimizer.values();
 | 
			
		||||
    } catch(std::exception& e) {
 | 
			
		||||
      cout << e.what() << endl;
 | 
			
		||||
      exit(1);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ************************************************************************* */
 | 
			
		||||
void runCompare()
 | 
			
		||||
{
 | 
			
		||||
  NonlinearFactorGraph graph1, graph2;
 | 
			
		||||
  Values soln1, soln2;
 | 
			
		||||
 | 
			
		||||
  cout << "Reading solution file " << compareFile1 << endl;
 | 
			
		||||
  {
 | 
			
		||||
    std::ifstream readerStream(compareFile1, ios::binary);
 | 
			
		||||
    boost::archive::binary_iarchive reader(readerStream);
 | 
			
		||||
    reader >> graph1 >> soln1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  cout << "Reading solution file " << compareFile2 << endl;
 | 
			
		||||
  {
 | 
			
		||||
    std::ifstream readerStream(compareFile2, ios::binary);
 | 
			
		||||
    boost::archive::binary_iarchive reader(readerStream);
 | 
			
		||||
    reader >> graph2 >> soln2;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Check factors for equality
 | 
			
		||||
  cout << "Comparing factor graphs..." << endl;
 | 
			
		||||
  if(graph1.size() != graph2.size())
 | 
			
		||||
    cout << "  Graph sizes not equal, " << graph1.size() << " vs. " << graph2.size() << endl;
 | 
			
		||||
  for(NonlinearFactorGraph::const_iterator f1 = graph1.begin(), f2 = graph2.begin();
 | 
			
		||||
    f1 != graph1.end() && f2 != graph2.end(); ++f1, ++f2)
 | 
			
		||||
  {
 | 
			
		||||
    if((*f1 || *f2) && ((*f1 && !*f2) || (!*f1 && *f2) || !(**f1).equals(**f2, 1e-9))) {
 | 
			
		||||
      cout << "  Factor " << f1 - graph1.begin() << " not equal.\n";
 | 
			
		||||
      (**f1).print("    From graph 1: ");
 | 
			
		||||
      (**f2).print("    From graph 2: ");
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Check solution for equality
 | 
			
		||||
  cout << "Comparing solutions..." << endl;
 | 
			
		||||
  vector<Key> missingKeys;
 | 
			
		||||
  br::set_symmetric_difference(soln1.keys(), soln2.keys(), std::back_inserter(missingKeys));
 | 
			
		||||
  if(!missingKeys.empty()) {
 | 
			
		||||
    cout << "  Keys unique to one solution file: ";
 | 
			
		||||
    for(size_t i = 0; i < missingKeys.size(); ++i) {
 | 
			
		||||
      cout << DefaultKeyFormatter(missingKeys[i]);
 | 
			
		||||
      if(i != missingKeys.size() - 1)
 | 
			
		||||
        cout << ", ";
 | 
			
		||||
    }
 | 
			
		||||
    cout << endl;
 | 
			
		||||
  }
 | 
			
		||||
  vector<Key> commonKeys;
 | 
			
		||||
  br::set_intersection(soln1.keys(), soln2.keys(), std::back_inserter(commonKeys));
 | 
			
		||||
  double maxDiff = 0.0;
 | 
			
		||||
  BOOST_FOREACH(Key j, commonKeys)
 | 
			
		||||
    maxDiff = std::max(maxDiff, soln1.at(j).localCoordinates_(soln2.at(j)).norm());
 | 
			
		||||
  cout << "  Maximum solution difference (norm of logmap): " << maxDiff << endl;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ************************************************************************* */
 | 
			
		||||
void runPerturb()
 | 
			
		||||
{
 | 
			
		||||
  // Set up random number generator
 | 
			
		||||
  boost::random::mt19937 rng;
 | 
			
		||||
  boost::random::normal_distribution<double> normal(0.0, perturbationNoise);
 | 
			
		||||
 | 
			
		||||
  // Perturb values
 | 
			
		||||
  VectorValues noise;
 | 
			
		||||
  Ordering ordering = *initial.orderingArbitrary();
 | 
			
		||||
  BOOST_FOREACH(const Values::KeyValuePair& key_val, initial)
 | 
			
		||||
  {
 | 
			
		||||
    Vector noisev(key_val.value.dim());
 | 
			
		||||
    for(Vector::Index i = 0; i < noisev.size(); ++i)
 | 
			
		||||
      noisev(i) = normal(rng);
 | 
			
		||||
    noise.insert(ordering[key_val.key], noisev);
 | 
			
		||||
  }
 | 
			
		||||
  Values perturbed = initial.retract(noise, ordering);
 | 
			
		||||
 | 
			
		||||
  // Write results
 | 
			
		||||
  try {
 | 
			
		||||
    cout << "Writing output file " << outputFile << endl;
 | 
			
		||||
    std::ofstream writerStream(outputFile, ios::binary);
 | 
			
		||||
    boost::archive::binary_oarchive writer(writerStream);
 | 
			
		||||
    writer << inputFileMeasurements << perturbed;
 | 
			
		||||
  } catch(std::exception& e) {
 | 
			
		||||
    cout << e.what() << endl;
 | 
			
		||||
    exit(1);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -91,7 +91,8 @@ pair<NonlinearFactorGraph::shared_ptr, Values::shared_ptr> load2D(
 | 
			
		|||
 | 
			
		||||
  // load the poses
 | 
			
		||||
  while (is) {
 | 
			
		||||
    is >> tag;
 | 
			
		||||
    if(! (is >> tag))
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    if ((tag == "VERTEX2") || (tag == "VERTEX")) {
 | 
			
		||||
      int id;
 | 
			
		||||
| 
						 | 
				
			
			@ -113,7 +114,8 @@ pair<NonlinearFactorGraph::shared_ptr, Values::shared_ptr> load2D(
 | 
			
		|||
  // load the factors
 | 
			
		||||
  bool haveLandmark = false;
 | 
			
		||||
  while (is) {
 | 
			
		||||
    is >> tag;
 | 
			
		||||
    if(! (is >> tag))
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    if ((tag == "EDGE2") || (tag == "EDGE") || (tag == "ODOMETRY")) {
 | 
			
		||||
      int id1, id2;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
 | 
			
		||||
 * -------------------------------------------------------------------------- */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * testTriangulation.cpp
 | 
			
		||||
 *
 | 
			
		||||
 *  Created on: July 30th, 2013
 | 
			
		||||
 *      Author: cbeall3
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <CppUnitLite/TestHarness.h>
 | 
			
		||||
 | 
			
		||||
#include <gtsam/base/Testable.h>
 | 
			
		||||
#include <gtsam/geometry/SimpleCamera.h>
 | 
			
		||||
 | 
			
		||||
#include <gtsam_unstable/geometry/InvDepthCamera3.h>
 | 
			
		||||
#include <gtsam_unstable/geometry/triangulation.h>
 | 
			
		||||
 | 
			
		||||
#include <boost/assign.hpp>
 | 
			
		||||
#include <boost/assign/std/vector.hpp>
 | 
			
		||||
 | 
			
		||||
using namespace std;
 | 
			
		||||
using namespace gtsam;
 | 
			
		||||
using namespace boost::assign;
 | 
			
		||||
 | 
			
		||||
/* ************************************************************************* */
 | 
			
		||||
TEST( triangulation, twoPoses) {
 | 
			
		||||
  Cal3_S2 K(1500, 1200, 0, 640, 480);
 | 
			
		||||
  // create first camera. Looking along X-axis, 1 meter above ground plane (x-y)
 | 
			
		||||
  Pose3 level_pose = Pose3(Rot3::ypr(-M_PI/2, 0., -M_PI/2), gtsam::Point3(0,0,1));
 | 
			
		||||
  SimpleCamera level_camera(level_pose, K);
 | 
			
		||||
 | 
			
		||||
  // create second camera 1 meter to the right of first camera
 | 
			
		||||
  Pose3 level_pose_right = level_pose * Pose3(Rot3(), Point3(1,0,0));
 | 
			
		||||
  SimpleCamera level_camera_right(level_pose_right, K);
 | 
			
		||||
 | 
			
		||||
  // landmark ~5 meters infront of camera
 | 
			
		||||
  Point3 landmark(5, 0.5, 1.2);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  // 1. Project two landmarks into two cameras and triangulate
 | 
			
		||||
  Point2 level_uv = level_camera.project(landmark);
 | 
			
		||||
  Point2 level_uv_right = level_camera_right.project(landmark);
 | 
			
		||||
 | 
			
		||||
  vector<Pose3> poses;
 | 
			
		||||
  vector<Point2> measurements;
 | 
			
		||||
 | 
			
		||||
  poses += level_pose, level_pose_right;
 | 
			
		||||
  measurements += level_uv, level_uv_right;
 | 
			
		||||
 | 
			
		||||
  boost::optional<Point3> triangulated_landmark = triangulatePoint3(poses, measurements, K);
 | 
			
		||||
  EXPECT(assert_equal(landmark, *triangulated_landmark, 1e-2));
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  // 2. Add some noise and try again: result should be ~ (4.995, 0.499167, 1.19814)
 | 
			
		||||
  measurements.at(0) += Point2(0.1,0.5);
 | 
			
		||||
  measurements.at(1) += Point2(-0.2, 0.3);
 | 
			
		||||
 | 
			
		||||
  boost::optional<Point3> triangulated_landmark_noise = triangulatePoint3(poses, measurements, K);
 | 
			
		||||
  EXPECT(assert_equal(landmark, *triangulated_landmark_noise, 1e-2));
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  // 3. Add a slightly rotated third camera above, again with measurement noise
 | 
			
		||||
  Pose3 pose_top = level_pose * Pose3(Rot3::ypr(0.1,0.2,0.1), Point3(0.1,-2,-.1));
 | 
			
		||||
  SimpleCamera camera_top(pose_top, K);
 | 
			
		||||
  Point2 top_uv = camera_top.project(landmark);
 | 
			
		||||
 | 
			
		||||
  poses += pose_top;
 | 
			
		||||
  measurements += top_uv + Point2(0.1, -0.1);
 | 
			
		||||
 | 
			
		||||
  boost::optional<Point3> triangulated_3cameras = triangulatePoint3(poses, measurements, K);
 | 
			
		||||
  EXPECT(assert_equal(landmark, *triangulated_3cameras, 1e-2));
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  // 4. Test failure: Add a 4th camera facing the wrong way
 | 
			
		||||
  Pose3 level_pose180 = Pose3(Rot3::ypr(M_PI/2, 0., -M_PI/2), Point3(0,0,1));
 | 
			
		||||
  SimpleCamera camera_180(level_pose180, K);
 | 
			
		||||
 | 
			
		||||
  CHECK_EXCEPTION(camera_180.project(landmark);, CheiralityException);
 | 
			
		||||
 | 
			
		||||
  poses += level_pose180;
 | 
			
		||||
  measurements += Point2(400,400);
 | 
			
		||||
 | 
			
		||||
  boost::optional<Point3> triangulated_4cameras = triangulatePoint3(poses, measurements, K);
 | 
			
		||||
  EXPECT(boost::none == triangulated_4cameras);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ************************************************************************* */
 | 
			
		||||
int main() { TestResult tr; return TestRegistry::runAllTests(tr);}
 | 
			
		||||
/* ************************************************************************* */
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,81 @@
 | 
			
		|||
/* ----------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
 * 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 triangulation.cpp
 | 
			
		||||
 * @brief Functions for triangulation
 | 
			
		||||
 * @author Chris Beall
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <gtsam_unstable/geometry/triangulation.h>
 | 
			
		||||
#include <gtsam/geometry/SimpleCamera.h>
 | 
			
		||||
#include <boost/foreach.hpp>
 | 
			
		||||
#include <boost/assign.hpp>
 | 
			
		||||
#include <boost/assign/std/vector.hpp>
 | 
			
		||||
 | 
			
		||||
using namespace std;
 | 
			
		||||
using namespace boost::assign;
 | 
			
		||||
 | 
			
		||||
namespace gtsam {
 | 
			
		||||
 | 
			
		||||
/* ************************************************************************* */
 | 
			
		||||
// See Hartley and Zisserman, 2nd Ed., page 312
 | 
			
		||||
Point3 triangulateDLT(const vector<Matrix>& projection_matrices,
 | 
			
		||||
    const vector<Point2>& measurements) {
 | 
			
		||||
 | 
			
		||||
  Matrix A = Matrix_(projection_matrices.size() *2, 4);
 | 
			
		||||
 | 
			
		||||
  for(size_t i=0; i< projection_matrices.size(); i++) {
 | 
			
		||||
    size_t row = i*2;
 | 
			
		||||
    const Matrix& projection = projection_matrices.at(i);
 | 
			
		||||
    const Point2& p = measurements.at(i);
 | 
			
		||||
 | 
			
		||||
    // build system of equations
 | 
			
		||||
    A.row(row) = p.x() * projection.row(2) - projection.row(0);
 | 
			
		||||
    A.row(row+1) = p.y() * projection.row(2) - projection.row(1);
 | 
			
		||||
  }
 | 
			
		||||
  int rank;
 | 
			
		||||
  double error;
 | 
			
		||||
  Vector v;
 | 
			
		||||
  boost::tie(rank, error, v) = DLT(A);
 | 
			
		||||
  return Point3(sub( (v / v(3)),0,3));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ************************************************************************* */
 | 
			
		||||
boost::optional<Point3> triangulatePoint3(const vector<Pose3>& poses,
 | 
			
		||||
    const vector<Point2>& measurements, const Cal3_S2& K) {
 | 
			
		||||
 | 
			
		||||
  assert(poses.size() == measurements.size());
 | 
			
		||||
 | 
			
		||||
  if(poses.size() < 2)
 | 
			
		||||
    return boost::none;
 | 
			
		||||
 | 
			
		||||
  vector<Matrix> projection_matrices;
 | 
			
		||||
 | 
			
		||||
  // construct projection matrices from poses & calibration
 | 
			
		||||
  BOOST_FOREACH(const Pose3& pose, poses)
 | 
			
		||||
    projection_matrices += K.matrix() * sub(pose.inverse().matrix(),0,3,0,4);
 | 
			
		||||
 | 
			
		||||
  Point3 triangulated_point = triangulateDLT(projection_matrices, measurements);
 | 
			
		||||
 | 
			
		||||
  // verify that the triangulated point lies infront of all cameras
 | 
			
		||||
  BOOST_FOREACH(const Pose3& pose, poses) {
 | 
			
		||||
    const Point3& p_local = pose.transform_to(triangulated_point);
 | 
			
		||||
    if(p_local.z() <= 0)
 | 
			
		||||
      return boost::none;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return triangulated_point;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* ************************************************************************* */
 | 
			
		||||
 | 
			
		||||
} // namespace gtsam
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,45 @@
 | 
			
		|||
/* ----------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
 * 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 triangulation.h
 | 
			
		||||
 * @brief Functions for triangulation
 | 
			
		||||
 * @date July 31, 2013
 | 
			
		||||
 * @author Chris Beall
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <gtsam/geometry/Pose3.h>
 | 
			
		||||
#include <gtsam/geometry/Point2.h>
 | 
			
		||||
#include <gtsam/geometry/Cal3_S2.h>
 | 
			
		||||
#include <gtsam_unstable/base/dllexport.h>
 | 
			
		||||
 | 
			
		||||
namespace gtsam {
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Function to triangulate 3D landmark point from an arbitrary number
 | 
			
		||||
 * of poses (at least 2) using the DLT. The function checks that the
 | 
			
		||||
 * resulting point lies in front of all cameras, but has no other checks
 | 
			
		||||
 * to verify the quality of the triangulation.
 | 
			
		||||
 * @param poses A vector of camera poses
 | 
			
		||||
 * @param measurements A vector of camera measurements
 | 
			
		||||
 * @param K The camera calibration
 | 
			
		||||
 * @return Returns a Point3 on success, boost::none otherwise.
 | 
			
		||||
 */
 | 
			
		||||
GTSAM_UNSTABLE_EXPORT boost::optional<Point3> triangulatePoint3(const std::vector<Pose3>& poses,
 | 
			
		||||
    const std::vector<Point2>& measurements, const Cal3_S2& K);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
} // \namespace gtsam
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,228 @@
 | 
			
		|||
/* ----------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
 * 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 ProjectionFactor.h
 | 
			
		||||
 * @brief Basic bearing factor from 2D measurement
 | 
			
		||||
 * @author Chris Beall
 | 
			
		||||
 * @author Richard Roberts
 | 
			
		||||
 * @author Frank Dellaert
 | 
			
		||||
 * @author Alex Cunningham
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <gtsam/nonlinear/NonlinearFactor.h>
 | 
			
		||||
#include <gtsam/geometry/SimpleCamera.h>
 | 
			
		||||
#include <boost/optional.hpp>
 | 
			
		||||
 | 
			
		||||
namespace gtsam {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Non-linear factor for a constraint derived from a 2D measurement. The calibration is known here.
 | 
			
		||||
   * i.e. the main building block for visual SLAM.
 | 
			
		||||
   * @addtogroup SLAM
 | 
			
		||||
   */
 | 
			
		||||
  template<class POSE, class LANDMARK, class CALIBRATION = Cal3_S2>
 | 
			
		||||
  class MultiProjectionFactor: public NoiseModelFactor {
 | 
			
		||||
  protected:
 | 
			
		||||
 | 
			
		||||
    // Keep a copy of measurement and calibration for I/O
 | 
			
		||||
    Vector measured_;                    ///< 2D measurement for each of the n views
 | 
			
		||||
    boost::shared_ptr<CALIBRATION> K_;  ///< shared pointer to calibration object
 | 
			
		||||
    boost::optional<POSE> body_P_sensor_; ///< The pose of the sensor in the body frame
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // verbosity handling for Cheirality Exceptions
 | 
			
		||||
    bool throwCheirality_; ///< If true, rethrows Cheirality exceptions (default: false)
 | 
			
		||||
    bool verboseCheirality_; ///< If true, prints text for Cheirality exceptions (default: false)
 | 
			
		||||
 | 
			
		||||
  public:
 | 
			
		||||
 | 
			
		||||
    /// shorthand for base class type
 | 
			
		||||
    typedef NoiseModelFactor Base;
 | 
			
		||||
 | 
			
		||||
    /// shorthand for this class
 | 
			
		||||
    typedef MultiProjectionFactor<POSE, LANDMARK, CALIBRATION> This;
 | 
			
		||||
 | 
			
		||||
    /// shorthand for a smart pointer to a factor
 | 
			
		||||
    typedef boost::shared_ptr<This> shared_ptr;
 | 
			
		||||
 | 
			
		||||
    /// Default constructor
 | 
			
		||||
    MultiProjectionFactor() : throwCheirality_(false), verboseCheirality_(false) {}
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Constructor
 | 
			
		||||
     * TODO: Mark argument order standard (keys, measurement, parameters)
 | 
			
		||||
     * @param measured is the 2n dimensional location of the n points in the n views (the measurements)
 | 
			
		||||
     * @param model is the standard deviation (current version assumes that the uncertainty is the same for all views)
 | 
			
		||||
     * @param poseKeys is the set of indices corresponding to the cameras observing the same landmark
 | 
			
		||||
     * @param pointKey is the index of the landmark
 | 
			
		||||
     * @param K shared pointer to the constant calibration
 | 
			
		||||
     * @param body_P_sensor is the transform from body to sensor frame (default identity)
 | 
			
		||||
     */
 | 
			
		||||
    MultiProjectionFactor(const Vector& measured, const SharedNoiseModel& model,
 | 
			
		||||
        FastSet<Key> poseKeys, Key pointKey, const boost::shared_ptr<CALIBRATION>& K,
 | 
			
		||||
        boost::optional<POSE> body_P_sensor = boost::none) :
 | 
			
		||||
          Base(model), measured_(measured), K_(K), body_P_sensor_(body_P_sensor),
 | 
			
		||||
          throwCheirality_(false), verboseCheirality_(false) {
 | 
			
		||||
      keys_.assign(poseKeys.begin(), poseKeys.end());
 | 
			
		||||
      keys_.push_back(pointKey);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Constructor with exception-handling flags
 | 
			
		||||
     * TODO: Mark argument order standard (keys, measurement, parameters)
 | 
			
		||||
     * @param measured is the 2 dimensional location of point in image (the measurement)
 | 
			
		||||
     * @param model is the standard deviation
 | 
			
		||||
     * @param poseKey is the index of the camera
 | 
			
		||||
     * @param pointKey is the index of the landmark
 | 
			
		||||
     * @param K shared pointer to the constant calibration
 | 
			
		||||
     * @param throwCheirality determines whether Cheirality exceptions are rethrown
 | 
			
		||||
     * @param verboseCheirality determines whether exceptions are printed for Cheirality
 | 
			
		||||
     * @param body_P_sensor is the transform from body to sensor frame  (default identity)
 | 
			
		||||
     */
 | 
			
		||||
    MultiProjectionFactor(const Vector& measured, const SharedNoiseModel& model,
 | 
			
		||||
        FastSet<Key> poseKeys, Key pointKey, const boost::shared_ptr<CALIBRATION>& K,
 | 
			
		||||
        bool throwCheirality, bool verboseCheirality,
 | 
			
		||||
        boost::optional<POSE> body_P_sensor = boost::none) :
 | 
			
		||||
          Base(model), measured_(measured), K_(K), body_P_sensor_(body_P_sensor),
 | 
			
		||||
          throwCheirality_(throwCheirality), verboseCheirality_(verboseCheirality) {}
 | 
			
		||||
 | 
			
		||||
    /** Virtual destructor */
 | 
			
		||||
    virtual ~MultiProjectionFactor() {}
 | 
			
		||||
 | 
			
		||||
    /// @return a deep copy of this factor
 | 
			
		||||
    virtual gtsam::NonlinearFactor::shared_ptr clone() const {
 | 
			
		||||
      return boost::static_pointer_cast<gtsam::NonlinearFactor>(
 | 
			
		||||
          gtsam::NonlinearFactor::shared_ptr(new This(*this))); }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * print
 | 
			
		||||
     * @param s optional string naming the factor
 | 
			
		||||
     * @param keyFormatter optional formatter useful for printing Symbols
 | 
			
		||||
     */
 | 
			
		||||
    void print(const std::string& s = "", const KeyFormatter& keyFormatter = DefaultKeyFormatter) const {
 | 
			
		||||
      std::cout << s << "MultiProjectionFactor, z = ";
 | 
			
		||||
      std::cout << measured_ << "measurements, z = ";
 | 
			
		||||
      if(this->body_P_sensor_)
 | 
			
		||||
        this->body_P_sensor_->print("  sensor pose in body frame: ");
 | 
			
		||||
      Base::print("", keyFormatter);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// equals
 | 
			
		||||
    virtual bool equals(const NonlinearFactor& p, double tol = 1e-9) const {
 | 
			
		||||
      const This *e = dynamic_cast<const This*>(&p);
 | 
			
		||||
      return e
 | 
			
		||||
          && Base::equals(p, tol)
 | 
			
		||||
          //&& this->measured_.equals(e->measured_, tol)
 | 
			
		||||
          && this->K_->equals(*e->K_, tol)
 | 
			
		||||
          && ((!body_P_sensor_ && !e->body_P_sensor_) || (body_P_sensor_ && e->body_P_sensor_ && body_P_sensor_->equals(*e->body_P_sensor_)));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Evaluate error h(x)-z and optionally derivatives
 | 
			
		||||
    Vector unwhitenedError(const Values& x, boost::optional<std::vector<Matrix>&> H = boost::none) const{
 | 
			
		||||
 | 
			
		||||
      Vector a;
 | 
			
		||||
      return a;
 | 
			
		||||
 | 
			
		||||
//      Point3 point = x.at<Point3>(*keys_.end());
 | 
			
		||||
//
 | 
			
		||||
//      std::vector<KeyType>::iterator vit;
 | 
			
		||||
//      for (vit = keys_.begin(); vit != keys_.end()-1; vit++) {
 | 
			
		||||
//        Key key = (*vit);
 | 
			
		||||
//        Pose3 pose = x.at<Pose3>(key);
 | 
			
		||||
//
 | 
			
		||||
//        if(body_P_sensor_) {
 | 
			
		||||
//          if(H1) {
 | 
			
		||||
//            gtsam::Matrix H0;
 | 
			
		||||
//            PinholeCamera<CALIBRATION> camera(pose.compose(*body_P_sensor_, H0), *K_);
 | 
			
		||||
//            Point2 reprojectionError(camera.project(point, H1, H2) - measured_);
 | 
			
		||||
//            *H1 = *H1 * H0;
 | 
			
		||||
//            return reprojectionError.vector();
 | 
			
		||||
//          } else {
 | 
			
		||||
//            PinholeCamera<CALIBRATION> camera(pose.compose(*body_P_sensor_), *K_);
 | 
			
		||||
//            Point2 reprojectionError(camera.project(point, H1, H2) - measured_);
 | 
			
		||||
//            return reprojectionError.vector();
 | 
			
		||||
//          }
 | 
			
		||||
//        } else {
 | 
			
		||||
//          PinholeCamera<CALIBRATION> camera(pose, *K_);
 | 
			
		||||
//          Point2 reprojectionError(camera.project(point, H1, H2) - measured_);
 | 
			
		||||
//          return reprojectionError.vector();
 | 
			
		||||
//        }
 | 
			
		||||
//      }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    Vector evaluateError(const Pose3& pose, const Point3& point,
 | 
			
		||||
        boost::optional<Matrix&> H1 = boost::none, boost::optional<Matrix&> H2 = boost::none) const {
 | 
			
		||||
      try {
 | 
			
		||||
        if(body_P_sensor_) {
 | 
			
		||||
          if(H1) {
 | 
			
		||||
            gtsam::Matrix H0;
 | 
			
		||||
            PinholeCamera<CALIBRATION> camera(pose.compose(*body_P_sensor_, H0), *K_);
 | 
			
		||||
            Point2 reprojectionError(camera.project(point, H1, H2) - measured_);
 | 
			
		||||
            *H1 = *H1 * H0;
 | 
			
		||||
            return reprojectionError.vector();
 | 
			
		||||
          } else {
 | 
			
		||||
            PinholeCamera<CALIBRATION> camera(pose.compose(*body_P_sensor_), *K_);
 | 
			
		||||
            Point2 reprojectionError(camera.project(point, H1, H2) - measured_);
 | 
			
		||||
            return reprojectionError.vector();
 | 
			
		||||
          }
 | 
			
		||||
        } else {
 | 
			
		||||
          PinholeCamera<CALIBRATION> camera(pose, *K_);
 | 
			
		||||
          Point2 reprojectionError(camera.project(point, H1, H2) - measured_);
 | 
			
		||||
          return reprojectionError.vector();
 | 
			
		||||
        }
 | 
			
		||||
      } catch( CheiralityException& e) {
 | 
			
		||||
        if (H1) *H1 = zeros(2,6);
 | 
			
		||||
        if (H2) *H2 = zeros(2,3);
 | 
			
		||||
        if (verboseCheirality_)
 | 
			
		||||
          std::cout << e.what() << ": Landmark "<< DefaultKeyFormatter(this->key2()) <<
 | 
			
		||||
              " moved behind camera " << DefaultKeyFormatter(this->key1()) << std::endl;
 | 
			
		||||
        if (throwCheirality_)
 | 
			
		||||
          throw e;
 | 
			
		||||
      }
 | 
			
		||||
      return ones(2) * 2.0 * K_->fx();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** return the measurements */
 | 
			
		||||
    const Vector& measured() const {
 | 
			
		||||
      return measured_;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** return the calibration object */
 | 
			
		||||
    inline const boost::shared_ptr<CALIBRATION> calibration() const {
 | 
			
		||||
      return K_;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** return verbosity */
 | 
			
		||||
    inline bool verboseCheirality() const { return verboseCheirality_; }
 | 
			
		||||
 | 
			
		||||
    /** return flag for throwing cheirality exceptions */
 | 
			
		||||
    inline bool throwCheirality() const { return throwCheirality_; }
 | 
			
		||||
 | 
			
		||||
  private:
 | 
			
		||||
 | 
			
		||||
    /// Serialization function
 | 
			
		||||
    friend class boost::serialization::access;
 | 
			
		||||
    template<class ARCHIVE>
 | 
			
		||||
    void serialize(ARCHIVE & ar, const unsigned int version) {
 | 
			
		||||
      ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(Base);
 | 
			
		||||
      ar & BOOST_SERIALIZATION_NVP(measured_);
 | 
			
		||||
      ar & BOOST_SERIALIZATION_NVP(K_);
 | 
			
		||||
      ar & BOOST_SERIALIZATION_NVP(body_P_sensor_);
 | 
			
		||||
      ar & BOOST_SERIALIZATION_NVP(throwCheirality_);
 | 
			
		||||
      ar & BOOST_SERIALIZATION_NVP(verboseCheirality_);
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
} // \ namespace gtsam
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,235 @@
 | 
			
		|||
/* ----------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
 * 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  testProjectionFactor.cpp
 | 
			
		||||
 *  @brief Unit tests for ProjectionFactor Class
 | 
			
		||||
 *  @author Frank Dellaert
 | 
			
		||||
 *  @date Nov 2009
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <gtsam_unstable/nonlinear/ConcurrentBatchFilter.h>
 | 
			
		||||
#include <gtsam/slam/PriorFactor.h>
 | 
			
		||||
#include <gtsam/slam/BetweenFactor.h>
 | 
			
		||||
#include <gtsam/slam/ProjectionFactor.h>
 | 
			
		||||
#include <gtsam_unstable/slam/MultiProjectionFactor.h>
 | 
			
		||||
#include <gtsam/nonlinear/ISAM2.h>
 | 
			
		||||
#include <gtsam/nonlinear/LevenbergMarquardtOptimizer.h>
 | 
			
		||||
#include <gtsam/nonlinear/NonlinearFactorGraph.h>
 | 
			
		||||
#include <gtsam/nonlinear/LinearContainerFactor.h>
 | 
			
		||||
#include <gtsam/nonlinear/Ordering.h>
 | 
			
		||||
#include <gtsam/nonlinear/Values.h>
 | 
			
		||||
#include <gtsam/nonlinear/Symbol.h>
 | 
			
		||||
#include <gtsam/nonlinear/Key.h>
 | 
			
		||||
#include <gtsam/linear/GaussianSequentialSolver.h>
 | 
			
		||||
#include <gtsam/inference/JunctionTree.h>
 | 
			
		||||
#include <gtsam/geometry/Pose3.h>
 | 
			
		||||
#include <gtsam/geometry/Point3.h>
 | 
			
		||||
#include <gtsam/geometry/Point2.h>
 | 
			
		||||
#include <gtsam/geometry/Cal3DS2.h>
 | 
			
		||||
#include <gtsam/geometry/Cal3_S2.h>
 | 
			
		||||
#include <CppUnitLite/TestHarness.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
using namespace std;
 | 
			
		||||
using namespace gtsam;
 | 
			
		||||
 | 
			
		||||
// make a realistic calibration matrix
 | 
			
		||||
static double fov = 60; // degrees
 | 
			
		||||
static size_t w=640,h=480;
 | 
			
		||||
static Cal3_S2::shared_ptr K(new Cal3_S2(fov,w,h));
 | 
			
		||||
 | 
			
		||||
// Create a noise model for the pixel error
 | 
			
		||||
static SharedNoiseModel model(noiseModel::Unit::Create(2));
 | 
			
		||||
 | 
			
		||||
// Convenience for named keys
 | 
			
		||||
//using symbol_shorthand::X;
 | 
			
		||||
//using symbol_shorthand::L;
 | 
			
		||||
 | 
			
		||||
//typedef GenericProjectionFactor<Pose3, Point3> TestProjectionFactor;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
///* ************************************************************************* */
 | 
			
		||||
TEST( MultiProjectionFactor, create ){
 | 
			
		||||
  Values theta;
 | 
			
		||||
  NonlinearFactorGraph graph;
 | 
			
		||||
 | 
			
		||||
  Symbol x1('X',  1);
 | 
			
		||||
  Symbol x2('X',  2);
 | 
			
		||||
  Symbol x3('X',  3);
 | 
			
		||||
 | 
			
		||||
  Symbol l1('l',  1);
 | 
			
		||||
  Vector n_measPixel(6); // Pixel measurements from 3 cameras observing landmark 1
 | 
			
		||||
  n_measPixel << 10, 10, 10, 10, 10, 10;
 | 
			
		||||
  const SharedDiagonal noiseProjection = noiseModel::Isotropic::Sigma(2, 1);
 | 
			
		||||
 | 
			
		||||
  FastSet<Key> views;
 | 
			
		||||
  views.insert(x1);
 | 
			
		||||
  views.insert(x2);
 | 
			
		||||
  views.insert(x3);
 | 
			
		||||
 | 
			
		||||
  MultiProjectionFactor<Pose3, Point3> mpFactor(n_measPixel, noiseProjection, views, l1, K);
 | 
			
		||||
  graph.add(mpFactor);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
///* ************************************************************************* */
 | 
			
		||||
//TEST( ProjectionFactor, nonStandard ) {
 | 
			
		||||
//  GenericProjectionFactor<Pose3, Point3, Cal3DS2> f;
 | 
			
		||||
//}
 | 
			
		||||
//
 | 
			
		||||
///* ************************************************************************* */
 | 
			
		||||
//TEST( ProjectionFactor, Constructor) {
 | 
			
		||||
//  Key poseKey(X(1));
 | 
			
		||||
//  Key pointKey(L(1));
 | 
			
		||||
//
 | 
			
		||||
//  Point2 measurement(323.0, 240.0);
 | 
			
		||||
//
 | 
			
		||||
//  TestProjectionFactor factor(measurement, model, poseKey, pointKey, K);
 | 
			
		||||
//}
 | 
			
		||||
//
 | 
			
		||||
///* ************************************************************************* */
 | 
			
		||||
//TEST( ProjectionFactor, ConstructorWithTransform) {
 | 
			
		||||
//  Key poseKey(X(1));
 | 
			
		||||
//  Key pointKey(L(1));
 | 
			
		||||
//
 | 
			
		||||
//  Point2 measurement(323.0, 240.0);
 | 
			
		||||
//  Pose3 body_P_sensor(Rot3::RzRyRx(-M_PI_2, 0.0, -M_PI_2), Point3(0.25, -0.10, 1.0));
 | 
			
		||||
//
 | 
			
		||||
//  TestProjectionFactor factor(measurement, model, poseKey, pointKey, K, body_P_sensor);
 | 
			
		||||
//}
 | 
			
		||||
//
 | 
			
		||||
///* ************************************************************************* */
 | 
			
		||||
//TEST( ProjectionFactor, Equals ) {
 | 
			
		||||
//  // Create two identical factors and make sure they're equal
 | 
			
		||||
//  Point2 measurement(323.0, 240.0);
 | 
			
		||||
//
 | 
			
		||||
//  TestProjectionFactor factor1(measurement, model, X(1), L(1), K);
 | 
			
		||||
//  TestProjectionFactor factor2(measurement, model, X(1), L(1), K);
 | 
			
		||||
//
 | 
			
		||||
//  CHECK(assert_equal(factor1, factor2));
 | 
			
		||||
//}
 | 
			
		||||
//
 | 
			
		||||
///* ************************************************************************* */
 | 
			
		||||
//TEST( ProjectionFactor, EqualsWithTransform ) {
 | 
			
		||||
//  // Create two identical factors and make sure they're equal
 | 
			
		||||
//  Point2 measurement(323.0, 240.0);
 | 
			
		||||
//  Pose3 body_P_sensor(Rot3::RzRyRx(-M_PI_2, 0.0, -M_PI_2), Point3(0.25, -0.10, 1.0));
 | 
			
		||||
//
 | 
			
		||||
//  TestProjectionFactor factor1(measurement, model, X(1), L(1), K, body_P_sensor);
 | 
			
		||||
//  TestProjectionFactor factor2(measurement, model, X(1), L(1), K, body_P_sensor);
 | 
			
		||||
//
 | 
			
		||||
//  CHECK(assert_equal(factor1, factor2));
 | 
			
		||||
//}
 | 
			
		||||
//
 | 
			
		||||
///* ************************************************************************* */
 | 
			
		||||
//TEST( ProjectionFactor, Error ) {
 | 
			
		||||
//  // Create the factor with a measurement that is 3 pixels off in x
 | 
			
		||||
//  Key poseKey(X(1));
 | 
			
		||||
//  Key pointKey(L(1));
 | 
			
		||||
//  Point2 measurement(323.0, 240.0);
 | 
			
		||||
//  TestProjectionFactor factor(measurement, model, poseKey, pointKey, K);
 | 
			
		||||
//
 | 
			
		||||
//  // Set the linearization point
 | 
			
		||||
//  Pose3 pose(Rot3(), Point3(0,0,-6));
 | 
			
		||||
//  Point3 point(0.0, 0.0, 0.0);
 | 
			
		||||
//
 | 
			
		||||
//  // Use the factor to calculate the error
 | 
			
		||||
//  Vector actualError(factor.evaluateError(pose, point));
 | 
			
		||||
//
 | 
			
		||||
//  // The expected error is (-3.0, 0.0) pixels / UnitCovariance
 | 
			
		||||
//  Vector expectedError = Vector_(2, -3.0, 0.0);
 | 
			
		||||
//
 | 
			
		||||
//  // Verify we get the expected error
 | 
			
		||||
//  CHECK(assert_equal(expectedError, actualError, 1e-9));
 | 
			
		||||
//}
 | 
			
		||||
//
 | 
			
		||||
///* ************************************************************************* */
 | 
			
		||||
//TEST( ProjectionFactor, ErrorWithTransform ) {
 | 
			
		||||
//  // Create the factor with a measurement that is 3 pixels off in x
 | 
			
		||||
//  Key poseKey(X(1));
 | 
			
		||||
//  Key pointKey(L(1));
 | 
			
		||||
//  Point2 measurement(323.0, 240.0);
 | 
			
		||||
//  Pose3 body_P_sensor(Rot3::RzRyRx(-M_PI_2, 0.0, -M_PI_2), Point3(0.25, -0.10, 1.0));
 | 
			
		||||
//  TestProjectionFactor factor(measurement, model, poseKey, pointKey, K, body_P_sensor);
 | 
			
		||||
//
 | 
			
		||||
//  // Set the linearization point. The vehicle pose has been selected to put the camera at (-6, 0, 0)
 | 
			
		||||
//  Pose3 pose(Rot3(), Point3(-6.25, 0.10 , -1.0));
 | 
			
		||||
//  Point3 point(0.0, 0.0, 0.0);
 | 
			
		||||
//
 | 
			
		||||
//  // Use the factor to calculate the error
 | 
			
		||||
//  Vector actualError(factor.evaluateError(pose, point));
 | 
			
		||||
//
 | 
			
		||||
//  // The expected error is (-3.0, 0.0) pixels / UnitCovariance
 | 
			
		||||
//  Vector expectedError = Vector_(2, -3.0, 0.0);
 | 
			
		||||
//
 | 
			
		||||
//  // Verify we get the expected error
 | 
			
		||||
//  CHECK(assert_equal(expectedError, actualError, 1e-9));
 | 
			
		||||
//}
 | 
			
		||||
//
 | 
			
		||||
///* ************************************************************************* */
 | 
			
		||||
//TEST( ProjectionFactor, Jacobian ) {
 | 
			
		||||
//  // Create the factor with a measurement that is 3 pixels off in x
 | 
			
		||||
//  Key poseKey(X(1));
 | 
			
		||||
//  Key pointKey(L(1));
 | 
			
		||||
//  Point2 measurement(323.0, 240.0);
 | 
			
		||||
//  TestProjectionFactor factor(measurement, model, poseKey, pointKey, K);
 | 
			
		||||
//
 | 
			
		||||
//  // Set the linearization point
 | 
			
		||||
//  Pose3 pose(Rot3(), Point3(0,0,-6));
 | 
			
		||||
//  Point3 point(0.0, 0.0, 0.0);
 | 
			
		||||
//
 | 
			
		||||
//  // Use the factor to calculate the Jacobians
 | 
			
		||||
//  Matrix H1Actual, H2Actual;
 | 
			
		||||
//  factor.evaluateError(pose, point, H1Actual, H2Actual);
 | 
			
		||||
//
 | 
			
		||||
//  // The expected Jacobians
 | 
			
		||||
//  Matrix H1Expected = Matrix_(2, 6, 0., -554.256, 0., -92.376, 0., 0., 554.256, 0., 0., 0., -92.376, 0.);
 | 
			
		||||
//  Matrix H2Expected = Matrix_(2, 3, 92.376, 0., 0., 0., 92.376, 0.);
 | 
			
		||||
//
 | 
			
		||||
//  // Verify the Jacobians are correct
 | 
			
		||||
//  CHECK(assert_equal(H1Expected, H1Actual, 1e-3));
 | 
			
		||||
//  CHECK(assert_equal(H2Expected, H2Actual, 1e-3));
 | 
			
		||||
//}
 | 
			
		||||
//
 | 
			
		||||
///* ************************************************************************* */
 | 
			
		||||
//TEST( ProjectionFactor, JacobianWithTransform ) {
 | 
			
		||||
//  // Create the factor with a measurement that is 3 pixels off in x
 | 
			
		||||
//  Key poseKey(X(1));
 | 
			
		||||
//  Key pointKey(L(1));
 | 
			
		||||
//  Point2 measurement(323.0, 240.0);
 | 
			
		||||
//  Pose3 body_P_sensor(Rot3::RzRyRx(-M_PI_2, 0.0, -M_PI_2), Point3(0.25, -0.10, 1.0));
 | 
			
		||||
//  TestProjectionFactor factor(measurement, model, poseKey, pointKey, K, body_P_sensor);
 | 
			
		||||
//
 | 
			
		||||
//  // Set the linearization point. The vehicle pose has been selected to put the camera at (-6, 0, 0)
 | 
			
		||||
//  Pose3 pose(Rot3(), Point3(-6.25, 0.10 , -1.0));
 | 
			
		||||
//  Point3 point(0.0, 0.0, 0.0);
 | 
			
		||||
//
 | 
			
		||||
//  // Use the factor to calculate the Jacobians
 | 
			
		||||
//  Matrix H1Actual, H2Actual;
 | 
			
		||||
//  factor.evaluateError(pose, point, H1Actual, H2Actual);
 | 
			
		||||
//
 | 
			
		||||
//  // The expected Jacobians
 | 
			
		||||
//  Matrix H1Expected = Matrix_(2, 6, -92.376, 0., 577.350, 0., 92.376, 0., -9.2376, -577.350, 0., 0., 0., 92.376);
 | 
			
		||||
//  Matrix H2Expected = Matrix_(2, 3, 0., -92.376, 0., 0., 0., -92.376);
 | 
			
		||||
//
 | 
			
		||||
//  // Verify the Jacobians are correct
 | 
			
		||||
//  CHECK(assert_equal(H1Expected, H1Actual, 1e-3));
 | 
			
		||||
//  CHECK(assert_equal(H2Expected, H2Actual, 1e-3));
 | 
			
		||||
//}
 | 
			
		||||
 | 
			
		||||
/* ************************************************************************* */
 | 
			
		||||
int main() { TestResult tr; return TestRegistry::runAllTests(tr); }
 | 
			
		||||
/* ************************************************************************* */
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,206 @@
 | 
			
		|||
%close all
 | 
			
		||||
%clc
 | 
			
		||||
 | 
			
		||||
import gtsam.*;
 | 
			
		||||
 | 
			
		||||
%% Read metadata and compute relative sensor pose transforms
 | 
			
		||||
IMU_metadata = importdata('KittiEquivBiasedImu_metadata.txt');
 | 
			
		||||
IMU_metadata = cell2struct(num2cell(IMU_metadata.data), IMU_metadata.colheaders, 2);
 | 
			
		||||
IMUinBody = Pose3.Expmap([
 | 
			
		||||
    IMU_metadata.BodyPtx; IMU_metadata.BodyPty; IMU_metadata.BodyPtz;
 | 
			
		||||
    IMU_metadata.BodyPrx; IMU_metadata.BodyPry; IMU_metadata.BodyPrz; ]);
 | 
			
		||||
 | 
			
		||||
VO_metadata = importdata('KittiRelativePose_metadata.txt');
 | 
			
		||||
VO_metadata = cell2struct(num2cell(VO_metadata.data), VO_metadata.colheaders, 2);
 | 
			
		||||
VOinBody = Pose3.Expmap([
 | 
			
		||||
    VO_metadata.BodyPtx; VO_metadata.BodyPty; VO_metadata.BodyPtz;
 | 
			
		||||
    VO_metadata.BodyPrx; VO_metadata.BodyPry; VO_metadata.BodyPrz; ]);
 | 
			
		||||
 | 
			
		||||
GPS_metadata = importdata('KittiGps_metadata.txt');
 | 
			
		||||
GPS_metadata = cell2struct(num2cell(GPS_metadata.data), GPS_metadata.colheaders, 2);
 | 
			
		||||
GPSinBody = Pose3.Expmap([
 | 
			
		||||
    GPS_metadata.BodyPtx; GPS_metadata.BodyPty; GPS_metadata.BodyPtz;
 | 
			
		||||
    GPS_metadata.BodyPrx; GPS_metadata.BodyPry; GPS_metadata.BodyPrz; ]);
 | 
			
		||||
 | 
			
		||||
VOinIMU = IMUinBody.inverse().compose(VOinBody);
 | 
			
		||||
GPSinIMU = IMUinBody.inverse().compose(GPSinBody);
 | 
			
		||||
 | 
			
		||||
%% Read data and change coordinate frame of GPS and VO measurements to IMU frame
 | 
			
		||||
IMU_data = importdata('KittiEquivBiasedImu.txt');
 | 
			
		||||
IMU_data = cell2struct(num2cell(IMU_data.data), IMU_data.colheaders, 2);
 | 
			
		||||
imum = cellfun(@(x) x', num2cell([ [IMU_data.accelX]' [IMU_data.accelY]' [IMU_data.accelZ]' [IMU_data.omegaX]' [IMU_data.omegaY]' [IMU_data.omegaZ]' ], 2), 'UniformOutput', false);
 | 
			
		||||
[IMU_data.acc_omega] = deal(imum{:});
 | 
			
		||||
IMU_data = rmfield(IMU_data, { 'accelX' 'accelY' 'accelZ' 'omegaX' 'omegaY' 'omegaZ' });
 | 
			
		||||
clear imum
 | 
			
		||||
 | 
			
		||||
VO_data = importdata('KittiRelativePose.txt');
 | 
			
		||||
VO_data = cell2struct(num2cell(VO_data.data), VO_data.colheaders, 2);
 | 
			
		||||
% Merge relative pose fields and convert to Pose3
 | 
			
		||||
logposes = [ [VO_data.dtx]' [VO_data.dty]' [VO_data.dtz]' [VO_data.drx]' [VO_data.dry]' [VO_data.drz]' ];
 | 
			
		||||
logposes = num2cell(logposes, 2);
 | 
			
		||||
relposes = arrayfun(@(x) {gtsam.Pose3.Expmap(x{1}')}, logposes);
 | 
			
		||||
% TODO: convert to IMU frame %relposes = arrayfun(
 | 
			
		||||
[VO_data.RelativePose] = deal(relposes{:});
 | 
			
		||||
VO_data = rmfield(VO_data, { 'dtx' 'dty' 'dtz' 'drx' 'dry' 'drz' });
 | 
			
		||||
clear logposes relposes
 | 
			
		||||
 | 
			
		||||
GPS_data = importdata('KittiGps.txt');
 | 
			
		||||
GPS_data = cell2struct(num2cell(GPS_data.data), GPS_data.colheaders, 2);
 | 
			
		||||
 | 
			
		||||
%%
 | 
			
		||||
SummaryTemplate = gtsam.ImuFactorPreintegratedMeasurements( ...
 | 
			
		||||
    gtsam.imuBias.ConstantBias([0;0;0], [0;0;0]), ...
 | 
			
		||||
    1e-3 * eye(3), 1e-3 * eye(3), 1e-3 * eye(3));
 | 
			
		||||
  
 | 
			
		||||
%% Set initial conditions for the estimated trajectory
 | 
			
		||||
disp('TODO: we have GPS so this initialization is not right')
 | 
			
		||||
currentPoseGlobal = Pose3; % initial pose is the reference frame (navigation frame)
 | 
			
		||||
currentVelocityGlobal = [0;0;0]; % the vehicle is stationary at the beginning
 | 
			
		||||
bias_acc = [0;0;0]; % we initialize accelerometer biases to zero
 | 
			
		||||
bias_omega = [0;0;0]; % we initialize gyro biases to zero
 | 
			
		||||
 | 
			
		||||
%% Solver object
 | 
			
		||||
isamParams = ISAM2Params;
 | 
			
		||||
isamParams.setRelinearizeSkip(1);
 | 
			
		||||
isam = gtsam.ISAM2(isamParams);
 | 
			
		||||
 | 
			
		||||
%% create nonlinear factor graph
 | 
			
		||||
factors = NonlinearFactorGraph;
 | 
			
		||||
values = Values;
 | 
			
		||||
 | 
			
		||||
%% Create prior on initial pose, velocity, and biases
 | 
			
		||||
sigma_init_x = 1.0
 | 
			
		||||
sigma_init_v = 1.0
 | 
			
		||||
sigma_init_b = 1.0
 | 
			
		||||
 | 
			
		||||
values.insert(symbol('x',0), currentPoseGlobal);
 | 
			
		||||
values.insert(symbol('v',0), LieVector(currentVelocityGlobal) );
 | 
			
		||||
values.insert(symbol('b',0), imuBias.ConstantBias(bias_acc,bias_omega) );
 | 
			
		||||
 | 
			
		||||
% Prior on initial pose
 | 
			
		||||
factors.add(PriorFactorPose3(symbol('x',0), ...
 | 
			
		||||
  currentPoseGlobal, noiseModel.Isotropic.Sigma(6, sigma_init_x)));
 | 
			
		||||
% Prior on initial velocity
 | 
			
		||||
factors.add(PriorFactorLieVector(symbol('v',0), ...
 | 
			
		||||
  LieVector(currentVelocityGlobal), noiseModel.Isotropic.Sigma(3, sigma_init_v)));
 | 
			
		||||
% Prior on initial bias
 | 
			
		||||
factors.add(PriorFactorConstantBias(symbol('b',0), ...
 | 
			
		||||
  imuBias.ConstantBias(bias_acc,bias_omega), noiseModel.Isotropic.Sigma(6, sigma_init_b)));
 | 
			
		||||
 | 
			
		||||
%% Main loop:
 | 
			
		||||
% (1) we read the measurements
 | 
			
		||||
% (2) we create the corresponding factors in the graph
 | 
			
		||||
% (3) we solve the graph to obtain and optimal estimate of robot trajectory
 | 
			
		||||
 | 
			
		||||
i = 2;
 | 
			
		||||
lastTime = 0;
 | 
			
		||||
lastIndex = 0;
 | 
			
		||||
currentSummarizedMeasurement = ImuFactorPreintegratedMeasurements(summaryTemplate);
 | 
			
		||||
 | 
			
		||||
times = sort([VO_data(:,1); GPS_data(:,1)]); % this are the time-stamps at which we want to initialize a new node in the graph
 | 
			
		||||
IMU_times = IMU_data(:,1);
 | 
			
		||||
 | 
			
		||||
disp('TODO: still needed to take care of the initial time')
 | 
			
		||||
 | 
			
		||||
for t = times
 | 
			
		||||
  % At each non=IMU measurement we initialize a new node in the graph
 | 
			
		||||
  currentIndex = find( times == t );
 | 
			
		||||
  currentPoseKey = symbol('x',currentIndex);
 | 
			
		||||
  currentVelKey = symbol('v',currentIndex);
 | 
			
		||||
  currentBiasKey = symbol('b',currentIndex);
 | 
			
		||||
  
 | 
			
		||||
  %% add preintegrated IMU factor between previous state and current state
 | 
			
		||||
  % =======================================================================
 | 
			
		||||
  IMUbetweenTimesIndices = find( IMU_times>+t_previous & IMU_times<= t);
 | 
			
		||||
  % all imu measurements occurred between t_previous and t
 | 
			
		||||
  
 | 
			
		||||
  % we assume that each row of the IMU.txt file has the following structure:
 | 
			
		||||
  % timestamp delta_t acc_x acc_y acc_z omega_x omega_y omega_z
 | 
			
		||||
  disp('TODO: We want don t want to preintegrate with zero bias, but to use the most recent estimate')
 | 
			
		||||
  currentSummarizedMeasurement = ImuFactorPreintegratedMeasurements(summaryTemplate);
 | 
			
		||||
  for i=1:length(IMUbetweenTimesIndices)
 | 
			
		||||
    index = IMUbetweenTimesIndices(i); % the row of the IMU_data matrix that we have to integrate
 | 
			
		||||
    deltaT = IMU_data(index,2);
 | 
			
		||||
    accMeas = IMU_data(index,3:5);
 | 
			
		||||
    omegaMeas = IMU_data(index,6:8);
 | 
			
		||||
    % Accumulate preintegrated measurement
 | 
			
		||||
    currentSummarizedMeasurement.integrateMeasurement(accMeas, omegaMeas, deltaT);
 | 
			
		||||
  end
 | 
			
		||||
  
 | 
			
		||||
  disp('TODO: is the imu noise right?')
 | 
			
		||||
  % Create IMU factor
 | 
			
		||||
  factors.add(ImuFactor( ...
 | 
			
		||||
    previousPoseKey, previousVelKey, ...
 | 
			
		||||
    currentPoseKey, currentVelKey, ...
 | 
			
		||||
    currentBiasKey, currentSummarizedMeasurement, g, cor_v, ...
 | 
			
		||||
    noiseModel.Isotropic.Sigma(9, 1e-6)));
 | 
			
		||||
  % =======================================================================
 | 
			
		||||
  
 | 
			
		||||
  
 | 
			
		||||
  %% add factor corresponding to GPS measurements (if available at the current time)
 | 
			
		||||
  % =======================================================================
 | 
			
		||||
  if isempty(  find(GPS_data(:,1) == t  ) ) == 0 % it is a GPS measurement
 | 
			
		||||
    if length( find(GPS_data(:,1)) ) > 1
 | 
			
		||||
      error('more GPS measurements at the same time stamp: it should be an error')
 | 
			
		||||
    end
 | 
			
		||||
    
 | 
			
		||||
    index = find(GPS_data(:,1) == t ); % the row of the IMU_data matrix that we have to integrate
 | 
			
		||||
    GPSmeas = GPS_data(index,2:4);
 | 
			
		||||
    
 | 
			
		||||
    noiseModelGPS = ???; % noiseModelGPS.Isotropic.Sigma(6, sigma_init_x))
 | 
			
		||||
    
 | 
			
		||||
    % add factor
 | 
			
		||||
    disp('TODO: is the GPS noise right?')
 | 
			
		||||
    factors.add(PriorFactor???(currentPoseKey, GPSmeas, noiseModelGPS) );
 | 
			
		||||
  end
 | 
			
		||||
  % =======================================================================
 | 
			
		||||
  
 | 
			
		||||
  
 | 
			
		||||
  %% add factor corresponding to VO measurements (if available at the current time)
 | 
			
		||||
  % =======================================================================
 | 
			
		||||
  if isempty(  find(VO_data(:,1) == t  )  )== 0 % it is a GPS measurement
 | 
			
		||||
    if length( find(VO_data(:,1)) ) > 1
 | 
			
		||||
      error('more VO measurements at the same time stamp: it should be an error')
 | 
			
		||||
    end
 | 
			
		||||
    
 | 
			
		||||
    index = find( VO_data(:,1) == t ); % the row of the IMU_data matrix that we have to integrate
 | 
			
		||||
    VOmeas_pos = VO_data(index,2:4)';
 | 
			
		||||
    VOmeas_ang = VO_data(index,5:7)';
 | 
			
		||||
    
 | 
			
		||||
    VOpose = Pose3( Rot3(VOmeas_ang) , Point3(VOmeas_pos) );
 | 
			
		||||
    noiseModelVO = ???; % noiseModelGPS.Isotropic.Sigma(6, sigma_init_x))
 | 
			
		||||
    
 | 
			
		||||
    % add factor
 | 
			
		||||
    disp('TODO: is the VO noise right?')
 | 
			
		||||
    factors.add(BetweenFactorPose3(lastVOPoseKey, currentPoseKey, VOpose, noiseModelVO));
 | 
			
		||||
    
 | 
			
		||||
    lastVOPoseKey = currentPoseKey;
 | 
			
		||||
  end
 | 
			
		||||
  % =======================================================================
 | 
			
		||||
  
 | 
			
		||||
  disp('TODO: add values')
 | 
			
		||||
  %     values.insert(, initialPose);
 | 
			
		||||
  %   values.insert(symbol('v',lastIndex+1), initialVel);
 | 
			
		||||
  
 | 
			
		||||
  %% Update solver
 | 
			
		||||
  % =======================================================================
 | 
			
		||||
  isam.update(factors, values);
 | 
			
		||||
  factors = NonlinearFactorGraph;
 | 
			
		||||
  values = Values;
 | 
			
		||||
  
 | 
			
		||||
  isam.calculateEstimate(currentPoseKey);
 | 
			
		||||
  %   M = isam.marginalCovariance(key_pose);
 | 
			
		||||
  % =======================================================================
 | 
			
		||||
  
 | 
			
		||||
  previousPoseKey = currentPoseKey;
 | 
			
		||||
  previousVelKey = currentVelKey;
 | 
			
		||||
  t_previous = t;
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
disp('TODO: display results')
 | 
			
		||||
% figure(1)
 | 
			
		||||
% hold on;
 | 
			
		||||
% plot(positions(1,:), positions(2,:), '-b');
 | 
			
		||||
% plot3DTrajectory(isam.calculateEstimate, 'g-');
 | 
			
		||||
% axis equal;
 | 
			
		||||
% legend('true trajectory', 'traj integrated in body', 'traj integrated in nav')
 | 
			
		||||
		Loading…
	
		Reference in New Issue