10 #include "g2o/types/slam2d/types_three_dof.h" 13 #include "g2o/types/slam3d/types_six_dof_quat.h" 52 return 1.0/(1e-6 + e->
chi2());
58 bool operator()(SparseOptimizer::Edge*
const & e1, SparseOptimizer::Edge*
const & e2)
60 const SparseOptimizer::Vertex* to1 =
static_cast<const SparseOptimizer::Vertex*
>(e1->vertex(0));
61 const SparseOptimizer::Vertex* to2 =
static_cast<const SparseOptimizer::Vertex*
>(e2->vertex(0));
63 int i11 = e1->vertex(0)->id(), i12 = e1->vertex(1)->id();
67 int i21 = e2->vertex(0)->id(), i22 = e2->vertex(1)->id();
75 if (to1->dimension() != to2->dimension()) {
76 return to1->dimension() > to2->dimension();
87 for (SparseOptimizer::VertexIDMap::const_iterator it = optimizer.
vertices().begin();
88 it != optimizer.
vertices().end(); ++it) {
89 const SparseOptimizer::Vertex* v =
static_cast<const SparseOptimizer::Vertex*
>(it->second);
90 Eigen::Quaterniond quat;
91 Eigen::Vector3d trans;
93 bool writeVertex =
false;
94 bool writePoint =
false;
102 static Eigen::Vector3d axis(0,0,1);
110 trans[1] = v2point->estimate()[1];
117 os.write((
char*)&
id,
sizeof(
int));
118 os.write((
char*)&trans.x(),
sizeof(double));
119 os.write((
char*)&trans.y(),
sizeof(double));
120 os.write((
char*)&trans.z(),
sizeof(double));
121 os.write((
char*)&quat.w(),
sizeof(double));
122 os.write((
char*)&quat.x(),
sizeof(double));
123 os.write((
char*)&quat.y(),
sizeof(double));
124 os.write((
char*)&quat.z(),
sizeof(double));
129 os.write((
char*)&trans.x(),
sizeof(double));
130 os.write((
char*)&trans.y(),
sizeof(double));
131 os.write((
char*)&trans.z(),
sizeof(double));
139 SparseOptimizer::Method
str2method(
const std::string& strMethod){
140 if (strMethod==
"Gauss") {
141 cerr <<
"# Doing Gauss" << endl;
142 return SparseOptimizer::GaussNewton;
144 if (strMethod==
"Levenberg") {
145 cerr <<
"# Doing Levenberg-Marquardt" << endl;
146 return SparseOptimizer::LevenbergMarquardt;
148 cerr <<
"# Unknown optimization method: " << strMethod <<
", setting default to Levenberg" << endl;
149 return SparseOptimizer::LevenbergMarquardt;
156 #ifdef GO_CHOLMOD_SUPPORT 168 string strSolver = strSolver_;
169 bool useCholmod =
false;
171 string::size_type idx = strSolver.rfind(
"_cholmod");
172 strSolver = strSolver.substr(0, idx);
175 #ifndef GO_CHOLMOD_SUPPORT 177 cerr <<
"Error: Cholmod is not supported in this build" << endl;
182 cerr <<
"# Using " << (useCholmod ?
"cholmod" :
"CSparse") <<
" " << strSolver << endl;
184 cerr <<
"# Using PCG " << strSolver<< endl;
186 if (strSolver==
"var") {
190 if (strSolver==
"fix3_2") {
194 if (strSolver==
"fix6_3") {
198 if (strSolver==
"fix7_3") {
202 if (strSolver ==
"pcg") {
206 if (strSolver ==
"pcg6_3") {
210 if (strSolver ==
"pcg3_2") {
214 cerr <<
"Unknown solver, setting the general but slower \"var\" "<< endl;
222 HyperGraph::EdgeSet::const_iterator begin,
223 HyperGraph::EdgeSet::const_iterator end,
228 if (gnudump.size() > 0) {
231 string newFilename =
formatString(
"%s_%s.%s", baseName.c_str(), file_suffix.c_str(), extension.c_str());
233 cerr <<
"Gnudump (" << newFilename <<
")... ";
236 ofstream fout(newFilename.c_str());
238 for (HyperGraph::EdgeSet::const_iterator eit = begin; eit != end; ++eit) {
244 double edgeChi2 = e->
chi2();
253 fout << p1[0] <<
" " << p1[1] <<
" " << p1[2] <<
" " << edgeChi2 <<
" " << endl;
254 fout << p2[0] <<
" " << p2[1] <<
" " << p1[2] <<
" " << edgeChi2 <<
" " << endl;
265 fout << p1[0] <<
" " << p1[1] <<
" " << p1[2] <<
" " << edgeChi2 <<
" " << endl;
266 fout << p2[0] <<
" " << p2[1] <<
" " << edgeChi2 <<
" " << endl;
275 cerr <<
" done" << endl;
281 HyperGraph::EdgeSet::const_iterator begin,
282 HyperGraph::EdgeSet::const_iterator end) {
285 if (gnudump.size() > 0) {
288 string newFilename =
formatString(
"%s_%s.%s", baseName.c_str(), file_suffix.c_str(), extension.c_str());
290 cerr <<
"Gnudump (" << newFilename <<
")... ";
293 ofstream fout(newFilename.c_str());
295 for (HyperGraph::EdgeSet::const_iterator eit = begin; eit != end; ++eit) {
301 double edgeChi2 = e->
chi2();
306 fout << p2[0] <<
" " << p2[1] <<
" " << edgeChi2 <<
" " << endl;
311 cerr <<
" done" << endl;
324 int main(
int argc,
char** argv)
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()
std::string getFileExtension(const std::string &filename)
statistics about the optimization
const Vertex * vertex(size_t i) const
Command line parsing of argc and argv.
BlockSolver< BlockSolverTraits< 6, 3 > > BlockSolver_6_3
T::LinearSolverType * allocateLinearSolver(bool useCholmod)
Vector3D toVector() const
convert to a 3D vector (x, y, theta)
bool dumpEdges(std::ostream &os, const OptimizableGraph &optimizer)
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)
linear solver using PCG, pre-conditioner is block Jacobi
const VertexIDMap & vertices() const
Vertex * vertex(int id)
returns the vertex number id appropriately casted
bool operator()(SparseOptimizer::Edge *const &e1, SparseOptimizer::Edge *const &e2)
Traits::LinearSolverType LinearSolverType
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
BlockSolver< BlockSolverTraits< 3, 2 > > BlockSolver_3_2
int main(int argc, char **argv)
double chi2() const
returns the chi2 of the current configuration
bool parseArgs(int argc, char **argv, bool exitOnError=true)
std::string formatString(const char *fmt,...)
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.
const Eigen::Rotation2Dd & rotation() const
rotational component
linear solver which uses CSparse
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
Generic interface for a sparse solver operating on a graph which solves one iteration of the lineariz...
void gnudump_edges(string gnudump, string file_suffix, HyperGraph::EdgeSet::const_iterator begin, HyperGraph::EdgeSet::const_iterator end, bool dumpEdges, bool dumpFeatures)
bool strStartsWith(const std::string &s, const std::string &start)
std::string getPureFilename(const std::string &filename)
void param(const std::string &name, bool &p, bool defValue, const std::string &desc)
basic solver for Ax = b which has to reimplemented for different linear algebra libraries ...
bool marginalized() const
true => this node is marginalized out during the optimization
A general case Vertex for optimization.
abstract Vertex, your types must derive from that one
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...
virtual double operator()(HyperGraph::Edge *edge, HyperGraph::Vertex *from, HyperGraph::Vertex *to)
void sigquit_handler(int q __attribute__((unused)))
Sort Edges for inserting them sequentially.
BlockSolver< BlockSolverTraits< 7, 3 > > BlockSolver_7_3
bool fixed() const
true => this node is fixed during the optimization
const EstimateType & estimate() const
return the current estimate of the vertex
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)
bool strEndsWith(const std::string &s, const std::string &end)
const Vector2D & translation() const
translational component
virtual bool addEdge(HyperGraph::Edge *e)
virtual bool addVertex(HyperGraph::Vertex *v, Data *userData)
bool verbose() const
verbose information during optimization
BlockSolver< BlockSolverTraits< Eigen::Dynamic, Eigen::Dynamic > > BlockSolverX
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)