Graph.cpp
Go to the documentation of this file.
00001 /*
00002 Copyright (c) 2010-2014, 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 #include "rtabmap/core/Graph.h"
00028 
00029 #include <rtabmap/utilite/ULogger.h>
00030 #include <rtabmap/utilite/UStl.h>
00031 #include <rtabmap/utilite/UMath.h>
00032 #include <rtabmap/utilite/UConversion.h>
00033 #include <pcl/search/kdtree.h>
00034 #include <pcl/common/eigen.h>
00035 #include <pcl/common/common.h>
00036 #include <set>
00037 #include <queue>
00038 #include "toro3d/treeoptimizer3.hh"
00039 #include "toro3d/treeoptimizer2.hh"
00040 
00041 #ifdef WITH_G2O
00042 #include "g2o/core/sparse_optimizer.h"
00043 #include "g2o/core/block_solver.h"
00044 #include "g2o/core/factory.h"
00045 #include "g2o/core/optimization_algorithm_factory.h"
00046 #include "g2o/core/optimization_algorithm_gauss_newton.h"
00047 #include "g2o/core/optimization_algorithm_levenberg.h"
00048 #include "g2o/solvers/csparse/linear_solver_csparse.h"
00049 #include "g2o/types/slam3d/vertex_se3.h"
00050 #include "g2o/types/slam3d/edge_se3.h"
00051 #include "g2o/types/slam2d/vertex_se2.h"
00052 #include "g2o/types/slam2d/edge_se2.h"
00053 #endif
00054 
00055 namespace rtabmap {
00056 
00057 namespace graph {
00058 
00060 // Graph optimizers
00062 
00063 Optimizer * Optimizer::create(const ParametersMap & parameters)
00064 {
00065         int optimizerTypeInt = Parameters::defaultRGBDOptimizeStrategy();
00066         Parameters::parse(parameters, Parameters::kRGBDOptimizeStrategy(), optimizerTypeInt);
00067         graph::Optimizer::Type type = (graph::Optimizer::Type)optimizerTypeInt;
00068 
00069         if(!G2OOptimizer::available() && type == Optimizer::kTypeG2O)
00070         {
00071                 UWARN("g2o optimizer not available. TORO will be used instead.");
00072                 type = Optimizer::kTypeTORO;
00073         }
00074         Optimizer * optimizer = 0;
00075         switch(type)
00076         {
00077         case Optimizer::kTypeG2O:
00078                 optimizer = new G2OOptimizer(parameters);
00079                 break;
00080         case Optimizer::kTypeTORO:
00081         default:
00082                 optimizer = new TOROOptimizer(parameters);
00083                 type = Optimizer::kTypeTORO;
00084                 break;
00085 
00086         }
00087         return optimizer;
00088 }
00089 
00090 Optimizer * Optimizer::create(Optimizer::Type & type, const ParametersMap & parameters)
00091 {
00092         if(!G2OOptimizer::available() && type == Optimizer::kTypeG2O)
00093         {
00094                 UWARN("g2o optimizer not available. TORO will be used instead.");
00095                 type = Optimizer::kTypeTORO;
00096         }
00097         Optimizer * optimizer = 0;
00098         switch(type)
00099         {
00100         case Optimizer::kTypeG2O:
00101                 optimizer = new G2OOptimizer(parameters);
00102                 break;
00103         case Optimizer::kTypeTORO:
00104         default:
00105                 optimizer = new TOROOptimizer(parameters);
00106                 type = Optimizer::kTypeTORO;
00107                 break;
00108 
00109         }
00110         return optimizer;
00111 }
00112 
00113 Optimizer::Optimizer(int iterations, bool slam2d, bool covarianceIgnored) :
00114                 iterations_(iterations),
00115                 slam2d_(slam2d),
00116                 covarianceIgnored_(covarianceIgnored)
00117 {
00118 }
00119 
00120 Optimizer::Optimizer(const ParametersMap & parameters) :
00121                 iterations_(100),
00122                 slam2d_(false),
00123                 covarianceIgnored_(false)
00124 {
00125         parseParameters(parameters);
00126 }
00127 
00128 void Optimizer::parseParameters(const ParametersMap & parameters)
00129 {
00130         Parameters::parse(parameters, Parameters::kRGBDOptimizeIterations(), iterations_);
00131         Parameters::parse(parameters, Parameters::kRGBDOptimizeVarianceIgnored(), covarianceIgnored_);
00132         Parameters::parse(parameters, Parameters::kRGBDOptimizeSlam2D(), slam2d_);
00133 }
00134 
00135 void Optimizer::getConnectedGraph(
00136                 int fromId,
00137                 const std::map<int, Transform> & posesIn,
00138                 const std::multimap<int, Link> & linksIn,
00139                 std::map<int, Transform> & posesOut,
00140                 std::multimap<int, Link> & linksOut,
00141                 int depth)
00142 {
00143         UASSERT(depth >= 0);
00144         UASSERT(fromId>0);
00145         UASSERT(uContains(posesIn, fromId));
00146 
00147         posesOut.clear();
00148         linksOut.clear();
00149 
00150         std::set<int> ids;
00151         std::set<int> curentDepth;
00152         std::set<int> nextDepth;
00153         nextDepth.insert(fromId);
00154         int d = 0;
00155         while((depth == 0 || d < depth) && nextDepth.size())
00156         {
00157                 curentDepth = nextDepth;
00158                 nextDepth.clear();
00159 
00160                 for(std::set<int>::iterator jter = curentDepth.begin(); jter!=curentDepth.end(); ++jter)
00161                 {
00162                         if(ids.find(*jter) == ids.end())
00163                         {
00164                                 ids.insert(*jter);
00165                                 posesOut.insert(*posesIn.find(*jter));
00166 
00167                                 for(std::multimap<int, Link>::const_iterator iter=linksIn.begin(); iter!=linksIn.end(); ++iter)
00168                                 {
00169                                         if(iter->second.from() == *jter)
00170                                         {
00171                                                 if(ids.find(iter->second.to()) == ids.end() && uContains(posesIn, iter->second.to()))
00172                                                 {
00173                                                         nextDepth.insert(iter->second.to());
00174                                                         if(depth == 0 || d < depth-1)
00175                                                         {
00176                                                                 linksOut.insert(*iter);
00177                                                         }
00178                                                         else if(curentDepth.find(iter->second.to()) != curentDepth.end() ||
00179                                                                         ids.find(iter->second.to()) != ids.end())
00180                                                         {
00181                                                                 linksOut.insert(*iter);
00182                                                         }
00183                                                 }
00184                                         }
00185                                         else if(iter->second.to() == *jter)
00186                                         {
00187                                                 if(ids.find(iter->second.from()) == ids.end() && uContains(posesIn, iter->second.from()))
00188                                                 {
00189                                                         nextDepth.insert(iter->second.from());
00190 
00191                                                         if(depth == 0 || d < depth-1)
00192                                                         {
00193                                                                 linksOut.insert(*iter);
00194                                                         }
00195                                                         else if(curentDepth.find(iter->second.from()) != curentDepth.end() ||
00196                                                                         ids.find(iter->second.from()) != ids.end())
00197                                                         {
00198                                                                 linksOut.insert(*iter);
00199                                                         }
00200                                                 }
00201                                         }
00202                                 }
00203                         }
00204                 }
00205                 ++d;
00206         }
00207 }
00208 
00210 // TORO
00212 std::map<int, Transform> TOROOptimizer::optimize(
00213                 int rootId,
00214                 const std::map<int, Transform> & poses,
00215                 const std::multimap<int, Link> & edgeConstraints,
00216                 std::list<std::map<int, Transform> > * intermediateGraphes) // contains poses after tree init to last one before the end
00217 {
00218         std::map<int, Transform> optimizedPoses;
00219         UDEBUG("Optimizing graph (pose=%d constraints=%d)...", (int)poses.size(), (int)edgeConstraints.size());
00220         if(edgeConstraints.size()>=1 && poses.size()>=2 && iterations() > 0)
00221         {
00222                 // Apply TORO optimization
00223                 AISNavigation::TreeOptimizer2 pg2;
00224                 AISNavigation::TreeOptimizer3 pg3;
00225                 pg2.verboseLevel = 0;
00226                 pg3.verboseLevel = 0;
00227 
00228                 UDEBUG("fill poses to TORO...");
00229                 if(isSlam2d())
00230                 {
00231                         for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
00232                         {
00233                                 UASSERT(!iter->second.isNull());
00234                                 AISNavigation::TreePoseGraph2::Pose p(iter->second.x(), iter->second.y(), iter->second.theta());
00235                                 AISNavigation::TreePoseGraph2::Vertex* v = pg2.addVertex(iter->first, p);
00236                                 UASSERT_MSG(v != 0, uFormat("cannot insert vertex %d!?", iter->first).c_str());
00237                         }
00238                 }
00239                 else
00240                 {
00241                         for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
00242                         {
00243                                 UASSERT(!iter->second.isNull());
00244                                 float x,y,z, roll,pitch,yaw;
00245                                 iter->second.getTranslationAndEulerAngles(x,y,z, roll,pitch,yaw);
00246                                 AISNavigation::TreePoseGraph3::Pose p(x, y, z, roll, pitch, yaw);
00247                                 AISNavigation::TreePoseGraph3::Vertex* v = pg3.addVertex(iter->first, p);
00248                                 UASSERT_MSG(v != 0, uFormat("cannot insert vertex %d!?", iter->first).c_str());
00249                                 v->transformation=AISNavigation::TreePoseGraph3::Transformation(p);
00250                         }
00251 
00252                 }
00253 
00254                 UDEBUG("fill edges to TORO...");
00255                 if(isSlam2d())
00256                 {
00257                         for(std::multimap<int, Link>::const_iterator iter=edgeConstraints.begin(); iter!=edgeConstraints.end(); ++iter)
00258                         {
00259                                 UASSERT(!iter->second.transform().isNull());
00260                                 AISNavigation::TreePoseGraph2::Pose p(iter->second.transform().x(), iter->second.transform().y(), iter->second.transform().theta());
00261                                 AISNavigation::TreePoseGraph2::InformationMatrix inf;
00262                                 //Identity:
00263                                 inf.values[0][0] = 1.0f; inf.values[0][1] = 0.0f; inf.values[0][2] = 0.0f; // x
00264                                 inf.values[1][0] = 0.0f; inf.values[1][1] = 1.0f; inf.values[1][2] = 0.0f; // y
00265                                 inf.values[2][0] = 0.0f; inf.values[2][1] = 0.0f; inf.values[2][2] = 1.0f; // theta
00266                                 if(!isCovarianceIgnored())
00267                                 {
00268                                         if(iter->second.transVariance()>0)
00269                                         {
00270                                                 inf.values[0][0] = 1.0f/iter->second.transVariance(); // x
00271                                                 inf.values[1][1] = 1.0f/iter->second.transVariance(); // y
00272                                         }
00273                                         if(iter->second.rotVariance()>0)
00274                                         {
00275                                                 inf.values[2][2] = 1.0f/iter->second.rotVariance(); // theta
00276                                         }
00277                                 }
00278 
00279                                 int id1 = iter->first;
00280                                 int id2 = iter->second.to();
00281                                 AISNavigation::TreePoseGraph2::Vertex* v1=pg2.vertex(id1);
00282                                 AISNavigation::TreePoseGraph2::Vertex* v2=pg2.vertex(id2);
00283                                 UASSERT(v1 != 0);
00284                                 UASSERT(v2 != 0);
00285                                 AISNavigation::TreePoseGraph2::Transformation t(p);
00286                                 if (!pg2.addEdge(v1, v2, t, inf))
00287                                 {
00288                                         UERROR("Map: Edge already exits between nodes %d and %d, skipping", id1, id2);
00289                                 }
00290                         }
00291                 }
00292                 else
00293                 {
00294                         for(std::multimap<int, Link>::const_iterator iter=edgeConstraints.begin(); iter!=edgeConstraints.end(); ++iter)
00295                         {
00296                                 UASSERT(!iter->second.transform().isNull());
00297                                 float x,y,z, roll,pitch,yaw;
00298                                 iter->second.transform().getTranslationAndEulerAngles(x,y,z, roll,pitch,yaw);
00299 
00300                                 AISNavigation::TreePoseGraph3::Pose p(x, y, z, roll, pitch, yaw);
00301                                 AISNavigation::TreePoseGraph3::InformationMatrix inf = DMatrix<double>::I(6);
00302                                 if(!isCovarianceIgnored())
00303                                 {
00304                                         if(iter->second.rotVariance()>0)
00305                                         {
00306                                                 inf[0][0] = 1.0f/iter->second.rotVariance(); // roll
00307                                                 inf[1][1] = 1.0f/iter->second.rotVariance(); // pitch
00308                                                 inf[2][2] = 1.0f/iter->second.rotVariance(); // yaw
00309                                         }
00310                                         if(iter->second.transVariance()>0)
00311                                         {
00312                                                 inf[3][3] = 1.0f/iter->second.transVariance(); // x
00313                                                 inf[4][4] = 1.0f/iter->second.transVariance(); // y
00314                                                 inf[5][5] = 1.0f/iter->second.transVariance(); // z
00315                                         }
00316                                 }
00317 
00318                                 int id1 = iter->first;
00319                                 int id2 = iter->second.to();
00320                                 AISNavigation::TreePoseGraph3::Vertex* v1=pg3.vertex(id1);
00321                                 AISNavigation::TreePoseGraph3::Vertex* v2=pg3.vertex(id2);
00322                                 UASSERT(v1 != 0);
00323                                 UASSERT(v2 != 0);
00324                                 AISNavigation::TreePoseGraph3::Transformation t(p);
00325                                 if (!pg3.addEdge(v1, v2, t, inf))
00326                                 {
00327                                         UERROR("Map: Edge already exits between nodes %d and %d, skipping", id1, id2);
00328                                 }
00329                         }
00330                 }
00331                 UDEBUG("buildMST...");
00332                 UASSERT(uContains(poses, rootId));
00333                 if(isSlam2d())
00334                 {
00335                         pg2.buildMST(rootId); // pg.buildSimpleTree();
00336                         pg2.initializeOnTree();
00337                         pg2.initializeTreeParameters();
00338                         UDEBUG("Building TORO tree... (if a crash happens just after this msg, "
00339                                    "TORO is not able to find the root of the graph!)");
00340                         pg2.initializeOptimization();
00341                 }
00342                 else
00343                 {
00344                         pg3.buildMST(rootId); // pg.buildSimpleTree();
00345                         pg3.initializeOnTree();
00346                         pg3.initializeTreeParameters();
00347                         UDEBUG("Building TORO tree... (if a crash happens just after this msg, "
00348                                    "TORO is not able to find the root of the graph!)");
00349                         pg3.initializeOptimization();
00350                 }
00351 
00352                 UINFO("TORO iterate begin (iterations=%d)", iterations());
00353                 for (int i=0; i<iterations(); i++)
00354                 {
00355                         if(intermediateGraphes && i>0)
00356                         {
00357                                 std::map<int, Transform> tmpPoses;
00358                                 if(isSlam2d())
00359                                 {
00360                                         for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
00361                                         {
00362                                                 AISNavigation::TreePoseGraph2::Vertex* v=pg2.vertex(iter->first);
00363                                                 float roll, pitch, yaw;
00364                                                 iter->second.getEulerAngles(roll, pitch, yaw);
00365                                                 Transform newPose(v->pose.x(), v->pose.y(), iter->second.z(), roll, pitch, v->pose.theta());
00366 
00367                                                 UASSERT_MSG(!newPose.isNull(), uFormat("Optimized pose %d is null!?!?", iter->first).c_str());
00368                                                 tmpPoses.insert(std::pair<int, Transform>(iter->first, newPose));
00369                                         }
00370                                 }
00371                                 else
00372                                 {
00373                                         for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
00374                                         {
00375                                                 AISNavigation::TreePoseGraph3::Vertex* v=pg3.vertex(iter->first);
00376                                                 AISNavigation::TreePoseGraph3::Pose pose=v->transformation.toPoseType();
00377                                                 Transform newPose(pose.x(), pose.y(), pose.z(), pose.roll(), pose.pitch(), pose.yaw());
00378 
00379                                                 UASSERT_MSG(!newPose.isNull(), uFormat("Optimized pose %d is null!?!?", iter->first).c_str());
00380                                                 tmpPoses.insert(std::pair<int, Transform>(iter->first, newPose));
00381                                         }
00382                                 }
00383                                 intermediateGraphes->push_back(tmpPoses);
00384                         }
00385                         if(isSlam2d())
00386                         {
00387                                 pg2.iterate();
00388 
00389                                 // compute the error and dump it
00390                                 double error=pg2.error();
00391                                 UDEBUG("iteration %d global error=%f error/constraint=%f", i, error, error/pg2.edges.size());
00392                         }
00393                         else
00394                         {
00395                                 pg3.iterate();
00396 
00397                                 // compute the error and dump it
00398                                 double mte, mre, are, ate;
00399                                 double error=pg3.error(&mre, &mte, &are, &ate);
00400                                 UDEBUG("i %d RotGain=%f global error=%f error/constraint=%f",
00401                                                 i, pg3.getRotGain(), error, error/pg3.edges.size());
00402                         }
00403                 }
00404                 UINFO("TORO iterate end");
00405 
00406                 if(isSlam2d())
00407                 {
00408                         for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
00409                         {
00410                                 AISNavigation::TreePoseGraph2::Vertex* v=pg2.vertex(iter->first);
00411                                 float roll, pitch, yaw;
00412                                 iter->second.getEulerAngles(roll, pitch, yaw);
00413                                 Transform newPose(v->pose.x(), v->pose.y(), iter->second.z(), roll, pitch, v->pose.theta());
00414 
00415                                 UASSERT_MSG(!newPose.isNull(), uFormat("Optimized pose %d is null!?!?", iter->first).c_str());
00416                                 optimizedPoses.insert(std::pair<int, Transform>(iter->first, newPose));
00417                         }
00418                 }
00419                 else
00420                 {
00421                         for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
00422                         {
00423                                 AISNavigation::TreePoseGraph3::Vertex* v=pg3.vertex(iter->first);
00424                                 AISNavigation::TreePoseGraph3::Pose pose=v->transformation.toPoseType();
00425                                 Transform newPose(pose.x(), pose.y(), pose.z(), pose.roll(), pose.pitch(), pose.yaw());
00426 
00427                                 UASSERT_MSG(!newPose.isNull(), uFormat("Optimized pose %d is null!?!?", iter->first).c_str());
00428                                 optimizedPoses.insert(std::pair<int, Transform>(iter->first, newPose));
00429                         }
00430                 }
00431         }
00432         else if(poses.size() == 1 || iterations() <= 0)
00433         {
00434                 optimizedPoses = poses;
00435         }
00436         else
00437         {
00438                 UWARN("This method should be called at least with 1 pose!");
00439         }
00440         UDEBUG("Optimizing graph...end!");
00441         return optimizedPoses;
00442 }
00443 
00444 bool TOROOptimizer::saveGraph(
00445                 const std::string & fileName,
00446                 const std::map<int, Transform> & poses,
00447                 const std::multimap<int, Link> & edgeConstraints)
00448 {
00449         FILE * file = 0;
00450 
00451 #ifdef _MSC_VER
00452         fopen_s(&file, fileName.c_str(), "w");
00453 #else
00454         file = fopen(fileName.c_str(), "w");
00455 #endif
00456 
00457         if(file)
00458         {
00459                 // VERTEX3 id x y z phi theta psi
00460                 for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
00461                 {
00462                         float x,y,z, yaw,pitch,roll;
00463                         pcl::getTranslationAndEulerAngles(iter->second.toEigen3f(), x,y,z, roll, pitch, yaw);
00464                         fprintf(file, "VERTEX3 %d %f %f %f %f %f %f\n",
00465                                         iter->first,
00466                                         x,
00467                                         y,
00468                                         z,
00469                                         roll,
00470                                         pitch,
00471                                         yaw);
00472                 }
00473 
00474                 //EDGE3 observed_vertex_id observing_vertex_id x y z roll pitch yaw inf_11 inf_12 .. inf_16 inf_22 .. inf_66
00475                 for(std::multimap<int, Link>::const_iterator iter = edgeConstraints.begin(); iter!=edgeConstraints.end(); ++iter)
00476                 {
00477                         float x,y,z, yaw,pitch,roll;
00478                         pcl::getTranslationAndEulerAngles(iter->second.transform().toEigen3f(), x,y,z, roll, pitch, yaw);
00479                         fprintf(file, "EDGE3 %d %d %f %f %f %f %f %f %f 0 0 0 0 0 %f 0 0 0 0 %f 0 0 0 %f 0 0 %f 0 %f\n",
00480                                         iter->first,
00481                                         iter->second.to(),
00482                                         x,
00483                                         y,
00484                                         z,
00485                                         roll,
00486                                         pitch,
00487                                         yaw,
00488                                         iter->second.rotVariance()>0?1.0f/iter->second.rotVariance():1.0f,
00489                                         iter->second.rotVariance()>0?1.0f/iter->second.rotVariance():1.0f,
00490                                         iter->second.rotVariance()>0?1.0f/iter->second.rotVariance():1.0f,
00491                                         iter->second.transVariance()>0?1.0f/iter->second.transVariance():1.0f,
00492                                         iter->second.transVariance()>0?1.0f/iter->second.transVariance():1.0f,
00493                                         iter->second.transVariance()>0?1.0f/iter->second.transVariance():1.0f);
00494                 }
00495                 UINFO("Graph saved to %s", fileName.c_str());
00496                 fclose(file);
00497         }
00498         else
00499         {
00500                 UERROR("Cannot save to file %s", fileName.c_str());
00501                 return false;
00502         }
00503         return true;
00504 }
00505 
00506 bool TOROOptimizer::loadGraph(
00507                 const std::string & fileName,
00508                 std::map<int, Transform> & poses,
00509                 std::multimap<int, Link> & edgeConstraints)
00510 {
00511         FILE * file = 0;
00512 #ifdef _MSC_VER
00513         fopen_s(&file, fileName.c_str(), "r");
00514 #else
00515         file = fopen(fileName.c_str(), "r");
00516 #endif
00517 
00518         if(file)
00519         {
00520                 char line[400];
00521                 while ( fgets (line , 400 , file) != NULL )
00522                 {
00523                         std::vector<std::string> strList = uListToVector(uSplit(uReplaceChar(line, '\n', ' '), ' '));
00524                         if(strList.size() == 8)
00525                         {
00526                                 //VERTEX3
00527                                 int id = atoi(strList[1].c_str());
00528                                 float x = uStr2Float(strList[2]);
00529                                 float y = uStr2Float(strList[3]);
00530                                 float z = uStr2Float(strList[4]);
00531                                 float roll = uStr2Float(strList[5]);
00532                                 float pitch = uStr2Float(strList[6]);
00533                                 float yaw = uStr2Float(strList[7]);
00534                                 Transform pose = Transform::fromEigen3f(pcl::getTransformation(x, y, z, roll, pitch, yaw));
00535                                 if(poses.find(id) == poses.end())
00536                                 {
00537                                         poses.insert(std::make_pair(id, pose));
00538                                 }
00539                                 else
00540                                 {
00541                                         UFATAL("Pose %d already added", id);
00542                                 }
00543                         }
00544                         else if(strList.size() == 30)
00545                         {
00546                                 //EDGE3
00547                                 int idFrom = atoi(strList[1].c_str());
00548                                 int idTo = atoi(strList[2].c_str());
00549                                 float x = uStr2Float(strList[3]);
00550                                 float y = uStr2Float(strList[4]);
00551                                 float z = uStr2Float(strList[5]);
00552                                 float roll = uStr2Float(strList[6]);
00553                                 float pitch = uStr2Float(strList[7]);
00554                                 float yaw = uStr2Float(strList[8]);
00555                                 float infR = uStr2Float(strList[9]);
00556                                 float infP = uStr2Float(strList[15]);
00557                                 float infW = uStr2Float(strList[20]);
00558                                 UASSERT_MSG(infR > 0 && infP > 0 && infW > 0, uFormat("Information matrix should not be null! line=\"%s\"", line).c_str());
00559                                 float rotVariance = infR<=infP && infR<=infW?infR:infP<=infW?infP:infW; // maximum variance
00560                                 float infX = uStr2Float(strList[24]);
00561                                 float infY = uStr2Float(strList[27]);
00562                                 float infZ = uStr2Float(strList[29]);
00563                                 UASSERT_MSG(infX > 0 && infY > 0 && infZ > 0, uFormat("Information matrix should not be null! line=\"%s\"", line).c_str());
00564                                 float transVariance = 1.0f/(infX<=infY && infX<=infZ?infX:infY<=infW?infY:infZ); // maximum variance
00565                                 UINFO("id=%d rotV=%f transV=%f", idFrom, rotVariance, transVariance);
00566                                 Transform transform = Transform::fromEigen3f(pcl::getTransformation(x, y, z, roll, pitch, yaw));
00567                                 if(poses.find(idFrom) != poses.end() && poses.find(idTo) != poses.end())
00568                                 {
00569                                         //Link type is unknown
00570                                         Link link(idFrom, idTo, Link::kUndef, transform, rotVariance, transVariance);
00571                                         edgeConstraints.insert(std::pair<int, Link>(idFrom, link));
00572                                 }
00573                                 else
00574                                 {
00575                                         UFATAL("Referred poses from the link not exist!");
00576                                 }
00577                         }
00578                         else if(strList.size())
00579                         {
00580                                 UFATAL("Error parsing graph file %s on line \"%s\" (strList.size()=%d)", fileName.c_str(), line, (int)strList.size());
00581                         }
00582                 }
00583 
00584                 UINFO("Graph loaded from %s", fileName.c_str());
00585                 fclose(file);
00586         }
00587         else
00588         {
00589                 UERROR("Cannot open file %s", fileName.c_str());
00590                 return false;
00591         }
00592         return true;
00593 }
00594 
00595 
00597 // g2o
00599 bool G2OOptimizer::available()
00600 {
00601 #ifdef WITH_G2O
00602         return true;
00603 #else
00604         return false;
00605 #endif
00606 }
00607 
00608 std::map<int, Transform> G2OOptimizer::optimize(
00609                 int rootId,
00610                 const std::map<int, Transform> & poses,
00611                 const std::multimap<int, Link> & edgeConstraints,
00612                 std::list<std::map<int, Transform> > * intermediateGraphes)
00613 {
00614         std::map<int, Transform> optimizedPoses;
00615 #ifdef WITH_G2O
00616         UDEBUG("Optimizing graph...");
00617         optimizedPoses.clear();
00618         if(edgeConstraints.size()>=1 && poses.size()>=2 && iterations() > 0)
00619         {
00620                 // Apply g2o optimization
00621 
00622                 // create the linear solver
00623                 g2o::BlockSolverX::LinearSolverType * linearSolver = new g2o::LinearSolverCSparse<g2o::BlockSolverX::PoseMatrixType>();
00624 
00625                 // create the block solver on top of the linear solver
00626                 g2o::BlockSolverX* blockSolver = new g2o::BlockSolverX(linearSolver);
00627 
00628                 // create the algorithm to carry out the optimization
00629                 //g2o::OptimizationAlgorithmGaussNewton* optimizationAlgorithm = new g2o::OptimizationAlgorithmGaussNewton(blockSolver);
00630                 g2o::OptimizationAlgorithmLevenberg* optimizationAlgorithm = new g2o::OptimizationAlgorithmLevenberg(blockSolver);
00631 
00632                 // create the optimizer to load the data and carry out the optimization
00633                 g2o::SparseOptimizer optimizer;
00634                 optimizer.setVerbose(false);
00635                 optimizer.setAlgorithm(optimizationAlgorithm);
00636 
00637                 UDEBUG("fill poses to g2o...");
00638                 for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
00639                 {
00640                         UASSERT(!iter->second.isNull());
00641                         g2o::HyperGraph::Vertex * vertex = 0;
00642                         if(isSlam2d())
00643                         {
00644                                 g2o::VertexSE2 * v2 = new g2o::VertexSE2();
00645                                 v2->setEstimate(g2o::SE2(iter->second.x(), iter->second.y(), iter->second.theta()));
00646                                 vertex = v2;
00647                         }
00648                         else
00649                         {
00650                                 g2o::VertexSE3 * v3 = new g2o::VertexSE3();
00651                                 Eigen::Isometry3d pose;
00652                                 Eigen::Affine3d a = iter->second.toEigen3d();
00653                                 pose.translation() = a.translation();
00654                                 pose.linear() = a.rotation();
00655                                 v3->setEstimate(pose);
00656                                 vertex = v3;
00657                         }
00658                         vertex->setId(iter->first);
00659                         UASSERT_MSG(optimizer.addVertex(vertex), uFormat("cannot insert vertex %d!?", iter->first).c_str());
00660                 }
00661 
00662                 UDEBUG("fill edges to g2o...");
00663                 for(std::multimap<int, Link>::const_iterator iter=edgeConstraints.begin(); iter!=edgeConstraints.end(); ++iter)
00664                 {
00665                         int id1 = iter->first;
00666                         int id2 = iter->second.to();
00667 
00668                         UASSERT(!iter->second.transform().isNull());
00669 
00670                         g2o::HyperGraph::Edge * edge = 0;
00671 
00672                         if(isSlam2d())
00673                         {
00674                                 Eigen::Matrix<double, 3, 3> information = Eigen::Matrix<double, 3, 3>::Identity();
00675                                 if(!isCovarianceIgnored())
00676                                 {
00677                                         if(iter->second.transVariance()>0)
00678                                         {
00679                                                 information(0,0) = 1.0f/iter->second.transVariance(); // x
00680                                                 information(1,1) = 1.0f/iter->second.transVariance(); // y
00681                                         }
00682                                         if(iter->second.rotVariance()>0)
00683                                         {
00684                                                 information(2,2) = 1.0f/iter->second.rotVariance(); // theta
00685                                         }
00686                                 }
00687 
00688                                 g2o::EdgeSE2 * e = new g2o::EdgeSE2();
00689                                 g2o::VertexSE2* v1 = (g2o::VertexSE2*)optimizer.vertex(id1);
00690                                 g2o::VertexSE2* v2 = (g2o::VertexSE2*)optimizer.vertex(id2);
00691                                 UASSERT(v1 != 0);
00692                                 UASSERT(v2 != 0);
00693                                 e->setVertex(0, v1);
00694                                 e->setVertex(1, v2);
00695                                 e->setMeasurement(g2o::SE2(iter->second.transform().x(), iter->second.transform().y(), iter->second.transform().theta()));
00696                                 e->setInformation(information);
00697                                 edge = e;
00698                         }
00699                         else
00700                         {
00701                                 Eigen::Matrix<double, 6, 6> information = Eigen::Matrix<double, 6, 6>::Identity();
00702                                 if(!isCovarianceIgnored())
00703                                 {
00704                                         if(iter->second.transVariance()>0)
00705                                         {
00706                                                 information(0,0) = 1.0f/iter->second.transVariance(); // x
00707                                                 information(1,1) = 1.0f/iter->second.transVariance(); // y
00708                                                 information(2,2) = 1.0f/iter->second.transVariance(); // z
00709                                         }
00710                                         if(iter->second.rotVariance()>0)
00711                                         {
00712                                                 information(3,3) = 1.0f/iter->second.rotVariance(); // roll
00713                                                 information(4,4) = 1.0f/iter->second.rotVariance(); // pitch
00714                                                 information(5,5) = 1.0f/iter->second.rotVariance(); // yaw
00715                                         }
00716                                 }
00717 
00718                                 Eigen::Affine3d a = iter->second.transform().toEigen3d();
00719                                 Eigen::Isometry3d constraint;
00720                                 constraint.translation() = a.translation();
00721                                 constraint.linear() = a.rotation();
00722 
00723                                 g2o::EdgeSE3 * e = new g2o::EdgeSE3();
00724                                 g2o::VertexSE3* v1 = (g2o::VertexSE3*)optimizer.vertex(id1);
00725                                 g2o::VertexSE3* v2 = (g2o::VertexSE3*)optimizer.vertex(id2);
00726                                 UASSERT(v1 != 0);
00727                                 UASSERT(v2 != 0);
00728                                 e->setVertex(0, v1);
00729                                 e->setVertex(1, v2);
00730                                 e->setMeasurement(constraint);
00731                                 e->setInformation(information);
00732                                 edge = e;
00733                         }
00734 
00735                         if (!optimizer.addEdge(edge))
00736                         {
00737                                 delete edge;
00738                                 UERROR("Map: Failed adding constraint between %d and %d, skipping", id1, id2);
00739                         }
00740                 }
00741 
00742                 UDEBUG("Initial optimization...");
00743                 UASSERT(uContains(poses, rootId));
00744                 if(isSlam2d())
00745                 {
00746                         g2o::VertexSE2* firstRobotPose = (g2o::VertexSE2*)optimizer.vertex(rootId);
00747                         UASSERT(firstRobotPose != 0);
00748                         firstRobotPose->setFixed(true);
00749                 }
00750                 else
00751                 {
00752                         g2o::VertexSE3* firstRobotPose = (g2o::VertexSE3*)optimizer.vertex(rootId);
00753                         UASSERT(firstRobotPose != 0);
00754                         firstRobotPose->setFixed(true);
00755                 }
00756 
00757                 UINFO("g2o iterate begin (max iterations=%d)", iterations());
00758                 int it = 0;
00759                 if(intermediateGraphes)
00760                 {
00761                         optimizer.initializeOptimization();
00762                         for(int i=0; i<iterations(); ++i)
00763                         {
00764                                 if(i > 0)
00765                                 {
00766                                         std::map<int, Transform> tmpPoses;
00767                                         if(isSlam2d())
00768                                         {
00769                                                 for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
00770                                                 {
00771                                                         const g2o::VertexSE2* v = (const g2o::VertexSE2*)optimizer.vertex(iter->first);
00772                                                         if(v)
00773                                                         {
00774                                                                 float roll, pitch, yaw;
00775                                                                 iter->second.getEulerAngles(roll, pitch, yaw);
00776                                                                 Transform t(v->estimate().translation()[0], v->estimate().translation()[1], iter->second.z(), roll, pitch, v->estimate().rotation().angle());
00777                                                                 tmpPoses.insert(std::pair<int, Transform>(iter->first, t));
00778                                                                 UASSERT_MSG(!t.isNull(), uFormat("Optimized pose %d is null!?!?", iter->first).c_str());
00779                                                         }
00780                                                         else
00781                                                         {
00782                                                                 UERROR("Vertex %d not found!?", iter->first);
00783                                                         }
00784                                                 }
00785                                         }
00786                                         else
00787                                         {
00788                                                 for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
00789                                                 {
00790                                                         const g2o::VertexSE3* v = (const g2o::VertexSE3*)optimizer.vertex(iter->first);
00791                                                         if(v)
00792                                                         {
00793                                                                 Transform t = Transform::fromEigen3d(v->estimate());
00794                                                                 tmpPoses.insert(std::pair<int, Transform>(iter->first, t));
00795                                                                 UASSERT_MSG(!t.isNull(), uFormat("Optimized pose %d is null!?!?", iter->first).c_str());
00796                                                         }
00797                                                         else
00798                                                         {
00799                                                                 UERROR("Vertex %d not found!?", iter->first);
00800                                                         }
00801                                                 }
00802                                         }
00803                                         intermediateGraphes->push_back(tmpPoses);
00804                                 }
00805 
00806                                 it += optimizer.optimize(1);
00807                                 if(ULogger::level() == ULogger::kDebug)
00808                                 {
00809                                         optimizer.computeActiveErrors();
00810                                         UDEBUG("iteration %d: %d nodes, %d edges, chi2: %f", i, (int)optimizer.vertices().size(), (int)optimizer.edges().size(), optimizer.chi2());
00811                                 }
00812                         }
00813                 }
00814                 else
00815                 {
00816                         optimizer.initializeOptimization();
00817                         it = optimizer.optimize(iterations());
00818                         optimizer.computeActiveErrors();
00819                         UDEBUG("%d nodes, %d edges, chi2: %f", (int)optimizer.vertices().size(), (int)optimizer.edges().size(), optimizer.chi2());
00820                 }
00821                 UINFO("g2o iterate end (%d iterations done)", it);
00822 
00823                 if(isSlam2d())
00824                 {
00825                         for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
00826                         {
00827                                 const g2o::VertexSE2* v = (const g2o::VertexSE2*)optimizer.vertex(iter->first);
00828                                 if(v)
00829                                 {
00830                                         float roll, pitch, yaw;
00831                                         iter->second.getEulerAngles(roll, pitch, yaw);
00832                                         Transform t(v->estimate().translation()[0], v->estimate().translation()[1], iter->second.z(), roll, pitch, v->estimate().rotation().angle());
00833                                         optimizedPoses.insert(std::pair<int, Transform>(iter->first, t));
00834                                         UASSERT_MSG(!t.isNull(), uFormat("Optimized pose %d is null!?!?", iter->first).c_str());
00835                                 }
00836                                 else
00837                                 {
00838                                         UERROR("Vertex %d not found!?", iter->first);
00839                                 }
00840                         }
00841                 }
00842                 else
00843                 {
00844                         for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
00845                         {
00846                                 const g2o::VertexSE3* v = (const g2o::VertexSE3*)optimizer.vertex(iter->first);
00847                                 if(v)
00848                                 {
00849                                         Transform t = Transform::fromEigen3d(v->estimate());
00850                                         optimizedPoses.insert(std::pair<int, Transform>(iter->first, t));
00851                                         UASSERT_MSG(!t.isNull(), uFormat("Optimized pose %d is null!?!?", iter->first).c_str());
00852                                 }
00853                                 else
00854                                 {
00855                                         UERROR("Vertex %d not found!?", iter->first);
00856                                 }
00857                         }
00858                 }
00859                 optimizer.clear();
00860                 g2o::Factory::destroy();
00861                 g2o::OptimizationAlgorithmFactory::destroy();
00862                 g2o::HyperGraphActionLibrary::destroy();
00863         }
00864         else if(poses.size() == 1 || iterations() <= 0)
00865         {
00866                 optimizedPoses = poses;
00867         }
00868         else
00869         {
00870                 UWARN("This method should be called at least with 1 pose!");
00871         }
00872         UDEBUG("Optimizing graph...end!");
00873 #else
00874         UERROR("Not built with G2O support!");
00875 #endif
00876         return optimizedPoses;
00877 }
00878 
00880 // Graph utilities
00882 std::multimap<int, Link>::iterator findLink(
00883                 std::multimap<int, Link> & links,
00884                 int from,
00885                 int to)
00886 {
00887         std::multimap<int, Link>::iterator iter = links.find(from);
00888         while(iter != links.end() && iter->first == from)
00889         {
00890                 if(iter->second.to() == to)
00891                 {
00892                         return iter;
00893                 }
00894                 ++iter;
00895         }
00896 
00897         // let's try to -> from
00898         iter = links.find(to);
00899         while(iter != links.end() && iter->first == to)
00900         {
00901                 if(iter->second.to() == from)
00902                 {
00903                         return iter;
00904                 }
00905                 ++iter;
00906         }
00907         return links.end();
00908 }
00909 
00910 std::multimap<int, int>::iterator findLink(
00911                 std::multimap<int, int> & links,
00912                 int from,
00913                 int to)
00914 {
00915         std::multimap<int, int>::iterator iter = links.find(from);
00916         while(iter != links.end() && iter->first == from)
00917         {
00918                 if(iter->second == to)
00919                 {
00920                         return iter;
00921                 }
00922                 ++iter;
00923         }
00924 
00925         // let's try to -> from
00926         iter = links.find(to);
00927         while(iter != links.end() && iter->first == to)
00928         {
00929                 if(iter->second == from)
00930                 {
00931                         return iter;
00932                 }
00933                 ++iter;
00934         }
00935         return links.end();
00936 }
00937 
00938 std::map<int, Transform> radiusPosesFiltering(
00939                 const std::map<int, Transform> & poses,
00940                 float radius,
00941                 float angle,
00942                 bool keepLatest)
00943 {
00944         if(poses.size() > 1 && radius > 0.0f)
00945         {
00946                 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
00947                 cloud->resize(poses.size());
00948                 int i=0;
00949                 for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter, ++i)
00950                 {
00951                         (*cloud)[i] = pcl::PointXYZ(iter->second.x(), iter->second.y(), iter->second.z());
00952                         UASSERT_MSG(pcl::isFinite((*cloud)[i]), uFormat("Invalid pose (%d) %s", iter->first, iter->second.prettyPrint().c_str()).c_str());
00953                 }
00954 
00955                 // radius filtering
00956                 std::vector<int> names = uKeys(poses);
00957                 std::vector<Transform> transforms = uValues(poses);
00958 
00959                 pcl::search::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZ> (false));
00960                 tree->setInputCloud(cloud);
00961                 std::set<int> indicesChecked;
00962                 std::set<int> indicesKept;
00963 
00964                 for(unsigned int i=0; i<cloud->size(); ++i)
00965                 {
00966                         if(indicesChecked.find(i) == indicesChecked.end())
00967                         {
00968                                 std::vector<int> kIndices;
00969                                 std::vector<float> kDistances;
00970                                 tree->radiusSearch(cloud->at(i), radius, kIndices, kDistances);
00971 
00972                                 std::set<int> cloudIndices;
00973                                 const Transform & currentT = transforms.at(i);
00974                                 Eigen::Vector3f vA = currentT.toEigen3f().rotation()*Eigen::Vector3f(1,0,0);
00975                                 for(unsigned int j=0; j<kIndices.size(); ++j)
00976                                 {
00977                                         if(indicesChecked.find(kIndices[j]) == indicesChecked.end())
00978                                         {
00979                                                 if(angle > 0.0f)
00980                                                 {
00981                                                         const Transform & checkT = transforms.at(kIndices[j]);
00982                                                         // same orientation?
00983                                                         Eigen::Vector3f vB = checkT.toEigen3f().rotation()*Eigen::Vector3f(1,0,0);
00984                                                         double a = pcl::getAngle3D(Eigen::Vector4f(vA[0], vA[1], vA[2], 0), Eigen::Vector4f(vB[0], vB[1], vB[2], 0));
00985                                                         if(a <= angle)
00986                                                         {
00987                                                                 cloudIndices.insert(kIndices[j]);
00988                                                         }
00989                                                 }
00990                                                 else
00991                                                 {
00992                                                         cloudIndices.insert(kIndices[j]);
00993                                                 }
00994                                         }
00995                                 }
00996 
00997                                 if(keepLatest)
00998                                 {
00999                                         bool lastAdded = false;
01000                                         for(std::set<int>::reverse_iterator iter = cloudIndices.rbegin(); iter!=cloudIndices.rend(); ++iter)
01001                                         {
01002                                                 if(!lastAdded)
01003                                                 {
01004                                                         indicesKept.insert(*iter);
01005                                                         lastAdded = true;
01006                                                 }
01007                                                 indicesChecked.insert(*iter);
01008                                         }
01009                                 }
01010                                 else
01011                                 {
01012                                         bool firstAdded = false;
01013                                         for(std::set<int>::iterator iter = cloudIndices.begin(); iter!=cloudIndices.end(); ++iter)
01014                                         {
01015                                                 if(!firstAdded)
01016                                                 {
01017                                                         indicesKept.insert(*iter);
01018                                                         firstAdded = true;
01019                                                 }
01020                                                 indicesChecked.insert(*iter);
01021                                         }
01022                                 }
01023                         }
01024                 }
01025 
01026                 //pcl::IndicesPtr indicesOut(new std::vector<int>);
01027                 //indicesOut->insert(indicesOut->end(), indicesKept.begin(), indicesKept.end());
01028                 UINFO("Cloud filtered In = %d, Out = %d", cloud->size(), indicesKept.size());
01029                 //pcl::io::savePCDFile("duplicateIn.pcd", *cloud);
01030                 //pcl::io::savePCDFile("duplicateOut.pcd", *cloud, *indicesOut);
01031 
01032                 std::map<int, Transform> keptPoses;
01033                 for(std::set<int>::iterator iter = indicesKept.begin(); iter!=indicesKept.end(); ++iter)
01034                 {
01035                         keptPoses.insert(std::make_pair(names.at(*iter), transforms.at(*iter)));
01036                 }
01037 
01038                 return keptPoses;
01039         }
01040         else
01041         {
01042                 return poses;
01043         }
01044 }
01045 
01046 std::multimap<int, int> radiusPosesClustering(const std::map<int, Transform> & poses, float radius, float angle)
01047 {
01048         std::multimap<int, int> clusters;
01049         if(poses.size() > 1 && radius > 0.0f)
01050         {
01051                 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
01052                 cloud->resize(poses.size());
01053                 int i=0;
01054                 for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter, ++i)
01055                 {
01056                         (*cloud)[i] = pcl::PointXYZ(iter->second.x(), iter->second.y(), iter->second.z());
01057                         UASSERT_MSG(pcl::isFinite((*cloud)[i]), uFormat("Invalid pose (%d) %s", iter->first, iter->second.prettyPrint().c_str()).c_str());
01058                 }
01059 
01060                 // radius clustering (nearest neighbors)
01061                 std::vector<int> ids = uKeys(poses);
01062                 std::vector<Transform> transforms = uValues(poses);
01063 
01064                 pcl::search::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZ> (false));
01065                 tree->setInputCloud(cloud);
01066 
01067                 for(unsigned int i=0; i<cloud->size(); ++i)
01068                 {
01069                         std::vector<int> kIndices;
01070                         std::vector<float> kDistances;
01071                         tree->radiusSearch(cloud->at(i), radius, kIndices, kDistances);
01072 
01073                         std::set<int> cloudIndices;
01074                         const Transform & currentT = transforms.at(i);
01075                         Eigen::Vector3f vA = currentT.toEigen3f().rotation()*Eigen::Vector3f(1,0,0);
01076                         for(unsigned int j=0; j<kIndices.size(); ++j)
01077                         {
01078                                 if((int)i != kIndices[j])
01079                                 {
01080                                         if(angle > 0.0f)
01081                                         {
01082                                                 const Transform & checkT = transforms.at(kIndices[j]);
01083                                                 // same orientation?
01084                                                 Eigen::Vector3f vB = checkT.toEigen3f().rotation()*Eigen::Vector3f(1,0,0);
01085                                                 double a = pcl::getAngle3D(Eigen::Vector4f(vA[0], vA[1], vA[2], 0), Eigen::Vector4f(vB[0], vB[1], vB[2], 0));
01086                                                 if(a <= angle)
01087                                                 {
01088                                                         clusters.insert(std::make_pair(ids[i], ids[kIndices[j]]));
01089                                                 }
01090                                         }
01091                                         else
01092                                         {
01093                                                 clusters.insert(std::make_pair(ids[i], ids[kIndices[j]]));
01094                                         }
01095                                 }
01096                         }
01097                 }
01098         }
01099         return clusters;
01100 }
01101 
01102 
01103 class Node
01104 {
01105 public:
01106         Node(int id, int fromId, const rtabmap::Transform & pose) :
01107                 id_(id),
01108                 costSoFar_(0.0f),
01109                 distToEnd_(0.0f),
01110                 fromId_(fromId),
01111                 closed_(false),
01112                 pose_(pose)
01113         {}
01114 
01115         int id() const {return id_;}
01116         int fromId() const {return fromId_;}
01117         bool isClosed() const {return closed_;}
01118         bool isOpened() const {return !closed_;}
01119         float costSoFar() const {return costSoFar_;} // Dijkstra cost
01120         float distToEnd() const {return distToEnd_;} // Breath-first cost
01121         float totalCost() const {return costSoFar_ + distToEnd_;} // A* cost
01122         rtabmap::Transform pose() const {return pose_;}
01123         float distFrom(const rtabmap::Transform & pose) const
01124         {
01125                 return pose_.getDistance(pose); // use sqrt distance
01126         }
01127 
01128         void setClosed(bool closed) {closed_ = closed;}
01129         void setFromId(int fromId) {fromId_ = fromId;}
01130         void setCostSoFar(float costSoFar) {costSoFar_ = costSoFar;}
01131         void setDistToEnd(float distToEnd) {distToEnd_ = distToEnd;}
01132 
01133 private:
01134         int id_;
01135         float costSoFar_;
01136         float distToEnd_;
01137         int fromId_;
01138         bool closed_;
01139         rtabmap::Transform pose_;
01140 };
01141 
01142 typedef std::pair<int, float> Pair; // first is id, second is cost
01143 struct Order
01144 {
01145     bool operator()(Pair const& a, Pair const& b) const
01146     {
01147         return a.second > b.second;
01148     }
01149 };
01150 
01151 std::list<std::pair<int, Transform> > computePath(
01152                         const std::map<int, rtabmap::Transform> & poses,
01153                         const std::multimap<int, int> & links,
01154                         int from,
01155                         int to,
01156                         bool updateNewCosts)
01157 {
01158         std::list<std::pair<int, Transform> > path;
01159 
01160         //A*
01161         int startNode = from;
01162         int endNode = to;
01163         rtabmap::Transform endPose = poses.at(endNode);
01164         std::map<int, Node> nodes;
01165         nodes.insert(std::make_pair(startNode, Node(startNode, 0, poses.at(startNode))));
01166         std::priority_queue<Pair, std::vector<Pair>, Order> pq;
01167         std::multimap<float, int> pqmap;
01168         if(updateNewCosts)
01169         {
01170                 pqmap.insert(std::make_pair(0, startNode));
01171         }
01172         else
01173         {
01174                 pq.push(Pair(startNode, 0));
01175         }
01176 
01177         while((updateNewCosts && pqmap.size()) || (!updateNewCosts && pq.size()))
01178         {
01179                 Node * currentNode;
01180                 if(updateNewCosts)
01181                 {
01182                         currentNode = &nodes.find(pqmap.begin()->second)->second;
01183                         pqmap.erase(pqmap.begin());
01184                 }
01185                 else
01186                 {
01187                         currentNode = &nodes.find(pq.top().first)->second;
01188                         pq.pop();
01189                 }
01190 
01191                 currentNode->setClosed(true);
01192 
01193                 if(currentNode->id() == endNode)
01194                 {
01195                         while(currentNode->id()!=startNode)
01196                         {
01197                                 path.push_front(std::make_pair(currentNode->id(), currentNode->pose()));
01198                                 currentNode = &nodes.find(currentNode->fromId())->second;
01199                         }
01200                         path.push_front(std::make_pair(startNode, poses.at(startNode)));
01201                         break;
01202                 }
01203 
01204                 // lookup neighbors
01205                 for(std::multimap<int, int>::const_iterator iter = links.find(currentNode->id());
01206                         iter!=links.end() && iter->first == currentNode->id();
01207                         ++iter)
01208                 {
01209                         std::map<int, Node>::iterator nodeIter = nodes.find(iter->second);
01210                         if(nodeIter == nodes.end())
01211                         {
01212                                 std::map<int, rtabmap::Transform>::const_iterator poseIter = poses.find(iter->second);
01213                                 UASSERT(poseIter != poses.end());
01214                                 Node n(iter->second, currentNode->id(), poseIter->second);
01215                                 n.setCostSoFar(currentNode->costSoFar() + currentNode->distFrom(poseIter->second));
01216                                 n.setDistToEnd(n.distFrom(endPose));
01217                                 nodes.insert(std::make_pair(iter->second, n));
01218                                 if(updateNewCosts)
01219                                 {
01220                                         pqmap.insert(std::make_pair(n.totalCost(), n.id()));
01221                                 }
01222                                 else
01223                                 {
01224                                         pq.push(Pair(n.id(), n.totalCost()));
01225                                 }
01226                         }
01227                         else if(updateNewCosts && nodeIter->second.isOpened())
01228                         {
01229                                 float newCostSoFar = currentNode->costSoFar() + currentNode->distFrom(nodeIter->second.pose());
01230                                 if(nodeIter->second.costSoFar() > newCostSoFar)
01231                                 {
01232                                         // update the cost in the priority queue
01233                                         for(std::multimap<float, int>::iterator mapIter=pqmap.begin(); mapIter!=pqmap.end(); ++mapIter)
01234                                         {
01235                                                 if(mapIter->second == nodeIter->first)
01236                                                 {
01237                                                         pqmap.erase(mapIter);
01238                                                         nodeIter->second.setCostSoFar(newCostSoFar);
01239                                                         pqmap.insert(std::make_pair(nodeIter->second.totalCost(), nodeIter->first));
01240                                                         break;
01241                                                 }
01242                                         }
01243                                 }
01244                         }
01245                 }
01246         }
01247         return path;
01248 }
01249 
01250 int findNearestNode(
01251                 const std::map<int, rtabmap::Transform> & nodes,
01252                 const rtabmap::Transform & targetPose)
01253 {
01254         int id = 0;
01255         if(nodes.size() && !targetPose.isNull())
01256         {
01257                 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
01258                 cloud->resize(nodes.size());
01259                 std::vector<int> ids(nodes.size());
01260                 int oi = 0;
01261                 for(std::map<int, Transform>::const_iterator iter = nodes.begin(); iter!=nodes.end(); ++iter)
01262                 {
01263                         (*cloud)[oi] = pcl::PointXYZ(iter->second.x(), iter->second.y(), iter->second.z());
01264                         ids[oi++] = iter->first;
01265                 }
01266 
01267                 std::map<int, float> foundNodes;
01268                 if(cloud->size())
01269                 {
01270                         pcl::search::KdTree<pcl::PointXYZ>::Ptr kdTree(new pcl::search::KdTree<pcl::PointXYZ>);
01271                         kdTree->setInputCloud(cloud);
01272                         std::vector<int> ind;
01273                         std::vector<float> dist;
01274                         pcl::PointXYZ pt(targetPose.x(), targetPose.y(), targetPose.z());
01275                         kdTree->nearestKSearch(pt, 1, ind, dist);
01276                         if(ind.size() && dist.size() && ind[0] >= 0)
01277                         {
01278                                 UDEBUG("Nearest node = %d: %f", ids[ind[0]], dist[0]);
01279                                 id = ids[ind[0]];
01280                         }
01281                 }
01282         }
01283         return id;
01284 }
01285 
01286 // return <id, sqrd distance>, excluding query
01287 std::map<int, float> getNodesInRadius(
01288                 int nodeId,
01289                 const std::map<int, Transform> & nodes,
01290                 float radius)
01291 {
01292         UASSERT(uContains(nodes, nodeId));
01293         std::map<int, float> foundNodes;
01294         if(nodes.size() <= 1)
01295         {
01296                 return foundNodes;
01297         }
01298 
01299         pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
01300         cloud->resize(nodes.size());
01301         std::vector<int> ids(nodes.size());
01302         int oi = 0;
01303         for(std::map<int, Transform>::const_iterator iter = nodes.begin(); iter!=nodes.end(); ++iter)
01304         {
01305                 if(iter->first != nodeId)
01306                 {
01307                         (*cloud)[oi] = pcl::PointXYZ(iter->second.x(), iter->second.y(), iter->second.z());
01308                         UASSERT_MSG(pcl::isFinite((*cloud)[oi]), uFormat("Invalid pose (%d) %s", iter->first, iter->second.prettyPrint().c_str()).c_str());
01309                         ids[oi] = iter->first;
01310                         ++oi;
01311                 }
01312         }
01313         cloud->resize(oi);
01314         ids.resize(oi);
01315 
01316         Transform fromT = nodes.at(nodeId);
01317 
01318         if(cloud->size())
01319         {
01320                 pcl::search::KdTree<pcl::PointXYZ>::Ptr kdTree(new pcl::search::KdTree<pcl::PointXYZ>);
01321                 kdTree->setInputCloud(cloud);
01322                 std::vector<int> ind;
01323                 std::vector<float> dist;
01324                 pcl::PointXYZ pt(fromT.x(), fromT.y(), fromT.z());
01325                 kdTree->radiusSearch(pt, radius, ind, dist, 0);
01326                 for(unsigned int i=0; i<ind.size(); ++i)
01327                 {
01328                         if(ind[i] >=0)
01329                         {
01330                                 UDEBUG("Inlier %d: %f", ids[ind[i]], sqrt(dist[i]));
01331                                 foundNodes.insert(std::make_pair(ids[ind[i]], dist[i]));
01332                         }
01333                 }
01334         }
01335         UDEBUG("found nodes=%d", (int)foundNodes.size());
01336         return foundNodes;
01337 }
01338 
01339 // return <id, Transform>, excluding query
01340 std::map<int, Transform> getPosesInRadius(
01341                 int nodeId,
01342                 const std::map<int, Transform> & nodes,
01343                 float radius)
01344 {
01345         UASSERT(uContains(nodes, nodeId));
01346         std::map<int, Transform> foundNodes;
01347         if(nodes.size() <= 1)
01348         {
01349                 return foundNodes;
01350         }
01351 
01352         pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
01353         cloud->resize(nodes.size());
01354         std::vector<int> ids(nodes.size());
01355         int oi = 0;
01356         for(std::map<int, Transform>::const_iterator iter = nodes.begin(); iter!=nodes.end(); ++iter)
01357         {
01358                 if(iter->first != nodeId)
01359                 {
01360                         (*cloud)[oi] = pcl::PointXYZ(iter->second.x(), iter->second.y(), iter->second.z());
01361                         UASSERT_MSG(pcl::isFinite((*cloud)[oi]), uFormat("Invalid pose (%d) %s", iter->first, iter->second.prettyPrint().c_str()).c_str());
01362                         ids[oi] = iter->first;
01363                         ++oi;
01364                 }
01365         }
01366         cloud->resize(oi);
01367         ids.resize(oi);
01368 
01369         Transform fromT = nodes.at(nodeId);
01370 
01371         if(cloud->size())
01372         {
01373                 pcl::search::KdTree<pcl::PointXYZ>::Ptr kdTree(new pcl::search::KdTree<pcl::PointXYZ>);
01374                 kdTree->setInputCloud(cloud);
01375                 std::vector<int> ind;
01376                 std::vector<float> dist;
01377                 pcl::PointXYZ pt(fromT.x(), fromT.y(), fromT.z());
01378                 kdTree->radiusSearch(pt, radius, ind, dist, 0);
01379                 for(unsigned int i=0; i<ind.size(); ++i)
01380                 {
01381                         if(ind[i] >=0)
01382                         {
01383                                 UDEBUG("Inlier %d: %f", ids[ind[i]], sqrt(dist[i]));
01384                                 foundNodes.insert(std::make_pair(ids[ind[i]], nodes.at(ids[ind[i]])));
01385                         }
01386                 }
01387         }
01388         UDEBUG("found nodes=%d", (int)foundNodes.size());
01389         return foundNodes;
01390 }
01391 
01392 float computePathLength(
01393                 const std::vector<std::pair<int, Transform> > & path,
01394                 unsigned int fromIndex,
01395                 unsigned int toIndex)
01396 {
01397         float length = 0.0f;
01398         if(path.size() > 1)
01399         {
01400                 UASSERT(fromIndex  < path.size() && toIndex < path.size() && fromIndex <= toIndex);
01401                 if(fromIndex >= toIndex)
01402                 {
01403                         toIndex = (unsigned int)path.size()-1;
01404                 }
01405                 float x=0, y=0, z=0;
01406                 for(unsigned int i=fromIndex; i<toIndex-1; ++i)
01407                 {
01408                         x += fabs(path[i].second.x() - path[i+1].second.x());
01409                         y += fabs(path[i].second.y() - path[i+1].second.y());
01410                         z += fabs(path[i].second.z() - path[i+1].second.z());
01411                 }
01412                 length = sqrt(x*x + y*y + z*z);
01413         }
01414         return length;
01415 }
01416 
01417 } /* namespace graph */
01418 
01419 } /* namespace rtabmap */


rtabmap
Author(s): Mathieu Labbe
autogenerated on Fri Aug 28 2015 12:51:31