Evaluation & Metrics
Absolute Trajectory Error
The root-mean-square distance between estimated and ground-truth camera positions after the two trajectories are aligned; how the alignment (SE(3), Sim(3), yaw-only, all poses or the first), timestamp association and reference frames change the number, and how it relates to the relative pose error and the KITTI odometry metric.
intermediate
The absolute trajectory error (ATE) measures how far an estimated camera trajectory lies from the true one. The estimated positions are first moved, as a whole, by the transformation that fits them best to the ground truth; ATE is then the root-mean-square distance between corresponding positions, in metres. Sturm et al. (2012) formalized it, with the relative pose error (RPE), as one of the two metrics of the TUM RGB-D benchmark, describing both as frequently employed; ATE measures the global consistency of SLAM trajectories, while RPE measures the drift of visual odometry.
The definitions of both metrics, and why monocular trajectories need a similarity alignment, are introduced in the camera pose article. This article covers what decides the number in practice: which transformation is allowed, which poses it is fitted to, how timestamps are matched, which frame the poses describe, and how ATE relates to RPE and to the KITTI odometry metric.
Formula
Let be the estimated poses and the ground-truth poses in , after their timestamps have been matched, and let and be their positions. Sturm et al. (2012) define ATE in pose form, as the RMS of the translational part of , where is the least-squares rigid alignment given by Horn’s (1987) closed form (their Eqs. 4–5, given in camera pose). The translation of is the gap between the aligned estimated position and the true position, rotated into the true camera frame, so its length is simply that distance; evo computes the translational ATE from positions alone for this reason. Written with positions, and with the alignment group left open,
where is () in Sturm et al., the similarity group for monocular estimates, or a yaw-only subgroup for visual-inertial ones. The value lies in , in the units of the ground truth; zero means the estimate equals the ground truth up to a transformation in . The alignment is usually fitted to positions only, even when orientations are available, which Zhang and Scaramuzza (2018), citing a 2015 workshop paper by Salas et al., consider sufficient.
Zhang and Scaramuzza (2018) also define a rotational ATE, the RMS of the angles of (their Eqs. 23–24), where and are the estimated and true orientations and is the alignment rotation; the angle of a rotation matrix is the geodesic distance of rotation representations.
The closed-form alignment
With known correspondences, fitting is the absolute orientation problem, the same least-squares fit that iterative closest point solves in each iteration; that article derives the rigid solution and the determinant correction that excludes reflections. Umeyama’s (1991) method, which Zhang and Scaramuzza (2018) call the de facto standard and give as their Algorithm 1, adds the scale. With means and , the variance of the estimated positions, and the cross-covariance
the solution is
with if and otherwise; gives the fit. Horn (1987) derives the same rotation with quaternions and discusses three scales: the one above, which scales the estimate onto the ground truth; the reciprocal of the fit in the other direction; and a symmetric scale, the ratio of the two RMS spreads about the centroids, which he prefers when both point sets are equally noisy; an asymmetric scale, he notes, suits the case where one set is far more precise than the other, as ground truth is. The choice does not change the optimal rotation. It follows from the ATE formula that only the first scale minimizes ATE, and it is the one evo’s Umeyama alignment computes.
For visual-inertial estimates, Zhang and Scaramuzza (2018) restrict to a rotation about the gravity axis. Maximizing , where the columns of and are the centred positions, reduces to their Eq. 18,
whose solution is . Aligning on the first pose only is simpler still: and (their Eq. 19).
Relative pose error
The relative pose error compares motions instead of positions. For an interval , Sturm et al. (2012) take the translational RMSE, , of over the pairs (their Eqs. 1–2, given in camera pose); the rotational RPE is the RMS of the rotation angles of . RPE needs no alignment, but it does depend on scale. Besides , the drift per frame, Sturm et al. suggest, for example, for the drift per second at 30 Hz. For SLAM they average over all intervals (their Eq. 3),
which costs time quadratic in the trajectory length, so they approximate it from a fixed number of sampled pairs.
The KITTI odometry errors
Geiger et al. (2012) keep rotation and translation separate (their Eqs. 2–3): for a set of frame pairs ,
where is the inverse compositional operator, so each term is a relative pose error. These are means, not RMS values, and no global alignment is involved. The benchmark averages them over all subsequences of fixed lengths, listed with their 2013 change in visual odometry, and reports percent and degrees per metre (as of October 2026); those units imply a division by the subsequence length, a step the paper’s equations do not show.
Intuition
ATE asks one question: if the whole estimated trajectory is placed as well as possible on top of the true one, how far apart are they on average? A single transformation is allowed, so an error made at one moment that displaces everything after it cannot be undone by the alignment; this is what makes ATE a measure of global consistency. RPE asks a local question: over every window of frames, did the estimate move the way the camera moved? A window that straddles one bad step sees the error; the others do not.
The two disagree in instructive ways. A heading error rotates the rest of the path about the point where it occurred, so it displaces every later pose, the more the farther that pose lies from the point; RPE charges it only in the windows that contain it. Sturm et al. (2012) note that rotational errors also appear as translational errors once the camera moves, which is why they found translational RPE sufficient, and they state that RPE averaged over all intervals is always slightly larger than ATE, illustrating it with RGB-D SLAM trajectories (their Fig. 7b); in their experiments the two metrics often ranked results in the same order.
Interpretation
ATE is an absolute length, so it means little without the size of the scene. The same 5 cm is a large error on a desk-sized sequence and a negligible one on a kilometre-long drive, and for a drifting system ATE also grows with the trajectory length, as the example shows. KITTI’s errors are given per unit distance instead.
The alignment changes the value substantially. Zhang and Scaramuzza (2018) ran VINS-Mono on EuRoC Machine Hall 01 and aligned with a yaw-only transformation; using all 1,807 states gave an ATE of 0.2795 m (rotation 2.4935°), while aligning with the first state only gave 0.4383 m, and the value fell steadily as more initial states were included (their Table III). They conclude that the states used for alignment must be the same for all compared algorithms and reported with the results. A reported ATE therefore needs four qualifiers: the alignment group, the poses used to fit it, the timestamp association, and whether it is RMSE, mean or median.
When to Use It
Use ATE for systems meant to produce a globally consistent trajectory: SLAM with loop closure, offline structure from motion on video, and dense reconstruction systems such as KinectFusion, whose maps are only as consistent as their poses. Use RPE, or KITTI’s length-normalized errors, for odometry, where drift is expected and its rate is what matters. The TUM benchmark documentation gives the same split, and Zhang and Scaramuzza (2018) recommend reporting both, since ATE is a single comparable number while relative errors over several lengths show how error grows.
ATE needs ground truth for absolute poses over the whole run, such as motion capture, a laser tracker or RTK-GPS; when only sparse relative constraints are known, Sturm et al. (2012) point to RPE. It also assumes the ground truth is far more accurate than the estimate. KITTI moved away from short subsequences because the GPS/IMU ground-truth error biased them, as its benchmark page explains.
Choosing the alignment
Zhang and Scaramuzza (2018) derive the alignment from what the sensors leave unobservable: the transformations that leave every measurement unchanged. Their Table I:
| Sensor setup | Unobservable | Alignment over all poses | From the first pose |
|---|---|---|---|
| Monocular camera | Similarity: rotation, translation, scale (7 DoF) | Sim(3), Umeyama with scale | Not possible (scale) |
| Stereo camera | Rigid: rotation and translation (6 DoF) | SE(3), Umeyama with | Eq. 19 |
| Visual-inertial | Yaw about gravity and translation (4 DoF) | Umeyama with the yaw rotation of Eq. 18 | Eq. 19 with the yaw rotation of Eq. 20 |
An RGB-D camera, like a stereo pair, gives metric scale, so the TUM benchmark aligns in SE(3). An IMU makes roll and pitch observable through gravity, so a 6-DoF alignment would forgive real tilt errors of a visual-inertial system. Zhang and Scaramuzza also note that aligning on all states tends to give a lower ATE, while aligning on the first state shows how the error grows over time; least-squares alignment treats every state as equally uncertain.
Association and frames
Estimated and ground-truth poses rarely share timestamps: Sturm et al. (2012) note that the two sequences usually differ in rate and length and may have gaps, so poses must be associated and possibly interpolated first. The TUM evaluation script associates estimated and ground-truth poses by timestamp, accepting matches within 0.02 s by default and allowing a time offset; evo 1.38.1 uses a maximum difference of 0.01 s by default. It follows that an uncorrected clock offset compares each estimate with where the camera was slightly earlier or later, an error that grows with speed and that a spatial alignment can absorb only where the motion is uniform. On EuRoC (Burri et al., 2016), ground truth comes from a Vicon motion-capture system (6-D pose) and a Leica MS50 laser tracker (3-D position), and the dataset page lists the synchronization between sensor data and ground truth, recorded by separate systems, as a known limitation (as of October 2026).
The poses must also describe the same physical frame. In the TUM benchmark both trajectories give the pose of the Kinect’s RGB optical frame relative to an arbitrary world frame (Sturm et al., 2012). If an estimator reports its camera pose while the ground truth tracks a marker body or an IMU, then, by the same reasoning, the fixed offset between the two frames turns every rotation of the platform into a position error that no global alignment can absorb, unless the estimate is first transformed by the calibrated offset. The world frames may differ, which is what the alignment is for; the body frames may not.
Example
The script verifies the Umeyama implementation on a known similarity transform, then evaluates four estimates of a camera body (x forward, z up) driving a 97 m closed loop at 10 Hz: per-pose jitter, a constant heading drift of 0.02° per frame, the jitter with half the true scale (as a monocular system might report), and an otherwise perfect estimate in which every pose from frame 300 on is displaced by one rigid correction of 0.4 m and 2°, a stand-in for the jump an online SLAM output shows at a loop closure. Each estimate is expressed in its own frame, rotated 35° about the vertical and shifted, so alignment is needed. It runs in under a second with NumPy.
import numpy as np
def rot_z(a):
c, s = np.cos(a), np.sin(a)
return np.array([[c, -s, 0], [s, c, 0], [0, 0, 1.0]])
def rot_y(a):
c, s = np.cos(a), np.sin(a)
return np.array([[c, 0, s], [0, 1, 0], [-s, 0, c]])
def angle_deg(R):
return np.degrees(np.arccos(np.clip((np.trace(R) - 1) / 2, -1.0, 1.0)))
def umeyama(P, Q, with_scale):
"""s, R, t minimizing sum ||q_i - (s R p_i + t)||^2 (Umeyama 1991; Zhang & Scaramuzza 2018, Alg. 1)."""
mp, mq = P.mean(0), Q.mean(0)
Pc, Qc = P - mp, Q - mq
U, D, Vt = np.linalg.svd(Qc.T @ Pc / len(P)) # covariance Sigma = U diag(D) V^T
W = np.diag([1.0, 1.0, np.sign(np.linalg.det(U) * np.linalg.det(Vt))])
R = U @ W @ Vt
s = np.trace(np.diag(D) @ W) / np.mean(np.sum(Pc ** 2, 1)) if with_scale else 1.0
return s, R, mq - s * R @ mp
def yaw_only(P, Q):
"""4-DoF alignment: rotation about gravity (z) plus translation (Zhang & Scaramuzza 2018, eq. 18)."""
mp, mq = P.mean(0), Q.mean(0)
M = (P - mp).T @ (Q - mq) # \hat{P} P^T
th = np.arctan2(M[0, 1] - M[1, 0], M[0, 0] + M[1, 1])
return 1.0, rot_z(th), mq - rot_z(th) @ mp
def first_pose(Rs_est, P, Rs_gt, Q):
"""Rigid alignment from the first pose only (Zhang & Scaramuzza 2018, eq. 19)."""
R = Rs_gt[0] @ Rs_est[0].T
return 1.0, R, Q[0] - R @ P[0]
def ate(est, gt, mode):
"""Translational ATE RMSE (m) and rotational ATE RMSE (deg) after the chosen alignment."""
(Rs_e, P), (Rs_g, Q) = est, gt
if mode == "none":
s, R, t = 1.0, np.eye(3), np.zeros(3)
elif mode == "first pose":
s, R, t = first_pose(Rs_e, P, Rs_g, Q)
elif mode == "4-DoF":
s, R, t = yaw_only(P, Q)
else:
s, R, t = umeyama(P, Q, with_scale=(mode == "Sim(3)"))
e_pos = Q - (s * P @ R.T + t)
e_rot = [angle_deg(Rg @ (R @ Re).T) for Re, Rg in zip(Rs_e, Rs_g)]
return np.sqrt(np.mean(np.sum(e_pos ** 2, 1))), np.sqrt(np.mean(np.square(e_rot))), s
def rpe(est, gt, delta):
"""RMSE of translation (m) and rotation angle (deg) of E_i = (Q_i^-1 Q_i+d)^-1 (P_i^-1 P_i+d)."""
(Rs_e, P), (Rs_g, Q) = est, gt
et, er = [], []
for i in range(len(P) - delta):
j = i + delta
dRg, dtg = Rs_g[i].T @ Rs_g[j], Rs_g[i].T @ (Q[j] - Q[i])
dRe, dte = Rs_e[i].T @ Rs_e[j], Rs_e[i].T @ (P[j] - P[i])
et.append(np.linalg.norm(dRg.T @ (dte - dtg)))
er.append(angle_deg(dRg.T @ dRe))
return np.sqrt(np.mean(np.square(et))), np.sqrt(np.mean(np.square(er)))
# 1. Umeyama recovers a known similarity transform exactly from noise-free points.
rng = np.random.default_rng(0)
X = rng.normal(size=(50, 3))
q, _ = np.linalg.qr(rng.normal(size=(3, 3)))
R_true = q * np.sign(np.linalg.det(q)) # a random rotation (det = +1)
s_true, t_true = 2.5, np.array([1.0, -4.0, 0.5])
s, R, t = umeyama(X, s_true * X @ R_true.T + t_true, with_scale=True)
print(f"recovery: |s - s_true| = {abs(s - s_true):.1e}, max|R - R_true| = {np.abs(R - R_true).max():.1e}, "
f"max|t - t_true| = {np.abs(t - t_true).max():.1e}")
# 2. Ground truth: a camera body (x forward, z up) driving a 97 m loop at 10 Hz, camera-to-world poses.
N = 601
th = np.linspace(0, 2 * np.pi, N)
Q = np.column_stack([20 * np.cos(th), 10 * np.sin(th), 1.5 + 0.3 * np.sin(3 * th)])
v = np.gradient(Q, axis=0)
Rs_gt = [rot_z(np.arctan2(d[1], d[0])) @ rot_y(-np.arctan2(d[2], np.hypot(d[0], d[1]))) for d in v]
gt = (Rs_gt, Q)
length = np.sum(np.linalg.norm(np.diff(Q, axis=0), axis=1))
def chain(yaw_err_deg):
"""Integrate the true frame-to-frame motions, adding a heading error (scalar or per frame)."""
Rs, P = [Rs_gt[0]], [Q[0]]
b = np.broadcast_to(np.radians(yaw_err_deg), N - 1)
for i in range(N - 1):
B = rot_z(b[i])
dR, dt = Rs_gt[i].T @ Rs_gt[i + 1], Rs_gt[i].T @ (Q[i + 1] - Q[i])
P.append(P[-1] + Rs[-1] @ B @ dt) # T_i+1 = T_i (B, 0) (dR, dt)
Rs.append(Rs[-1] @ B @ dR)
return Rs, np.array(P)
def to_foreign_frame(est, scale=1.0, yaw=np.radians(35), t=np.array([3.0, -7.0, 0.4])):
"""Express an estimate in the estimator's own frame: unknown yaw, offset and (monocular) scale."""
Rs, P = est
G = rot_z(yaw)
return [G @ R for R in Rs], scale * P @ G.T + t
def noisy(seed, sig_p=0.05, sig_r=0.5):
"""Per-pose jitter without drift: 5 cm and 0.5 degrees (std per axis)."""
r = np.random.default_rng(seed)
Rs = [R @ rot_z(np.radians(sig_r) * r.normal()) @ rot_y(np.radians(sig_r) * r.normal()) for R in Rs_gt]
return Rs, Q + sig_p * r.normal(size=Q.shape)
def jump(k=300, dx=np.array([0.4, 0.0, 0.0]), dyaw=np.radians(2.0)):
"""Accurate poses, but every pose from frame k on is displaced by one rigid correction."""
J = rot_z(dyaw)
Rs = [R if i < k else J @ R for i, R in enumerate(Rs_gt)]
P = np.array([p if i < k else J @ (p - Q[k]) + Q[k] + dx for i, p in enumerate(Q)])
return Rs, P
cases = {
"noise 5 cm, 0.5 deg": to_foreign_frame(noisy(0)),
"heading drift 0.02 deg/frame": to_foreign_frame(chain(0.02)),
"scale 0.5 + noise (mono)": to_foreign_frame(noisy(0), scale=0.5),
"loop-closure jump at 300": to_foreign_frame(jump()),
}
modes = ["none", "first pose", "4-DoF", "SE(3)", "Sim(3)"]
print(f"\nloop length {length:.1f} m, {N} poses")
print(f"{'translational ATE (m)':30s}" + "".join(f"{m:>11s}" for m in modes) + " rot. ATE SE(3) Sim(3) scale")
for name, est in cases.items():
res = [ate(est, gt, m) for m in modes]
print(f"{name:30s}" + "".join(f"{r[0]:11.3f}" for r in res) + f"{res[3][1]:11.2f} deg{res[4][2]:10.3f}")
print(f"\n{'RPE RMSE: trans (m) / rot (deg)':30s}" + "".join(f"{'Delta = ' + str(d):>17s}" for d in (1, 10, 100)))
for name, est in cases.items():
print(f"{name:30s}" + "".join("{:9.3f} /{:6.2f}".format(*rpe(est, gt, d)) for d in (1, 10, 100)))
# 3. Spread over noise seeds; when a heading error happens; how much of the loop is evaluated.
a = np.array([[ate(to_foreign_frame(noisy(sd)), gt, m)[0] for m in ("first pose", "SE(3)")] for sd in range(10)])
print(f"\nnoise case, seeds 0-9: first-pose ATE {a[:, 0].min():.3f}-{a[:, 0].max():.3f} m, "
f"SE(3) ATE {a[:, 1].min():.3f}-{a[:, 1].max():.3f} m")
for k in (60, 300, 540):
e = np.zeros(N - 1); e[k] = 2.0
est = to_foreign_frame(chain(e))
t_err, r_err = rpe(est, gt, 10)
print(f"single 2 deg heading error at frame {k}: ATE first pose {ate(est, gt, 'first pose')[0]:.3f} m, "
f"SE(3) {ate(est, gt, 'SE(3)')[0]:.3f} m; RPE (Delta = 10) {t_err:.3f} m / {r_err:.2f} deg")
Rs_d, P_d = to_foreign_frame(chain(0.02))
for n in (151, 301, 601):
e = ate((Rs_d[:n], P_d[:n]), (Rs_gt[:n], Q[:n]), "SE(3)")[0]
print(f"heading drift, first {n - 1:3d} frames: SE(3) ATE {e:.3f} m")
Output:
recovery: |s - s_true| = 4.4e-16, max|R - R_true| = 6.7e-16, max|t - t_true| = 1.8e-15
loop length 97.0 m, 601 poses
translational ATE (m) none first pose 4-DoF SE(3) Sim(3) rot. ATE SE(3) Sim(3) scale
noise 5 cm, 0.5 deg 12.184 0.137 0.087 0.087 0.087 0.69 deg 1.000
heading drift 0.02 deg/frame 13.289 2.086 1.150 1.150 1.090 3.47 deg 1.024
scale 0.5 + noise (mono) 12.874 12.709 7.910 7.910 0.087 0.69 deg 2.000
loop-closure jump at 300 12.180 0.749 0.467 0.467 0.449 1.04 deg 1.008
RPE RMSE: trans (m) / rot (deg) Delta = 1 Delta = 10 Delta = 100
noise 5 cm, 0.5 deg 0.122 / 0.98 0.124 / 0.99 0.223 / 0.99
heading drift 0.02 deg/frame 0.000 / 0.02 0.003 / 0.20 0.296 / 2.00
scale 0.5 + noise (mono) 0.103 / 0.98 0.834 / 0.99 8.290 / 0.99
loop-closure jump at 300 0.016 / 0.08 0.054 / 0.26 0.265 / 0.89
noise case, seeds 0-9: first-pose ATE 0.129-0.453 m, SE(3) ATE 0.084-0.090 m
single 2 deg heading error at frame 60: ATE first pose 0.814 m, SE(3) 0.045 m; RPE (Delta = 10) 0.004 m / 0.26 deg
single 2 deg heading error at frame 300: ATE first pose 0.630 m, SE(3) 0.431 m; RPE (Delta = 10) 0.003 m / 0.26 deg
single 2 deg heading error at frame 540: ATE first pose 0.048 m, SE(3) 0.045 m; RPE (Delta = 10) 0.004 m / 0.26 deg
heading drift, first 150 frames: SE(3) ATE 0.048 m
heading drift, first 300 frames: SE(3) ATE 0.191 m
heading drift, first 600 frames: SE(3) ATE 1.150 m
The implementation recovers the scale, rotation and translation to rounding error. Without alignment every estimate is 12 to 13 m off, which measures only the change of frame. With jitter alone, SE(3) alignment leaves about the expected cm of independent noise in three axes; aligning on the first pose instead ties the result to that pose’s own error, so over seeds 0–9 it varies more than threefold while the all-pose ATE barely moves. The yaw-only fit matches SE(3) in every row because the frame change here is a rotation about the vertical, so the extra roll and pitch of SE(3) have nothing systematic to fit; for a visual-inertial system that is the point, since it uses no freedom the sensors do not leave open.
Scale shows how much the group matters. No rigid alignment can fit the half-scale estimate, while Sim(3) recovers the factor of 2 and brings it to the jitter level. Applied to the metric heading-drift estimate, Sim(3) enlarges the trajectory slightly and lowers the ATE by about 5%: extra freedom absorbs part of a real error. Rotational ATE ignores scale entirely, and translational RPE of the monocular estimate is in the estimate’s own units, so it is meaningless until the scale is fixed.
The relative errors separate the error types that ATE mixes. Jitter gives a rotational RPE that does not depend on , while its translational RPE grows with because orientation noise swings the longer displacement, the effect Sturm et al. describe. Heading drift gives a rotational RPE exactly proportional to (0.02° per frame) and a translational RPE that grows roughly with its square. The loop-closure jump is invisible to RPE except in the windows that straddle frame 300: at the RMS is the 0.4 m jump diluted over 600 windows, while ATE charges half of the trajectory for it.
The single heading error makes the timing effect concrete. Its RPE is nearly the same wherever it happens. With first-pose alignment an early error costs most, the effect Geiger et al. (2012) and Sturm et al. (2012) describe for the end-point error and Zhang and Scaramuzza (2018) state as a tendency of ATE, without naming an alignment. With all-pose alignment this example shows a different pattern: an error near either end costs little, and one in the middle costs about ten times as much, because it splits the loop into two halves that one rigid motion cannot both fit. That is the result for this trajectory and error size, not a general law, but it shows that which errors ATE punishes depends on the alignment. Finally, the same drift gives an ATE more than twenty times larger over the whole loop than over its first quarter, so ATEs from sequences of different lengths are not comparable.
Failure Cases
- Different alignment conventions. An ATE after Sim(3) alignment, after SE(3) alignment over all poses, and after first-pose alignment are different numbers for the same trajectory; tables that mix them, or omit which was used, compare nothing. Tool defaults differ:
evo_apein evo 1.38.1 does not align unless-a(SE(3)) is given, aligns in Sim(3) with-a -s(-salone corrects only the scale), and can align on the first poses (--n_to_align) or the origin (--align_origin); the rpg_trajectory_evaluation toolbox falls back to Sim(3) over all poses when no configuration file is present, and offerssim3,se3,posyawandnone. - Too much freedom. Besides the Sim(3) case covered in camera pose, SE(3) alignment of a visual-inertial estimate forgives tilt errors, and the example shows Sim(3) absorbing part of a metric trajectory’s drift.
- Different RPE settings. The interval and its unit (frames, seconds, metres), consecutive versus all pairs, and RMSE versus mean change RPE. By default the TUM
evaluate_rpe.pysamples up to 10,000 pairs from all timestamp pairs and prints the mean translational error, whileevo_rpeuses frame; for larger it uses non-overlapping windows unless--all_pairsrequests the overlapping ones of Sturm et al. - Trajectory length. For a drifting system ATE grows faster than the length (more than twentyfold for a fourfold longer path in the example), so averaging ATE over sequences of different lengths weights the long ones. KITTI normalizes by distance instead.
- Rotation left out. Translational ATE scores positions only. Rotation errors count only once they have displaced later positions, so an orientation error near the end of a run, or while the camera rotates in place, barely registers; a system can have a small ATE and still point its camera wrongly, which matters for rendering or projecting into images. Report rotational ATE or RPE alongside.
- Missing poses. It follows from the definition that ATE averages over the poses that were associated. A system that loses tracking and outputs fewer poses is scored on the easier part of the run; robustness needs a separate measure, such as the success rate the SLAM article mentions.
- Mismatched frames and clocks. Camera poses compared with body or marker poses, a time offset between clocks, or poses compared in the wrong direction (camera-to-world against world-to-camera, see camera pose) produce errors that alignment cannot remove, or remove errors that are real.
- Outliers. RMSE weights large errors heavily; Sturm et al. (2012) note that the mean, and more so the median, give outliers less influence.
Variants
- Rotational ATE (Zhang and Scaramuzza, 2018): the RMS rotation angle after alignment, in degrees.
- Relative pose error, translational and rotational, per frame, per second or per metre, for one or averaged over all (Sturm et al., 2012).
- Relative error over sub-trajectories (Zhang and Scaramuzza, 2018): each sub-trajectory of a given length is aligned on its first state and the error of its end state collected, reported as statistics or box plots for several lengths rather than one number.
- KITTI translational and rotational errors (Geiger et al., 2012): mean relative errors over 100–800 m subsequences, per unit length.
- Statistics other than RMSE: the mean, median and standard deviation of the per-pose errors, which the TUM ATE script prints with
--verbose, and the minimum and maximum, which evo also prints.
Related Metrics
ATE is the RMS of a distance, the mean squared error family applied to positions after a fit; the endpoint error plays the same role for optical flow vectors. Reprojection error measures how well a reconstruction explains its own images without any ground truth, which Sturm et al. (2012) note does not guarantee an accurate trajectory. The pose errors of a single camera, a rotation angle and a centre distance, are defined in camera pose.
Related
- Visual Odometry
Estimating the motion of a camera, and of the vehicle or device carrying it, incrementally from a video stream, frame after frame, without building a globally consistent map.
- KITTI
A benchmark suite of real driving data from Karlsruhe, with stereo, optical flow, scene flow, odometry, object detection, and tracking benchmarks built from camera, LiDAR, and GPS/IMU recordings.
- Iterative Closest Point (ICP)
An algorithm that rigidly aligns two point clouds without known correspondences by alternating closest-point matching with a least-squares estimate of the rotation and translation.
- Mean Squared Error
The average squared difference between a prediction and its reference, over pixels, channels or values, with its square root RMSE and the mean absolute error MAE; how they are computed for images, what their units and statistical meaning are, and why very different distortions can share one MSE.
- Rotation Representations
The ways a 3D rotation can be written down (rotation matrices, Euler angles, rotation vectors, unit quaternions, 6D vectors and the Lie algebra), how they convert into each other, and where each one fails.
- KinectFusion
A real-time dense RGB-D SLAM system that fuses every depth frame into a TSDF volume on the GPU and tracks the camera by point-to-plane ICP against a surface ray cast from that volume.
References
- Sturm, J., Engelhard, N., Endres, F., Burgard, W. & Cremers, D. (2012). A Benchmark for the Evaluation of RGB-D SLAM Systems. IEEE/RSJ International Conference on Intelligent Robots and Systems (IROS), 573–580.
- Horn, B. K. P. (1987). Closed-Form Solution of Absolute Orientation Using Unit Quaternions. Journal of the Optical Society of America A, 4(4), 629–642.
- Umeyama, S. (1991). Least-Squares Estimation of Transformation Parameters Between Two Point Patterns. IEEE Transactions on Pattern Analysis and Machine Intelligence, 13(4), 376–380.
- Zhang, Z. & Scaramuzza, D. (2018). A Tutorial on Quantitative Trajectory Evaluation for Visual(-Inertial) Odometry. IEEE/RSJ International Conference on Intelligent Robots and Systems (IROS), 7244–7251.
- Geiger, A., Lenz, P. & Urtasun, R. (2012). Are We Ready for Autonomous Driving? The KITTI Vision Benchmark Suite. IEEE Conference on Computer Vision and Pattern Recognition (CVPR), 3354–3361.
- Burri, M., Nikolic, J., Gohl, P., Schneider, T., Rehder, J., Omari, S., Achtelik, M. W. & Siegwart, R. (2016). The EuRoC Micro Aerial Vehicle Datasets. The International Journal of Robotics Research, 35(10), 1157–1163.
- KITTI Vision Benchmark Suite. Visual Odometry / SLAM Evaluation 2012 (benchmark page, accessed October 2026).
- TUM Computer Vision Group. RGB-D SLAM Dataset and Benchmark: Useful Tools for the RGB-D Benchmark (accessed October 2026).
- Grupp, M. (2017). evo: Python Package for the Evaluation of Odometry and SLAM. GitHub repository, version 1.38.1 (2026).
- Robotics and Perception Group, University of Zurich. rpg_trajectory_evaluation: README (GitHub repository, commit 8c8ceec, 2022).
- Autonomous Systems Lab, ETH Zurich. EuRoC MAV Dataset (dataset page, accessed October 2026).