g2o_slam_interface.cpp
Go to the documentation of this file.
00001 // g2o - General Graph Optimization
00002 // Copyright (C) 2011 R. Kuemmerle, G. Grisetti, W. Burgard
00003 // 
00004 // g2o is free software: you can redistribute it and/or modify
00005 // it under the terms of the GNU Lesser General Public License as published
00006 // by the Free Software Foundation, either version 3 of the License, or
00007 // (at your option) any later version.
00008 // 
00009 // g2o is distributed in the hope that it will be useful,
00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 // GNU Lesser General Public License for more details.
00013 // 
00014 // You should have received a copy of the GNU Lesser General Public License
00015 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
00016 
00017 #include "g2o_slam_interface.h"
00018 
00019 #include "g2o/stuff/fast_output.h"
00020 
00021 #include "types_slam2d_online.h"
00022 #include "types_slam3d_online.h"
00023 
00024 #include "graph_optimizer_sparse_online.h"
00025 
00026 #include <iostream>
00027 using namespace std;
00028 
00029 namespace g2o {
00030 
00032 Eigen::Quaterniond euler_to_quat(double yaw, double pitch, double roll)
00033 {
00034   double sy = sin(yaw*0.5);
00035   double cy = cos(yaw*0.5);
00036   double sp = sin(pitch*0.5);
00037   double cp = cos(pitch*0.5);
00038   double sr = sin(roll*0.5);
00039   double cr = cos(roll*0.5);
00040   double w = cr*cp*cy + sr*sp*sy;
00041   double x = sr*cp*cy - cr*sp*sy;
00042   double y = cr*sp*cy + sr*cp*sy;
00043   double z = cr*cp*sy - sr*sp*cy;
00044   return Eigen::Quaterniond(w,x,y,z);
00045 }
00046 
00047 void quat_to_euler(Eigen::Quaterniond q, double& yaw, double& pitch, double& roll)
00048 {
00049   const double& q0 = q.w();
00050   const double& q1 = q.x();
00051   const double& q2 = q.y();
00052   const double& q3 = q.z();
00053   roll = atan2(2*(q0*q1+q2*q3), 1-2*(q1*q1+q2*q2));
00054   pitch = asin(2*(q0*q2-q3*q1));
00055   yaw = atan2(2*(q0*q3+q1*q2), 1-2*(q2*q2+q3*q3));
00056 }
00057 
00058 void jac_quat3_euler3(Eigen::Matrix<double, 6, 6>& J, const SE3Quat& t)
00059 {
00060   const Vector3d& tr0 = t.translation();
00061   const Quaterniond& q0 = t.rotation();
00062 
00063   double delta=1e-6;
00064   double idelta= 1. / (2. * delta);
00065 
00066   for (int i=0; i<6; i++){
00067     SE3Quat ta, tb;
00068     if (i<3){
00069       Vector3d tra=tr0;
00070       Vector3d trb=tr0;
00071       tra[i] -= delta;
00072       trb[i] += delta;
00073       ta = SE3Quat(q0, tra); 
00074       tb = SE3Quat(q0, trb); 
00075     } else {
00076       Quaterniond qa=q0;
00077       Quaterniond qb=q0;
00078       if (i == 3) {
00079         qa.x() -= delta;
00080         qb.x() += delta;
00081       }
00082       else if (i == 4) {
00083         qa.y() -= delta;
00084         qb.y() += delta;
00085       }
00086       else if (i == 5) {
00087         qa.z() -= delta;
00088         qb.z() += delta;
00089       }
00090       qa.normalize();
00091       qb.normalize();
00092       ta = SE3Quat(qa, tr0); 
00093       tb = SE3Quat(qb, tr0); 
00094     }
00095 
00096     Vector3d dtr = (tb.translation() - ta.translation())*idelta;
00097     Vector3d taAngles, tbAngles;
00098     quat_to_euler(ta.rotation(), taAngles(2), taAngles(1), taAngles(0));
00099     quat_to_euler(tb.rotation(), tbAngles(2), tbAngles(1), tbAngles(0));
00100     Vector3d da = (tbAngles - taAngles) * idelta; //TODO wraparounds not handled
00101 
00102     for (int j=0; j<6; j++){
00103       if (j<3){
00104         J(j, i) = dtr(j);
00105       } else {
00106         J(j, i) = da(j-3);
00107       }
00108     }
00109   }
00110 }
00111 
00112 G2oSlamInterface::G2oSlamInterface(SparseOptimizerOnline* optimizer) :
00113   _optimizer(optimizer), _firstOptimization(true), _nodesAdded(0),
00114   _incIterations(1), _updateGraphEachN(10), _batchEveryN(100),
00115   _lastBatchStep(0), _initSolverDone(false)
00116 {
00117 }
00118 
00119 bool G2oSlamInterface::addNode(const std::string& tag, int id, int dimension, const std::vector<double>& values)
00120 {
00121   // allocating the desired solver + testing whether the solver is okay
00122   if (! _initSolverDone) {
00123     _initSolverDone = true;
00124     _optimizer->initSolver(dimension, _batchEveryN);
00125   }
00126 
00127   // we add the node when we are asked to add the according edge
00128   (void) tag;
00129   (void) id;
00130   (void) dimension;
00131   (void) values;
00132   return true;
00133 }
00134 
00135 bool G2oSlamInterface::addEdge(const std::string& tag, int id, int dimension, int v1Id, int v2Id, const std::vector<double>& measurement, const std::vector<double>& information)
00136 {
00137   (void) tag;
00138   (void) id;
00139 
00140   if (dimension == 3) {
00141 
00142     SE2 transf(measurement[0], measurement[1], measurement[2]);
00143     Eigen::Matrix3d infMat;
00144     int idx = 0;
00145     for (int r = 0; r < 3; ++r)
00146       for (int c = r; c < 3; ++c, ++idx) {
00147         assert(idx < (int)information.size());
00148         infMat(r,c) = infMat(c,r) = information[idx];
00149       }
00150     //cerr << PVAR(infMat) << endl;
00151 
00152     int doInit = 0;
00153     SparseOptimizer::Vertex* v1 = _optimizer->vertex(v1Id);
00154     SparseOptimizer::Vertex* v2 = _optimizer->vertex(v2Id);
00155     if (! v1) {
00156       OptimizableGraph::Vertex* v = v1 = addVertex(dimension, v1Id);
00157       _verticesAdded.insert(v);
00158       doInit = 1;
00159       ++_nodesAdded;
00160     }
00161 
00162     if (! v2) {
00163       OptimizableGraph::Vertex* v = v2 = addVertex(dimension, v2Id);
00164       _verticesAdded.insert(v);
00165       doInit = 2;
00166       ++_nodesAdded;
00167     }
00168 
00169     if (_optimizer->edges().size() == 0) {
00170       cerr << "FIRST EDGE ";
00171       if (v1->id() < v2->id()) {
00172         cerr << "fixing " << v1->id() << endl;
00173         v1->setFixed(true);
00174       }
00175       else {
00176         cerr << "fixing " << v2->id() << endl;
00177         v2->setFixed(true);
00178       }
00179     }
00180 
00181     OnlineEdgeSE2* e = new OnlineEdgeSE2;
00182     e->vertices()[0] = v1;
00183     e->vertices()[1] = v2;
00184     e->setMeasurement(transf);
00185     e->setInverseMeasurement(transf.inverse());
00186     e->setInformation(infMat);
00187     _optimizer->addEdge(e);
00188     _edgesAdded.insert(e);
00189 
00190     if (doInit) {
00191       OptimizableGraph::Vertex* from = static_cast<OptimizableGraph::Vertex*>(e->vertices()[0]);
00192       OptimizableGraph::Vertex* to   = static_cast<OptimizableGraph::Vertex*>(e->vertices()[1]);
00193       switch (doInit){
00194         case 1: // initialize v1 from v2
00195           {
00196             HyperGraph::VertexSet toSet;
00197             toSet.insert(to);
00198             if (e->initialEstimatePossible(toSet, from) > 0.) {
00199               e->initialEstimate(toSet, from);
00200             }
00201             break;
00202           }
00203         case 2: 
00204           {
00205             HyperGraph::VertexSet fromSet;
00206             fromSet.insert(from);
00207             if (e->initialEstimatePossible(fromSet, to) > 0.) {
00208               e->initialEstimate(fromSet, to);  
00209             }
00210             break;
00211           }
00212         default: cerr << "doInit wrong value\n"; 
00213       }
00214     }
00215 
00216   }
00217   else if (dimension == 6) {
00218 
00219     Vector3d translation(measurement[0], measurement[1], measurement[2]);
00220     Quaterniond rotation = euler_to_quat(measurement[5], measurement[4], measurement[3]);
00221     SE3Quat transf(rotation, translation);
00222     Matrix<double, 6, 6> infMatEuler;
00223     int idx = 0;
00224     for (int r = 0; r < 6; ++r)
00225       for (int c = r; c < 6; ++c, ++idx) {
00226         assert(idx < (int)information.size());
00227         infMatEuler(r,c) = infMatEuler(c,r) = information[idx];
00228       }
00229     // convert information matrix to our internal representation
00230     Matrix<double, 6, 6> J;
00231     jac_quat3_euler3(J, transf);
00232     Matrix<double, 6, 6> infMat = J.transpose() * infMatEuler * J;
00233     //cerr << PVAR(infMat) << endl;
00234 
00235     int doInit = 0;
00236     SparseOptimizer::Vertex* v1 = _optimizer->vertex(v1Id);
00237     SparseOptimizer::Vertex* v2 = _optimizer->vertex(v2Id);
00238     if (! v1) {
00239       OptimizableGraph::Vertex* v = v1 = addVertex(dimension, v1Id);
00240       _verticesAdded.insert(v);
00241       doInit = 1;
00242       ++_nodesAdded;
00243     }
00244 
00245     if (! v2) {
00246       OptimizableGraph::Vertex* v = v2 = addVertex(dimension, v2Id);
00247       _verticesAdded.insert(v);
00248       doInit = 2;
00249       ++_nodesAdded;
00250     }
00251 
00252     if (_optimizer->edges().size() == 0) {
00253       cerr << "FIRST EDGE ";
00254       if (v1->id() < v2->id()) {
00255         cerr << "fixing " << v1->id() << endl;
00256         v1->setFixed(true);
00257       }
00258       else {
00259         cerr << "fixing " << v2->id() << endl;
00260         v2->setFixed(true);
00261       }
00262     }
00263 
00264     OnlineEdgeSE3* e = new OnlineEdgeSE3;
00265     e->vertices()[0] = v1;
00266     e->vertices()[1] = v2;
00267     e->setMeasurement(transf);
00268     e->setInverseMeasurement(transf.inverse());
00269     e->setInformation(infMat);
00270     _optimizer->addEdge(e);
00271     _edgesAdded.insert(e);
00272 
00273     if (doInit) {
00274       OptimizableGraph::Vertex* from = static_cast<OptimizableGraph::Vertex*>(e->vertices()[0]);
00275       OptimizableGraph::Vertex* to   = static_cast<OptimizableGraph::Vertex*>(e->vertices()[1]);
00276       switch (doInit){
00277         case 1: // initialize v1 from v2
00278           {
00279             HyperGraph::VertexSet toSet;
00280             toSet.insert(to);
00281             if (e->initialEstimatePossible(toSet, from) > 0.) {
00282               e->initialEstimate(toSet, from);
00283             }
00284             break;
00285           }
00286         case 2: 
00287           {
00288             HyperGraph::VertexSet fromSet;
00289             fromSet.insert(from);
00290             if (e->initialEstimatePossible(fromSet, to) > 0.) {
00291               e->initialEstimate(fromSet, to);  
00292             }
00293             break;
00294           }
00295         default: cerr << "doInit wrong value\n"; 
00296       }
00297     }
00298 
00299   }
00300   else {
00301     cerr << __PRETTY_FUNCTION__ << " not implemented for this dimension" << endl;
00302     return false;
00303   }
00304 
00305   return true;
00306 }
00307 
00308 bool G2oSlamInterface::fixNode(const std::vector<int>& nodes)
00309 {
00310   for (size_t i = 0; i < nodes.size(); ++i) {
00311     OptimizableGraph::Vertex* v = _optimizer->vertex(nodes[i]);
00312     if (v)
00313       v->setFixed(true);
00314   }
00315   return true;
00316 }
00317 
00318 bool G2oSlamInterface::queryState(const std::vector<int>& nodes)
00319 {
00320   //return true;
00321   cout << "BEGIN" << endl;
00322 #if 1
00323   if (nodes.size() == 0) {
00324     for (OptimizableGraph::VertexIDMap::const_iterator it = _optimizer->vertices().begin(); it != _optimizer->vertices().end(); ++it) {
00325       OptimizableGraph::Vertex* v = static_cast<OptimizableGraph::Vertex*>(it->second);
00326       printVertex(v);
00327     }
00328   } else {
00329     for (size_t i = 0; i < nodes.size(); ++i) {
00330       OptimizableGraph::Vertex* v = _optimizer->vertex(nodes[i]);
00331       if (v)
00332         printVertex(v);
00333     }
00334   }
00335 #endif
00336   cout << "END" << endl << flush;
00337 
00338   return true;
00339 }
00340 
00341 bool G2oSlamInterface::solveState()
00342 {
00343   if (_nodesAdded >= _updateGraphEachN) {
00344 
00345     // decide on batch step or normal step
00346     _optimizer->batchStep = false;
00347     if ((int)_optimizer->vertices().size() - _lastBatchStep >= _batchEveryN) {
00348       _lastBatchStep = _optimizer->vertices().size();
00349       _optimizer->batchStep = true;
00350     }
00351 
00352     if (_firstOptimization) {
00353       if (!_optimizer->initializeOptimization()){
00354         cerr << "initialization failed" << endl;
00355         return false;
00356       }
00357     } else {
00358       if (! _optimizer->updateInitialization(_verticesAdded, _edgesAdded)) {
00359         cerr << "updating initialization failed" << endl;
00360         return false;
00361       }
00362     }
00363 
00364     int currentIt = _optimizer->optimize(_incIterations, !_firstOptimization); (void) currentIt;
00365     _firstOptimization = false;
00366     _nodesAdded = 0;
00367     _verticesAdded.clear();
00368     _edgesAdded.clear();
00369   }
00370 
00371   return true;
00372 }
00373 
00374 OptimizableGraph::Vertex* G2oSlamInterface::addVertex(int dimension, int id)
00375 {
00376   if (dimension == 3) {
00377     OnlineVertexSE2* v =  new OnlineVertexSE2;
00378     v->setId(id); // estimate will be set later when the edge is added
00379     _optimizer->addVertex(v);
00380     return v;
00381   }
00382   else if (dimension == 6) {
00383     OnlineVertexSE3* v =  new OnlineVertexSE3;
00384     v->setId(id); // estimate will be set later when the edge is added
00385     _optimizer->addVertex(v);
00386     return v;
00387   }
00388   else {
00389     return 0;
00390   }
00391 }
00392 
00393 bool G2oSlamInterface::printVertex(OptimizableGraph::Vertex* v)
00394 {
00395   static char buffer[10000]; // that should be more than enough
00396   int vdim = v->dimension();
00397   if (vdim == 3) {
00398     char* s = buffer;
00399     OnlineVertexSE2* v2 = static_cast<OnlineVertexSE2*>(v);
00400     memcpy(s, "VERTEX_XYT ", 11);
00401     s += 11;
00402     s += modp_itoa10(v->id(), s);
00403     *s++ = ' ';
00404     s += modp_dtoa(v2->updatedEstimate.translation().x(), s, 6);
00405     *s++ = ' ';
00406     s += modp_dtoa(v2->updatedEstimate.translation().y(), s, 6);
00407     *s++ = ' ';
00408     s += modp_dtoa(v2->updatedEstimate.rotation().angle(), s, 6);
00409     *s++ = '\n';
00410     cout.write(buffer, s - buffer);
00411     return true;
00412   }
00413   else if (vdim == 6) {
00414     char* s = buffer;
00415     OnlineVertexSE3* v3 = static_cast<OnlineVertexSE3*>(v);
00416     double roll, pitch, yaw;
00417     quat_to_euler(v3->updatedEstimate.rotation(), yaw, pitch, roll);
00418     memcpy(s, "VERTEX_XYZRPY ", 14);
00419     s += 14;
00420     s += modp_itoa10(v->id(), s);
00421     *s++ = ' ';
00422     s += modp_dtoa(v3->updatedEstimate.translation().x(), s, 6);
00423     *s++ = ' ';
00424     s += modp_dtoa(v3->updatedEstimate.translation().y(), s, 6);
00425     *s++ = ' ';
00426     s += modp_dtoa(v3->updatedEstimate.translation().z(), s, 6);
00427     *s++ = ' ';
00428     s += modp_dtoa(roll, s, 6);
00429     *s++ = ' ';
00430     s += modp_dtoa(pitch, s, 6);
00431     *s++ = ' ';
00432     s += modp_dtoa(yaw, s, 6);
00433     *s++ = '\n';
00434     cout.write(buffer, s - buffer);
00435     return true;
00436   }
00437   return false;
00438 }
00439 
00440 void G2oSlamInterface::setUpdateGraphEachN(int n)
00441 {
00442   _updateGraphEachN = n;
00443 }
00444 
00445 } // end namespace


re_vision
Author(s): Dorian Galvez-Lopez
autogenerated on Sun Jan 5 2014 11:31:13