00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
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
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()) {
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
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
00153 DlWrapper dlTypesWrapper;
00154 loadStandardTypes(dlTypesWrapper, argc, argv);
00155
00156
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
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
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
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
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
00272 HyperDijkstra d(&optimizer);
00273 UniformCostFunction f;
00274 d.shortestPaths(gauge,&f);
00275
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
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
00340 SparseOptimizer::Vertex* v = dynamic_cast<SparseOptimizer::Vertex*>(e->vertices()[0]);
00341 bool v1Added = optimizer.addVertex(v);
00342 maxInGraph = max(maxInGraph, v->id());
00343
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
00357 bool v2Added = optimizer.addVertex(v);
00358 maxInGraph = max(maxInGraph, v->id());
00359
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
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:
00383 {
00384 HyperGraph::VertexSet toSet;
00385 toSet.insert(to);
00386 if (e->initialEstimatePossible(toSet, from) > 0.) {
00387
00388
00389
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
00400
00401
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
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
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 }
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
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
00477
00478 if (statsFile!=""){
00479
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
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 }