38#include "g2o/config.h"
82 if (i12 < i22)
return true;
83 if (i12 > i22)
return false;
85 return to1->dimension() > to2->dimension();
100int main(
int argc,
char** argv) {
104 string inputFilename;
106 string outputfilename;
107 string solverProperties;
111 bool initialGuessOdometry;
115 bool listRobustKernels;
120 bool computeMarginals;
121 bool printSolverProperties;
124 int maxIterationsWithGain;
126 int updateGraphEachN = 10;
131 std::vector<int> gaugeList;
133 arg.
param(
"i", maxIterations, 5,
134 "perform n iterations, if negative consider the gain");
135 arg.
param(
"gain", gain, 1e-6,
"the gain used to stop optimization");
136 arg.
param(
"ig", maxIterationsWithGain, std::numeric_limits<int>::max(),
137 "Maximum number of iterations with gain enabled");
138 arg.
param(
"v", verbose,
false,
"verbose output of the optimization process");
139 arg.
param(
"guess", initialGuess,
false,
140 "initial guess based on spanning tree");
141 arg.
param(
"guessOdometry", initialGuessOdometry,
false,
142 "initial guess based on odometry");
143 arg.
param(
"inc", incremental,
false,
"run incremetally");
144 arg.
param(
"update", updateGraphEachN, 10,
"updates after x odometry nodes");
145 arg.
param(
"guiout", guiOut,
false,
"gui output while running incrementally");
146 arg.
param(
"marginalize", marginalize,
false,
"on or off");
147 arg.
param(
"printSolverProperties", printSolverProperties,
false,
148 "print the properties of the solver");
149 arg.
param(
"solverProperties", solverProperties,
"",
150 "set the internal properties of a solver,\n\te.g., "
151 "initialLambda=0.0001,maxTrialsAfterFailure=2");
152 arg.
param(
"gnudump", gnudump,
"",
"dump to gnuplot data file");
153 arg.
param(
"robustKernel", robustKernel,
"",
"use this robust error function");
154 arg.
param(
"robustKernelWidth", huberWidth, -1.,
155 "width for the robust Kernel (only if robustKernel)");
156 arg.
param(
"computeMarginals", computeMarginals,
false,
157 "computes the marginal covariances of something. FOR TESTING ONLY");
158 arg.
param(
"gaugeId", gaugeId, -1,
"force the gauge");
159 arg.
param(
"o", outputfilename,
"",
"output final version of the graph");
160 arg.
param(
"solver", strSolver,
"gn_var",
161 "specify which solver to use underneat\n\t {gn_var, lm_fix3_2, "
162 "gn_fix6_3, lm_fix7_3}");
163#ifndef G2O_DISABLE_DYNAMIC_LOADING_OF_LIBRARIES
165 arg.
param(
"solverlib", dummy,
"",
166 "specify a solver library which will be loaded");
167 arg.
param(
"typeslib", dummy,
"",
168 "specify a types library which will be loaded");
170 arg.
param(
"stats", statsFile,
"",
"specify a file for the statistics");
171 arg.
param(
"listTypes", listTypes,
false,
"list the registered types");
172 arg.
param(
"listRobustKernels", listRobustKernels,
false,
173 "list the registered robust kernels");
174 arg.
param(
"listSolvers", listSolvers,
false,
"list the available solvers");
175 arg.
param(
"renameTypes", loadLookup,
"",
176 "create a lookup for loading types into other types,\n\t "
177 "TAG_IN_FILE=INTERNAL_TAG_FOR_TYPE,TAG2=INTERNAL2\n\t e.g., "
178 "VERTEX_CAM=VERTEX_SE3:EXPMAP");
179 arg.
param(
"gaugeList", gaugeList, std::vector<int>(),
180 "set the list of gauges separated by commas without spaces \n "
182 arg.
param(
"summary", summaryFile,
"",
183 "append a summary of this optimization run to the summary file "
184 "passed as argument");
186 "graph file which will be processed",
true);
187 arg.
param(
"nonSequential", nonSequential,
false,
188 "apply the robust kernel only on loop closures and not odometries");
193 cout <<
"# Used Compiler: " << G2O_CXX_COMPILER << endl;
196#ifndef G2O_DISABLE_DYNAMIC_LOADING_OF_LIBRARIES
204 if (verbose) cout <<
"# linked version of g2o" << endl;
217 if (listRobustKernels) {
218 std::vector<std::string> kernels;
220 cout <<
"Robust Kernels:" << endl;
221 for (
size_t i = 0; i < kernels.size(); ++i) {
222 cout << kernels[i] << endl;
230 if (maxIterations < 0) {
231 cerr <<
"# setup termination criterion based on the gain of the iteration"
233 maxIterations = maxIterationsWithGain;
244 if (!optimizer.
solver()) {
245 cerr <<
"Error allocating solver. Allocating \"" << strSolver
246 <<
"\" failed!" << endl;
250 if (solverProperties.size() > 0) {
254 cerr <<
"Failure while updating the solver properties from the given "
259 if (solverProperties.size() > 0 || printSolverProperties) {
264 if (loadLookup.size() > 0) {
267 if (inputFilename.size() == 0) {
268 cerr <<
"No input data specified" << endl;
270 }
else if (inputFilename ==
"-") {
271 cerr <<
"Read input from stdin" << endl;
272 if (!optimizer.
load(cin)) {
273 cerr <<
"Error loading graph" << endl;
277 cerr <<
"Read input from " << inputFilename << endl;
278 ifstream ifs(inputFilename.c_str());
280 cerr <<
"Failed to open file" << endl;
283 if (!optimizer.
load(ifs)) {
284 cerr <<
"Error loading graph" << endl;
288 cerr <<
"Loaded " << optimizer.
vertices().size() <<
" vertices" << endl;
289 cerr <<
"Loaded " << optimizer.
edges().size() <<
" edges" << endl;
291 if (optimizer.
vertices().size() == 0) {
292 cerr <<
"Graph contains no vertices" << endl;
296 set<int> vertexDimensions = optimizer.
dimensions();
298 cerr <<
"The selected solver is not suitable for optimizing the given graph"
302 assert(optimizer.
solver());
309 if (gaugeList.size()) {
310 cerr <<
"Fixing gauges: ";
311 for (
size_t i = 0; i < gaugeList.size(); i++) {
312 int id = gaugeList[i];
315 cerr <<
"fatal, not found the vertex of id " <<
id
316 <<
" in the gaugeList. Aborting";
319 if (i == 0) gauge = v;
320 cerr << v->
id() <<
" ";
325 gaugeFreedom =
false;
331 cerr <<
"# cannot find a vertex to fix in this thing" << endl;
334 cerr <<
"# graph is fixed by node " << gauge->
id() << endl;
338 cerr <<
"# graph is fixed by priors or already fixed vertex" << endl;
343 int maxDim = *vertexDimensions.rbegin();
344 int minDim = *vertexDimensions.begin();
345 if (maxDim != minDim) {
346 cerr <<
"# Preparing Marginalization of the Landmarks ... ";
347 for (HyperGraph::VertexIDMap::iterator it = optimizer.
vertices().begin();
348 it != optimizer.
vertices().end(); ++it) {
355 cerr <<
"done." << endl;
359 if (robustKernel.size() > 0) {
362 cerr <<
"# Preparing robust error function ... ";
365 for (SparseOptimizer::EdgeSet::iterator it = optimizer.
edges().begin();
366 it != optimizer.
edges().end(); ++it) {
370 e->setRobustKernel(creator->
construct());
371 if (huberWidth > 0) e->robustKernel()->setDelta(huberWidth);
375 for (SparseOptimizer::EdgeSet::iterator it = optimizer.
edges().begin();
376 it != optimizer.
edges().end(); ++it) {
378 e->setRobustKernel(creator->
construct());
379 if (huberWidth > 0) e->robustKernel()->setDelta(huberWidth);
382 cerr <<
"done." << endl;
384 cerr <<
"Unknown Robust Kernel: " << robustKernel << endl;
395 cerr <<
CL_RED(
"Warning: d.visited().size() != optimizer.vertices().size()")
397 cerr <<
"visited: " << d.
visited().size() << endl;
398 cerr <<
"vertices: " << optimizer.
vertices().size() << endl;
403 "# Note: this variant performs batch steps in each time step")
406 "# For a variant which updates the Cholesky factor use "
407 "the binary g2o_incremental")
409 int incIterations = maxIterations;
411 cerr <<
"# Setting default number of iterations" << endl;
414 int updateDisplayEveryN = updateGraphEachN;
417 cerr <<
"# incremental settings" << endl;
418 cerr <<
"#\t solve every " << updateGraphEachN << endl;
419 cerr <<
"#\t iterations " << incIterations << endl;
422 for (SparseOptimizer::VertexIDMap::const_iterator it = vertices.begin();
423 it != vertices.end(); ++it) {
426 maxDim = max(maxDim, v->dimension());
429 vector<SparseOptimizer::Edge*> edges;
430 for (SparseOptimizer::EdgeSet::iterator it = optimizer.
edges().begin();
431 it != optimizer.
edges().end(); ++it) {
435 optimizer.
edges().clear();
444 int lastOptimizedVertexCount = 0;
445 int lastVisUpdateVertexCount = 0;
446 bool freshlyOptimized =
false;
447 bool firstRound =
true;
450 for (vector<SparseOptimizer::Edge*>::iterator it = edges.begin();
451 it != edges.end(); ++it) {
465 cerr <<
"Error adding vertex " << v->
id() << endl;
467 verticesAdded.insert(v);
469 if (v->dimension() == maxDim) vertexCount++;
479 cerr <<
"Error adding vertex " << v->
id() << endl;
481 verticesAdded.insert(v);
483 if (v->dimension() == maxDim) vertexCount++;
491 cerr <<
"Unable to add edge " << e->
vertices()[0]->id() <<
" -> "
494 edgesAdded.insert(e);
507 if (e->initialEstimatePossible(toSet, from) > 0.) {
511 e->initialEstimate(toSet, from);
513 assert(0 &&
"Added uninitialized variable to the graph");
519 fromSet.insert(from);
520 if (e->initialEstimatePossible(fromSet, to) > 0.) {
524 e->initialEstimate(fromSet, to);
526 assert(0 &&
"Added uninitialized variable to the graph");
531 cerr <<
"doInit wrong value\n";
536 freshlyOptimized =
false;
539 if (vertexCount - lastOptimizedVertexCount >= updateGraphEachN) {
542 cerr <<
"initialization failed" << endl;
547 cerr <<
"updating initialization failed" << endl;
551 verticesAdded.clear();
554 int currentIt = optimizer.
optimize(incIterations, !firstRound);
560 double chi2 = optimizer.
chi2();
561 cerr <<
"nodes= " << optimizer.
vertices().size()
562 <<
"\t edges= " << optimizer.
edges().size()
563 <<
"\t chi2= " << chi2 <<
"\t time= " << dts
564 <<
"\t iterations= " << currentIt <<
"\t cumTime= " << cumTime
567 lastOptimizedVertexCount = vertexCount;
568 freshlyOptimized =
true;
571 if (vertexCount - lastVisUpdateVertexCount >= updateDisplayEveryN) {
573 lastVisUpdateVertexCount = vertexCount;
578 if (!verbose) cerr <<
".";
583 if (!freshlyOptimized) {
585 int currentIt = optimizer.
optimize(incIterations, !firstRound);
590 double chi2 = optimizer.
chi2();
591 cerr <<
"nodes= " << optimizer.
vertices().size()
592 <<
"\t edges= " << optimizer.
edges().size() <<
"\t chi2= " << chi2
593 <<
"\t time= " << dts <<
"\t iterations= " << currentIt
594 <<
"\t cumTime= " << cumTime << endl;
601 if (statsFile !=
"") {
607 double loadChi = optimizer.
chi2();
608 cerr <<
"Initial chi2 = " << FIXED(loadChi) << endl;
612 }
else if (initialGuessOdometry) {
616 double initChi = optimizer.
chi2();
619 int result = optimizer.
optimize(maxIterations);
620 if (maxIterations > 0 && result == OptimizationAlgorithm::Fail) {
621 cerr <<
"Cholesky failed, result might be invalid" << endl;
622 }
else if (computeMarginals) {
623 std::vector<std::pair<int, int> > blockIndices;
627 blockIndices.push_back(
631 blockIndices.push_back(
639 cerr <<
"Vertex id:" << v->
id() << endl;
657 double finalChi = optimizer.
chi2();
659 if (summaryFile !=
"") {
668 int maxDim = *vertexDimensions.rbegin();
669 for (HyperGraph::VertexIDMap::iterator it = optimizer.
vertices().begin();
670 it != optimizer.
vertices().end(); ++it) {
678 set<string> edgeTypes;
679 for (HyperGraph::EdgeSet::iterator it = optimizer.
edges().begin();
680 it != optimizer.
edges().end(); ++it) {
683 stringstream edgeTypesString;
684 for (std::set<string>::iterator it = edgeTypes.begin();
685 it != edgeTypes.end(); ++it) {
686 edgeTypesString << *it <<
" ";
695 robustKernel.size() > 0);
701 os.open(summaryFile.c_str(), ios::app);
705 if (statsFile !=
"") {
706 cerr <<
"writing stats to file \"" << statsFile <<
"\" ... ";
707 ofstream os(statsFile.c_str());
710 for (
int i = 0; i < maxIterations; i++) {
711 os << bsc[i] << endl;
713 cerr <<
"done." << endl;
718 if (gnudump.size() > 0) {
719 bool gnuPlotStatus =
saveGnuplot(gnudump, optimizer);
720 if (!gnuPlotStatus) {
721 cerr <<
"Error while writing gnuplot files" << endl;
725 if (outputfilename.size() > 0) {
726 if (outputfilename ==
"-") {
727 cerr <<
"saving to stdout";
728 optimizer.
save(cout);
730 cerr <<
"saving " << outputfilename <<
" ... ";
731 optimizer.
save(outputfilename.c_str());
733 cerr <<
"done." << endl;
Abstract interface for allocating a robust kernel.
virtual RobustKernel * construct()=0
Command line parsing of argc and argv.
bool parseArgs(int argc, char **argv, bool exitOnError=true)
void paramLeftOver(const std::string &name, std::string &p, const std::string &defValue, const std::string &desc, bool optional=false)
void param(const std::string &name, bool &p, bool defValue, const std::string &desc)
bool parsedParam(const std::string ¶mFlag) const
Loading libraries during run-time.
cost for traversing only odometry edges.
void printRegisteredTypes(std::ostream &os, bool comment=false) const
static Factory * instance()
return the instance
const std::string & tag(const HyperGraph::HyperGraphElement *v) const
return the TAG given a vertex
const VertexContainer & vertices() const
const Vertex * vertex(size_t i) const
abstract Vertex, your types must derive from that one
int id() const
returns the id
std::unordered_map< int, Vertex * > VertexIDMap
std::set< Edge * > EdgeSet
std::set< Vertex * > VertexSet
const EdgeSet & edges() const
const VertexIDMap & vertices() const
A general case Vertex for optimization.
int dimension() const
dimension of the estimated state belonging to this node
void setFixed(bool fixed)
true => this node should be considered fixed during the optimization
void setMarginalized(bool marginalized)
true => this node should be marginalized out during the optimization
create solvers based on their short name
OptimizationAlgorithm * construct(const std::string &tag, OptimizationAlgorithmProperty &solverProperty) const
static OptimizationAlgorithmFactory * instance()
return the instance
void listSolvers(std::ostream &os) const
list the known solvers into a stream
void printProperties(std::ostream &os) const
bool updatePropertiesFromString(const std::string &propString)
a collection of properties mapping from name to the property itself
P * makeProperty(const std::string &name_, const typename P::ValueType &v)
void writeToCSV(std::ostream &os) const
void fillKnownKernels(std::vector< std::string > &types) const
AbstractRobustKernelCreator * creator(const std::string &tag) const
static RobustKernelFactory * instance()
return the instance
Sparse matrix which uses blocks.
SparseMatrixBlock * block(int r, int c, bool alloc=false)
stop iterating based on the gain which is (oldChi - currentChi) / currentChi.
void setMaxIterations(int maxit)
void setGainThreshold(double gainThreshold)
void computeActiveErrors()
int optimize(int iterations, bool online=false)
void setForceStopFlag(bool *flag)
void setVerbose(bool verbose)
virtual bool initializeOptimization(HyperGraph::EdgeSet &eset)
virtual void computeInitialGuess()
void setAlgorithm(OptimizationAlgorithm *algorithm)
const BatchStatisticsContainer & batchStatistics() const
void setComputeBatchStatistics(bool computeBatchStatistics)
virtual Vertex * findGauge()
finds a gauge in the graph to remove the undefined dof.
const VertexContainer & activeVertices() const
the vertices active in the current optimization
OptimizationAlgorithm * solver()
virtual bool updateInitialization(HyperGraph::VertexSet &vset, HyperGraph::EdgeSet &eset)
bool computeMarginals(SparseBlockMatrix< MatrixX > &spinv, const std::vector< std::pair< int, int > > &blockIndices)
void sigquit_handler(int sig)
#define __PRETTY_FUNCTION__
Property< std::string > StringProperty
void loadStandardTypes(DlWrapper &dlTypesWrapper, int argc, char **argv)
std::vector< G2OBatchStatistics > BatchStatisticsContainer
bool saveGnuplot(const std::string &gnudump, const OptimizableGraph &optimizer)
bool dumpEdges(std::ostream &os, const OptimizableGraph &optimizer)
double get_monotonic_time()
void loadStandardSolver(DlWrapper &dlSolverWrapper, int argc, char **argv)
Sort Edges for inserting them sequentially.
bool operator()(SparseOptimizer::Edge *const &e1, SparseOptimizer::Edge *const &e2)
HyperGraph::VertexSet & visited()
void shortestPaths(HyperGraph::Vertex *v, HyperDijkstra::CostFunction *cost, double maxDistance=std::numeric_limits< double >::max(), double comparisonConditioner=1e-3, bool directed=false, double maxEdgeCost=std::numeric_limits< double >::max())
std::set< int > dimensions() const
bool isSolverSuitable(const OptimizationAlgorithmProperty &solverProperty, const std::set< int > &vertDims=std::set< int >()) const
virtual bool addEdge(HyperGraph::Edge *e)
bool addPostIterationAction(HyperGraphAction *action)
add an action to be executed after each iteration
static bool initMultiThreading()
virtual bool save(std::ostream &os, int level=0) const
save the graph to a stream. Again uses the Factory system.
virtual bool addVertex(HyperGraph::Vertex *v, Data *userData)
virtual bool load(std::istream &is)
double chi2() const
returns the chi2 of the current configuration
Vertex * vertex(int id)
returns the vertex number id appropriately casted
void setRenamedTypesFromString(const std::string &types)
describe the properties of a solver
utility functions for handling time related stuff