OptimizerG2O.cpp
Go to the documentation of this file.
00001 /*
00002 Copyright (c) 2010-2016, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
00003 All rights reserved.
00004 
00005 Redistribution and use in source and binary forms, with or without
00006 modification, are permitted provided that the following conditions are met:
00007     * Redistributions of source code must retain the above copyright
00008       notice, this list of conditions and the following disclaimer.
00009     * Redistributions in binary form must reproduce the above copyright
00010       notice, this list of conditions and the following disclaimer in the
00011       documentation and/or other materials provided with the distribution.
00012     * Neither the name of the Universite de Sherbrooke nor the
00013       names of its contributors may be used to endorse or promote products
00014       derived from this software without specific prior written permission.
00015 
00016 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
00017 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00018 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00019 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
00020 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00021 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00022 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00023 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00024 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00025 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
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                 // Apply g2o optimization
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                         //chmold
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                         //csparse
00178                         SlamLinearCSparseSolver* linearSolver = new SlamLinearCSparseSolver();
00179                         linearSolver->setBlockOrdering(false);
00180                         blockSolver = new SlamBlockSolver(linearSolver);
00181 #endif
00182                 }
00183 
00184                 if(blockSolver == 0)
00185                 {
00186                         //pcg
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                                 // For loop closure links, add switchable edges
00252 
00253                                 // create new switch variable
00254                                 // Sunderhauf IROS 2012:
00255                                 // "Since it is reasonable to initially accept all loop closure constraints,
00256                                 //  a proper and convenient initial value for all switch variables would be
00257                                 //  sij = 1 when using the linear switch function"
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                                 // create switch prior factor
00264                                 // "If the front-end is not able to assign sound individual values
00265                                 //  for Ξij , it is save to set all Ξij = 1, since this value is close
00266                                 //  to the individual optimal choice of Ξij for a large range of
00267                                 //  outliers."
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); // x-x
00281                                         information(0,1) = iter->second.infMatrix().at<double>(0,1); // x-y
00282                                         information(0,2) = iter->second.infMatrix().at<double>(0,5); // x-theta
00283                                         information(1,0) = iter->second.infMatrix().at<double>(1,0); // y-x
00284                                         information(1,1) = iter->second.infMatrix().at<double>(1,1); // y-y
00285                                         information(1,2) = iter->second.infMatrix().at<double>(1,5); // y-theta
00286                                         information(2,0) = iter->second.infMatrix().at<double>(5,0); // theta-x
00287                                         information(2,1) = iter->second.infMatrix().at<double>(5,1); // theta-y
00288                                         information(2,2) = iter->second.infMatrix().at<double>(5,5); // theta-theta
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                                 // early stop condition
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                         //chmold
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                         //csparse
00575                         linearSolver = new g2o::LinearSolverCSparse<g2o::BlockSolver_6_3::PoseMatrixType>();
00576 #endif
00577                 }
00578 
00579                 if(linearSolver == 0)
00580                 {
00581                         //pcg
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                         // Get camera model
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                                 //iter->second = (iter->second * model.localTransform()).inverse();
00630                                 UDEBUG("%d t=%s", iter->first, camPose.prettyPrint().c_str());
00631 
00632                                 // Add node's pose
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                                 // add edge
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                                 // between cameras, not base_link
00682                                 Transform camLink = models.at(id1).localTransform().inverse()*iter->second.transform()*models.at(id2).localTransform();
00683                                 //Transform t = iter->second.transform();
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; // <ID words, IDs frames + keypoint>
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                         // set observations
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                                 // early stop condition
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                                 // remove model local transform
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                 // VERTEX_SE3 id x y z qw qx qy qz
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                 //EDGE_SE3 observed_vertex_id observing_vertex_id x y z qx qy qz qw inf_11 inf_12 .. inf_16 inf_22 .. inf_66
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 } /* namespace rtabmap */


rtabmap
Author(s): Mathieu Labbe
autogenerated on Sat Jul 23 2016 11:44:17