/* * OpenVINS: An Open Platform for Visual-Inertial Research * Copyright (C) 2018-2022 Patrick Geneva * Copyright (C) 2018-2022 Guoquan Huang * Copyright (C) 2018-2022 OpenVINS Contributors * Copyright (C) 2018-2019 Kevin Eckenhoff * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include "alignment/AlignTrajectory.h" #include "alignment/AlignUtils.h" #include "utils/Loader.h" #include "utils/colors.h" #include "utils/print.h" #include "utils/quat_ops.h" #ifdef HAVE_PYTHONLIBS // import the c++ wrapper for matplot lib // https://github.com/lava/matplotlib-cpp // sudo apt-get install python-matplotlib python-numpy python2.7-dev #include "plot/matplotlibcpp.h" // Will plot the xy 3d position of the pose trajectories void plot_xy_positions(const std::string &name, const std::string &color, const std::vector> &poses) { // Paramters for our line std::map params; params.insert({"label", name}); params.insert({"linestyle", "-"}); params.insert({"color", color}); // Create vectors of our x and y axis std::vector x, y; for (size_t i = 0; i < poses.size(); i++) { x.push_back(poses.at(i)(0)); y.push_back(poses.at(i)(1)); } // Finally plot matplotlibcpp::plot(x, y, params); } // Will plot the z 3d position of the pose trajectories void plot_z_positions(const std::string &name, const std::string &color, const std::vector ×, const std::vector> &poses) { // Paramters for our line std::map params; params.insert({"label", name}); params.insert({"linestyle", "-"}); params.insert({"color", color}); // Create vectors of our x and y axis std::vector time, z; for (size_t i = 0; i < poses.size(); i++) { time.push_back(times.at(i)); z.push_back(poses.at(i)(2)); } // Finally plot matplotlibcpp::plot(time, z, params); } #endif int main(int argc, char **argv) { // Verbosity setting ov_core::Printer::setPrintLevel("INFO"); // Ensure we have a path if (argc < 3) { PRINT_ERROR(RED "ERROR: Please specify a align mode and trajectory file\n" RESET); PRINT_ERROR(RED "ERROR: ./plot_trajectories ... \n" RESET); PRINT_ERROR(RED "ERROR: rosrun ov_eval plot_trajectories ... \n" RESET); std::exit(EXIT_FAILURE); } // Read in all our trajectories from file std::vector names; std::vector> times; std::vector>> poses; for (int i = 2; i < argc; i++) { // Read in trajectory data std::vector times_temp; std::vector> poses_temp; std::vector cov_ori_temp, cov_pos_temp; ov_eval::Loader::load_data(argv[i], times_temp, poses_temp, cov_ori_temp, cov_pos_temp); // Align all the non-groundtruth trajectories to the base one if (i > 2) { // Intersect timestamps std::vector gt_times_temp(times.at(0)); std::vector> gt_poses_temp(poses.at(0)); ov_eval::AlignUtils::perform_association(0, 0.02, times_temp, gt_times_temp, poses_temp, gt_poses_temp); // Return failure if we didn't have any common timestamps if (poses_temp.size() < 3) { PRINT_ERROR(RED "[TRAJ]: unable to get enough common timestamps between trajectories.\n" RESET); PRINT_ERROR(RED "[TRAJ]: does the estimated trajectory publish the rosbag timestamps??\n" RESET); std::exit(EXIT_FAILURE); } // Perform alignment of the trajectories Eigen::Matrix3d R_ESTtoGT; Eigen::Vector3d t_ESTinGT; double s_ESTtoGT; ov_eval::AlignTrajectory::align_trajectory(poses_temp, gt_poses_temp, R_ESTtoGT, t_ESTinGT, s_ESTtoGT, argv[1]); // Debug print to the user Eigen::Vector4d q_ESTtoGT = ov_core::rot_2_quat(R_ESTtoGT); PRINT_DEBUG("[TRAJ]: q_ESTtoGT = %.3f, %.3f, %.3f, %.3f | p_ESTinGT = %.3f, %.3f, %.3f | s = %.2f\n", q_ESTtoGT(0), q_ESTtoGT(1), q_ESTtoGT(2), q_ESTtoGT(3), t_ESTinGT(0), t_ESTinGT(1), t_ESTinGT(2), s_ESTtoGT); // Finally lets calculate the aligned trajectories std::vector> est_poses_aignedtoGT; for (size_t j = 0; j < gt_times_temp.size(); j++) { Eigen::Matrix pose_ESTinGT; pose_ESTinGT.block(0, 0, 3, 1) = s_ESTtoGT * R_ESTtoGT * poses_temp.at(j).block(0, 0, 3, 1) + t_ESTinGT; pose_ESTinGT.block(3, 0, 4, 1) = ov_core::quat_multiply(poses_temp.at(j).block(3, 0, 4, 1), ov_core::Inv(q_ESTtoGT)); est_poses_aignedtoGT.push_back(pose_ESTinGT); } // Overwrite our poses with the corrected ones poses_temp = est_poses_aignedtoGT; } // Debug print the length stats boost::filesystem::path path(argv[i]); std::string name = path.stem().string(); double length = ov_eval::Loader::get_total_length(poses_temp); PRINT_INFO("[COMP]: %d poses in %s => length of %.2f meters\n", (int)times_temp.size(), name.c_str(), length); // Save this to our arrays names.push_back(name); times.push_back(times_temp); poses.push_back(poses_temp); } #ifdef HAVE_PYTHONLIBS // Colors that we are plotting std::vector colors = {"black", "blue", "red", "green", "cyan", "magenta"}; // assert(algo_rpe.size() <= colors.size()*linestyle.size()); // Plot this figure matplotlibcpp::figure_size(1000, 750); // Plot the position trajectories for (size_t i = 0; i < times.size(); i++) { plot_xy_positions(names.at(i), colors.at(i), poses.at(i)); } // Display to the user matplotlibcpp::xlabel("x-axis (m)"); matplotlibcpp::ylabel("y-axis (m)"); matplotlibcpp::show(false); // Plot this figure matplotlibcpp::figure_size(1000, 350); // Zero our time arrays double starttime = (times.at(0).empty()) ? 0 : times.at(0).at(0); double endtime = (times.at(0).empty()) ? 0 : times.at(0).at(times.at(0).size() - 1); for (size_t i = 0; i < times.size(); i++) { for (size_t j = 0; j < times.at(i).size(); j++) { times.at(i).at(j) -= starttime; } } // Plot the position trajectories for (size_t i = 0; i < times.size(); i++) { plot_z_positions(names.at(i), colors.at(i), times.at(i), poses.at(i)); } // Display to the user matplotlibcpp::xlabel("timestamp (sec)"); matplotlibcpp::ylabel("z-axis (m)"); matplotlibcpp::xlim(0.0, endtime - starttime); matplotlibcpp::legend(); matplotlibcpp::show(true); #endif // Done! return EXIT_SUCCESS; }