g2o.cpp
Go to the documentation of this file.
00001 // g2o - General Graph Optimization
00002 // Copyright (C) 2011 R. Kuemmerle, G. Grisetti, W. Burgard
00003 // 
00004 // g2o is free software: you can redistribute it and/or modify
00005 // it under the terms of the GNU Lesser General Public License as published
00006 // by the Free Software Foundation, either version 3 of the License, or
00007 // (at your option) any later version.
00008 // 
00009 // g2o is distributed in the hope that it will be useful,
00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 // GNU Lesser General Public License for more details.
00013 // 
00014 // You should have received a copy of the GNU Lesser General Public License
00015 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
00016 
00017 #include <signal.h>
00018 #include <iostream>
00019 #include <iomanip>
00020 #include <string>
00021 #include <fstream>
00022 #include <sstream>
00023 #include <algorithm>
00024 #include <cassert>
00025 
00026 #include "dl_wrapper.h"
00027 #include "output_helper.h"
00028 #include "g2o_common.h"
00029 
00030 #include "g2o/core/estimate_propagator.h"
00031 #include "g2o/core/graph_optimizer_sparse.h"
00032 #include "g2o/core/factory.h"
00033 #include "g2o/core/solver_factory.h"
00034 #include "g2o/core/hyper_dijkstra.h"
00035 
00036 #include "g2o/stuff/macros.h"
00037 #include "g2o/stuff/color_macros.h"
00038 #include "g2o/stuff/command_args.h"
00039 #include "g2o/stuff/filesys_tools.h"
00040 #include "g2o/stuff/string_tools.h"
00041 #include "g2o/stuff/timeutil.h"
00042 
00043 static bool hasToStop=false;
00044 
00045 using namespace std;
00046 using namespace g2o;
00047 
00048 // sort according to max id, dimension, min id
00049 struct IncrementalEdgesCompare {
00050   bool operator()(SparseOptimizer::Edge* const & e1, SparseOptimizer::Edge* const & e2)
00051   {
00052     const SparseOptimizer::Vertex* to1 = static_cast<const SparseOptimizer::Vertex*>(e1->vertices()[0]);
00053     const SparseOptimizer::Vertex* to2 = static_cast<const SparseOptimizer::Vertex*>(e2->vertices()[0]);
00054 
00055     int i11 = e1->vertices()[0]->id(), i12 = e1->vertices()[1]->id();
00056     if (i11 > i12){
00057       swap(i11, i12);
00058     }
00059     int i21 = e2->vertices()[0]->id(), i22 = e2->vertices()[1]->id();
00060     if (i21 > i22){
00061       swap(i21, i22);
00062     }
00063     if (i12 < i22)
00064       return true;
00065     if (i12 > i22)
00066       return false;
00067     if (to1->dimension() != to2->dimension()) { // push the odometry to be the first
00068       return to1->dimension() > to2->dimension();
00069     }
00070     return (i11<i21);
00071   }
00072 };
00073 
00074 SparseOptimizer::Method str2method(const std::string& strMethod_){
00075   string strMethod = strToLower(strMethod_);
00076   if (strMethod=="gauss") {
00077     cerr << "# Doing Gauss" << endl;
00078     return SparseOptimizer::GaussNewton;
00079   }
00080   if (strMethod=="levenberg") {
00081     cerr << "# Doing Levenberg-Marquardt" << endl;
00082     return SparseOptimizer::LevenbergMarquardt;
00083   }
00084   cerr << "# Unknown optimization method: " << strMethod << ", setting  default to Levenberg"  << endl;
00085   return SparseOptimizer::LevenbergMarquardt;
00086 }
00087 
00088 void sigquit_handler(int sig)
00089 {
00090   if (sig == SIGINT) {
00091     hasToStop = 1;
00092     static int cnt = 0;
00093     if (cnt++ == 2) {
00094       cerr << __PRETTY_FUNCTION__ << " forcing exit" << endl;
00095       exit(1);
00096     }
00097   }
00098 }
00099 
00100 int main(int argc, char** argv)
00101 {
00102   int maxIterations;
00103   bool verbose;
00104   string inputFilename;
00105   string gnudump;
00106   string outputfilename;
00107   string strMethod;
00108   string strSolver;
00109   string loadLookup;
00110   bool initialGuess;
00111   bool marginalize;
00112   bool listTypes;
00113   bool listSolvers;
00114   bool incremental;
00115   bool guiOut;
00116   int gaugeId;
00117   bool robustKernel;
00118   bool computeMarginals;
00119   double huberWidth;
00120   double lambdaInit;
00121   int updateGraphEachN = 10;
00122   string statsFile;
00123   string dummy;
00124   // command line parsing
00125   CommandArgs arg;
00126   arg.param("i", maxIterations, 5, "perform n iterations");
00127   arg.param("v", verbose, false, "verbose output of the optimization process");
00128   arg.param("guess", initialGuess, false, "initial guess based on spanning tree");
00129   arg.param("inc", incremental, false, "run incremetally");
00130   arg.param("update", updateGraphEachN, 10, "updates after x odometry nodes, (default: 10)");
00131   arg.param("guiout", guiOut, false, "gui output while running incrementally");
00132   arg.param("lambdaInit", lambdaInit, 0, "user specified lambda init for levenberg");
00133   arg.param("marginalize", marginalize, false, "on or off");
00134   arg.param("method", strMethod, "Gauss", "Gauss or Levenberg");
00135   arg.param("gnudump", gnudump, "", "dump to gnuplot data file");
00136   arg.param("robustKernel", robustKernel, false, "use robust error functions");
00137   arg.param("computeMarginals", computeMarginals, false, "computes the marginal covariances of something. FOR TESTING ONLY");
00138   arg.param("gaugeId", gaugeId, -1, "force the gauge");
00139   arg.param("huberWidth", huberWidth, -1., "width for the robust Huber Kernel (only if robustKernel)");
00140   arg.param("o", outputfilename, "", "output final version of the graph");
00141   arg.param("solver", strSolver, "var", "specify which solver to use underneat\n\t {var, fix3_2, fix6_3, fix_7_3}");
00142   arg.param("solverlib", dummy, "", "specify a solver library which will be loaded");
00143   arg.param("typeslib", dummy, "", "specify a types library which will be loaded");
00144   arg.param("stats", statsFile, "", "specify a file for the statistics");
00145   arg.param("listTypes", listTypes, false, "list the registered types");
00146   arg.param("listSolvers", listSolvers, false, "list the available solvers");
00147   arg.param("renameTypes", loadLookup, "", "create a lookup for loading types into other types,\n\t TAG_IN_FILE=INTERNAL_TAG_FOR_TYPE,TAG2=INTERNAL2\n\t e.g., VERTEX_CAM=VERTEX_SE3:EXPMAP");
00148   arg.paramLeftOver("graph-input", inputFilename, "", "graph file which will be processed", true);
00149 
00150   arg.parseArgs(argc, argv);
00151 
00152   // registering all the types from the libraries
00153   DlWrapper dlTypesWrapper;
00154   loadStandardTypes(dlTypesWrapper, argc, argv);
00155 
00156   // register all the solvers
00157   SolverFactory* solverFactory = SolverFactory::instance();
00158   DlWrapper dlSolverWrapper;
00159   loadStandardSolver(dlSolverWrapper, argc, argv);
00160   if (listSolvers)
00161     solverFactory->listSolvers(cerr);
00162 
00163   if (listTypes) {
00164     Factory::instance()->printRegisteredTypes(cout, true);
00165   }
00166 
00167   SparseOptimizer optimizer;
00168   optimizer.setVerbose(verbose);
00169   optimizer.setForceStopFlag(&hasToStop);
00170 
00171   // Loading the input data
00172   if (loadLookup.size() > 0) {
00173     optimizer.setRenamedTypesFromString(loadLookup);
00174   }
00175   if (inputFilename.size() == 0) {
00176     cerr << "No input data specified" << endl;
00177     return 0;
00178   } else if (inputFilename == "-") {
00179     cerr << "Read input from stdin" << endl;
00180     if (!optimizer.load(cin)) {
00181       cerr << "Error loading graph" << endl;
00182       return 2;
00183     }
00184   } else {
00185     cerr << "Read input from " << inputFilename << endl;
00186     ifstream ifs(inputFilename.c_str());
00187     if (!ifs) {
00188       cerr << "Failed to open file" << endl;
00189       return 1;
00190     }
00191     if (!optimizer.load(ifs)) {
00192       cerr << "Error loading graph" << endl;
00193       return 2;
00194     }
00195   }
00196   cerr << "Loaded " << optimizer.vertices().size() << " vertices" << endl;
00197   cerr << "Loaded " << optimizer.edges().size() << " edges" << endl;
00198 
00199   if (optimizer.vertices().size() == 0) {
00200     cerr << "Graph contains no vertices" << endl;
00201     return 1;
00202   }
00203 
00204   // allocating the desired solver + testing whether the solver is okay
00205   SolverProperty solverProperty;
00206   optimizer.setSolver(solverFactory->construct(strSolver, &optimizer, solverProperty));
00207   if (! optimizer.solver()) {
00208     cerr << "Error allocating solver. Allocating \"" << strSolver << "\" failed!" << endl;
00209     return 0;
00210   }
00211   set<int> vertexDimensions = optimizer.dimensions();
00212   if (! optimizer.isSolverSuitable(solverProperty, vertexDimensions)) {
00213     cerr << "The selected solver is not suitable for optimizing the given graph" << endl;
00214     return 3;
00215   }
00216   assert (optimizer.solver());
00217   optimizer.setMethod(str2method(strMethod));
00218   optimizer.setUserLambdaInit(lambdaInit);
00219 
00220   // check for vertices to fix to remove DoF
00221   bool gaugeFreedom = optimizer.gaugeFreedom();
00222   OptimizableGraph::Vertex* gauge=0;
00223   if (gaugeId>-1){
00224     HyperGraph::VertexIDMap::iterator gIt = optimizer.vertices().find(gaugeId);
00225     if (gIt!=optimizer.vertices().end())
00226       gauge = (OptimizableGraph::Vertex*)gIt->second;
00227     else
00228       gauge=optimizer.findGauge();
00229   } else {
00230       gauge=optimizer.findGauge();
00231   }
00232   if (gaugeFreedom) {
00233     if (! gauge) {
00234       cerr <<  "# cannot find a vertex to fix in this thing" << endl;
00235       return 2;
00236     } else {
00237       cerr << "# graph is fixed by node " << gauge->id() << endl;
00238       gauge->setFixed(true);
00239     }
00240   } else {
00241     cerr << "# graph is fixed by priors" << endl;
00242   }
00243 
00244   // if schur, we wanna marginalize the landmarks...
00245   if (marginalize || solverProperty.requiresMarginalize) {
00246     int maxDim = *vertexDimensions.rbegin();
00247     int minDim = *vertexDimensions.begin();
00248     if (maxDim != minDim) {
00249       cerr << "# Preparing Marginalization of the Landmarks ... ";
00250       for (HyperGraph::VertexIDMap::iterator it=optimizer.vertices().begin(); it!=optimizer.vertices().end(); it++){
00251         OptimizableGraph::Vertex* v=static_cast<OptimizableGraph::Vertex*>(it->second);
00252         if (v->dimension() != maxDim) {
00253           v->setMarginalized(true);
00254         }
00255       }
00256       cerr << "done." << endl;
00257     }
00258   }
00259 
00260   if (robustKernel) {
00261     cerr << "# Preparing robust error function ... ";
00262     for (SparseOptimizer::EdgeSet::iterator it = optimizer.edges().begin(); it != optimizer.edges().end(); ++it) {
00263       SparseOptimizer::Edge* e = dynamic_cast<SparseOptimizer::Edge*>(*it);
00264       e->setRobustKernel(true);
00265       if (huberWidth > 0)
00266         e->setHuberWidth(huberWidth);
00267     }
00268     cerr << "done." << endl;
00269   }
00270 
00271   // sanity check
00272   HyperDijkstra d(&optimizer);
00273   UniformCostFunction f;
00274   d.shortestPaths(gauge,&f);
00275   //cerr << PVAR(d.visited().size()) << endl;
00276 
00277   if (d.visited().size()!=optimizer.vertices().size()) {
00278     cerr << CL_RED("Warning: d.visited().size() != optimizer.vertices().size()") << endl;
00279     cerr << "visited: " << d.visited().size() << endl;
00280     cerr << "vertices: " << optimizer.vertices().size() << endl;
00281   }
00282 
00283   if (incremental) {
00284     int incIterations = maxIterations;
00285     int updateDisplayEveryN = updateGraphEachN;
00286     int maxDim = 0;
00287 
00288     cerr << "# incremental setttings" << endl;
00289     cerr << "#\t solve every " << updateGraphEachN << endl;
00290     cerr << "#\t iterations  " << incIterations << endl;
00291 
00292     SparseOptimizer::VertexIDMap vertices = optimizer.vertices();
00293     for (SparseOptimizer::VertexIDMap::const_iterator it = vertices.begin(); it != vertices.end(); ++it) {
00294       const SparseOptimizer::Vertex* v = static_cast<const SparseOptimizer::Vertex*>(it->second);
00295       maxDim = max(maxDim, v->dimension());
00296     }
00297 
00298     vector<SparseOptimizer::Edge*> edges;
00299     for (SparseOptimizer::EdgeSet::iterator it = optimizer.edges().begin(); it != optimizer.edges().end(); ++it) {
00300       SparseOptimizer::Edge* e = dynamic_cast<SparseOptimizer::Edge*>(*it);
00301       edges.push_back(e);
00302     }
00303     optimizer.edges().clear();
00304     optimizer.vertices().clear();
00305     optimizer.setVerbose(false);
00306 
00307     // sort the edges in a way that inserting them makes sense
00308     sort(edges.begin(), edges.end(), IncrementalEdgesCompare());
00309 
00310     double cumTime = 0.;
00311     int vertexCount=0;
00312     int lastOptimizedVertexCount = 0;
00313     int lastVisUpdateVertexCount = 0;
00314     bool addNextEdge=true;
00315     bool freshlyOptimized=false;
00316     bool firstRound = true;
00317     HyperGraph::VertexSet verticesAdded;
00318     HyperGraph::EdgeSet edgesAdded;
00319     int maxInGraph = -1;
00320     for (vector<SparseOptimizer::Edge*>::iterator it = edges.begin(); it != edges.end(); ++it) {
00321       SparseOptimizer::Edge* e = *it;
00322       bool optimize=false;
00323 
00324       if (addNextEdge && !optimizer.vertices().empty()){
00325         int idMax = max(e->vertices()[0]->id(), e->vertices()[1]->id());
00326         if (maxInGraph < idMax && ! freshlyOptimized){
00327           addNextEdge=false;
00328           optimize=true;
00329         } else {
00330           addNextEdge=true;
00331           optimize=false;
00332         }
00333       }
00334 
00335       int doInit = 0;
00336       SparseOptimizer::Vertex* v1 = optimizer.vertex(e->vertices()[0]->id());
00337       SparseOptimizer::Vertex* v2 = optimizer.vertex(e->vertices()[1]->id());
00338       if (! v1 && addNextEdge) {
00339         //cerr << " adding vertex " << it->id1 << endl;
00340         SparseOptimizer::Vertex* v = dynamic_cast<SparseOptimizer::Vertex*>(e->vertices()[0]);
00341         bool v1Added = optimizer.addVertex(v);
00342         maxInGraph = max(maxInGraph, v->id());
00343         //cerr << "adding" << v->id() << "(" << v->dimension() << ")" << endl;
00344         assert(v1Added);
00345         if (! v1Added)
00346           cerr << "Error adding vertex " << v->id() << endl;
00347         else
00348           verticesAdded.insert(v);
00349         doInit = 1;
00350         if (v->dimension() == maxDim)
00351           vertexCount++;
00352       }
00353 
00354       if (! v2 && addNextEdge) {
00355         SparseOptimizer::Vertex* v = dynamic_cast<SparseOptimizer::Vertex*>(e->vertices()[1]);
00356         //cerr << " adding vertex " << v->id() << endl;
00357         bool v2Added = optimizer.addVertex(v);
00358         maxInGraph = max(maxInGraph, v->id());
00359         //cerr << "adding" << v->id() << "(" << v->dimension() << ")" << endl;
00360         assert(v2Added);
00361         if (! v2Added)
00362           cerr << "Error adding vertex " << v->id() << endl;
00363         else
00364           verticesAdded.insert(v);
00365         doInit = 2;
00366         if (v->dimension() == maxDim)
00367           vertexCount++;
00368       }
00369 
00370       if (addNextEdge){
00371         //cerr << " adding edge " << e->vertices()[0]->id() <<  " " << e->vertices()[1]->id() << endl;
00372         if (! optimizer.addEdge(e)) {
00373           cerr << "Unable to add edge " << e->vertices()[0]->id() << " -> " << e->vertices()[1]->id() << endl;
00374         } else {
00375           edgesAdded.insert(e);
00376         }
00377 
00378         if (doInit) {
00379           OptimizableGraph::Vertex* from = static_cast<OptimizableGraph::Vertex*>(e->vertices()[0]);
00380           OptimizableGraph::Vertex* to   = static_cast<OptimizableGraph::Vertex*>(e->vertices()[1]);
00381           switch (doInit){
00382             case 1: // initialize v1 from v2
00383               {
00384                 HyperGraph::VertexSet toSet;
00385                 toSet.insert(to);
00386                 if (e->initialEstimatePossible(toSet, from) > 0.) {
00387                   //cerr << "init: " 
00388                     //<< to->id() << "(" << to->dimension() << ") -> " 
00389                     //<< from->id() << "(" << from->dimension() << ") " << endl;
00390                   e->initialEstimate(toSet, from);
00391                 }
00392                 break;
00393               }
00394             case 2: 
00395               {
00396                 HyperGraph::VertexSet fromSet;
00397                 fromSet.insert(from);
00398                 if (e->initialEstimatePossible(fromSet, to) > 0.) {
00399                   //cerr << "init: " 
00400                     //<< from->id() << "(" << from->dimension() << ") -> " 
00401                     //<< to->id() << "(" << to->dimension() << ") " << endl;
00402                   e->initialEstimate(fromSet, to);  
00403                 }
00404                 break;
00405               }
00406             default: cerr << "doInit wrong value\n"; 
00407           }
00408 
00409         }
00410 
00411       }
00412 
00413       freshlyOptimized=false;
00414       if (optimize){
00415         //cerr << "Optimize" << endl;
00416         if (vertexCount - lastOptimizedVertexCount >= updateGraphEachN) {
00417           if (firstRound) {
00418             if (!optimizer.initializeOptimization()){
00419               cerr << "initialization failed" << endl;
00420               return 0;
00421             }
00422           } else {
00423             if (! optimizer.updateInitialization(verticesAdded, edgesAdded)) {
00424               cerr << "updating initialization failed" << endl;
00425               return 0;
00426             }
00427           }
00428           verticesAdded.clear();
00429           edgesAdded.clear();
00430           double ts = get_time();
00431           int currentIt=optimizer.optimize(incIterations, !firstRound);
00432           double dts = get_time() - ts;
00433           cumTime += dts;
00434           firstRound = false;
00435           //optimizer->setOptimizationTime(cumTime);
00436           if (verbose) {
00437             double chi2 = optimizer.chi2();
00438             cerr << "nodes= " << optimizer.vertices().size() << "\t edges= " << optimizer.edges().size() << "\t chi2= " << chi2
00439               << "\t time= " << dts << "\t iterations= " << currentIt <<  "\t cumTime= " << cumTime << endl;
00440           }
00441           lastOptimizedVertexCount = vertexCount;
00442         }
00443 
00444         if (! verbose)
00445           cerr << ".";
00446         addNextEdge=true;
00447         freshlyOptimized=true;
00448         it--;
00449       }
00450 
00451       if (guiOut) {
00452         if (vertexCount - lastVisUpdateVertexCount >= updateDisplayEveryN && freshlyOptimized) {
00453           dumpEdges(cout, optimizer);
00454           lastVisUpdateVertexCount = vertexCount;
00455         }
00456       }
00457 
00458     } // for all edges
00459 
00460     if (! freshlyOptimized) {
00461       double ts = get_time();
00462       int currentIt=optimizer.optimize(incIterations, !firstRound);
00463       double dts = get_time() - ts;
00464       cumTime += dts;
00465       //optimizer->setOptimizationTime(cumTime);
00466       if (verbose) {
00467         double chi2 = optimizer.chi2();
00468         cerr << "nodes= " << optimizer.vertices().size() << "\t edges= " << optimizer.edges().size() << "\t chi2= " << chi2
00469           << "\t time= " << dts << "\t iterations= " << currentIt <<  "\t cumTime= " << cumTime << endl;
00470       }
00471 
00472     }
00473 
00474   } else {
00475 
00476     // BATCH optimization
00477 
00478     if (statsFile!=""){
00479       // allocate buffer for statistics;
00480       optimizer._statistics=new G2OBatchStatistics[maxIterations];
00481     }
00482     optimizer.initializeOptimization();
00483     optimizer.computeActiveErrors();
00484     cerr << "Initial chi2 = " << FIXED(optimizer.chi2()) << endl;
00485 
00486     if (initialGuess)
00487       optimizer.computeInitialGuess();
00488     signal(SIGINT, sigquit_handler);
00489     int i=optimizer.optimize(maxIterations);
00490     if (maxIterations > 0 && !i){
00491       cerr << "Cholesky failed, result might be invalid" << endl;
00492     } else if (computeMarginals){
00493       std::vector<std::pair<int, int> > blockIndices;
00494       for (size_t i=0; i<optimizer.activeVertices().size(); i++){
00495         OptimizableGraph::Vertex* v=optimizer.activeVertices()[i];
00496         if (v->tempIndex()>=0){
00497           blockIndices.push_back(make_pair(v->tempIndex(), v->tempIndex()));
00498         }
00499         if (v->tempIndex()>0){
00500           blockIndices.push_back(make_pair(v->tempIndex()-1, v->tempIndex()));
00501         }
00502       }
00503       SparseBlockMatrix<MatrixXd> spinv;
00504       if (optimizer.computeMarginals(spinv, blockIndices)) {
00505         for (size_t i=0; i<optimizer.activeVertices().size(); i++){
00506           OptimizableGraph::Vertex* v=optimizer.activeVertices()[i];
00507           cerr << "Vertex id:" << v->id() << endl;
00508           if (v->tempIndex()>=0){
00509             cerr << "inv block :" << v->tempIndex() << ", " << v->tempIndex()<< endl;
00510             cerr << *(spinv.block(v->tempIndex(), v->tempIndex()));
00511             cerr << endl;
00512           }
00513           if (v->tempIndex()>0){
00514             cerr << "inv block :" << v->tempIndex()-1 << ", " << v->tempIndex()<< endl;
00515             cerr << *(spinv.block(v->tempIndex()-1, v->tempIndex()));
00516             cerr << endl;
00517           }
00518         }
00519       }
00520     }
00521 
00522     if (statsFile!=""){
00523       cerr << "writing stats to file \"" << statsFile << "\" ... ";
00524       ofstream os(statsFile.c_str());
00525       for (int i=0; i<maxIterations; i++){
00526         os << optimizer._statistics[i] << endl;
00527       }
00528       cerr << "done." << endl;
00529     }
00530 
00531   }
00532 
00533   // saving again
00534   if (gnudump.size() > 0) {
00535     return saveGnuplot(gnudump, optimizer);
00536   }
00537 
00538   if (outputfilename.size() > 0) {
00539     if (outputfilename == "-") {
00540       cerr << "saving to stdout";
00541       optimizer.save(cout);
00542     } else {
00543       cerr << "saving " << outputfilename << " ... ";
00544       optimizer.save(outputfilename.c_str());
00545     }
00546     cerr << "done." << endl;
00547   }
00548 
00549   return 0;
00550 }


re_vision
Author(s): Dorian Galvez-Lopez
autogenerated on Sun Jan 5 2014 11:31:13