initial commit
This commit is contained in:
170
tum_vi/eval.py
Normal file
170
tum_vi/eval.py
Normal file
@@ -0,0 +1,170 @@
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from matplotlib import pyplot as plt
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
def absoluteOrientationMatrix(D, M):
|
||||
dmean = D.mean(1)
|
||||
mmean = M.mean(1)
|
||||
|
||||
print("dmean is: ", dmean[:, np.newaxis])
|
||||
print("mmean is: ", mmean[:, np.newaxis])
|
||||
|
||||
D_centr = D - dmean[:, np.newaxis]
|
||||
M_centr = M - mmean[:, np.newaxis]
|
||||
|
||||
print("D_centr is: ", D_centr)
|
||||
print("M_centr is: ", M_centr)
|
||||
|
||||
# Comment with reference (1).
|
||||
# If you don't understand what's written here, plese refer to the links and the code below
|
||||
H = np.zeros(shape=(3, 3))
|
||||
for column in range(D.shape[1]):
|
||||
H += np.outer(D_centr[:, column], M_centr[:, column])
|
||||
# print("H is: ", H)
|
||||
print("H is: ", H.T)
|
||||
|
||||
u, s, vh = np.linalg.svd(H.T)
|
||||
#R = vh @ u.T
|
||||
I = np.identity(3)
|
||||
if(np.linalg.det(u) * np.linalg.det(vh)<0):
|
||||
I[2,2] = -1
|
||||
R = u @ I @ vh
|
||||
# R = R.T
|
||||
|
||||
# Output of rotmodel has to be the vector.
|
||||
# Comment with reference (2).
|
||||
rotmodel = R @ M_centr
|
||||
# print(rotmodel)
|
||||
dots = 0.0
|
||||
norms = 0.0
|
||||
|
||||
for column in range(D_centr.shape[1]):
|
||||
dots += np.dot(D_centr[:,column].transpose(),rotmodel[:,column])
|
||||
normi = np.linalg.norm(M_centr[:,column])
|
||||
norms += normi*normi
|
||||
|
||||
# s has to be the scalar.
|
||||
s = float(dots/norms)
|
||||
|
||||
T = dmean - R @ mmean
|
||||
T_GT = dmean - s * R @ mmean
|
||||
|
||||
print("The Rotation matrix is: \n", R, "\n")
|
||||
print("The Translation matrix is: \n", T, "\n")
|
||||
print("The scale factor is: ", s)
|
||||
|
||||
return R, T, T_GT, s
|
||||
|
||||
|
||||
def find_closest_times(res, gt, difference):
|
||||
gt_adapted = pd.DataFrame({"time[s]":[0], "tx":[0], "ty":[0], "tz":[0], "qx":[0], "qy":[0], "qz":[0], "qw":[0]})
|
||||
gt_adapted = gt_adapted.drop(0)
|
||||
|
||||
res = res.rename(columns={"x":"tx", "y":"ty", "z":"tz"})
|
||||
res_initial = res.copy()
|
||||
res = res_initial[0:0]
|
||||
|
||||
# Последний момент времени в res неадкватный, поэтому уберем его:
|
||||
# res = res.drop(len(res)-1)
|
||||
all_gt_times = gt["# timestamp[ns]"].to_numpy()
|
||||
|
||||
for i in range(len(res_initial)):
|
||||
time_res = res_initial.iloc[i][0]
|
||||
compar_res = np.where(all_gt_times < time_res)
|
||||
# print(compar_res[0])
|
||||
if not compar_res[0].shape[0] == 0:
|
||||
gt_time = gt.iloc[compar_res[0][-1]][0]
|
||||
# print(abs(gt_time - time_res) < difference)
|
||||
if ( (not compar_res[0].shape[0] == 0) and (abs(gt_time - time_res) < difference) ):
|
||||
index = compar_res[0][-1]
|
||||
else:
|
||||
continue
|
||||
|
||||
# print(index)
|
||||
closest_row = gt.iloc[index]
|
||||
closest_row = closest_row.rename({ "# timestamp[ns]":"time[s]" })
|
||||
closest_row = closest_row.rename(i)
|
||||
# print(closest_row)
|
||||
gt_adapted = gt_adapted.append(closest_row)
|
||||
res = res.append(res_initial.iloc[i])
|
||||
|
||||
return res, gt_adapted
|
||||
|
||||
|
||||
def ATE_error(D, M):
|
||||
diff = D.T - M.T
|
||||
print("alignmentGT resuls is: \n", diff)
|
||||
trans_error1 = np.sqrt(np.sum(np.multiply(diff,diff), 0))
|
||||
error1 = np.sqrt(np.dot(trans_error1, trans_error1)/len(trans_error1))
|
||||
return error1
|
||||
|
||||
|
||||
def plot_and_save(M, D, name):
|
||||
plot = plt.figure(figsize=(10,10))
|
||||
ax1 = plot.add_subplot(1, 1, 1)
|
||||
ax1.grid()
|
||||
# ax1.set_aspect("equal")
|
||||
ax1.plot(M[:, 0], M[:, 1], label="estimated")
|
||||
ax1.plot(D[:, 0], D[:, 1], label="ground truth")
|
||||
plot.legend()
|
||||
plot.savefig(name)
|
||||
|
||||
|
||||
def read_file(filename, sep_=",", switcher=0):
|
||||
file = pd.read_csv(filename, sep=sep_)
|
||||
if switcher == "1":
|
||||
file = file.rename(columns={ "#timestamp [ns]":"# timestamp[ns]", " p_RS_R_x [m]":"tx", " p_RS_R_y [m]":"ty",
|
||||
" p_RS_R_z [m]":"tz", " q_RS_w []":"qw", " q_RS_x []":"qx", " q_RS_y []":"qy",
|
||||
" q_RS_z []":"qz" })
|
||||
return file
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
parser = argparse.ArgumentParser(description='''This script computes the absolute trajectory error from the ground truth trajectory and the estimated trajectory. ''')
|
||||
parser.add_argument("first_file", help='ground truth trajectory (format: # timestamp[ns],tx,ty,tz,qw,qx,qy,qz) or (format: #timestamp [ns], p_RS_R_x [m], p_RS_R_y [m], p_RS_R_z [m], q_RS_w [], q_RS_x [], q_RS_y [], q_RS_z []). Use --switch_gt=0 to use first format or --switch_gt=1 to use second format')
|
||||
parser.add_argument("second_file", help="estimated trajectory (format: time[s] x y z qx qy qz qw)")
|
||||
parser.add_argument("--switch_gt", help="Use --switch_gt=0 to use first format or --switch_gt=1 to use second format", default=0)
|
||||
parser.add_argument("--slam_name", help="the name of the slam used for the processing. ORB-SLAM3 by default", default="orb_slam3")
|
||||
parser.add_argument("--savefile_folder", help="absolute or relative path to save the file", default=".")
|
||||
|
||||
args = parser.parse_args()
|
||||
first_file = args.first_file
|
||||
switcher = args.switch_gt
|
||||
second_file = args.second_file
|
||||
|
||||
gt = read_file(first_file, switcher=switcher)
|
||||
res = read_file(second_file, sep_=" ")
|
||||
|
||||
tmp = gt["qw"]
|
||||
del gt["qw"]
|
||||
gt["qw"] = tmp
|
||||
gt
|
||||
|
||||
# by default is 20 mlns of ns
|
||||
difference = 20000000
|
||||
res, gt_adapted = find_closest_times(res, gt, difference)
|
||||
|
||||
D = gt_adapted[["tx", "ty", "tz"]].to_numpy()
|
||||
M = res[["tx", "ty", "tz"]].to_numpy()
|
||||
|
||||
R, T, T_GT, s = absoluteOrientationMatrix(D.T, M.T)
|
||||
M_GT_aligned = (s * R @ M.T).T + T_GT
|
||||
M_aligned = (R @ M.T).T + T
|
||||
|
||||
error_GT = ATE_error(D, M_GT_aligned)
|
||||
error = ATE_error(D, M_aligned)
|
||||
|
||||
folder = args.savefile_folder
|
||||
f = open(folder + "/" + args.slam_name + "_errors.txt", "w")
|
||||
f.write("RMSError using scaling parameter: " + str(error_GT) + " m.")
|
||||
f.write("\n")
|
||||
f.write("RMSError without scaling parameter: " + str(error) + " m.")
|
||||
f.close()
|
||||
|
||||
plot_and_save(M_aligned, D, folder + "/" + args.slam_name + "_trajectory.png")
|
||||
plot_and_save(M_GT_aligned, D, folder + "/" + args.slam_name + "_trajectory_GT.png")
|
||||
|
||||
print("End of processing. Success! Check the created plots and files with data")
|
||||
Reference in New Issue
Block a user