00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #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 <rtabmap/utilite/UTimer.h>
00034 #include <set>
00035
00036 #include <rtabmap/core/OptimizerTORO.h>
00037
00038 #ifdef RTABMAP_TORO
00039 #include "toro3d/treeoptimizer3.hh"
00040 #include "toro3d/treeoptimizer2.hh"
00041 #endif
00042
00043 namespace rtabmap {
00044
00045 bool OptimizerTORO::available()
00046 {
00047 #ifdef RTABMAP_TORO
00048 return true;
00049 #else
00050 return false;
00051 #endif
00052 }
00053
00054 std::map<int, Transform> OptimizerTORO::optimize(
00055 int rootId,
00056 const std::map<int, Transform> & poses,
00057 const std::multimap<int, Link> & edgeConstraints,
00058 cv::Mat & outputCovariance,
00059 std::list<std::map<int, Transform> > * intermediateGraphes,
00060 double * finalError,
00061 int * iterationsDone)
00062 {
00063 outputCovariance = cv::Mat::eye(6,6,CV_64FC1);
00064 std::map<int, Transform> optimizedPoses;
00065 #ifdef RTABMAP_TORO
00066 UDEBUG("Optimizing graph (pose=%d constraints=%d)...", (int)poses.size(), (int)edgeConstraints.size());
00067 if(edgeConstraints.size()>=1 && poses.size()>=2 && iterations() > 0)
00068 {
00069
00070 AISNavigation::TreeOptimizer2 pg2;
00071 AISNavigation::TreeOptimizer3 pg3;
00072 pg2.verboseLevel = 0;
00073 pg3.verboseLevel = 0;
00074
00075 UDEBUG("fill poses to TORO...");
00076 if(isSlam2d())
00077 {
00078 for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
00079 {
00080 UASSERT(!iter->second.isNull());
00081 AISNavigation::TreePoseGraph2::Pose p(iter->second.x(), iter->second.y(), iter->second.theta());
00082 AISNavigation::TreePoseGraph2::Vertex* v = pg2.addVertex(iter->first, p);
00083 UASSERT_MSG(v != 0, uFormat("cannot insert vertex %d!?", iter->first).c_str());
00084 }
00085 }
00086 else
00087 {
00088 for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
00089 {
00090 UASSERT(!iter->second.isNull());
00091 float x,y,z, roll,pitch,yaw;
00092 iter->second.getTranslationAndEulerAngles(x,y,z, roll,pitch,yaw);
00093 AISNavigation::TreePoseGraph3::Pose p(x, y, z, roll, pitch, yaw);
00094 AISNavigation::TreePoseGraph3::Vertex* v = pg3.addVertex(iter->first, p);
00095 UASSERT_MSG(v != 0, uFormat("cannot insert vertex %d!?", iter->first).c_str());
00096 v->transformation=AISNavigation::TreePoseGraph3::Transformation(p);
00097 }
00098
00099 }
00100
00101 UDEBUG("fill edges to TORO...");
00102 if(isSlam2d())
00103 {
00104 for(std::multimap<int, Link>::const_iterator iter=edgeConstraints.begin(); iter!=edgeConstraints.end(); ++iter)
00105 {
00106 UASSERT(!iter->second.transform().isNull());
00107 AISNavigation::TreePoseGraph2::Pose p(iter->second.transform().x(), iter->second.transform().y(), iter->second.transform().theta());
00108 AISNavigation::TreePoseGraph2::InformationMatrix inf;
00109
00110 if(isCovarianceIgnored())
00111 {
00112 inf.values[0][0] = 1.0; inf.values[0][1] = 0.0; inf.values[0][2] = 0.0;
00113 inf.values[1][0] = 0.0; inf.values[1][1] = 1.0; inf.values[1][2] = 0.0;
00114 inf.values[2][0] = 0.0; inf.values[2][1] = 0.0; inf.values[2][2] = 1.0;
00115 }
00116 else
00117 {
00118 inf.values[0][0] = iter->second.infMatrix().at<double>(0,0);
00119 inf.values[0][1] = iter->second.infMatrix().at<double>(0,1);
00120 inf.values[0][2] = iter->second.infMatrix().at<double>(0,5);
00121 inf.values[1][0] = iter->second.infMatrix().at<double>(1,0);
00122 inf.values[1][1] = iter->second.infMatrix().at<double>(1,1);
00123 inf.values[1][2] = iter->second.infMatrix().at<double>(1,5);
00124 inf.values[2][0] = iter->second.infMatrix().at<double>(5,0);
00125 inf.values[2][1] = iter->second.infMatrix().at<double>(5,1);
00126 inf.values[2][2] = iter->second.infMatrix().at<double>(5,5);
00127 }
00128
00129 int id1 = iter->second.from();
00130 int id2 = iter->second.to();
00131 if(id1 != id2)
00132 {
00133 AISNavigation::TreePoseGraph2::Vertex* v1=pg2.vertex(id1);
00134 AISNavigation::TreePoseGraph2::Vertex* v2=pg2.vertex(id2);
00135 UASSERT(v1 != 0);
00136 UASSERT(v2 != 0);
00137 AISNavigation::TreePoseGraph2::Transformation t(p);
00138 if (!pg2.addEdge(v1, v2, t, inf))
00139 {
00140 UERROR("Map: Edge already exits between nodes %d and %d, skipping", id1, id2);
00141 }
00142 }
00143
00144 }
00145 }
00146 else
00147 {
00148 for(std::multimap<int, Link>::const_iterator iter=edgeConstraints.begin(); iter!=edgeConstraints.end(); ++iter)
00149 {
00150 UASSERT(!iter->second.transform().isNull());
00151 float x,y,z, roll,pitch,yaw;
00152 iter->second.transform().getTranslationAndEulerAngles(x,y,z, roll,pitch,yaw);
00153
00154 AISNavigation::TreePoseGraph3::Pose p(x, y, z, roll, pitch, yaw);
00155 AISNavigation::TreePoseGraph3::InformationMatrix inf = DMatrix<double>::I(6);
00156 if(!isCovarianceIgnored())
00157 {
00158 memcpy(inf[0], iter->second.infMatrix().data, iter->second.infMatrix().total()*sizeof(double));
00159 }
00160
00161 int id1 = iter->second.from();
00162 int id2 = iter->second.to();
00163 if(id1 != id2)
00164 {
00165 AISNavigation::TreePoseGraph3::Vertex* v1=pg3.vertex(id1);
00166 AISNavigation::TreePoseGraph3::Vertex* v2=pg3.vertex(id2);
00167 UASSERT(v1 != 0);
00168 UASSERT(v2 != 0);
00169 AISNavigation::TreePoseGraph3::Transformation t(p);
00170 if (!pg3.addEdge(v1, v2, t, inf))
00171 {
00172 UERROR("Map: Edge already exits between nodes %d and %d, skipping", id1, id2);
00173 }
00174 }
00175
00176 }
00177 }
00178 UDEBUG("buildMST... root=%d", rootId);
00179 UASSERT(uContains(poses, rootId));
00180 if(isSlam2d())
00181 {
00182 pg2.buildMST(rootId);
00183
00184
00185 UDEBUG("initializeTreeParameters()");
00186 pg2.initializeTreeParameters();
00187 UDEBUG("Building TORO tree... (if a crash happens just after this msg, "
00188 "TORO is not able to find the root of the graph!)");
00189 pg2.initializeOptimization();
00190 }
00191 else
00192 {
00193 pg3.buildMST(rootId);
00194
00195
00196 UDEBUG("initializeTreeParameters()");
00197 pg3.initializeTreeParameters();
00198 UDEBUG("Building TORO tree... (if a crash happens just after this msg, "
00199 "TORO is not able to find the root of the graph!)");
00200 pg3.initializeOptimization();
00201 }
00202
00203 UINFO("Initial error = %f", pg2.error());
00204 UINFO("TORO optimizing begin (iterations=%d)", iterations());
00205 double lastError = 0;
00206 int i=0;
00207 UTimer timer;
00208 for (; i<iterations(); i++)
00209 {
00210 if(intermediateGraphes && i>0)
00211 {
00212 std::map<int, Transform> tmpPoses;
00213 if(isSlam2d())
00214 {
00215 for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
00216 {
00217 AISNavigation::TreePoseGraph2::Vertex* v=pg2.vertex(iter->first);
00218 float roll, pitch, yaw;
00219 iter->second.getEulerAngles(roll, pitch, yaw);
00220 Transform newPose(v->pose.x(), v->pose.y(), iter->second.z(), roll, pitch, v->pose.theta());
00221
00222 UASSERT_MSG(!newPose.isNull(), uFormat("Optimized pose %d is null!?!?", iter->first).c_str());
00223 tmpPoses.insert(std::pair<int, Transform>(iter->first, newPose));
00224 }
00225 }
00226 else
00227 {
00228 for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
00229 {
00230 AISNavigation::TreePoseGraph3::Vertex* v=pg3.vertex(iter->first);
00231 AISNavigation::TreePoseGraph3::Pose pose=v->transformation.toPoseType();
00232 Transform newPose(pose.x(), pose.y(), pose.z(), pose.roll(), pose.pitch(), pose.yaw());
00233
00234 UASSERT_MSG(!newPose.isNull(), uFormat("Optimized pose %d is null!?!?", iter->first).c_str());
00235 tmpPoses.insert(std::pair<int, Transform>(iter->first, newPose));
00236 }
00237 }
00238 intermediateGraphes->push_back(tmpPoses);
00239 }
00240
00241 double error = 0;
00242 if(isSlam2d())
00243 {
00244 pg2.iterate();
00245
00246
00247 error=pg2.error();
00248 UDEBUG("iteration %d global error=%f error/constraint=%f", i, error, error/pg2.edges.size());
00249 }
00250 else
00251 {
00252 pg3.iterate();
00253
00254
00255 double mte, mre, are, ate;
00256 error=pg3.error(&mre, &mte, &are, &ate);
00257 UDEBUG("i %d RotGain=%f global error=%f error/constraint=%f",
00258 i, pg3.getRotGain(), error, error/pg3.edges.size());
00259 }
00260
00261
00262 double errorDelta = lastError - error;
00263 if(i>0 && errorDelta < this->epsilon())
00264 {
00265 if(errorDelta < 0)
00266 {
00267 UDEBUG("Negative improvement?! Ignore and continue optimizing... (%f < %f)", errorDelta, this->epsilon());
00268 }
00269 else
00270 {
00271 UINFO("Stop optimizing, not enough improvement (%f < %f)", errorDelta, this->epsilon());
00272 break;
00273 }
00274 }
00275 else if(i==0 && error < this->epsilon())
00276 {
00277 UINFO("Stop optimizing, error is already under epsilon (%f < %f)", error, this->epsilon());
00278 break;
00279 }
00280 lastError = error;
00281 }
00282 if(finalError)
00283 {
00284 *finalError = lastError;
00285 }
00286 if(iterationsDone)
00287 {
00288 *iterationsDone = i;
00289 }
00290 UINFO("TORO optimizing end (%d iterations done, error=%f, time = %f s)", i, lastError, timer.ticks());
00291
00292 if(isSlam2d())
00293 {
00294 for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
00295 {
00296 AISNavigation::TreePoseGraph2::Vertex* v=pg2.vertex(iter->first);
00297 float roll, pitch, yaw;
00298 iter->second.getEulerAngles(roll, pitch, yaw);
00299 Transform newPose(v->pose.x(), v->pose.y(), iter->second.z(), roll, pitch, v->pose.theta());
00300
00301 UASSERT_MSG(!newPose.isNull(), uFormat("Optimized pose %d is null!?!?", iter->first).c_str());
00302 optimizedPoses.insert(std::pair<int, Transform>(iter->first, newPose));
00303 }
00304 }
00305 else
00306 {
00307 for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
00308 {
00309 AISNavigation::TreePoseGraph3::Vertex* v=pg3.vertex(iter->first);
00310 AISNavigation::TreePoseGraph3::Pose pose=v->transformation.toPoseType();
00311 Transform newPose(pose.x(), pose.y(), pose.z(), pose.roll(), pose.pitch(), pose.yaw());
00312
00313 UASSERT_MSG(!newPose.isNull(), uFormat("Optimized pose %d is null!?!?", iter->first).c_str());
00314 optimizedPoses.insert(std::pair<int, Transform>(iter->first, newPose));
00315 }
00316 }
00317
00318
00319 }
00320 else if(poses.size() == 1 || iterations() <= 0)
00321 {
00322 optimizedPoses = poses;
00323 }
00324 else
00325 {
00326 UWARN("This method should be called at least with 1 pose!");
00327 }
00328 UDEBUG("Optimizing graph...end!");
00329 #else
00330 UERROR("Not built with TORO support!");
00331 #endif
00332 return optimizedPoses;
00333 }
00334
00335 bool OptimizerTORO::saveGraph(
00336 const std::string & fileName,
00337 const std::map<int, Transform> & poses,
00338 const std::multimap<int, Link> & edgeConstraints)
00339 {
00340 FILE * file = 0;
00341
00342 #ifdef _MSC_VER
00343 fopen_s(&file, fileName.c_str(), "w");
00344 #else
00345 file = fopen(fileName.c_str(), "w");
00346 #endif
00347
00348 if(file)
00349 {
00350
00351 for(std::map<int, Transform>::const_iterator iter = poses.begin(); iter!=poses.end(); ++iter)
00352 {
00353 float x,y,z, yaw,pitch,roll;
00354 iter->second.getTranslationAndEulerAngles(x,y,z, roll, pitch, yaw);
00355 fprintf(file, "VERTEX3 %d %f %f %f %f %f %f\n",
00356 iter->first,
00357 x,
00358 y,
00359 z,
00360 roll,
00361 pitch,
00362 yaw);
00363 }
00364
00365
00366 for(std::multimap<int, Link>::const_iterator iter = edgeConstraints.begin(); iter!=edgeConstraints.end(); ++iter)
00367 {
00368 float x,y,z, yaw,pitch,roll;
00369 iter->second.transform().getTranslationAndEulerAngles(x,y,z, roll, pitch, yaw);
00370 fprintf(file, "EDGE3 %d %d %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",
00371 iter->second.from(),
00372 iter->second.to(),
00373 x,
00374 y,
00375 z,
00376 roll,
00377 pitch,
00378 yaw,
00379 iter->second.infMatrix().at<double>(0,0),
00380 iter->second.infMatrix().at<double>(0,1),
00381 iter->second.infMatrix().at<double>(0,2),
00382 iter->second.infMatrix().at<double>(0,3),
00383 iter->second.infMatrix().at<double>(0,4),
00384 iter->second.infMatrix().at<double>(0,5),
00385 iter->second.infMatrix().at<double>(1,1),
00386 iter->second.infMatrix().at<double>(1,2),
00387 iter->second.infMatrix().at<double>(1,3),
00388 iter->second.infMatrix().at<double>(1,4),
00389 iter->second.infMatrix().at<double>(1,5),
00390 iter->second.infMatrix().at<double>(2,2),
00391 iter->second.infMatrix().at<double>(2,3),
00392 iter->second.infMatrix().at<double>(2,4),
00393 iter->second.infMatrix().at<double>(2,5),
00394 iter->second.infMatrix().at<double>(3,3),
00395 iter->second.infMatrix().at<double>(3,4),
00396 iter->second.infMatrix().at<double>(3,5),
00397 iter->second.infMatrix().at<double>(4,4),
00398 iter->second.infMatrix().at<double>(4,5),
00399 iter->second.infMatrix().at<double>(5,5));
00400 }
00401 UINFO("Graph saved to %s", fileName.c_str());
00402 fclose(file);
00403 }
00404 else
00405 {
00406 UERROR("Cannot save to file %s", fileName.c_str());
00407 return false;
00408 }
00409 return true;
00410 }
00411
00412 bool OptimizerTORO::loadGraph(
00413 const std::string & fileName,
00414 std::map<int, Transform> & poses,
00415 std::multimap<int, Link> & edgeConstraints)
00416 {
00417 FILE * file = 0;
00418 #ifdef _MSC_VER
00419 fopen_s(&file, fileName.c_str(), "r");
00420 #else
00421 file = fopen(fileName.c_str(), "r");
00422 #endif
00423
00424 if(file)
00425 {
00426 char line[400];
00427 while ( fgets (line , 400 , file) != NULL )
00428 {
00429 std::vector<std::string> strList = uListToVector(uSplit(uReplaceChar(line, '\n', ' '), ' '));
00430 if(strList.size() == 8)
00431 {
00432
00433 int id = atoi(strList[1].c_str());
00434 float x = uStr2Float(strList[2]);
00435 float y = uStr2Float(strList[3]);
00436 float z = uStr2Float(strList[4]);
00437 float roll = uStr2Float(strList[5]);
00438 float pitch = uStr2Float(strList[6]);
00439 float yaw = uStr2Float(strList[7]);
00440 Transform pose(x, y, z, roll, pitch, yaw);
00441 if(poses.find(id) == poses.end())
00442 {
00443 poses.insert(std::make_pair(id, pose));
00444 }
00445 else
00446 {
00447 UFATAL("Pose %d already added", id);
00448 }
00449 }
00450 else if(strList.size() == 30)
00451 {
00452
00453 int idFrom = atoi(strList[1].c_str());
00454 int idTo = atoi(strList[2].c_str());
00455 float x = uStr2Float(strList[3]);
00456 float y = uStr2Float(strList[4]);
00457 float z = uStr2Float(strList[5]);
00458 float roll = uStr2Float(strList[6]);
00459 float pitch = uStr2Float(strList[7]);
00460 float yaw = uStr2Float(strList[8]);
00461 cv::Mat informationMatrix(6,6,CV_64FC1);
00462 informationMatrix.at<double>(3,3) = uStr2Float(strList[9]);
00463 informationMatrix.at<double>(4,4) = uStr2Float(strList[15]);
00464 informationMatrix.at<double>(5,5) = uStr2Float(strList[20]);
00465 UASSERT_MSG(informationMatrix.at<double>(3,3) > 0.0 && informationMatrix.at<double>(4,4) > 0.0 && informationMatrix.at<double>(5,5) > 0.0, uFormat("Information matrix should not be null! line=\"%s\"", line).c_str());
00466 informationMatrix.at<double>(0,0) = uStr2Float(strList[24]);
00467 informationMatrix.at<double>(1,1) = uStr2Float(strList[27]);
00468 informationMatrix.at<double>(2,2) = uStr2Float(strList[29]);
00469 UASSERT_MSG(informationMatrix.at<double>(0,0) > 0.0 && informationMatrix.at<double>(1,1) > 0.0 && informationMatrix.at<double>(2,2) > 0.0, uFormat("Information matrix should not be null! line=\"%s\"", line).c_str());
00470 Transform transform(x, y, z, roll, pitch, yaw);
00471 if(poses.find(idFrom) != poses.end() && poses.find(idTo) != poses.end())
00472 {
00473
00474 Link link(idFrom, idTo, Link::kUndef, transform, informationMatrix);
00475 edgeConstraints.insert(std::pair<int, Link>(idFrom, link));
00476 }
00477 else
00478 {
00479 UFATAL("Referred poses from the link not exist!");
00480 }
00481 }
00482 else if(strList.size())
00483 {
00484 UFATAL("Error parsing graph file %s on line \"%s\" (strList.size()=%d)", fileName.c_str(), line, (int)strList.size());
00485 }
00486 }
00487
00488 UINFO("Graph loaded from %s", fileName.c_str());
00489 fclose(file);
00490 }
00491 else
00492 {
00493 UERROR("Cannot open file %s", fileName.c_str());
00494 return false;
00495 }
00496 return true;
00497 }
00498
00499 }