00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
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;
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
00122 if (! _initSolverDone) {
00123 _initSolverDone = true;
00124 _optimizer->initSolver(dimension, _batchEveryN);
00125 }
00126
00127
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
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:
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
00230 Matrix<double, 6, 6> J;
00231 jac_quat3_euler3(J, transf);
00232 Matrix<double, 6, 6> infMat = J.transpose() * infMatEuler * J;
00233
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:
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
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
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);
00379 _optimizer->addVertex(v);
00380 return v;
00381 }
00382 else if (dimension == 6) {
00383 OnlineVertexSE3* v = new OnlineVertexSE3;
00384 v->setId(id);
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];
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 }