100 {
102 int maxIterations;
103 bool verbose;
104 string inputFilename;
105 string gnudump;
106 string outputfilename;
107 string solverProperties;
108 string strSolver;
109 string loadLookup;
110 bool initialGuess;
111 bool initialGuessOdometry;
112 bool marginalize;
113 bool listTypes;
114 bool listSolvers;
115 bool listRobustKernels;
116 bool incremental;
117 bool guiOut;
118 int gaugeId;
119 string robustKernel;
120 bool computeMarginals;
121 bool printSolverProperties;
122 double huberWidth;
123 double gain;
124 int maxIterationsWithGain;
125
126 int updateGraphEachN = 10;
127 string statsFile;
128 string summaryFile;
129 bool nonSequential;
130
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
164 string dummy;
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");
169#endif
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 "
181 "e.g: 1,2,3,4,5 ");
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");
189
191
192 if (verbose) {
193 cout << "# Used Compiler: " << G2O_CXX_COMPILER << endl;
194 }
195
196#ifndef G2O_DISABLE_DYNAMIC_LOADING_OF_LIBRARIES
197
200
203#else
204 if (verbose) cout << "# linked version of g2o" << endl;
205#endif
206
209 if (listSolvers) {
211 }
212
213 if (listTypes) {
215 }
216
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;
223 }
224 }
225
229
230 if (maxIterations < 0) {
231 cerr << "# setup termination criterion based on the gain of the iteration"
232 << endl;
233 maxIterations = maxIterationsWithGain;
239 }
240
241
244 if (!optimizer.
solver()) {
245 cerr << "Error allocating solver. Allocating \"" << strSolver
246 << "\" failed!" << endl;
247 return 0;
248 }
249
250 if (solverProperties.size() > 0) {
251 bool updateStatus =
253 if (!updateStatus) {
254 cerr << "Failure while updating the solver properties from the given "
255 "string"
256 << endl;
257 }
258 }
259 if (solverProperties.size() > 0 || printSolverProperties) {
261 }
262
263
264 if (loadLookup.size() > 0) {
266 }
267 if (inputFilename.size() == 0) {
268 cerr << "No input data specified" << endl;
269 return 0;
270 } else if (inputFilename == "-") {
271 cerr << "Read input from stdin" << endl;
272 if (!optimizer.
load(cin)) {
273 cerr << "Error loading graph" << endl;
274 return 2;
275 }
276 } else {
277 cerr << "Read input from " << inputFilename << endl;
278 ifstream ifs(inputFilename.c_str());
279 if (!ifs) {
280 cerr << "Failed to open file" << endl;
281 return 1;
282 }
283 if (!optimizer.
load(ifs)) {
284 cerr << "Error loading graph" << endl;
285 return 2;
286 }
287 }
288 cerr <<
"Loaded " << optimizer.
vertices().size() <<
" vertices" << endl;
289 cerr <<
"Loaded " << optimizer.
edges().size() <<
" edges" << endl;
290
291 if (optimizer.
vertices().size() == 0) {
292 cerr << "Graph contains no vertices" << endl;
293 return 1;
294 }
295
296 set<int> vertexDimensions = optimizer.
dimensions();
298 cerr << "The selected solver is not suitable for optimizing the given graph"
299 << endl;
300 return 3;
301 }
302 assert(optimizer.
solver());
303
304
305
306
309 if (gaugeList.size()) {
310 cerr << "Fixing gauges: ";
311 for (size_t i = 0; i < gaugeList.size(); i++) {
312 int id = gaugeList[i];
314 if (!v) {
315 cerr << "fatal, not found the vertex of id " << id
316 << " in the gaugeList. Aborting";
317 return -1;
318 } else {
319 if (i == 0) gauge = v;
320 cerr << v->
id() <<
" ";
322 }
323 }
324 cerr << endl;
325 gaugeFreedom = false;
326 } else {
328 }
329 if (gaugeFreedom) {
330 if (!gauge) {
331 cerr << "# cannot find a vertex to fix in this thing" << endl;
332 return 2;
333 } else {
334 cerr <<
"# graph is fixed by node " << gauge->
id() << endl;
336 }
337 } else {
338 cerr << "# graph is fixed by priors or already fixed vertex" << endl;
339 }
340
341
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) {
353 }
354 }
355 cerr << "done." << endl;
356 }
357 }
358
359 if (robustKernel.size() > 0) {
362 cerr << "# Preparing robust error function ... ";
363 if (creator) {
364 if (nonSequential) {
365 for (SparseOptimizer::EdgeSet::iterator it = optimizer.
edges().begin();
366 it != optimizer.
edges().end(); ++it) {
367 SparseOptimizer::Edge* e = dynamic_cast<SparseOptimizer::Edge*>(*it);
368 if (e->vertices().size() >= 2 &&
369 std::abs(e->vertex(0)->id() - e->vertex(1)->id()) != 1) {
370 e->setRobustKernel(creator->
construct());
371 if (huberWidth > 0) e->robustKernel()->setDelta(huberWidth);
372 }
373 }
374 } else {
375 for (SparseOptimizer::EdgeSet::iterator it = optimizer.
edges().begin();
376 it != optimizer.
edges().end(); ++it) {
377 SparseOptimizer::Edge* e = dynamic_cast<SparseOptimizer::Edge*>(*it);
378 e->setRobustKernel(creator->
construct());
379 if (huberWidth > 0) e->robustKernel()->setDelta(huberWidth);
380 }
381 }
382 cerr << "done." << endl;
383 } else {
384 cerr << "Unknown Robust Kernel: " << robustKernel << endl;
385 }
386 }
387
388
391 d.shortestPaths(gauge, &f);
392
393
394 if (d.visited().size() != optimizer.
vertices().size()) {
395 cerr <<
CL_RED(
"Warning: d.visited().size() != optimizer.vertices().size()")
396 << endl;
397 cerr << "visited: " << d.visited().size() << endl;
398 cerr <<
"vertices: " << optimizer.
vertices().size() << endl;
399 }
400
401 if (incremental) {
403 "# Note: this variant performs batch steps in each time step")
404 << endl;
406 "# For a variant which updates the Cholesky factor use "
407 "the binary g2o_incremental")
408 << endl;
409 int incIterations = maxIterations;
411 cerr << "# Setting default number of iterations" << endl;
412 incIterations = 1;
413 }
414 int updateDisplayEveryN = updateGraphEachN;
415 int maxDim = 0;
416
417 cerr << "# incremental settings" << endl;
418 cerr << "#\t solve every " << updateGraphEachN << endl;
419 cerr << "#\t iterations " << incIterations << endl;
420
421 SparseOptimizer::VertexIDMap vertices = optimizer.
vertices();
422 for (SparseOptimizer::VertexIDMap::const_iterator it = vertices.begin();
423 it != vertices.end(); ++it) {
424 const SparseOptimizer::Vertex* v =
425 static_cast<const SparseOptimizer::Vertex*>(it->second);
426 maxDim = max(maxDim, v->dimension());
427 }
428
429 vector<SparseOptimizer::Edge*> edges;
430 for (SparseOptimizer::EdgeSet::iterator it = optimizer.
edges().begin();
431 it != optimizer.
edges().end(); ++it) {
432 SparseOptimizer::Edge* e = dynamic_cast<SparseOptimizer::Edge*>(*it);
433 edges.push_back(e);
434 }
435 optimizer.
edges().clear();
438
439
441
442 double cumTime = 0.;
443 int vertexCount = 0;
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) {
452 SparseOptimizer::Edge* e = *it;
453
454 int doInit = 0;
455 SparseOptimizer::Vertex* v1 = optimizer.
vertex(e->vertices()[0]->id());
456 SparseOptimizer::Vertex* v2 = optimizer.
vertex(e->vertices()[1]->id());
457
458 if (!v1) {
459 SparseOptimizer::Vertex* v = v1 =
460 dynamic_cast<SparseOptimizer::Vertex*>(e->vertices()[0]);
462
463 assert(v1Added);
464 if (!v1Added)
465 cerr << "Error adding vertex " << v->id() << endl;
466 else
467 verticesAdded.insert(v);
468 doInit = 1;
469 if (v->dimension() == maxDim) vertexCount++;
470 }
471
472 if (!v2) {
473 SparseOptimizer::Vertex* v = v2 =
474 dynamic_cast<SparseOptimizer::Vertex*>(e->vertices()[1]);
476
477 assert(v2Added);
478 if (!v2Added)
479 cerr << "Error adding vertex " << v->id() << endl;
480 else
481 verticesAdded.insert(v);
482 doInit = 2;
483 if (v->dimension() == maxDim) vertexCount++;
484 }
485
486
487 {
488
489
491 cerr << "Unable to add edge " << e->vertices()[0]->id() << " -> "
492 << e->vertices()[1]->id() << endl;
493 } else {
494 edgesAdded.insert(e);
495 }
496
497 if (doInit) {
502 switch (doInit) {
503 case 1:
504 {
506 toSet.insert(to);
507 if (e->initialEstimatePossible(toSet, from) > 0.) {
508
509
510
511 e->initialEstimate(toSet, from);
512 } else {
513 assert(0 && "Added uninitialized variable to the graph");
514 }
515 break;
516 }
517 case 2: {
519 fromSet.insert(from);
520 if (e->initialEstimatePossible(fromSet, to) > 0.) {
521
522
523
524 e->initialEstimate(fromSet, to);
525 } else {
526 assert(0 && "Added uninitialized variable to the graph");
527 }
528 break;
529 }
530 default:
531 cerr << "doInit wrong value\n";
532 }
533 }
534 }
535
536 freshlyOptimized = false;
537 {
538
539 if (vertexCount - lastOptimizedVertexCount >= updateGraphEachN) {
540 if (firstRound) {
542 cerr << "initialization failed" << endl;
543 return 0;
544 }
545 } else {
547 cerr << "updating initialization failed" << endl;
548 return 0;
549 }
550 }
551 verticesAdded.clear();
552 edgesAdded.clear();
554 int currentIt = optimizer.
optimize(incIterations, !firstRound);
556 cumTime += dts;
557 firstRound = false;
558
559 if (verbose) {
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
565 << endl;
566 }
567 lastOptimizedVertexCount = vertexCount;
568 freshlyOptimized = true;
569
570 if (guiOut) {
571 if (vertexCount - lastVisUpdateVertexCount >= updateDisplayEveryN) {
573 lastVisUpdateVertexCount = vertexCount;
574 }
575 }
576 }
577
578 if (!verbose) cerr << ".";
579 }
580
581 }
582
583 if (!freshlyOptimized) {
585 int currentIt = optimizer.
optimize(incIterations, !firstRound);
587 cumTime += dts;
588
589 if (verbose) {
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;
595 }
596 }
597
598 } else {
599
600
601 if (statsFile != "") {
602
604 }
607 double loadChi = optimizer.
chi2();
608 cerr << "Initial chi2 = " << FIXED(loadChi) << endl;
609
610 if (initialGuess) {
612 } else if (initialGuessOdometry) {
615 }
616 double initChi = optimizer.
chi2();
617
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(
629 }
631 blockIndices.push_back(
633 }
634 }
639 cerr <<
"Vertex id:" << v->
id() << endl;
644 cerr << endl;
645 }
650 cerr << endl;
651 }
652 }
653 }
654 }
655
657 double finalChi = optimizer.
chi2();
658
659 if (summaryFile != "") {
665
666 int nLandmarks = 0;
667 int nPoses = 0;
668 int maxDim = *vertexDimensions.rbegin();
669 for (HyperGraph::VertexIDMap::iterator it = optimizer.
vertices().begin();
670 it != optimizer.
vertices().end(); ++it) {
674 nLandmarks++;
675 } else
676 nPoses++;
677 }
678 set<string> edgeTypes;
679 for (HyperGraph::EdgeSet::iterator it = optimizer.
edges().begin();
680 it != optimizer.
edges().end(); ++it) {
682 }
683 stringstream edgeTypesString;
684 for (std::set<string>::iterator it = edgeTypes.begin();
685 it != edgeTypes.end(); ++it) {
686 edgeTypesString << *it << " ";
687 }
688
695 robustKernel.size() > 0);
700 ofstream os;
701 os.open(summaryFile.c_str(), ios::app);
703 }
704
705 if (statsFile != "") {
706 cerr << "writing stats to file \"" << statsFile << "\" ... ";
707 ofstream os(statsFile.c_str());
709
710 for (int i = 0; i < maxIterations; i++) {
711 os << bsc[i] << endl;
712 }
713 cerr << "done." << endl;
714 }
715 }
716
717
718 if (gnudump.size() > 0) {
719 bool gnuPlotStatus =
saveGnuplot(gnudump, optimizer);
720 if (!gnuPlotStatus) {
721 cerr << "Error while writing gnuplot files" << endl;
722 }
723 }
724
725 if (outputfilename.size() > 0) {
726 if (outputfilename == "-") {
727 cerr << "saving to stdout";
728 optimizer.
save(cout);
729 } else {
730 cerr << "saving " << outputfilename << " ... ";
731 optimizer.
save(outputfilename.c_str());
732 }
733 cerr << "done." << endl;
734 }
735
736
737
738
739
740
741 return 0;
742}
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
int id() const
returns the id
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)
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.
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