00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 #include <rtabmap/utilite/ULogger.h>
00029 #include <rtabmap/utilite/UStl.h>
00030 #include <rtabmap/utilite/UMath.h>
00031 #include <rtabmap/utilite/UConversion.h>
00032 #include <rtabmap/utilite/UTimer.h>
00033 #include <set>
00034
00035 #include <rtabmap/core/OptimizerG2O.h>
00036 #include <rtabmap/core/util3d_transforms.h>
00037 #include <rtabmap/core/util3d_motion_estimation.h>
00038
00039 #ifdef RTABMAP_G2O
00040 #include "g2o/config.h"
00041 #include "g2o/core/sparse_optimizer.h"
00042 #include "g2o/core/block_solver.h"
00043 #include "g2o/core/factory.h"
00044 #include "g2o/core/optimization_algorithm_factory.h"
00045 #include "g2o/core/optimization_algorithm_gauss_newton.h"
00046 #include "g2o/core/optimization_algorithm_levenberg.h"
00047 #include "g2o/core/linear_solver.h"
00048 #include "g2o/types/sba/types_sba.h"
00049 #include "g2o/core/robust_kernel_impl.h"
00050 #ifdef G2O_HAVE_CSPARSE
00051 #include "g2o/solvers/csparse/linear_solver_csparse.h"
00052 #endif
00053 #include "g2o/solvers/pcg/linear_solver_pcg.h"
00054 #ifdef G2O_HAVE_CHOLMOD
00055 #include "g2o/solvers/cholmod/linear_solver_cholmod.h"
00056 #endif
00057 #include "g2o/types/slam3d/vertex_se3.h"
00058 #include "g2o/types/slam3d/edge_se3.h"
00059 #include "g2o/types/slam2d/vertex_se2.h"
00060 #include "g2o/types/slam2d/edge_se2.h"
00061
00062 typedef g2o::BlockSolver< g2o::BlockSolverTraits<-1, -1> > SlamBlockSolver;
00063 typedef g2o::LinearSolverPCG<SlamBlockSolver::PoseMatrixType> SlamLinearPCGSolver;
00064 #ifdef G2O_HAVE_CSPARSE
00065 typedef g2o::LinearSolverCSparse<SlamBlockSolver::PoseMatrixType> SlamLinearCSparseSolver;
00066 #endif
00067 #ifdef G2O_HAVE_CHOLMOD
00068 typedef g2o::LinearSolverCholmod<SlamBlockSolver::PoseMatrixType> SlamLinearCholmodSolver;
00069 #endif
00070
00071 #ifdef RTABMAP_VERTIGO
00072 #include "vertigo/g2o/edge_switchPrior.h"
00073 #include "vertigo/g2o/edge_se2Switchable.h"
00074 #include "vertigo/g2o/edge_se3Switchable.h"
00075 #include "vertigo/g2o/vertex_switchLinear.h"
00076 #endif
00077
00078 #endif // end RTABMAP_G2O
00079
00080 namespace rtabmap {
00081
00082 bool OptimizerG2O::available()
00083 {
00084 #ifdef RTABMAP_G2O
00085 return true;
00086 #else
00087 return false;
00088 #endif
00089 }
00090
00091 bool OptimizerG2O::isCSparseAvailable()
00092 {
00093 #ifdef G2O_HAVE_CSPARSE
00094 return true;
00095 #else
00096 return false;
00097 #endif
00098 }
00099
00100 bool OptimizerG2O::isCholmodAvailable()
00101 {
00102 #ifdef G2O_HAVE_CHOLMOD
00103 return true;
00104 #else
00105 return false;
00106 #endif
00107 }
00108
00109 void OptimizerG2O::parseParameters(const ParametersMap & parameters)
00110 {
00111 Optimizer::parseParameters(parameters);
00112
00113 Parameters::parse(parameters, Parameters::kg2oSolver(), solver_);
00114 Parameters::parse(parameters, Parameters::kg2oOptimizer(), optimizer_);
00115 Parameters::parse(parameters, Parameters::kg2oPixelVariance(), pixelVariance_);
00116 UASSERT(pixelVariance_ > 0.0);
00117
00118 #ifndef G2O_HAVE_CHOLMOD
00119 if(solver_ == 2)
00120 {
00121 UWARN("g2o is not built with chmold, so it cannot be used as solver. Using CSparse instead.");
00122 solver_ = 0;
00123 }
00124 #endif
00125
00126 #ifndef G2O_HAVE_CSPARSE
00127 if(solver_ == 0)
00128 {
00129 UWARN("g2o is not built with csparse, so it cannot be used as solver. Using PCG instead.");
00130 solver_ = 1;
00131 }
00132 #endif
00133 }
00134
00135 std::map<int, Transform> OptimizerG2O::optimize(
00136 int rootId,
00137 const std::map<int, Transform> & poses,
00138 const std::multimap<int, Link> & edgeConstraints,
00139 std::list<std::map<int, Transform> > * intermediateGraphes,
00140 double * finalError,
00141 int * iterationsDone)
00142 {
00143 std::map<int, Transform> optimizedPoses;
00144 #ifdef RTABMAP_G2O
00145 UDEBUG("Optimizing graph...");
00146
00147 #ifndef RTABMAP_VERTIGO
00148 if(this->isRobust())
00149 {
00150 UWARN("Vertigo robust optimization is not available! Robust optimization is now disabled.");
00151 setRobust(false);
00152 }
00153 #endif
00154
00155 optimizedPoses.clear();
00156 if(edgeConstraints.size()>=1 && poses.size()>=2 && iterations() > 0)
00157 {
00158
00159
00160 g2o::SparseOptimizer optimizer;
00161 optimizer.setVerbose(ULogger::level()==ULogger::kDebug);
00162
00163 SlamBlockSolver * blockSolver = 0;
00164
00165 if(solver_ == 2)
00166 {
00167 #ifdef G2O_HAVE_CHOLMOD
00168
00169 SlamLinearCholmodSolver * linearSolver = new SlamLinearCholmodSolver();
00170 linearSolver->setBlockOrdering(false);
00171 blockSolver = new SlamBlockSolver(linearSolver);
00172 #endif
00173 }
00174 else if(solver_ == 0)
00175 {
00176 #ifdef G2O_HAVE_CSPARSE
00177
00178 SlamLinearCSparseSolver* linearSolver = new SlamLinearCSparseSolver();
00179 linearSolver->setBlockOrdering(false);
00180 blockSolver = new SlamBlockSolver(linearSolver);
00181 #endif
00182 }
00183
00184 if(blockSolver == 0)
00185 {
00186
00187 SlamLinearPCGSolver * linearSolver = new SlamLinearPCGSolver();
00188 blockSolver = new SlamBlockSolver(linearSolver);
00189 }
00190
00191 if(optimizer_ == 1)
00192 {
00193 optimizer.setAlgorithm(new g2o::OptimizationAlgorithmGaussNewton(blockSolver));
00194 }
00195 else
00196 {
00197 optimizer.setAlgorithm(new g2o::OptimizationAlgorithmLevenberg(blockSolver));
00198 }
00199
00200 UDEBUG("fill poses to g2o...");
00201 for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
00202 {
00203 UASSERT(!iter->second.isNull());
00204 g2o::HyperGraph::Vertex * vertex = 0;
00205 if(isSlam2d())
00206 {
00207 g2o::VertexSE2 * v2 = new g2o::VertexSE2();
00208 v2->setEstimate(g2o::SE2(iter->second.x(), iter->second.y(), iter->second.theta()));
00209 if(iter->first == rootId)
00210 {
00211 v2->setFixed(true);
00212 }
00213 vertex = v2;
00214 }
00215 else
00216 {
00217 g2o::VertexSE3 * v3 = new g2o::VertexSE3();
00218
00219 Eigen::Affine3d a = iter->second.toEigen3d();
00220 Eigen::Isometry3d pose;
00221 pose = a.rotation();
00222 pose.translation() = a.translation();
00223 v3->setEstimate(pose);
00224 if(iter->first == rootId)
00225 {
00226 v3->setFixed(true);
00227 }
00228 vertex = v3;
00229 }
00230 vertex->setId(iter->first);
00231 UASSERT_MSG(optimizer.addVertex(vertex), uFormat("cannot insert vertex %d!?", iter->first).c_str());
00232 }
00233
00234 UDEBUG("fill edges to g2o...");
00235 int vertigoVertexId = poses.rbegin()->first+1;
00236 for(std::multimap<int, Link>::const_iterator iter=edgeConstraints.begin(); iter!=edgeConstraints.end(); ++iter)
00237 {
00238 int id1 = iter->first;
00239 int id2 = iter->second.to();
00240
00241 UASSERT(!iter->second.transform().isNull());
00242
00243 g2o::HyperGraph::Edge * edge = 0;
00244
00245 #ifdef RTABMAP_VERTIGO
00246 VertexSwitchLinear * v = 0;
00247 if(this->isRobust() &&
00248 iter->second.type() != Link::kNeighbor &&
00249 iter->second.type() != Link::kNeighborMerged)
00250 {
00251
00252
00253
00254
00255
00256
00257
00258 v = new VertexSwitchLinear();
00259 v->setEstimate(1.0);
00260 v->setId(vertigoVertexId++);
00261 UASSERT_MSG(optimizer.addVertex(v), uFormat("cannot insert switchable vertex %d!?", v->id()).c_str());
00262
00263
00264
00265
00266
00267
00268 EdgeSwitchPrior * prior = new EdgeSwitchPrior();
00269 prior->setMeasurement(1.0);
00270 prior->setVertex(0, v);
00271 UASSERT_MSG(optimizer.addEdge(prior), uFormat("cannot insert switchable prior edge %d!?", v->id()).c_str());
00272 }
00273 #endif
00274
00275 if(isSlam2d())
00276 {
00277 Eigen::Matrix<double, 3, 3> information = Eigen::Matrix<double, 3, 3>::Identity();
00278 if(!isCovarianceIgnored())
00279 {
00280 information(0,0) = iter->second.infMatrix().at<double>(0,0);
00281 information(0,1) = iter->second.infMatrix().at<double>(0,1);
00282 information(0,2) = iter->second.infMatrix().at<double>(0,5);
00283 information(1,0) = iter->second.infMatrix().at<double>(1,0);
00284 information(1,1) = iter->second.infMatrix().at<double>(1,1);
00285 information(1,2) = iter->second.infMatrix().at<double>(1,5);
00286 information(2,0) = iter->second.infMatrix().at<double>(5,0);
00287 information(2,1) = iter->second.infMatrix().at<double>(5,1);
00288 information(2,2) = iter->second.infMatrix().at<double>(5,5);
00289 }
00290
00291 #ifdef RTABMAP_VERTIGO
00292 if(this->isRobust() &&
00293 iter->second.type() != Link::kNeighbor &&
00294 iter->second.type() != Link::kNeighborMerged)
00295 {
00296 EdgeSE2Switchable * e = new EdgeSE2Switchable();
00297 g2o::VertexSE2* v1 = (g2o::VertexSE2*)optimizer.vertex(id1);
00298 g2o::VertexSE2* v2 = (g2o::VertexSE2*)optimizer.vertex(id2);
00299 UASSERT(v1 != 0);
00300 UASSERT(v2 != 0);
00301 e->setVertex(0, v1);
00302 e->setVertex(1, v2);
00303 e->setVertex(2, v);
00304 e->setMeasurement(g2o::SE2(iter->second.transform().x(), iter->second.transform().y(), iter->second.transform().theta()));
00305 e->setInformation(information);
00306 edge = e;
00307 }
00308 else
00309 #endif
00310 {
00311 g2o::EdgeSE2 * e = new g2o::EdgeSE2();
00312 g2o::VertexSE2* v1 = (g2o::VertexSE2*)optimizer.vertex(id1);
00313 g2o::VertexSE2* v2 = (g2o::VertexSE2*)optimizer.vertex(id2);
00314 UASSERT(v1 != 0);
00315 UASSERT(v2 != 0);
00316 e->setVertex(0, v1);
00317 e->setVertex(1, v2);
00318 e->setMeasurement(g2o::SE2(iter->second.transform().x(), iter->second.transform().y(), iter->second.transform().theta()));
00319 e->setInformation(information);
00320 edge = e;
00321 }
00322 }
00323 else
00324 {
00325 Eigen::Matrix<double, 6, 6> information = Eigen::Matrix<double, 6, 6>::Identity();
00326 if(!isCovarianceIgnored())
00327 {
00328 memcpy(information.data(), iter->second.infMatrix().data, iter->second.infMatrix().total()*sizeof(double));
00329 }
00330
00331 Eigen::Affine3d a = iter->second.transform().toEigen3d();
00332 Eigen::Isometry3d constraint;
00333 constraint = a.rotation();
00334 constraint.translation() = a.translation();
00335
00336 #ifdef RTABMAP_VERTIGO
00337 if(this->isRobust() &&
00338 iter->second.type() != Link::kNeighbor &&
00339 iter->second.type() != Link::kNeighborMerged)
00340 {
00341 EdgeSE3Switchable * e = new EdgeSE3Switchable();
00342 g2o::VertexSE3* v1 = (g2o::VertexSE3*)optimizer.vertex(id1);
00343 g2o::VertexSE3* v2 = (g2o::VertexSE3*)optimizer.vertex(id2);
00344 UASSERT(v1 != 0);
00345 UASSERT(v2 != 0);
00346 e->setVertex(0, v1);
00347 e->setVertex(1, v2);
00348 e->setVertex(2, v);
00349 e->setMeasurement(constraint);
00350 e->setInformation(information);
00351 edge = e;
00352 }
00353 else
00354 #endif
00355 {
00356 g2o::EdgeSE3 * e = new g2o::EdgeSE3();
00357 g2o::VertexSE3* v1 = (g2o::VertexSE3*)optimizer.vertex(id1);
00358 g2o::VertexSE3* v2 = (g2o::VertexSE3*)optimizer.vertex(id2);
00359 UASSERT(v1 != 0);
00360 UASSERT(v2 != 0);
00361 e->setVertex(0, v1);
00362 e->setVertex(1, v2);
00363 e->setMeasurement(constraint);
00364 e->setInformation(information);
00365 edge = e;
00366 }
00367 }
00368
00369 if (!optimizer.addEdge(edge))
00370 {
00371 delete edge;
00372 UERROR("Map: Failed adding constraint between %d and %d, skipping", id1, id2);
00373 }
00374 }
00375
00376 UDEBUG("Initial optimization...");
00377 optimizer.initializeOptimization();
00378
00379 UASSERT(optimizer.verifyInformationMatrices());
00380
00381 UINFO("g2o optimizing begin (max iterations=%d, robust=%d)", iterations(), isRobust()?1:0);
00382 int it = 0;
00383 UTimer timer;
00384 double lastError = 0.0;
00385 if(intermediateGraphes || this->epsilon() > 0.0)
00386 {
00387 for(int i=0; i<iterations(); ++i)
00388 {
00389 if(intermediateGraphes)
00390 {
00391 if(i > 0)
00392 {
00393 std::map<int, Transform> tmpPoses;
00394 if(isSlam2d())
00395 {
00396 for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
00397 {
00398 const g2o::VertexSE2* v = (const g2o::VertexSE2*)optimizer.vertex(iter->first);
00399 if(v)
00400 {
00401 float roll, pitch, yaw;
00402 iter->second.getEulerAngles(roll, pitch, yaw);
00403 Transform t(v->estimate().translation()[0], v->estimate().translation()[1], iter->second.z(), roll, pitch, v->estimate().rotation().angle());
00404 tmpPoses.insert(std::pair<int, Transform>(iter->first, t));
00405 UASSERT_MSG(!t.isNull(), uFormat("Optimized pose %d is null!?!?", iter->first).c_str());
00406 }
00407 else
00408 {
00409 UERROR("Vertex %d not found!?", iter->first);
00410 }
00411 }
00412 }
00413 else
00414 {
00415 for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
00416 {
00417 const g2o::VertexSE3* v = (const g2o::VertexSE3*)optimizer.vertex(iter->first);
00418 if(v)
00419 {
00420 Transform t = Transform::fromEigen3d(v->estimate());
00421 tmpPoses.insert(std::pair<int, Transform>(iter->first, t));
00422 UASSERT_MSG(!t.isNull(), uFormat("Optimized pose %d is null!?!?", iter->first).c_str());
00423 }
00424 else
00425 {
00426 UERROR("Vertex %d not found!?", iter->first);
00427 }
00428 }
00429 }
00430 intermediateGraphes->push_back(tmpPoses);
00431 }
00432 }
00433
00434 it += optimizer.optimize(1);
00435
00436
00437 optimizer.computeActiveErrors();
00438 double chi2 = optimizer.activeRobustChi2();
00439 UDEBUG("iteration %d: %d nodes, %d edges, chi2: %f", i, (int)optimizer.vertices().size(), (int)optimizer.edges().size(), chi2);
00440
00441 if(i>0 && optimizer.activeRobustChi2() > 1000000000000.0)
00442 {
00443 UWARN("g2o: Large optimimzation error detected (%f), aborting optimization!");
00444 return optimizedPoses;
00445 }
00446
00447 double errorDelta = lastError - chi2;
00448 if(i>0 && errorDelta < this->epsilon())
00449 {
00450 if(errorDelta < 0)
00451 {
00452 UDEBUG("Negative improvement?! Ignore and continue optimizing... (%f < %f)", errorDelta, this->epsilon());
00453 }
00454 else
00455 {
00456 UINFO("Stop optimizing, not enough improvement (%f < %f)", errorDelta, this->epsilon());
00457 break;
00458 }
00459 }
00460 else if(i==0 && chi2 < this->epsilon())
00461 {
00462 UINFO("Stop optimizing, error is already under epsilon (%f < %f)", chi2, this->epsilon());
00463 break;
00464 }
00465 lastError = chi2;
00466 }
00467 }
00468 else
00469 {
00470 it = optimizer.optimize(iterations());
00471 optimizer.computeActiveErrors();
00472 UDEBUG("%d nodes, %d edges, chi2: %f", (int)optimizer.vertices().size(), (int)optimizer.edges().size(), optimizer.activeRobustChi2());
00473 }
00474 if(finalError)
00475 {
00476 *finalError = lastError;
00477 }
00478 if(iterationsDone)
00479 {
00480 *iterationsDone = it;
00481 }
00482 UINFO("g2o optimizing end (%d iterations done, error=%f, time = %f s)", it, optimizer.activeRobustChi2(), timer.ticks());
00483
00484 if(optimizer.activeRobustChi2() > 1000000000000.0)
00485 {
00486 UWARN("g2o: Large optimimzation error detected (%f), aborting optimization!");
00487 return optimizedPoses;
00488 }
00489
00490 if(isSlam2d())
00491 {
00492 for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
00493 {
00494 const g2o::VertexSE2* v = (const g2o::VertexSE2*)optimizer.vertex(iter->first);
00495 if(v)
00496 {
00497 float roll, pitch, yaw;
00498 iter->second.getEulerAngles(roll, pitch, yaw);
00499 Transform t(v->estimate().translation()[0], v->estimate().translation()[1], iter->second.z(), roll, pitch, v->estimate().rotation().angle());
00500 optimizedPoses.insert(std::pair<int, Transform>(iter->first, t));
00501 UASSERT_MSG(!t.isNull(), uFormat("Optimized pose %d is null!?!?", iter->first).c_str());
00502 }
00503 else
00504 {
00505 UERROR("Vertex %d not found!?", iter->first);
00506 }
00507 }
00508 }
00509 else
00510 {
00511 for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
00512 {
00513 const g2o::VertexSE3* v = (const g2o::VertexSE3*)optimizer.vertex(iter->first);
00514 if(v)
00515 {
00516 Transform t = Transform::fromEigen3d(v->estimate());
00517 optimizedPoses.insert(std::pair<int, Transform>(iter->first, t));
00518 UASSERT_MSG(!t.isNull(), uFormat("Optimized pose %d is null!?!?", iter->first).c_str());
00519 }
00520 else
00521 {
00522 UERROR("Vertex %d not found!?", iter->first);
00523 }
00524 }
00525 }
00526 optimizer.clear();
00527 g2o::Factory::destroy();
00528 g2o::OptimizationAlgorithmFactory::destroy();
00529 g2o::HyperGraphActionLibrary::destroy();
00530 }
00531 else if(poses.size() == 1 || iterations() <= 0)
00532 {
00533 optimizedPoses = poses;
00534 }
00535 else
00536 {
00537 UWARN("This method should be called at least with 1 pose!");
00538 }
00539 UDEBUG("Optimizing graph...end!");
00540 #else
00541 UERROR("Not built with G2O support!");
00542 #endif
00543 return optimizedPoses;
00544 }
00545
00546 std::map<int, Transform> OptimizerG2O::optimizeBA(
00547 int rootId,
00548 const std::map<int, Transform> & poses,
00549 const std::multimap<int, Link> & links,
00550 const std::map<int, Signature> & signatures)
00551 {
00552 std::map<int, Transform> optimizedPoses;
00553 #ifdef RTABMAP_G2O
00554 UDEBUG("Optimizing graph...");
00555
00556 optimizedPoses.clear();
00557 if(links.size()>=1 && poses.size()>=2 && iterations() > 0)
00558 {
00559 g2o::SparseOptimizer optimizer;
00560 optimizer.setVerbose(ULogger::level()==ULogger::kDebug);
00561 g2o::BlockSolver_6_3::LinearSolverType * linearSolver = 0;
00562 bool robustKernel = true;
00563
00564 if(solver_ == 2)
00565 {
00566 #ifdef G2O_HAVE_CHOLMOD
00567
00568 linearSolver = new g2o::LinearSolverCholmod<g2o::BlockSolver_6_3::PoseMatrixType>();
00569 #endif
00570 }
00571 else if(solver_ == 0)
00572 {
00573 #ifdef G2O_HAVE_CSPARSE
00574
00575 linearSolver = new g2o::LinearSolverCSparse<g2o::BlockSolver_6_3::PoseMatrixType>();
00576 #endif
00577 }
00578
00579 if(linearSolver == 0)
00580 {
00581
00582 linearSolver = new g2o::LinearSolverPCG<g2o::BlockSolver_6_3::PoseMatrixType>();
00583 }
00584
00585 g2o::BlockSolver_6_3 * solver_ptr = new g2o::BlockSolver_6_3(linearSolver);
00586
00587 if(optimizer_ == 1)
00588 {
00589 optimizer.setAlgorithm(new g2o::OptimizationAlgorithmGaussNewton(solver_ptr));
00590 }
00591 else
00592 {
00593 optimizer.setAlgorithm(new g2o::OptimizationAlgorithmLevenberg(solver_ptr));
00594 }
00595
00596 std::map<int, Transform> frames = poses;
00597
00598 UDEBUG("fill poses to g2o...");
00599 std::map<int, CameraModel> models;
00600 for(std::map<int, Transform>::iterator iter=frames.begin(); iter!=frames.end(); )
00601 {
00602
00603 CameraModel model;
00604 if(uContains(signatures, iter->first))
00605 {
00606 if(signatures.at(iter->first).sensorData().cameraModels().size() == 1 && signatures.at(iter->first).sensorData().cameraModels().at(0).isValidForProjection())
00607 {
00608 model = signatures.at(iter->first).sensorData().cameraModels()[0];
00609 }
00610 else if(signatures.at(iter->first).sensorData().stereoCameraModel().isValidForProjection())
00611 {
00612 model = signatures.at(iter->first).sensorData().stereoCameraModel().left();
00613 }
00614 else
00615 {
00616 UERROR("Missing calibration for node %d", iter->first);
00617 return optimizedPoses;
00618 }
00619 }
00620 else
00621 {
00622 UERROR("Did not find node %d in cache", iter->first);
00623 }
00624
00625 if(model.isValidForProjection())
00626 {
00627 models.insert(std::make_pair(iter->first, model));
00628 Transform camPose = iter->second * model.localTransform();
00629
00630 UDEBUG("%d t=%s", iter->first, camPose.prettyPrint().c_str());
00631
00632
00633 UASSERT(!camPose.isNull());
00634 g2o::VertexCam * vCam = new g2o::VertexCam();
00635
00636 Eigen::Affine3d a = camPose.toEigen3d();
00637 g2o::SBACam cam(Eigen::Quaterniond(a.rotation()), a.translation());
00638 cam.setKcam(model.fx(), model.fy(), model.cx(), model.cy(), 0);
00639 vCam->setEstimate(cam);
00640 if(iter->first == rootId)
00641 {
00642 vCam->setFixed(true);
00643 }
00644 vCam->setId(iter->first);
00645 std::cout << cam << std::endl;
00646 UASSERT_MSG(optimizer.addVertex(vCam), uFormat("cannot insert vertex %d!?", iter->first).c_str());
00647
00648 ++iter;
00649 }
00650 else
00651 {
00652 frames.erase(iter++);
00653 }
00654 }
00655
00656 UDEBUG("fill edges to g2o and associate each 3D point to all frames observing it...");
00657 for(std::multimap<int, Link>::const_iterator iter=links.begin(); iter!=links.end(); ++iter)
00658 {
00659 Link link = iter->second;
00660 if(link.to() < link.from())
00661 {
00662 link = link.inverse();
00663 }
00664 if(uContains(signatures, link.from()) &&
00665 uContains(signatures, link.to()) &&
00666 uContains(frames, link.from()) &&
00667 uContains(frames, link.to()))
00668 {
00669
00670 int id1 = iter->first;
00671 int id2 = iter->second.to();
00672
00673 UASSERT(!iter->second.transform().isNull());
00674
00675 Eigen::Matrix<double, 6, 6> information = Eigen::Matrix<double, 6, 6>::Identity();
00676 if(!isCovarianceIgnored())
00677 {
00678 memcpy(information.data(), iter->second.infMatrix().data, iter->second.infMatrix().total()*sizeof(double));
00679 }
00680
00681
00682 Transform camLink = models.at(id1).localTransform().inverse()*iter->second.transform()*models.at(id2).localTransform();
00683
00684 UDEBUG("added edge %d=%s -> %d=%s",
00685 id1,
00686 iter->second.transform().prettyPrint().c_str(),
00687 id2,
00688 camLink.prettyPrint().c_str());
00689 Eigen::Affine3d a = camLink.toEigen3d();
00690
00691 g2o::EdgeSBACam * e = new g2o::EdgeSBACam();
00692 g2o::VertexSE3* v1 = (g2o::VertexSE3*)optimizer.vertex(id1);
00693 g2o::VertexSE3* v2 = (g2o::VertexSE3*)optimizer.vertex(id2);
00694 UASSERT(v1 != 0);
00695 UASSERT(v2 != 0);
00696 e->setVertex(0, v1);
00697 e->setVertex(1, v2);
00698 e->setMeasurement(g2o::SE3Quat(a.rotation(), a.translation()));
00699 e->setInformation(information);
00700
00701 if (!optimizer.addEdge(e))
00702 {
00703 delete e;
00704 UERROR("Map: Failed adding constraint between %d and %d, skipping", id1, id2);
00705 return optimizedPoses;
00706 }
00707 }
00708 }
00709
00710 std::map<int, cv::Point3f> points3DMap;
00711 std::map<int, std::map<int, cv::Point2f> > wordReferences;
00712 this->computeBACorrespondences(frames, links, signatures, points3DMap, wordReferences);
00713
00714 UDEBUG("fill 3D points to g2o...");
00715 int stepVertexId = frames.rbegin()->first+1;
00716 for(std::map<int, std::map<int, cv::Point2f> >::iterator iter = wordReferences.begin(); iter!=wordReferences.end(); ++iter)
00717 {
00718 const cv::Point3f & pt3d = points3DMap.at(iter->first);
00719 g2o::VertexSBAPointXYZ* vpt3d = new g2o::VertexSBAPointXYZ();
00720
00721 vpt3d->setEstimate(Eigen::Vector3d(pt3d.x, pt3d.y, pt3d.z));
00722 vpt3d->setId(stepVertexId + iter->first);
00723 vpt3d->setMarginalized(true);
00724 optimizer.addVertex(vpt3d);
00725
00726
00727 for(std::map<int, cv::Point2f>::const_iterator jter=iter->second.begin(); jter!=iter->second.end(); ++jter)
00728 {
00729 int camId = jter->first;
00730
00731 const cv::Point2f & pt = jter->second;
00732
00733 Eigen::Matrix<double,2,1> obs;
00734 obs << pt.x, pt.y;
00735
00736 UDEBUG("Added observation pt=%d to cam=%d (%f,%f)", vpt3d->id(), camId, pt.x, pt.y);
00737
00738 g2o::EdgeProjectP2MC* e = new g2o::EdgeProjectP2MC();
00739
00740 e->setVertex(0, vpt3d);
00741 e->setVertex(1, dynamic_cast<g2o::OptimizableGraph::Vertex*>(optimizer.vertex(camId)));
00742 e->setMeasurement(obs);
00743 e->setInformation(Eigen::Matrix2d::Identity() / pixelVariance_);
00744
00745 if(robustKernel)
00746 {
00747 e->setRobustKernel(new g2o::RobustKernelHuber);
00748 }
00749
00750 optimizer.addEdge(e);
00751 }
00752 }
00753
00754 UDEBUG("Initial optimization...");
00755 optimizer.initializeOptimization();
00756
00757 UASSERT(optimizer.verifyInformationMatrices());
00758
00759 UINFO("g2o optimizing begin (max iterations=%d, epsilon=%f robustKernel=%d)", iterations(), this->epsilon(), robustKernel?1:0);
00760
00761 int it = 0;
00762 UTimer timer;
00763 double lastError = 0.0;
00764 if(this->epsilon() > 0.0)
00765 {
00766 for(int i=0; i<iterations(); ++i)
00767 {
00768 it += optimizer.optimize(1);
00769
00770
00771 optimizer.computeActiveErrors();
00772 double chi2 = optimizer.activeRobustChi2();
00773 UDEBUG("iteration %d: %d nodes, %d edges, chi2: %f", i, (int)optimizer.vertices().size(), (int)optimizer.edges().size(), chi2);
00774
00775 if(i>0 && (optimizer.activeRobustChi2() > 1000000000000.0 || !uIsFinite(optimizer.activeRobustChi2())))
00776 {
00777 UWARN("g2o: Large optimization error detected (%f), aborting optimization!");
00778 return optimizedPoses;
00779 }
00780
00781 double errorDelta = lastError - chi2;
00782 if(i>0 && errorDelta < this->epsilon())
00783 {
00784 if(errorDelta < 0)
00785 {
00786 UDEBUG("Negative improvement?! Ignore and continue optimizing... (%f < %f)", errorDelta, this->epsilon());
00787 }
00788 else
00789 {
00790 UINFO("Stop optimizing, not enough improvement (%f < %f)", errorDelta, this->epsilon());
00791 break;
00792 }
00793 }
00794 else if(i==0 && chi2 < this->epsilon())
00795 {
00796 UINFO("Stop optimizing, error is already under epsilon (%f < %f)", chi2, this->epsilon());
00797 break;
00798 }
00799 lastError = chi2;
00800 }
00801 }
00802 else
00803 {
00804 it = optimizer.optimize(iterations());
00805 optimizer.computeActiveErrors();
00806 UDEBUG("%d nodes, %d edges, chi2: %f", (int)optimizer.vertices().size(), (int)optimizer.edges().size(), optimizer.activeRobustChi2());
00807 }
00808 UINFO("g2o optimizing end (%d iterations done, error=%f, time = %f s)", it, optimizer.activeRobustChi2(), timer.ticks());
00809
00810 if(optimizer.activeRobustChi2() > 1000000000000.0)
00811 {
00812 UWARN("g2o: Large optimimzation error detected (%f), aborting optimization!");
00813 return optimizedPoses;
00814 }
00815
00816 for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
00817 {
00818 const g2o::VertexCam* v = (const g2o::VertexCam*)optimizer.vertex(iter->first);
00819 if(v)
00820 {
00821 Transform t = Transform::fromEigen3d(v->estimate());
00822 UDEBUG("%d t=%s", iter->first, t.prettyPrint().c_str());
00823
00824 t *= models.at(iter->first).localTransform().inverse();
00825 optimizedPoses.insert(std::pair<int, Transform>(iter->first, t));
00826 UASSERT_MSG(!t.isNull(), uFormat("Optimized pose %d is null!?!?", iter->first).c_str());
00827 }
00828 else
00829 {
00830 UERROR("Vertex %d not found!?", iter->first);
00831 }
00832 }
00833 }
00834 else if(poses.size() == 1 || iterations() <= 0)
00835 {
00836 optimizedPoses = poses;
00837 }
00838 else
00839 {
00840 UWARN("This method should be called at least with 1 pose!");
00841 }
00842 UDEBUG("Optimizing graph...end!");
00843 #else
00844 UERROR("Not built with G2O support!");
00845 #endif
00846 return optimizedPoses;
00847 }
00848
00849 bool OptimizerG2O::saveGraph(
00850 const std::string & fileName,
00851 const std::map<int, Transform> & poses,
00852 const std::multimap<int, Link> & edgeConstraints,
00853 bool useRobustConstraints)
00854 {
00855 FILE * file = 0;
00856
00857 #ifdef _MSC_VER
00858 fopen_s(&file, fileName.c_str(), "w");
00859 #else
00860 file = fopen(fileName.c_str(), "w");
00861 #endif
00862
00863 if(file)
00864 {
00865
00866 for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
00867 {
00868 Eigen::Quaternionf q = iter->second.getQuaternionf();
00869 fprintf(file, "VERTEX_SE3:QUAT %d %f %f %f %f %f %f %f\n",
00870 iter->first,
00871 iter->second.x(),
00872 iter->second.y(),
00873 iter->second.z(),
00874 q.x(),
00875 q.y(),
00876 q.z(),
00877 q.w());
00878 }
00879
00880
00881 int virtualVertexId = poses.size()?poses.rbegin()->first+1:0;
00882 for(std::multimap<int, Link>::const_iterator iter = edgeConstraints.begin(); iter!=edgeConstraints.end(); ++iter)
00883 {
00884 std::string prefix = "EDGE_SE3:QUAT";
00885 std::string suffix = "";
00886
00887 if(useRobustConstraints &&
00888 iter->second.type() != Link::kNeighbor &&
00889 iter->second.type() != Link::kNeighborMerged)
00890 {
00891 prefix = "EDGE_SE3_SWITCHABLE";
00892 fprintf(file, "VERTEX_SWITCH %d 1\n", virtualVertexId);
00893 fprintf(file, "EDGE_SWITCH_PRIOR %d 1 1.0\n", virtualVertexId);
00894 suffix = uFormat(" %d", virtualVertexId++);
00895 }
00896
00897 Eigen::Quaternionf q = iter->second.transform().getQuaternionf();
00898 fprintf(file, "%s %d %d%s %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f\n",
00899 prefix.c_str(),
00900 iter->first,
00901 iter->second.to(),
00902 suffix.c_str(),
00903 iter->second.transform().x(),
00904 iter->second.transform().y(),
00905 iter->second.transform().z(),
00906 q.x(),
00907 q.y(),
00908 q.z(),
00909 q.w(),
00910 iter->second.infMatrix().at<double>(0,0),
00911 iter->second.infMatrix().at<double>(0,1),
00912 iter->second.infMatrix().at<double>(0,2),
00913 iter->second.infMatrix().at<double>(0,3),
00914 iter->second.infMatrix().at<double>(0,4),
00915 iter->second.infMatrix().at<double>(0,5),
00916 iter->second.infMatrix().at<double>(1,1),
00917 iter->second.infMatrix().at<double>(1,2),
00918 iter->second.infMatrix().at<double>(1,3),
00919 iter->second.infMatrix().at<double>(1,4),
00920 iter->second.infMatrix().at<double>(1,5),
00921 iter->second.infMatrix().at<double>(2,2),
00922 iter->second.infMatrix().at<double>(2,3),
00923 iter->second.infMatrix().at<double>(2,4),
00924 iter->second.infMatrix().at<double>(2,5),
00925 iter->second.infMatrix().at<double>(3,3),
00926 iter->second.infMatrix().at<double>(3,4),
00927 iter->second.infMatrix().at<double>(3,5),
00928 iter->second.infMatrix().at<double>(4,4),
00929 iter->second.infMatrix().at<double>(4,5),
00930 iter->second.infMatrix().at<double>(5,5));
00931 }
00932 UINFO("Graph saved to %s", fileName.c_str());
00933 fclose(file);
00934 }
00935 else
00936 {
00937 UERROR("Cannot save to file %s", fileName.c_str());
00938 return false;
00939 }
00940 return true;
00941 }
00942
00943 }