39 double ratio = len_est / len_gt;
40 if (ratio > 1.1 || ratio < 0.9) {
41 PRINT_WARNING(
YELLOW "[TRAJ]: Trajectory is a bad ratio of %.2f length (est %.2f, gt %.2f)\n", ratio, len_est, len_gt);
50 PRINT_ERROR(
RED "[TRAJ]: unable to get enough common timestamps between trajectories.\n" RESET);
51 PRINT_ERROR(
RED "[TRAJ]: does the estimated trajectory publish the rosbag timestamps??\n" RESET);
52 std::exit(EXIT_FAILURE);
56 Eigen::Matrix3d R_ESTtoGT, R_GTtoEST;
57 Eigen::Vector3d t_ESTinGT, t_GTinEST;
58 double s_ESTtoGT, s_GTtoEST;
65 PRINT_DEBUG(
"[TRAJ]: q_ESTtoGT = %.3f, %.3f, %.3f, %.3f | p_ESTinGT = %.3f, %.3f, %.3f | s = %.2f\n", q_ESTtoGT(0), q_ESTtoGT(1),
66 q_ESTtoGT(2), q_ESTtoGT(3), t_ESTinGT(0), t_ESTinGT(1), t_ESTinGT(2), s_ESTtoGT);
71 for (
size_t i = 0; i <
gt_times.size(); i++) {
72 Eigen::Matrix<double, 7, 1> pose_ESTinGT, pose_GTinEST;
73 pose_ESTinGT.block(0, 0, 3, 1) = s_ESTtoGT * R_ESTtoGT *
est_poses.at(i).block(0, 0, 3, 1) + t_ESTinGT;
75 pose_GTinEST.block(0, 0, 3, 1) = s_GTtoEST * R_GTtoEST *
gt_poses.at(i).block(0, 0, 3, 1) + t_GTinEST;
101 error_ori.
values.push_back(ori_err);
103 error_pos.
values.push_back(pos_err);
130 error_ori.
values.push_back(ori_err);
132 error_pos.
values.push_back(pos_err);
141 std::map<
double, std::pair<Statistics, Statistics>> &error_rpe) {
144 double average_pos_difference = 0;
145 std::vector<double> accum_distances(
gt_poses.size());
146 accum_distances[0] = 0;
147 for (
size_t i = 1; i <
gt_poses.size(); i++) {
148 double pos_diff = (
gt_poses[i].block(0, 0, 3, 1) -
gt_poses[i - 1].block(0, 0, 3, 1)).norm();
149 accum_distances[i] = accum_distances[i - 1] + pos_diff;
150 average_pos_difference += pos_diff;
152 average_pos_difference /=
gt_poses.size();
155 double max_dist_diff = 0.5;
156 if (average_pos_difference > max_dist_diff) {
157 PRINT_WARNING(
YELLOW "[COMP]: average groundtruth position difference %.2f > %.2f is too large\n" RESET, average_pos_difference,
159 PRINT_WARNING(
YELLOW "[COMP]: this will prevent the RPE from finding valid trajectory segments!!!\n" RESET);
161 "[COMP]: the recommendation is to use a higher frequency groundtruth, or relax this trajectory segment logic...\n" RESET);
165 for (
const double &distance : segment_lengths) {
175 assert(comparisons.size() ==
gt_poses.size());
178 for (
size_t id_start = 0; id_start < comparisons.size(); id_start++) {
181 int id_end = comparisons[id_start];
187 Eigen::Matrix4d T_c1 = Eigen::Matrix4d::Identity();
192 Eigen::Matrix4d T_c2 = Eigen::Matrix4d::Identity();
201 Eigen::Matrix4d T_m1 = Eigen::Matrix4d::Identity();
203 T_m1.block(0, 3, 3, 1) =
gt_poses.at(id_start).block(0, 0, 3, 1);
206 Eigen::Matrix4d T_m2 = Eigen::Matrix4d::Identity();
208 T_m2.block(0, 3, 3, 1) =
gt_poses.at(id_end).block(0, 0, 3, 1);
217 Eigen::Matrix4d T_c2_rot = Eigen::Matrix4d::Identity();
218 T_c2_rot.block(0, 0, 3, 3) = T_c2.block(0, 0, 3, 3);
220 Eigen::Matrix4d T_c2_rot_inv = Eigen::Matrix4d::Identity();
221 T_c2_rot_inv.block(0, 0, 3, 3) = T_c2.block(0, 0, 3, 3).transpose();
224 Eigen::Matrix4d T_error_in_w = T_c2_rot * T_error_in_c2 * T_c2_rot_inv;
229 error_pos.
values.push_back(T_error_in_w.block(0, 3, 3, 1).norm());
232 double ori_err = 180.0 / M_PI *
ov_core::log_so3(T_error_in_w.block(0, 0, 3, 3)).norm();
234 error_ori.
values.push_back(ori_err);
240 error_rpe.insert({distance, {error_ori, error_pos}});
249 PRINT_WARNING(
YELLOW "[TRAJ]: Normalized Estimation Error Squared called but trajectory does not have any covariances...\n" RESET);
250 PRINT_WARNING(
YELLOW "[TRAJ]: Did you record using a Odometry or PoseWithCovarianceStamped????\n" RESET);
259 for (
size_t i = 0; i <
est_poses.size(); i++) {
268 double ori_nees = errori.transpose() *
est_covori.at(i).inverse() * errori;
272 double pos_nees = errpos.transpose() *
est_covpos.at(i).inverse() * errpos;
275 if (std::isnan(ori_nees) || std::isnan(pos_nees)) {
282 nees_ori.
values.push_back(ori_nees);
284 nees_pos.
values.push_back(pos_nees);
307 for (
size_t i = 0; i <
est_poses.size(); i++) {
310 Eigen::Vector4d e_q =
312 Eigen::Vector3d errori_local = 2 * e_q.block(0, 0, 3, 1);
314 Eigen::Matrix3d cov_global;
325 Eigen::Vector3d errori_rpy = ypr_gt_ItoG - ypr_est_ItoG;
326 for (
size_t idx = 0; idx < 3; idx++) {
327 while (errori_rpy(idx) < -M_PI) {
328 errori_rpy(idx) += 2 * M_PI;
330 while (errori_rpy(idx) > M_PI) {
331 errori_rpy(idx) -= 2 * M_PI;
339 Eigen::Matrix<double, 3, 3> H_xyz2rpy = Eigen::Matrix<double, 3, 3>::Identity();
340 H_xyz2rpy.block(0, 1, 3, 1) =
ov_core::rot_x(-ypr_est_ItoG(0)) * H_xyz2rpy.block(0, 1, 3, 1);
342 Eigen::Matrix3d cov_rpy;
344 cov_rpy = H_xyz2rpy.inverse() *
est_covori.at(i) * H_xyz2rpy.inverse().transpose();
352 posx.
values.push_back(errpos(0));
354 posy.
values.push_back(errpos(1));
356 posz.
values.push_back(errpos(2));
365 orix.
values.push_back(180.0 / M_PI * errori_global(0));
367 oriy.
values.push_back(180.0 / M_PI * errori_global(1));
369 oriz.
values.push_back(180.0 / M_PI * errori_global(2));
371 orix.
values_bound.push_back(3 * 180.0 / M_PI * std::sqrt(cov_global(0, 0)));
372 oriy.
values_bound.push_back(3 * 180.0 / M_PI * std::sqrt(cov_global(1, 1)));
373 oriz.
values_bound.push_back(3 * 180.0 / M_PI * std::sqrt(cov_global(2, 2)));
378 roll.
values.push_back(180.0 / M_PI * errori_rpy(0));
380 pitch.
values.push_back(180.0 / M_PI * errori_rpy(1));
382 yaw.
values.push_back(180.0 / M_PI * errori_rpy(2));
384 roll.
values_bound.push_back(3 * 180.0 / M_PI * std::sqrt(cov_rpy(0, 0)));
385 pitch.
values_bound.push_back(3 * 180.0 / M_PI * std::sqrt(cov_rpy(1, 1)));
386 yaw.
values_bound.push_back(3 * 180.0 / M_PI * std::sqrt(cov_rpy(2, 2)));
Eigen::Matrix< double, 4, 1 > quat_multiply(const Eigen::Matrix< double, 4, 1 > &q, const Eigen::Matrix< double, 4, 1 > &p)
Statistics object for a given set scalar time series values.
std::vector< Eigen::Matrix< double, 7, 1 > > est_poses
std::vector< Eigen::Matrix< double, 7, 1 > > est_poses_aignedtoGT
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.
static void perform_association(double offset, double max_difference, std::vector< double > &est_times, std::vector< double > >_times, std::vector< Eigen::Matrix< double, 7, 1 >> &est_poses, std::vector< Eigen::Matrix< double, 7, 1 >> >_poses)
Will intersect our loaded data so that we have common timestamps.
Eigen::Matrix< double, 3, 3 > rot_x(double t)
void calculate_nees(Statistics &nees_ori, Statistics &nees_pos)
Computes the Normalized Estimation Error Squared (NEES) for this trajectory.
std::vector< Eigen::Matrix< double, 7, 1 > > gt_poses_aignedtoEST
std::vector< double > values
Values (e.g. error or nees at a given time)
Eigen::Matrix< double, 4, 1 > Inv(Eigen::Matrix< double, 4, 1 > q)
void calculate_error(Statistics &posx, Statistics &posy, Statistics &posz, Statistics &orix, Statistics &oriy, Statistics &oriz, Statistics &roll, Statistics &pitch, Statistics &yaw)
Computes the error at each timestamp for this trajectory.
static double get_total_length(const std::vector< Eigen::Matrix< double, 7, 1 >> &poses)
Will calculate the total trajectory distance.
std::vector< double > gt_times
Evaluation and recording utilities.
std::vector< Eigen::Matrix3d > est_covori
void calculate()
Will calculate all values from our vectors of information.
static void align_trajectory(const std::vector< Eigen::Matrix< double, 7, 1 >> &traj_es, const std::vector< Eigen::Matrix< double, 7, 1 >> &traj_gt, Eigen::Matrix3d &R, Eigen::Vector3d &t, double &s, std::string method, int n_aligned=-1)
Align estimate to GT using a desired method using a set of initial poses.
ResultTrajectory(std::string path_est, std::string path_gt, std::string alignment_method)
Default constructor that will load, intersect, and align our trajectories.
std::vector< Eigen::Matrix3d > est_covpos
Eigen::Matrix< double, 3, 1 > log_so3(const Eigen::Matrix< double, 3, 3 > &R)
std::vector< Eigen::Matrix3d > gt_covpos
std::vector< double > values_bound
Bound of these values (e.g. our expected covariance bound)
Eigen::Matrix< double, 4, 1 > rot_2_quat(const Eigen::Matrix< double, 3, 3 > &rot)
void calculate_ate(Statistics &error_ori, Statistics &error_pos)
Computes the Absolute Trajectory Error (ATE) for this trajectory.
std::vector< Eigen::Matrix3d > gt_covori
#define PRINT_WARNING(x...)
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.
#define PRINT_ERROR(x...)
Eigen::Matrix< double, 3, 1 > rot2rpy(const Eigen::Matrix< double, 3, 3 > &rot)
Eigen::Matrix4d Inv_se3(const Eigen::Matrix4d &T)
std::vector< int > compute_comparison_indices_length(std::vector< double > &distances, double distance, double max_dist_diff)
Gets the indices at the end of subtractories of a given length when starting at each index...
Eigen::Matrix< double, 3, 3 > quat_2_Rot(const Eigen::Matrix< double, 4, 1 > &q)
void clear()
Will clear any old values.
Eigen::Matrix< double, 3, 3 > rot_y(double t)
std::vector< Eigen::Matrix< double, 7, 1 > > gt_poses
#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 > est_times
std::vector< double > timestamps
Timestamp when these values occured at.