328 string inputFilename;
330 string outputfilename;
340 int updateGraphEachN = 10;
344 arg.
param(
"i", maxIterations, 5,
"perform n iterations");
345 arg.
param(
"v", verbose,
false,
"verbose output of the optimization process");
346 arg.
param(
"guess", initialGuess,
false,
"initial guess based on spanning tree");
347 arg.
param(
"inc", incremental,
false,
"run incremetally");
348 arg.
param(
"update", updateGraphEachN, 10,
"updates afert x odometry nodes, (default: 10)");
349 arg.
param(
"guiOut", guiOut,
false,
"gui output while running incrementally");
350 arg.
param(
"lambdaInit", lambdaInit, 0,
"user specified lambda init for levenberg");
351 arg.
param(
"marginalize", marginalize,
false,
"on or off");
352 arg.
param(
"method", strMethod,
"Gauss",
"Gauss or Levenberg");
353 arg.
param(
"gnudump", gnudump,
"",
"dump to gnuplot data file");
354 arg.
param(
"o", outputfilename,
"",
"output final version of the graph");
355 arg.
param(
"solver", strSolver,
"var",
"specify which solver to use underneat\n\t {var, fix3_2, fix6_3, fix_7_3}");
356 arg.
param(
"stats", statsFile,
"",
"specify a file for the statistics");
357 arg.
param(
"listTypes", listTypes,
false,
"list the registered types");
358 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");
359 arg.
paramLeftOver(
"graph-input", inputFilename,
"",
"graph file which will be processed");
364 OptimizableGraph::printRegisteredTypes(cout,
true);
373 if (! optimizer.
solver()) {
374 cerr <<
"Error allocating solver" << endl;
377 optimizer.userLambdaInit()=lambdaInit;
379 assert (optimizer.
solver());
380 ActivePathCostFunction* guessCostFunction=0;
382 guessCostFunction=
new ActivePathCostFunction(&optimizer);
384 cerr <<
"Read input from " << inputFilename << endl;
385 ifstream ifs(inputFilename.c_str());
387 cerr <<
"Failed to open file" << endl;
391 if (loadLookup.size() > 0) {
395 if (!optimizer.
load(ifs)) {
396 cerr <<
"Error loading graph" << endl;
400 cerr <<
"Loaded " << optimizer.
vertices().size() <<
" vertices" << endl;
401 cerr <<
"Loaded " << optimizer.
edges().size() <<
" edges" << endl;
403 if (optimizer.
vertices().size() == 0) {
404 cerr <<
"HyperGraph contains no vertices" << endl;
411 cerr <<
"Preparing Marginalization of the Landmarks ... ";
413 for (HyperGraph::VertexIDMap::iterator it=optimizer.
vertices().begin(); it!=optimizer.
vertices().end(); it++){
417 for (HyperGraph::VertexIDMap::iterator it=optimizer.
vertices().begin(); it!=optimizer.
vertices().end(); it++){
424 cerr <<
"done." << endl;
436 int incIterations = 1;
437 int updateDisplayEveryN = updateGraphEachN;
441 for (SparseOptimizer::VertexIDMap::const_iterator it = vertices.begin(); it != vertices.end(); ++it) {
442 const SparseOptimizer::Vertex* v =
static_cast<const SparseOptimizer::Vertex*
>(it->second);
443 maxDim = (max)(maxDim, v->dimension());
446 vector<SparseOptimizer::Edge*> edges;
447 for (SparseOptimizer::EdgeSet::iterator it = optimizer.
edges().begin(); it != optimizer.
edges().end(); ++it) {
448 SparseOptimizer::Edge* e =
dynamic_cast<SparseOptimizer::Edge*
>(*it);
451 optimizer.
edges().clear();
460 int lastOptimizedVertexCount = 0;
461 int lastVisUpdateVertexCount = 0;
462 bool addNextEdge=
true;
463 bool freshlyOptimized=
false;
464 for (vector<SparseOptimizer::Edge*>::iterator it = edges.begin(); it != edges.end(); ++it) {
465 SparseOptimizer::Edge* e = *it;
468 if (addNextEdge && !optimizer.
vertices().empty()){
469 int maxInGraph = optimizer.
vertices().rbegin()->first;
470 int idMax = max(e->vertex(0)->id(), e->vertex(1)->id());
471 if (maxInGraph < idMax && ! freshlyOptimized){
481 SparseOptimizer::Vertex* v1 = optimizer.
vertex(e->vertex(0)->id());
482 SparseOptimizer::Vertex* v2 = optimizer.
vertex(e->vertex(1)->id());
483 if (! v1 && addNextEdge) {
485 SparseOptimizer::Vertex* v =
dynamic_cast<SparseOptimizer::Vertex*
>(e->vertex(0));
489 if (v->dimension() == maxDim)
493 if (! v2 && addNextEdge) {
495 SparseOptimizer::Vertex* v =
dynamic_cast<SparseOptimizer::Vertex*
>(e->vertex(1));
499 if (v->dimension() == maxDim)
506 cerr <<
"Unable to add edge " << e->vertex(0)->id() <<
" -> " << e->vertex(1)->id() << endl;
514 if (e->initialEstimatePossible(to, from)) {
518 e->initialEstimate(to, from);
522 if (e->initialEstimatePossible(from, to)) {
526 e->initialEstimate(from, to);
529 default: cerr <<
"doInit wrong value\n";
535 freshlyOptimized=
false;
538 if (vertexCount - lastOptimizedVertexCount >= updateGraphEachN) {
540 int currentIt=optimizer.
optimize(incIterations);
545 double chi2 = optimizer.
chi2();
546 cerr <<
"nodes= " << optimizer.
vertices().size() <<
"\t edges= " << optimizer.
edges().size() <<
"\t chi2= " << chi2
547 <<
"\t time= " << dts <<
"\t iterations= " << currentIt <<
"\t cumTime= " << cumTime << endl;
549 lastOptimizedVertexCount = vertexCount;
555 freshlyOptimized=
true;
560 if (vertexCount - lastVisUpdateVertexCount >= updateDisplayEveryN) {
562 lastVisUpdateVertexCount = vertexCount;
571 if (gnudump.size() > 0)
573 if (gnudump.size() > 0)
575 if (gnudump.size() > 0)
580 cerr <<
"Initial chi2 = " << optimizer.
chi2() << endl;
587 int i=optimizer.
optimize(maxIterations,guessCostFunction);
589 cerr <<
"Cholesky failed, result might be invalid" << endl;
592 if (gnudump.size() > 0)
594 if (gnudump.size() > 0)
596 if (gnudump.size() > 0)
600 cerr <<
"Computing chi2 statistics ";
602 double chi2Thres=0.0;
605 for (HyperGraph::EdgeSet::const_iterator it = optimizer.
edges().begin(); it != optimizer.
edges().end(); ++it) {
610 sumChi2 += e->
chi2();
613 chi2Thres = sumChi2/cnt * 1.4;
614 cerr <<
" threshold=" << chi2Thres <<
" done" << endl;
616 cerr <<
"Searchin for high error edges .. ";
624 for (HyperGraph::EdgeSet::const_iterator it = optimizer.
edges().begin(); it != optimizer.
edges().end(); ++it) {
629 double edgeChi2 = e->
chi2();
630 if (edgeChi2 > chi2Thres) {
636 highErrorEdges.insert(*it);
641 highErrorEdgesToFeatures.insert(*it);
646 cerr <<
" found=" << highErrorEdges.size() <<
"/" << highErrorEdgesToFeatures.size() <<
" edges with high errors .. done" << endl;
649 if (gnudump.size() > 0)
650 gnudump_edges(gnudump,
"hee", highErrorEdges.begin(), highErrorEdges.end(),
true,
false);
652 if (gnudump.size() > 0)
653 gnudump_edges(gnudump,
"heef_", highErrorEdgesToFeatures.begin(), highErrorEdgesToFeatures.end(),
true,
true);
657 cerr <<
"Fixing the whole graph ";
658 for (std::map<int, HyperGraph::Vertex*>::iterator vit = optimizer.
vertices().begin();
659 vit != optimizer.
vertices().end(); ++vit) {
663 cerr <<
" done" << endl;
665 typedef std::pair< HyperGraph::EdgeSet, HyperGraph::EdgeSet > EdgeSetPair;
666 typedef std::vector< EdgeSetPair > EdgeSetPairVector;
668 EdgeSetPairVector myEdgeStack;
670 cerr <<
"Collecting clusters...";
672 while (!highErrorEdges.empty()) {
674 HyperGraph::EdgeSet::iterator it = highErrorEdges.begin();
681 bool edgesSelected =
false;
686 if (selected.size() > 10) {
687 edgesSelected =
true;
688 cerr <<
" (" << selected.size() <<
", " << border.size() <<
")";
692 edgesToOptimize_selected.insert(selected.begin(), selected.end());
693 edgesToOptimize_border.insert(border.begin(), border.end());
694 edgesToOptimize.insert(selected.begin(), selected.end());
695 edgesToOptimize.insert(border.begin(), border.end());
698 cerr <<
" [" << selected.size() <<
", " << border.size() <<
"]";
702 for (HyperGraph::EdgeSet::iterator eit = selected.begin(); eit != selected.end(); ++eit) {
703 HyperGraph::EdgeSet::iterator removeMe = highErrorEdges.find(*eit);
704 if (removeMe != highErrorEdges.end()) {
705 highErrorEdges.erase(removeMe);
710 myEdgeStack.push_back( EdgeSetPair(selected,border) );
713 cerr <<
" done" << endl;
715 cerr <<
"Found " << myEdgeStack.size()
716 <<
" clusters in sum with " << edgesToOptimize_selected.size() <<
" high error edges" 717 <<
" and " << edgesToOptimize_border.size() <<
" border edges." << endl;
720 if (gnudump.size() > 0)
721 gnudump_edges(gnudump,
"selected", edgesToOptimize_selected.begin(), edgesToOptimize_selected.end(),
true,
false);
723 if (gnudump.size() > 0)
724 gnudump_edges(gnudump,
"border", edgesToOptimize_border.begin(), edgesToOptimize_border.end(),
true,
false);
726 if (gnudump.size() > 0)
727 gnudump_edges(gnudump,
"reoptimizeme", edgesToOptimize.begin(), edgesToOptimize.end(),
true,
false);
729 cerr <<
"Unfixing high error edges ";
730 for (EdgeSetPairVector::iterator it = myEdgeStack.begin();
731 it != myEdgeStack.end(); ++it) {
734 for (HyperGraph::EdgeSet::iterator eit = (*it).first.begin(); eit != (*it).first.end(); ++eit) {
738 vfrom->
fixed() =
false;
740 vto->
fixed() =
false;
743 cerr <<
"done" << endl;
747 cerr <<
"Initialize optimization of subgraphs .. ";
749 cerr <<
"done" << endl;
751 if (guessCostFunction==0)
752 guessCostFunction=
new ActivePathCostFunction(&optimizer);
755 cerr <<
"Searching for init nodes and re-initialize subgraphs .. ";
756 for (EdgeSetPairVector::iterator it = myEdgeStack.begin();
757 it != myEdgeStack.end(); ++it) {
761 HyperGraph::EdgeSet::iterator eit = (*it).second.begin();
767 if (vfrom && vfrom->
fixed())
769 else if (vto && vto->
fixed())
776 optimizer.initializeActiveSubsetViaMeasurements(initNode, guessCostFunction);
779 cerr <<
" .. done" << endl;
781 if (gnudump.size() > 0)
786 cerr <<
"Optimize subgraphs .. "<< endl;
787 optimizer.optimizeLoop(maxIterations);
788 cerr <<
"done" << endl;
790 if (gnudump.size() > 0)
795 if (highErrorEdgesToFeatures.size() > 0) {
800 cerr <<
"Fixing the whole graph ";
801 for (std::map<int, HyperGraph::Vertex*>::iterator vit = optimizer.
vertices().begin();
802 vit != optimizer.
vertices().end(); ++vit) {
806 cerr <<
" done" << endl;
808 cerr <<
"Unfixing bad features";
809 for (HyperGraph::EdgeSet::iterator eit = highErrorEdgesToFeatures.begin();
810 eit != highErrorEdgesToFeatures.end(); ++eit) {
815 edgesToBadFeatures.insert(v->
edges().begin(), v->
edges().end());
818 cerr <<
" considering " << edgesToBadFeatures.size() <<
" edges .. done"<< endl;
820 if (gnudump.size() > 0)
821 gnudump_edges(gnudump,
"featurereinit", edgesToBadFeatures.begin(), edgesToBadFeatures.end(),
true,
true);
822 if (gnudump.size() > 0)
823 gnudump_features(gnudump,
"featurereinitfeatures", edgesToBadFeatures.begin(), edgesToBadFeatures.end());
826 cerr <<
"Initialize optimization of bad features .. ";
828 cerr <<
"done" << endl;
832 cerr <<
"Optimize bad features .. "<< endl;
833 optimizer.optimizeLoop(maxIterations/2);
834 cerr <<
"done" << endl;
836 if (gnudump.size() > 0)
842 for (std::map<int, HyperGraph::Vertex*>::iterator vit = optimizer.
vertices().begin();
843 vit != optimizer.
vertices().end(); ++vit) {
849 cerr <<
"Initialize final optimization .. ";
851 cerr <<
"done" << endl;
852 cerr <<
"Optimize .. " << endl;
853 optimizer.optimizeLoop(maxIterations);
854 cerr <<
"done" << endl;
859 if (gnudump.size() > 0)
861 if (gnudump.size() > 0)
863 if (gnudump.size() > 0)
868 cerr <<
"writing stats to file \"" << statsFile <<
"\"";
869 ofstream os(statsFile.c_str());
870 for (
int i=0; i<maxIterations; i++){
871 os << optimizer._statistics[i] << endl;
908 if (outputfilename.size() > 0) {
909 cerr <<
"saving " << outputfilename <<
" ... ";
910 optimizer.
save(outputfilename.c_str());
911 cerr <<
"done." << endl;
double get_monotonic_time()
statistics about the optimization
const Vertex * vertex(size_t i) const
Command line parsing of argc and argv.
virtual double chi2() const =0
computes the chi2 based on the cached error value, only valid after computeError has been called...
int optimize(int iterations, bool online=false)
OptimizationAlgorithm * solver()
2D pose Vertex, (x,y,theta)
const VertexIDMap & vertices() const
Vertex * vertex(int id)
returns the vertex number id appropriately casted
SparseOptimizer::Method str2method(const std::string &strMethod)
std::set< Edge * > EdgeSet
int dimension() const
dimension of the estimated state belonging to this node
Protocol The SLAM executable accepts such as solving the and retrieving or vertices in the explicitly state the reprensentation poses are represented by poses by VERTEX_XYZRPY In the Quaternions and other representations could be but note that it is up to the SLAM algorithm to choose the internal representation of the angles The keyword is followed by a unique vertex ID and an optional initialization of the or edges in the explicitly state the type of the constraint pose constraints are given by pose constraints by EDGE_XYZRPY The keyword is followed by a unique edge the IDs of the referenced vertices
double chi2() const
returns the chi2 of the current configuration
bool parseArgs(int argc, char **argv, bool exitOnError=true)
const EdgeSet & edges() const
returns the set of hyper-edges that are leaving/entering in this vertex
virtual bool save(std::ostream &os, int level=0) const
save the graph to a stream. Again uses the Factory system.
RobustKernel * robustKernel() const
if NOT NULL, error of this edge will be robustifed with the kernel
const EdgeSet & edges() const
void setForceStopFlag(bool *flag)
void paramLeftOver(const std::string &name, std::string &p, const std::string &defValue, const std::string &desc, bool optional=false)
virtual void computeError()=0
void gnudump_edges(string gnudump, string file_suffix, HyperGraph::EdgeSet::const_iterator begin, HyperGraph::EdgeSet::const_iterator end, bool dumpEdges, bool dumpFeatures)
void param(const std::string &name, bool &p, bool defValue, const std::string &desc)
bool marginalized() const
true => this node is marginalized out during the optimization
A general case Vertex for optimization.
void setRenamedTypesFromString(const std::string &types)
virtual bool load(std::istream &is, bool createEdges=true)
load the graph from a stream. Uses the Factory singleton for creating the vertices and edges...
Sort Edges for inserting them sequentially.
bool fixed() const
true => this node is fixed during the optimization
void findConnectedEdgesWithCostLimit(HyperGraph::EdgeSet &selected, HyperGraph::EdgeSet &border, HyperGraph::Edge *start, HyperDijkstra::CostFunction *cost, double maxEdgeCost, double comparisonConditioner)
void updateDisplay(ostream &os, const SparseOptimizer &optimizer)
virtual bool initializeOptimization(HyperGraph::EdgeSet &eset)
virtual bool addEdge(HyperGraph::Edge *e)
virtual bool addVertex(HyperGraph::Vertex *v, Data *userData)
bool verbose() const
verbose information during optimization
Solver * str2solver(const std::string &strSolver_, SparseOptimizer *opt)
void gnudump_features(string gnudump, string file_suffix, HyperGraph::EdgeSet::const_iterator begin, HyperGraph::EdgeSet::const_iterator end)