42 #include <QApplication>
51 "rtabmap-report [\"Statistic/Id\"] [options] path\n"
53 "[Not built with Qt, statistics cannot be plotted]\n"
55 " path Directory containing rtabmap databases or path of a database.\n"
57 " --latex Print table formatted in LaTeX with results.\n"
58 " --kitti Compute error based on KITTI benchmark.\n"
59 " --relative Compute relative motion error between poses.\n"
60 " --loop Compute relative motion error of loop closures.\n"
61 " --scale Find the best scale for the map against the ground truth\n"
62 " and compute error based on the scaled path.\n"
63 " --poses Export odometry to [path]_odom.txt, optimized graph to [path]_slam.txt \n"
64 " and ground truth to [path]_gt.txt in TUM RGB-D format.\n"
65 " --poses_raw Same as --poses, but poses are not aligned to gt."
66 " --gt FILE.txt Use this file as ground truth (TUM RGB-D format). It will\n"
67 " override the ground truth set in database if there is one.\n"
68 " If extension is *.db, the optimized poses of that database will\n"
69 " be used as ground truth.\n"
70 " --gt_max_t # Maximum time interval (sec) to interpolate between a pose and\n"
71 " corresponding ground truth (default 1 sec).\n"
72 " --inc Incremental optimization. \n"
73 " --stats Show available statistics \"Statistic/Id\" to plot or get localization statistics (if path is a file). \n"
75 " --invert When reading many databases, put all curves from a same \n"
76 " database in same figure, instead of all same curves from \n"
77 " different database in same figure. When reading a single \n"
78 " database, the inverse will be done. \n"
79 " --ids Use IDs for x axis instead of time in the figures. \n"
80 " --start # Start from this node ID for the figures.\n"
81 " --export Export figures' data to txt files.\n"
82 " --export_prefix Prefix to filenames of exported figures' data (default is \"Stat\").\n"
84 " --report Export all evaluation statistics values in report.txt \n"
85 " --loc [#] Show localization statistics for each \"Statistic/Id\" per\n"
86 " session. Optionally set number 1=min,2=max,4=mean,8=stddev,16=total,32=nonnull%%\n"
87 " to show cumulative results on console (it is a mask, \n"
88 " we can combine those numbers, e.g., 63 for all) \n"
89 " --loc_delay # Delay to split sessions for localization statistics (default 60 seconds).\n"
90 " --ignore_inter_nodes Ignore intermediate poses and statistics.\n"
91 " --udebug Show debug log.\n"
92 " --\"parameter name\" \"value\" Overwrite a specific RTAB-Map's parameter.\n"
93 " --help,-h Show usage\n\n");
141 bool outputLatex =
false;
142 bool outputScaled =
false;
143 bool outputPoses =
false;
144 bool outputKittiError =
false;
145 bool outputRelativeError =
false;
146 bool outputReport =
false;
147 bool outputLoopAccuracy =
false;
148 bool outputPosesAlignedToGt =
true;
149 bool incrementalOptimization =
false;
150 bool showAvailableStats =
false;
151 bool invertFigures =
false;
152 bool ignoreInterNodes =
false;
155 bool exportFigures =
false;
156 std::string exportPrefix =
"Stat";
160 double gtMaxInterval = 1;
161 std::vector<std::string> statsToShow;
163 std::map<std::string, UPlot*> figures;
166 for(
int i=1;
i<argc; ++
i)
168 if(strcmp(
argv[
i],
"--help") == 0 || strcmp(
argv[
i],
"--h") == 0)
172 else if(strcmp(
argv[
i],
"--udebug") == 0)
176 else if(strcmp(
argv[
i],
"--latex") == 0)
180 else if(strcmp(
argv[
i],
"--kitti") == 0)
182 outputKittiError =
true;
184 else if(strcmp(
argv[
i],
"--relative") == 0)
186 outputRelativeError =
true;
188 else if(strcmp(
argv[
i],
"--scale") == 0)
192 else if(strcmp(
argv[
i],
"--poses") == 0)
196 else if(strcmp(
argv[
i],
"--poses_raw") == 0)
199 outputPosesAlignedToGt =
false;
201 else if(strcmp(
argv[
i],
"--loop") == 0)
203 outputLoopAccuracy =
true;
205 else if(strcmp(
argv[
i],
"--report") == 0)
209 else if(strcmp(
argv[
i],
"--inc") == 0)
211 incrementalOptimization =
true;
213 else if(strcmp(
argv[
i],
"--stats") == 0)
215 showAvailableStats =
true;
217 else if(strcmp(
argv[
i],
"--invert") == 0)
219 invertFigures =
true;
221 else if(strcmp(
argv[
i],
"--ids") == 0)
225 else if(strcmp(
argv[
i],
"--ignore_inter_nodes") == 0)
227 ignoreInterNodes =
true;
229 else if(strcmp(
argv[
i],
"--export") == 0)
231 exportFigures =
true;
233 else if(strcmp(
argv[
i],
"--export_prefix") == 0)
238 exportPrefix =
argv[
i];
239 printf(
"Export prefix=%s (--export_prefix)\n", exportPrefix.c_str());
243 printf(
"Missing value for \"--export_prefix\" option.\n");
247 else if(strcmp(
argv[
i],
"--gt") == 0)
253 printf(
"Ground truth file=%s (--gt)\n", gtFile.c_str());
256 printf(
"Wrong file format set for \"--gt\" option.\n");
262 printf(
"Missing value for \"--gt\" option.\n");
266 else if(strcmp(
argv[
i],
"--gt_max_t") == 0)
272 printf(
"Ground truth max interval=%f sec (--gt_max_t)\n", gtMaxInterval);
273 if(gtMaxInterval<0 || gtMaxInterval>10)
275 printf(
"\"--gt_max_t\" option should be between 0 and 10 sec, parsed %f sec.\n", gtMaxInterval);
281 printf(
"Missing value for \"--gt\" option.\n");
285 else if(strcmp(
argv[
i],
"--loc") == 0)
292 showLoc = atoi(
argv[
i]);
293 printf(
"Localization statistics=%d (--loc)\n", showLoc);
299 printf(
"Localization statistics (--loc)\n");
304 printf(
"Missing type for \"--loc\" option.\n");
308 else if(strcmp(
argv[
i],
"--loc_delay") == 0)
313 locDelay = atof(
argv[
i]);
314 printf(
"Localization delay=%fs (--loc_delay)\n", locDelay);
318 printf(
"Missing value for \"--loc_delay\" option.\n");
322 else if(strcmp(
argv[
i],
"--start") == 0)
327 startId = atoi(
argv[
i]);
328 printf(
"Figures will be plotted from id=%d (--start)\n", startId);
332 printf(
"Missing id for \"--start\" option.\n");
343 statsToShow.push_back(
argv[
i]);
347 if(!overriddenParams.empty())
349 printf(
"Parameters overridden:\n");
350 for(
auto iter=overriddenParams.begin();
iter!=overriddenParams.end(); ++
iter)
352 printf(
" %s = %s\n",
iter->first.c_str(),
iter->second.c_str());
361 invertFigures = !invertFigures;
363 std::map<std::string, std::vector<std::pair<std::string, std::vector<LocStats> > > > localizationMultiStats;
364 for(
size_t i=0;
i<statsToShow.size(); ++
i)
366 std::string figureTitle = statsToShow[
i];
370 printf(
"Plot %s\n", figureTitle.c_str());
372 fig->resize(QSize(640,480));
373 fig->setWindowTitle(figureTitle.c_str());
382 figures.insert(std::make_pair(figureTitle, fig));
385 if(showLoc & 0b111111)
387 localizationMultiStats.insert(std::make_pair(figureTitle, std::vector<std::pair<std::string, std::vector<LocStats> > >()));
396 std::map<double, Transform> externalGtPoses;
405 for(std::map<int, Transform>::iterator
iter=poses.begin();
iter!=poses.end(); ++
iter)
412 std::vector<float>
v;
416 externalGtPoses.insert(std::make_pair(
s,
iter->second));
425 std::map<int, Transform> poses;
426 std::map<int, double> stamps;
429 for(std::map<int, Transform>::iterator
iter=poses.begin();
iter!=poses.end(); ++
iter)
431 externalGtPoses.insert(std::make_pair(stamps.at(
iter->first),
iter->second));
435 if(externalGtPoses.size() < 2)
437 printf(
"Failed loading ground truth poses from \"%s\" (\"--gt\" option).\n", gtFile.c_str());
442 printf(
"Loading %ld ground truth poses from \"%s\".\n", externalGtPoses.size(), gtFile.c_str());
446 std::string fileName;
447 std::list<std::string> paths;
448 paths.push_back(
path);
449 std::vector<std::map<std::string, std::vector<float> > > outputLatexStatistics;
450 std::map<std::string, std::vector<float> > outputLatexStatisticsMap;
451 bool odomRAMSet =
false;
452 std::set<std::string> topDirs;
455 std::string currentPath = paths.front();
458 bool currentPathIsDatabase =
false;
463 currentPathIsDatabase=
true;
464 printf(
"Database: %s\n", currentPath.c_str());
471 std::list<std::string> subDirs;
472 if(!currentPathIsDatabase)
474 printf(
"Directory: %s\n", currentPath.c_str());
475 std::list<std::string> fileNames = currentDir.
getFileNames();
478 for(std::list<std::string>::iterator
iter = fileNames.begin();
iter!=fileNames.end(); ++
iter)
480 topDirs.insert(currentPath+
"/"+*
iter);
485 if(topDirs.find(currentPath) != topDirs.end())
487 if(outputLatexStatisticsMap.size())
489 outputLatexStatistics.push_back(outputLatexStatisticsMap);
490 outputLatexStatisticsMap.clear();
497 while(currentPathIsDatabase || !(fileName = currentDir.
getNextFileName()).empty())
499 int startIdPerDb = startId;
502 std::string filePath;
503 if(currentPathIsDatabase)
505 filePath = currentPath;
527 std::set<int> allIds = ids;
533 std::map<int, Transform> odomPoses, gtPoses;
534 std::map<int, double> odomStamps;
535 std::vector<float> cameraTime;
536 cameraTime.reserve(ids.size());
537 std::vector<float> odomTime;
538 odomTime.reserve(ids.size());
539 std::vector<float> slamTime;
540 slamTime.reserve(ids.size());
543 float maxOdomRAM = -1;
544 float maxMapRAM = -1;
546 if(currentPathIsDatabase && showAvailableStats)
548 std::map<std::string, int> availableStats;
549 for(std::set<int>::iterator
iter=ids.begin();
iter!=ids.end(); ++
iter)
553 for(std::map<std::string, float>::iterator jter=
stats.at(*iter).first.begin(); jter!=
stats.at(*iter).first.end(); ++jter)
555 if(availableStats.find(jter->first) != availableStats.end())
557 ++availableStats.at(jter->first);
561 availableStats.insert(std::make_pair(jter->first, 1));
566 printf(
"Showing available statistics in \"%s\":\n", filePath.c_str());
567 for(std::map<std::string, int>::iterator
iter=availableStats.begin();
iter!=availableStats.end(); ++
iter)
569 printf(
"%s (%d)\n",
iter->first.c_str(),
iter->second);
575 std::map<std::string, UPlotCurve*> curves;
576 if(statsToShow.empty())
578 for(std::map<std::string, UPlot*>::iterator
iter=figures.begin();
iter!=figures.end(); ++
iter)
580 curves.insert(std::make_pair(
iter->first,
iter->second->addCurve(filePath.c_str())));
581 if(!localizationMultiStats.empty())
582 localizationMultiStats.at(
iter->first).push_back(std::make_pair(fileName, std::vector<LocStats>()));
588 fig->setWindowTitle(filePath.c_str());
597 if(!figures.insert(std::make_pair(filePath.c_str(), fig)).second)
600 printf(
"Figure %s already added!\n", filePath.c_str());
604 for(
size_t i=0;
i<statsToShow.size(); ++
i)
606 curves.insert(std::make_pair(statsToShow[
i], fig->
addCurve(statsToShow[
i].c_str())));
607 if(!localizationMultiStats.empty())
608 localizationMultiStats.at(statsToShow[
i]).push_back(std::make_pair(fileName, std::vector<LocStats>()));
613 for(
size_t i=0;
i<statsToShow.size(); ++
i)
615 if(!localizationMultiStats.empty())
616 localizationMultiStats.at(statsToShow[
i]).push_back(std::make_pair(fileName, std::vector<LocStats>()));
621 std::set<int> mappingSessionIds;
622 if(!localizationMultiStats.empty())
627 for(std::map<int, Transform>::iterator
iter=poses.begin();
iter!=poses.end(); ++
iter)
634 std::vector<float>
v;
638 mappingSessionIds.insert(
m);
644 startIdPerDb = poses.rbegin()->first+1;
649 std::map<std::string, std::vector<float> > localizationSessionStats;
650 double previousStamp = 0.0;
651 std::map<int, int> allWeights;
653 int previousMapId = 0;
654 float totalOdomDistance = 0.0f;
655 for(std::set<int>::iterator
iter=allIds.begin();
iter!=allIds.end(); ++
iter)
662 std::vector<float>
v;
666 if(previousMapId ==
m)
668 if(!
p.isNull() && !previousPose.
isNull())
670 totalOdomDistance +=
p.getDistance(previousPose);
676 allWeights.insert(std::make_pair(*
iter,
w));
677 if(!ignoreInterNodes ||
w!=-1)
681 odomPoses.insert(std::make_pair(*
iter,
p));
682 odomStamps.insert(std::make_pair(*
iter,
s));
683 if(!externalGtPoses.empty())
685 std::map<double, rtabmap::Transform>::iterator nextIter = externalGtPoses.upper_bound(
s);
686 if(nextIter!=externalGtPoses.end())
688 std::map<double, rtabmap::Transform>::iterator previousIter = nextIter;
690 if(
s == previousIter->first || (nextIter->first-s <= gtMaxInterval && s-previousIter->
first <= gtMaxInterval))
692 UASSERT(
s-previousIter->first >= 0);
693 gtPoses.insert(std::make_pair(*
iter, previousIter->second.interpolate((
s-previousIter->first)/(nextIter->first-previousIter->first),nextIter->second)));
699 gtPoses.insert(std::make_pair(*
iter, gt));
703 if(!localizationMultiStats.empty() && mappingSessionIds.find(
m) != mappingSessionIds.end())
710 const std::map<std::string, float> & stat =
stats.at(*iter).first;
711 if(
uContains(stat, Statistics::kGtTranslational_rmse()))
713 rmse = stat.at(Statistics::kGtTranslational_rmse());
714 if(maxRMSE==-1 || maxRMSE < rmse)
719 if(
uContains(stat, std::string(
"Camera/TotalTime/ms")))
721 cameraTime.push_back(stat.at(std::string(
"Camera/TotalTime/ms")));
723 if(
uContains(stat, std::string(
"Odometry/TotalTime/ms")))
725 odomTime.push_back(stat.at(std::string(
"Odometry/TotalTime/ms")));
727 else if(
uContains(stat, std::string(
"Odometry/TimeEstimation/ms")))
729 odomTime.push_back(stat.at(std::string(
"Odometry/TimeEstimation/ms")));
732 if(
uContains(stat, std::string(
"RtabmapROS/TotalTime/ms")))
736 slamTime.push_back(stat.at(
"RtabmapROS/TotalTime/ms"));
739 else if(
uContains(stat, Statistics::kTimingTotal()))
743 slamTime.push_back(stat.at(Statistics::kTimingTotal()));
747 if(
uContains(stat, std::string(Statistics::kMemoryRAM_usage())))
749 float ram = stat.at(Statistics::kMemoryRAM_usage());
750 if(maxMapRAM==-1 || maxMapRAM < ram)
755 if(
uContains(stat, std::string(
"Odometry/RAM_usage/MB")))
757 float ram = stat.at(
"Odometry/RAM_usage/MB");
758 if(maxOdomRAM==-1 || maxOdomRAM < ram)
765 for(std::map<std::string, UPlotCurve*>::iterator jter=curves.begin(); jter!=curves.end(); ++jter)
768 for(std::map<std::string, std::vector<std::pair<std::string, std::vector<LocStats> > > >::
iterator jter=localizationMultiStats.begin();
769 jter!=localizationMultiStats.end();
775 double y = stat.at(jter->first);
782 jter->second->addValue(
x,
y);
785 if(!localizationMultiStats.empty())
787 if(previousStamp > 0 &&
fabs(
s - previousStamp) > locDelay &&
uContains(localizationSessionStats, jter->first))
790 for(std::map<std::string, std::vector<float> >::
iterator kter=localizationSessionStats.begin(); kter!=localizationSessionStats.end(); ++kter)
793 localizationMultiStats.at(kter->first).rbegin()->second.push_back(
values);
794 localizationSessionStats.at(kter->first).clear();
800 if(!
uContains(localizationSessionStats, jter->first))
802 localizationSessionStats.insert(std::make_pair(jter->first, std::vector<float>()));
804 localizationSessionStats.at(jter->first).push_back(
y);
814 for(std::map<std::string, std::vector<std::pair<std::string, std::vector<LocStats> > > >::
iterator jter=localizationMultiStats.begin();
815 jter!=localizationMultiStats.end();
818 if(
uContains(localizationSessionStats, jter->first) &&
819 !localizationSessionStats.at(jter->first).empty())
823 localizationMultiStats.at(jter->first).rbegin()->second.push_back(
values);
827 std::multimap<int, Link> links;
828 std::multimap<int, Link> allLinks;
832 std::multimap<int, Link> allBiLinks;
833 for(std::multimap<int, Link>::iterator jter=allLinks.begin(); jter!=allLinks.end(); ++jter)
835 if(jter->second.from() != jter->second.to() &&
839 allBiLinks.insert(std::make_pair(jter->second.to(), jter->second.inverse()));
841 allBiLinks.insert(*jter);
845 std::multimap<int, Link> loopClosureLinks;
847 for(std::multimap<int, Link>::iterator jter=allLinks.begin(); jter!=allLinks.end(); ++jter)
849 if(jter->second.from() == jter->second.to() ||
graph::findLink(links, jter->second.from(), jter->second.to(),
true) == links.end())
851 Link link = jter->second;
853 if(link.
from() != link.
to() &&
858 while(
uContains(allWeights, link.
to()) && allWeights.at(link.
to()) < 0)
860 std::multimap<int, Link>::iterator uter = allLinks.find(link.
to());
861 while(uter != allLinks.end() &&
862 uter->first==link.
to() &&
863 uter->second.from()>uter->second.to())
867 if(uter != allLinks.end())
869 link = link.
merge(uter->second, uter->second.type());
870 allLinks.erase(uter->first);
878 links.insert(std::make_pair(jter->first, link));
884 graph::findLink(loopClosureLinks, jter->second.from(), jter->second.to()) == loopClosureLinks.end())
886 loopClosureLinks.insert(*jter);
894 float bestScale = 1.0f;
896 float bestRMSEAng = -1;
897 float bestVoRMSE = -1;
900 float kitti_t_err = 0.0f;
901 float kitti_r_err = 0.0f;
902 float relative_t_err = 0.0f;
903 float relative_r_err = 0.0f;
904 float loop_t_err = 0.0f;
905 float loop_r_err = 0.0f;
909 std::map<int, Transform> posesOut;
910 std::multimap<int, Link> linksOut;
911 int firstId = *ids.begin();
913 bool useOdomGravity = Parameters::defaultMemUseOdomGravity();
917 for(std::map<int, Transform>::iterator
iter=odomPoses.begin();
iter!=odomPoses.end(); ++
iter)
922 std::map<int, Transform> posesWithLandmarks = odomPoses;
924 for(std::multimap<int, Link>::iterator
iter=links.begin();
iter!=links.end(); ++
iter)
929 if(posesWithLandmarks.find(
iter->second.from()) != posesWithLandmarks.end() && posesWithLandmarks.find(
iter->second.to()) == posesWithLandmarks.end())
931 posesWithLandmarks.insert(std::make_pair(
iter->second.to(), posesWithLandmarks.at(
iter->second.from())*
iter->second.transform()));
935 optimizer->
getConnectedGraph(firstId, posesWithLandmarks, links, posesOut, linksOut);
936 std::list<std::map<int, Transform> > intermediateGraphes;
937 std::map<int, Transform> poses = optimizer->
optimize(firstId, posesOut, linksOut, incrementalOptimization?&intermediateGraphes:0);
941 UWARN(
"Optimization failed! Try incremental optimization...");
945 UERROR(
"Incremental optimization also failed! Only original RMSE will be shown.");
950 UWARN(
"Incremental optimization succeeded!");
957 std::map<int, Transform>::iterator
iter=poses.begin();
958 std::map<int, Transform> optimizedLandmarks;
959 while(
iter!=poses.end() &&
iter->first < 0)
961 optimizedLandmarks.insert(*
iter);
964 if(outputKittiError) {
966 std::map<int, Transform>::iterator
iter=posesOut.begin();
967 while(
iter!=posesOut.end() &&
iter->first < 0)
969 posesOut.erase(
iter++);
973 std::map<int, Transform> groundTruth;
974 for(std::map<int, Transform>::const_iterator
iter=poses.begin();
iter!=poses.end(); ++
iter)
976 if(gtPoses.find(
iter->first) != gtPoses.end())
978 groundTruth.insert(*gtPoses.find(
iter->first));
982 outputScaled = outputScaled && groundTruth.size();
985 std::map<int, Transform> scaledPoses;
986 std::map<int, Transform> scaledOdomPoses;
988 for(std::map<int, Transform>::iterator
iter=poses.begin();
iter!=poses.end(); ++
iter)
994 scaledPoses.insert(std::make_pair(
iter->first,
t));
996 UASSERT(posesOut.find(
iter->first)!=posesOut.end());
997 t = posesOut.at(
iter->first).clone();
1001 scaledOdomPoses.insert(std::make_pair(
iter->first,
t));
1004 float translational_rmse = 0.0f;
1005 float translational_mean = 0.0f;
1006 float translational_median = 0.0f;
1007 float translational_std = 0.0f;
1008 float translational_min = 0.0f;
1009 float translational_max = 0.0f;
1010 float rotational_rmse = 0.0f;
1011 float rotational_mean = 0.0f;
1012 float rotational_median = 0.0f;
1013 float rotational_std = 0.0f;
1014 float rotational_min = 0.0f;
1015 float rotational_max = 0.0f;
1022 translational_median,
1032 float translational_rmse_vo = translational_rmse;
1039 translational_median,
1050 if(bestRMSE!=-1 && translational_rmse > bestRMSE)
1054 bestRMSE = translational_rmse;
1055 bestVoRMSE = translational_rmse_vo;
1056 bestRMSEAng = rotational_rmse;
1058 bestGtToMap = gtToMap;
1059 bestGtToOdom = gtToOdom;
1068 for(std::map<int, Transform>::iterator
iter=poses.begin();
iter!=poses.end(); ++
iter)
1070 iter->second.x()*=bestScale;
1071 iter->second.y()*=bestScale;
1072 iter->second.z()*=bestScale;
1073 if(outputPosesAlignedToGt)
1074 iter->second = bestGtToMap *
iter->second;
1076 for(std::map<int, Transform>::iterator
iter=optimizedLandmarks.begin();
iter!=optimizedLandmarks.end(); ++
iter)
1078 iter->second.x()*=bestScale;
1079 iter->second.y()*=bestScale;
1080 iter->second.z()*=bestScale;
1081 if(outputPosesAlignedToGt)
1082 iter->second = bestGtToMap *
iter->second;
1086 for(std::map<int, Transform>::iterator
iter=posesOut.begin();
iter!=posesOut.end(); ++
iter)
1088 iter->second.x()*=bestScale;
1089 iter->second.y()*=bestScale;
1090 iter->second.z()*=bestScale;
1091 if(outputPosesAlignedToGt)
1092 iter->second = bestGtToOdom *
iter->second;
1095 if(outputRelativeError)
1097 if(groundTruth.size() == poses.size())
1104 std::vector<Transform> gtPosesTmp;
1105 std::vector<Transform> rPoses;
1106 for(std::map<int, Transform>::iterator
iter=poses.begin();
iter!=poses.end(); ++
iter)
1108 if(groundTruth.find(
iter->first) != groundTruth.end())
1110 gtPosesTmp.push_back(groundTruth.at(
iter->first));
1111 rPoses.push_back(poses.at(
iter->first));
1114 if(!gtPosesTmp.empty())
1121 if(outputKittiError)
1123 if(groundTruth.size() == poses.size())
1130 printf(
"Cannot compute KITTI statistics as optimized poses and ground truth don't have the same size (%d vs %d).\n",
1131 (
int)poses.size(), (
int)groundTruth.size());
1139 dbName = dbName.substr(0, dbName.size()-3);
1141 std::multimap<int, Link> dummyLinks;
1142 std::map<int, double> stamps;
1143 if(!outputKittiError)
1146 uInsert(poses, optimizedLandmarks);
1148 if(!outputKittiError)
1150 for(std::map<int, Transform>::iterator
iter=poses.begin();
iter!=poses.end(); ++
iter)
1154 stamps.insert(std::make_pair(
iter->first, 0));
1158 UASSERT(odomStamps.find(
iter->first) != odomStamps.end());
1159 stamps.insert(*odomStamps.find(
iter->first));
1165 printf(
"Could not export the poses to \"%s\"!?!\n",
path.c_str());
1171 if(!outputKittiError)
1173 for(std::map<int, Transform>::iterator
iter=posesOut.begin();
iter!=posesOut.end(); ++
iter)
1177 stamps.insert(std::make_pair(
iter->first, 0));
1181 UASSERT(odomStamps.find(
iter->first) != odomStamps.end());
1182 stamps.insert(*odomStamps.find(
iter->first));
1188 printf(
"Could not export the odometry to \"%s\"!?!\n",
path.c_str());
1192 if(groundTruth.size())
1196 if(!outputKittiError)
1198 for(std::map<int, Transform>::iterator
iter=groundTruth.begin();
iter!=groundTruth.end(); ++
iter)
1200 UASSERT(odomStamps.find(
iter->first) != odomStamps.end());
1201 stamps.insert(*odomStamps.find(
iter->first));
1206 printf(
"Could not export the ground truth to \"%s\"!?!\n",
path.c_str());
1213 bool fillHeader =
false;
1214 std::ifstream
f(
"report.csv");
1220 std::ofstream myfile;
1221 myfile.open(
"report.csv", std::fstream::in | std::fstream::out |
std::fstream::app);
1224 myfile <<
"Rosbag name"<<
";"<<
"error linear (m)"<<
";"<<
"error linear max (m)"<<
";"<<
"error linear odom (m)"<<
";"
1225 <<
"error angular"<<
";"
1226 <<
"Slam avg (hz)"<<
";"<<
"Slam max (hz)"<<
";"
1227 <<
"Odom avg (hz)"<<
";"<<
"Odom max (hz)"<<std::endl;
1230 myfile <<fileName.c_str()<<
";"
1235 <<(1/(
uMean(slamTime)/1000.0))<<
";"
1236 <<(1/(
uMax(slamTime)/1000.0))<<
";"
1237 <<(1/(
uMean(odomTime)/1000.0))<<
";"
1238 <<(1/(
uMax(odomTime)/1000.0))<<
";"<<std::endl;
1242 if(outputLoopAccuracy && !groundTruth.empty() && !linksOut.empty())
1244 float sumDist = 0.0f;
1245 float sumAngle = 0.0f;
1247 for(std::multimap<int, Link>::iterator
iter=loopClosureLinks.begin();
iter!=loopClosureLinks.end(); ++
iter)
1249 if( groundTruth.find(
iter->second.from())!=groundTruth.end() &&
1250 groundTruth.find(
iter->second.to())!=groundTruth.end())
1255 t.r11(),
t.r12(),
t.r13(),
t.
x()*bestScale,
1256 t.r21(),
t.r22(),
t.r23(),
t.
y()*bestScale,
1257 t.r31(),
t.r32(),
t.r33(),
t.z()*bestScale);
1259 sumDist +=
diff.getNorm();
1260 sumAngle +=
diff.getAngle();
1268 loop_r_err *= 180/CV_PI;
1273 printf(
" %s (%d, %.1f m%s): RMSE= %.3f m (max=%s, odom=%.3f m) ang=%.1f deg%s%s, %s: avg=%d ms (max=%d ms) loops=%d%s%s%s%s%s%s\n",
1277 bestScale != 1.0f?
uFormat(
", s=%.3f", bestScale).
c_str():
"",
1282 !outputKittiError?
"":
uFormat(
", KITTI: t_err=%.2f%% r_err=%.2f deg/100m", kitti_t_err, kitti_r_err*100).
c_str(),
1283 !outputRelativeError?
"":
uFormat(
", Relative: t_err=%.3fm r_err=%.2f deg", relative_t_err, relative_r_err).
c_str(),
1284 !localizationMultiStats.empty()?
"loc":
"slam",
1285 (
int)
uMean(slamTime), (
int)
uMax(slamTime),
1286 (
int)loopClosureLinks.size(),
1287 landmarks==0?
"":
uFormat(
", landmarks = %d", landmarks).
c_str(),
1288 !outputLoopAccuracy?
"":
uFormat(
" (t_err=%.3fm r_err=%.2f deg)", loop_t_err, loop_r_err).
c_str(),
1289 odomTime.empty()?
"":
uFormat(
", odom: avg=%dms (max=%dms)", (
int)
uMean(odomTime), (
int)
uMax(odomTime)).
c_str(),
1290 cameraTime.empty()?
"":
uFormat(
", camera: avg=%dms", (
int)
uMean(cameraTime)).
c_str(),
1291 maxOdomRAM!=-1.0
f?
uFormat(
", RAM odom=%dMB ", (
int)maxOdomRAM).
c_str():
"",
1292 maxMapRAM!=-1.0
f?
uFormat(
", map=%dMB",(
int)maxMapRAM).
c_str():
"");
1296 std::vector<float>
stats;
1297 stats.push_back(ids.size());
1298 stats.push_back(bestRMSE);
1299 stats.push_back(maxRMSE);
1300 stats.push_back(bestRMSEAng);
1304 stats.push_back(maxOdomRAM);
1305 stats.push_back(maxMapRAM);
1306 outputLatexStatisticsMap.insert(std::make_pair(filePath,
stats));
1308 if(maxOdomRAM != -1.0
f)
1322 currentPathIsDatabase =
false;
1325 if(!localizationMultiStats.empty() && showLoc!=-1)
1327 printf(
"---Localization results---\n");
1328 std::string prefix =
"header={";
1329 printf(
"%s", prefix.c_str());
1330 for(std::vector<std::pair<std::string, std::vector<LocStats> > >::
iterator iter=localizationMultiStats.begin()->second.begin();
1331 iter!=localizationMultiStats.begin()->second.end();)
1333 if(
iter!=localizationMultiStats.begin()->second.begin())
1335 printf(
"%s", std::string(prefix.size(),
' ').c_str());
1337 printf(
"%s",
iter->first.c_str());
1339 if(
iter!=localizationMultiStats.begin()->second.end())
1347 for(std::map<std::string, std::vector<std::pair<std::string, std::vector<LocStats> > > >::
iterator iter=localizationMultiStats.begin();
1348 iter!=localizationMultiStats.end();
1351 printf(
"%s\n",
iter->first.c_str());
1352 for(
int k=0; k<6; ++k)
1354 if(showLoc & (0
x1 << k))
1356 std::string prefix =
uFormat(
" %s=[",
1363 printf(
"%s", prefix.c_str());
1364 for(std::vector<std::pair<std::string, std::vector<LocStats> > >::
iterator jter=
iter->second.begin(); jter!=
iter->second.end();)
1366 if(jter!=
iter->second.begin())
1368 printf(
"%s", std::string(prefix.size(),
' ').c_str());
1370 for(
size_t j=0;
j<jter->second.size(); ++
j)
1375 k==0?jter->second[
j].min:
1376 k==1?jter->second[
j].max:
1377 k==2?jter->second[
j].mean:
1378 jter->second[
j].stddev);
1382 printf(
"%d",jter->second[
j].total);
1386 printf(
"%.2f", (jter->second[
j].nonNull*100));
1388 if(
j+1 < jter->second.size())
1394 if(jter!=
iter->second.end())
1402 iter->second.clear();
1406 for(std::list<std::string>::iterator
iter=subDirs.begin();
iter!=subDirs.end(); ++
iter)
1408 paths.push_front(*
iter);
1411 if(outputLatexStatisticsMap.size() && paths.empty())
1413 outputLatexStatistics.push_back(outputLatexStatisticsMap);
1414 outputLatexStatisticsMap.clear();
1418 if(outputLatex && outputLatexStatistics.size())
1420 printf(
"\nLaTeX output:\n----------------\n");
1421 printf(
"\\begin{table*}[!t]\n");
1422 printf(
"\\caption{$t_{end}$ is the absolute translational RMSE value at the end "
1423 "of the experiment as $ATE_{max}$ is the maximum during the experiment. "
1424 "$r_{end}$ is rotational RMSE value at the end of the experiment. "
1425 "$o_{avg}$ and $m_{avg}$ are the average computational time "
1426 "for odometry (front-end) and map update (back-end). "
1427 "$m_{avg}$ is the maximum computational time for map update. "
1428 "$O_{end}$ and $M_{end}$ are the RAM usage at the end of the experiment "
1429 "for odometry and map management respectively.}\n");
1430 printf(
"\\label{}\n");
1431 printf(
"\\centering\n");
1434 printf(
"\\begin{tabular}{l|c|c|c|c|c|c|c|c|c}\n");
1435 printf(
"\\cline{2-10}\n");
1436 printf(
" & Size & $t_{end}$ & $t_{max}$ & $r_{end}$ & $o_{avg}$ & $m_{avg}$ & $m_{max}$ & $O_{end}$ & $M_{end}$ \\\\\n");
1437 printf(
" & (nodes) & (m) & (m) & (deg) & (ms) & (ms) & (ms) & (MB) & (MB) \\\\\n");
1441 printf(
"\\begin{tabular}{l|c|c|c|c|c|c|c|c}\n");
1442 printf(
"\\cline{2-9}\n");
1443 printf(
" & Size & $t_{end}$ & $t_{max}$ & $r_{end}$ & $o_{avg}$ & $m_{avg}$ & $m_{max}$ & $M_{end}$ \\\\\n");
1444 printf(
" & (nodes) & (m) & (m) & (deg) & (ms) & (ms) & (ms) & (MB) \\\\\n");
1447 printf(
"\\hline\n");
1449 for(
unsigned int j=0;
j<outputLatexStatistics.size(); ++
j)
1451 if(outputLatexStatistics[
j].
size())
1453 std::vector<int> lowestIndex;
1454 if(outputLatexStatistics[
j].
size() > 1)
1456 std::vector<float> lowestValue(outputLatexStatistics[
j].begin()->
second.size(),-1);
1457 lowestIndex = std::vector<int>(lowestValue.size(),0);
1459 for(std::map<std::string, std::vector<float> >::
iterator iter=outputLatexStatistics[
j].begin();
iter!=outputLatexStatistics[
j].end(); ++
iter)
1461 UASSERT(lowestValue.size() ==
iter->second.size());
1462 for(
unsigned int i=0;
i<
iter->second.size(); ++
i)
1464 if(lowestValue[
i] == -1 || (
iter->second[
i]>0.0f && lowestValue[
i]>
iter->second[
i]))
1466 lowestValue[
i] =
iter->second[
i];
1467 lowestIndex[
i] = index;
1475 for(std::map<std::string, std::vector<float> >::
iterator iter=outputLatexStatistics[
j].begin();
iter!=outputLatexStatistics[
j].end(); ++
iter)
1479 printf(
"%d & ", (
int)
iter->second[0]);
1480 printf(
"%s%.3f%s & ", lowestIndex.size()&&lowestIndex[1]==index?
"\\textbf{":
"",
iter->second[1], lowestIndex.size()&&lowestIndex[1]==index?
"}":
"");
1481 printf(
"%s%.3f%s & ", lowestIndex.size()&&lowestIndex[2]==index?
"\\textbf{":
"",
iter->second[2], lowestIndex.size()&&lowestIndex[2]==index?
"}":
"");
1482 printf(
"%s%.2f%s & ", lowestIndex.size()&&lowestIndex[3]==index?
"\\textbf{":
"",
iter->second[3], lowestIndex.size()&&lowestIndex[3]==index?
"}":
"");
1483 printf(
"%s%d%s & ", lowestIndex.size()&&lowestIndex[4]==index?
"\\textbf{":
"", (
int)
iter->second[4], lowestIndex.size()&&lowestIndex[4]==index?
"}":
"");
1484 printf(
"%s%d%s & ", lowestIndex.size()&&lowestIndex[5]==index?
"\\textbf{":
"", (
int)
iter->second[5], lowestIndex.size()&&lowestIndex[5]==index?
"}":
"");
1485 printf(
"%s%d%s & ", lowestIndex.size()&&lowestIndex[6]==index?
"\\textbf{":
"", (
int)
iter->second[6], lowestIndex.size()&&lowestIndex[6]==index?
"}":
"");
1488 printf(
"%s%d%s & ", lowestIndex.size()&&lowestIndex[7]==index?
"\\textbf{":
"", (
int)
iter->second[7], lowestIndex.size()&&lowestIndex[7]==index?
"}":
"");
1490 printf(
"%s%d%s ", lowestIndex.size()&&lowestIndex[8]==index?
"\\textbf{":
"", (
int)
iter->second[8], lowestIndex.size()&&lowestIndex[8]==index?
"}":
"");
1494 printf(
"\\hline\n");
1499 printf(
"\\end{tabular}\n");
1500 printf(
"\\end{table*}\n----------------\n");
1505 for(std::map<std::string, UPlot*>::iterator
iter=figures.begin();
iter!=figures.end(); ++
iter)
1509 iter->second->frameData();
1513 QString
data =
iter->second->getAllCurveDataAsText();
1516 QString filePath = QString(exportPrefix.c_str()) + (exportPrefix.empty()?
"":
"-") +
iter->second->windowTitle().replace(
'/',
"-") +
".txt";
1517 QFile
file(filePath);
1518 if(
file.open(QIODevice::Text | QIODevice::WriteOnly))
1522 printf(
"Exported \"%s\".\n", filePath.toStdString().c_str());
1526 printf(
"ERROR: could not open file \"%s\" for writing!\n", filePath.toStdString().c_str());
1532 iter->second->show();