22 #include <Eigen/Eigen> 23 #include <boost/algorithm/string.hpp> 24 #include <boost/filesystem.hpp> 32 #ifdef HAVE_PYTHONLIBS 41 int main(
int argc,
char **argv) {
49 PRINT_ERROR(
RED "ERROR: ./error_dataset <align_mode> <file_gt.txt> <folder_algorithms>\n" RESET);
50 PRINT_ERROR(
RED "ERROR: rosrun ov_eval error_dataset <align_mode> <file_gt.txt> <folder_algorithms>\n" RESET);
51 std::exit(EXIT_FAILURE);
55 boost::filesystem::path path_gt(argv[2]);
56 std::vector<double> times;
57 std::vector<Eigen::Matrix<double, 7, 1>> poses;
58 std::vector<Eigen::Matrix3d> cov_ori, cov_pos;
63 PRINT_INFO(
"[COMP]: %d poses in %s => length of %.2f meters\n", (
int)times.size(), path_gt.stem().string().c_str(), length);
67 std::string path_algos(argv[3]);
68 std::vector<boost::filesystem::path> path_algorithms;
69 for (
const auto &entry : boost::filesystem::directory_iterator(path_algos)) {
70 if (boost::filesystem::is_directory(entry)) {
71 path_algorithms.push_back(entry.path());
74 std::sort(path_algorithms.begin(), path_algorithms.end());
81 std::map<std::string, std::pair<ov_eval::Statistics, ov_eval::Statistics>> algo_ate;
82 std::map<std::string, std::pair<ov_eval::Statistics, ov_eval::Statistics>> algo_nees;
83 for (
const auto &p : path_algorithms) {
90 std::vector<double> segments = {7.0, 14.0, 21.0, 28.0, 35.0};
96 std::map<std::string, std::map<double, std::pair<ov_eval::Statistics, ov_eval::Statistics>>> algo_rpe;
97 for (
const auto &p : path_algorithms) {
98 std::map<double, std::pair<ov_eval::Statistics, ov_eval::Statistics>> temp;
99 for (
const auto &len : segments) {
102 algo_rpe.insert({p.filename().string(), temp});
110 for (
size_t i = 0; i < path_algorithms.size(); i++) {
113 PRINT_DEBUG(
"======================================\n");
114 PRINT_DEBUG(
"[COMP]: processing %s algorithm\n", path_algorithms.at(i).filename().c_str());
117 std::map<std::string, boost::filesystem::path> path_algo_datasets;
118 for (
auto &entry : boost::filesystem::directory_iterator(path_algorithms.at(i))) {
119 if (boost::filesystem::is_directory(entry)) {
120 path_algo_datasets.insert({entry.path().filename().string(), entry.path()});
125 if (path_algo_datasets.find(path_gt.stem().string()) == path_algo_datasets.end()) {
126 PRINT_DEBUG(
RED "[COMP]: %s dataset does not have any runs for %s!!!!!\n" RESET, path_algorithms.at(i).filename().c_str(),
127 path_gt.stem().c_str());
134 std::map<double, std::pair<ov_eval::Statistics, ov_eval::Statistics>> rpe_dataset;
135 for (
const auto &len : segments) {
138 std::map<double, std::pair<ov_eval::Statistics, ov_eval::Statistics>> rmse_dataset;
139 std::map<double, std::pair<ov_eval::Statistics, ov_eval::Statistics>> rmse_2d_dataset;
140 std::map<double, std::pair<ov_eval::Statistics, ov_eval::Statistics>> nees_dataset;
143 std::vector<std::string> file_paths;
144 for (
auto &entry : boost::filesystem::directory_iterator(path_algo_datasets.at(path_gt.stem().string()))) {
145 if (entry.path().extension() !=
".txt")
147 file_paths.push_back(entry.path().string());
149 std::sort(file_paths.begin(), file_paths.end());
152 if (file_paths.empty()) {
153 PRINT_DEBUG(
RED "\tERROR: No runs found for %s, is the folder structure right??\n" RESET, path_algorithms.at(i).filename().c_str());
158 for (
auto &path_esttxt : file_paths) {
161 std::string path_gttxt = path_gt.string();
167 ate_dataset_ori.
values.push_back(error_ori.
rmse);
168 ate_dataset_pos.
values.push_back(error_pos.
rmse);
169 for (
size_t j = 0; j < error_ori.
values.size(); j++) {
170 rmse_dataset[error_ori.
timestamps.at(j)].first.values.push_back(error_ori.
values.at(j));
171 rmse_dataset[error_pos.
timestamps.at(j)].second.values.push_back(error_pos.
values.at(j));
178 ate_2d_dataset_ori.
values.push_back(error_ori_2d.rmse);
179 ate_2d_dataset_pos.
values.push_back(error_pos_2d.rmse);
180 for (
size_t j = 0; j < error_ori_2d.values.size(); j++) {
181 rmse_2d_dataset[error_ori_2d.timestamps.at(j)].first.values.push_back(error_ori_2d.values.at(j));
182 rmse_2d_dataset[error_pos_2d.timestamps.at(j)].second.values.push_back(error_pos_2d.values.at(j));
183 assert(error_ori_2d.timestamps.at(j) == error_pos_2d.timestamps.at(j));
189 for (
size_t j = 0; j < nees_ori.values.size(); j++) {
190 nees_dataset[nees_ori.timestamps.at(j)].first.values.push_back(nees_ori.values.at(j));
191 nees_dataset[nees_ori.timestamps.at(j)].second.values.push_back(nees_pos.values.at(j));
192 assert(nees_ori.timestamps.at(j) == nees_pos.timestamps.at(j));
196 std::map<double, std::pair<ov_eval::Statistics, ov_eval::Statistics>> error_rpe;
198 for (
const auto &elm : error_rpe) {
199 rpe_dataset.at(elm.first).first.values.insert(rpe_dataset.at(elm.first).first.values.end(), elm.second.first.values.begin(),
200 elm.second.first.values.end());
201 rpe_dataset.at(elm.first).first.timestamps.insert(rpe_dataset.at(elm.first).first.timestamps.end(),
202 elm.second.first.timestamps.begin(), elm.second.first.timestamps.end());
203 rpe_dataset.at(elm.first).second.values.insert(rpe_dataset.at(elm.first).second.values.end(), elm.second.second.values.begin(),
204 elm.second.second.values.end());
205 rpe_dataset.at(elm.first).second.timestamps.insert(rpe_dataset.at(elm.first).second.timestamps.end(),
206 elm.second.second.timestamps.begin(), elm.second.second.timestamps.end());
217 std::string prefix = (ate_dataset_ori.
mean > 10 || ate_dataset_pos.
mean > 10) ?
RED :
"";
218 PRINT_DEBUG(
"%s\tATE: mean_ori = %.3f | mean_pos = %.3f (%d runs)\n" RESET, prefix.c_str(), ate_dataset_ori.
mean, ate_dataset_pos.
mean,
219 (int)ate_dataset_ori.
values.size());
220 PRINT_DEBUG(
"\tATE: std_ori = %.5f | std_pos = %.5f\n", ate_dataset_ori.
std, ate_dataset_pos.
std);
221 PRINT_DEBUG(
"\tATE 2D: mean_ori = %.3f | mean_pos = %.3f (%d runs)\n", ate_2d_dataset_ori.
mean, ate_2d_dataset_pos.
mean,
222 (
int)ate_2d_dataset_ori.
values.size());
223 PRINT_DEBUG(
"\tATE 2D: std_ori = %.5f | std_pos = %.5f\n", ate_2d_dataset_ori.
std, ate_2d_dataset_pos.
std);
224 for (
auto &seg : rpe_dataset) {
225 seg.second.first.calculate();
226 seg.second.second.calculate();
227 PRINT_DEBUG(
"\tRPE: seg %d - mean_ori = %.3f | mean_pos = %.3f (%d samples)\n", (
int)seg.first, seg.second.first.mean,
228 seg.second.second.mean, (
int)seg.second.second.values.size());
234 for (
auto &elm : rmse_dataset) {
235 if (elm.second.first.values.size() == file_paths.size()) {
237 elm.second.second.calculate();
238 rmse_ori.timestamps.push_back(elm.first);
239 rmse_ori.values.push_back(elm.second.first.rmse);
240 rmse_pos.timestamps.push_back(elm.first);
241 rmse_pos.values.push_back(elm.second.second.rmse);
244 rmse_ori.calculate();
245 rmse_pos.calculate();
246 PRINT_DEBUG(
"\tRMSE: mean_ori = %.3f | mean_pos = %.3f\n", rmse_ori.mean, rmse_pos.mean);
250 for (
auto &elm : rmse_2d_dataset) {
251 if (elm.second.first.values.size() == file_paths.size()) {
253 elm.second.second.calculate();
255 rmse_2d_ori.
values.push_back(elm.second.first.rmse);
257 rmse_2d_pos.
values.push_back(elm.second.second.rmse);
262 PRINT_DEBUG(
"\tRMSE 2D: mean_ori = %.3f | mean_pos = %.3f\n", rmse_2d_ori.
mean, rmse_2d_pos.
mean);
266 for (
auto &elm : nees_dataset) {
267 if (elm.second.first.values.size() == file_paths.size()) {
269 elm.second.second.calculate();
271 nees_ori.
values.push_back(elm.second.first.mean);
273 nees_pos.
values.push_back(elm.second.second.mean);
278 PRINT_DEBUG(
"\tNEES: mean_ori = %.3f | mean_pos = %.3f\n", nees_ori.
mean, nees_pos.
mean);
280 #ifdef HAVE_PYTHONLIBS 287 double starttime1 = (rmse_ori.timestamps.empty()) ? 0 : rmse_ori.timestamps.at(0);
288 double endtime1 = (rmse_ori.timestamps.empty()) ? 0 : rmse_ori.timestamps.at(rmse_ori.timestamps.size() - 1);
289 for (
size_t j = 0; j < rmse_ori.timestamps.size(); j++) {
290 rmse_ori.timestamps.at(j) -= starttime1;
291 rmse_pos.timestamps.at(j) -= starttime1;
312 if (!nees_ori.
values.empty() && !nees_pos.
values.empty()) {
319 for (
size_t j = 0; j < nees_ori.
timestamps.size(); j++) {
326 matplotlibcpp::title(
"Normalized Estimation Error Squared - " + path_algorithms.at(i).filename().string());
344 std::string algo = path_algorithms.at(i).filename().string();
345 algo_ate.at(algo).first = ate_dataset_ori;
346 algo_ate.at(algo).second = ate_dataset_pos;
347 algo_nees.at(algo).first = nees_ori;
348 algo_nees.at(algo).second = nees_pos;
351 for (
const auto &elm : rpe_dataset) {
352 algo_rpe.at(algo).at(elm.first).first.
values.insert(algo_rpe.at(algo).at(elm.first).first.values.end(),
353 elm.second.first.values.begin(), elm.second.first.values.end());
354 algo_rpe.at(algo).at(elm.first).first.timestamps.insert(algo_rpe.at(algo).at(elm.first).first.timestamps.end(),
355 elm.second.first.timestamps.begin(), elm.second.first.timestamps.end());
356 algo_rpe.at(algo).at(elm.first).second.values.insert(algo_rpe.at(algo).at(elm.first).second.values.end(),
357 elm.second.second.values.begin(), elm.second.second.values.end());
358 algo_rpe.at(algo).at(elm.first).second.timestamps.insert(algo_rpe.at(algo).at(elm.first).second.timestamps.end(),
359 elm.second.second.timestamps.begin(), elm.second.second.timestamps.end());
365 PRINT_INFO(
"============================================\n");
367 PRINT_INFO(
"============================================\n");
368 PRINT_INFO(
" & \\textbf{ATE (deg/m)} & \\textbf{NEES (deg/m)} \\\\\\hline\n");
369 for (
auto &algo : algo_ate) {
370 std::string algoname = algo.first;
371 boost::replace_all(algoname,
"_",
"\\_");
374 auto ate_oripos = algo.second;
375 if (ate_oripos.first.values.empty() || ate_oripos.second.values.empty()) {
378 ate_oripos.first.calculate();
379 ate_oripos.second.calculate();
380 PRINT_INFO(
" & %.3f / %.3f", ate_oripos.first.mean, ate_oripos.second.mean);
383 auto nees_oripos = algo_nees.at(algo.first);
384 if (nees_oripos.first.values.empty() || nees_oripos.second.values.empty()) {
387 nees_oripos.first.calculate();
388 nees_oripos.second.calculate();
389 PRINT_INFO(
" & %.3f / %.3f", nees_oripos.first.mean, nees_oripos.second.mean);
393 PRINT_INFO(
"============================================\n");
396 PRINT_INFO(
"============================================\n");
398 PRINT_INFO(
"============================================\n");
399 for (
const auto &len : segments) {
403 for (
auto &algo : algo_rpe) {
404 std::string algoname = algo.first;
405 boost::replace_all(algoname,
"_",
"\\_");
407 for (
auto &seg : algo.second) {
408 seg.second.first.calculate();
409 seg.second.second.calculate();
410 PRINT_INFO(
" & %.3f / %.3f", seg.second.first.mean, seg.second.second.mean);
414 PRINT_INFO(
"============================================\n");
416 #ifdef HAVE_PYTHONLIBS Statistics object for a given set scalar time series values.
int main(int argc, char **argv)
static void load_data(std::string path_traj, std::vector< double > ×, std::vector< Eigen::Matrix< double, 7, 1 >> &poses, std::vector< Eigen::Matrix3d > &cov_ori, std::vector< Eigen::Matrix3d > &cov_pos)
This will load space separated trajectory into memory.
void calculate_nees(Statistics &nees_ori, Statistics &nees_pos)
Computes the Normalized Estimation Error Squared (NEES) for this trajectory.
double rmse
Root mean squared for the given values.
double mean
Mean of the given values.
std::vector< double > values
Values (e.g. error or nees at a given time)
void figure_size(size_t w, size_t h)
bool plot(const std::vector< Numeric > &x, const std::vector< Numeric > &y, const std::map< std::string, std::string > &keywords)
static double get_total_length(const std::vector< Eigen::Matrix< double, 7, 1 >> &poses)
Will calculate the total trajectory distance.
void calculate()
Will calculate all values from our vectors of information.
void show(const bool block=true)
void calculate_ate(Statistics &error_ori, Statistics &error_pos)
Computes the Absolute Trajectory Error (ATE) for this trajectory.
double std
Standard deviation of given values.
A single run for a given dataset.
void calculate_ate_2d(Statistics &error_ori, Statistics &error_pos)
Computes the Absolute Trajectory Error (ATE) for this trajectory in the 2d x-y plane.
static void setPrintLevel(const std::string &level)
#define PRINT_ERROR(x...)
void xlim(Numeric left, Numeric right)
void xlabel(const std::string &str, const std::map< std::string, std::string > &keywords={})
void subplot(long nrows, long ncols, long plot_number)
void title(const std::string &titlestr, const std::map< std::string, std::string > &keywords={})
void ylabel(const std::string &str, const std::map< std::string, std::string > &keywords={})
#define PRINT_DEBUG(x...)
void calculate_rpe(const std::vector< double > &segment_lengths, std::map< double, std::pair< Statistics, Statistics >> &error_rpe)
Computes the Relative Pose Error (RPE) for this trajectory.
std::vector< double > timestamps
Timestamp when these values occured at.