Skip to content

Commit 5adb48e

Browse files
committed
Umeyama on a time window
1 parent bafb629 commit 5adb48e

3 files changed

Lines changed: 92 additions & 2 deletions

File tree

evo/core/trajectory.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,82 @@ def speeds(self) -> np.ndarray:
420420
for i in range(len(self.positions_xyz) - 1)
421421
])
422422

423+
def align_on_window(self, traj_ref: 'PoseTrajectory3D', correct_scale: bool = False,
424+
correct_only_scale: bool = False, n: int = -1,
425+
start_time: typing.Optional[float] = None,
426+
end_time: typing.Optional[float] = None) -> geometry.UmeyamaResult:
427+
"""
428+
align to a reference trajectory using Umeyama alignment
429+
:param traj_ref: reference trajectory
430+
:param correct_scale: set to True to adjust also the scale
431+
:param correct_only_scale: set to True to correct the scale, but not the pose
432+
:param n: the number of poses to use, counted from the start (default: all)
433+
:param start_time: the time to start the Umeyama alignment window (default: start of the trajectory)
434+
:param end_time: the time to end the Umeyama alignment window (default: end of the trajectory)
435+
:return: the result parameters of the Umeyama algorithm
436+
"""
437+
if start_time is None and end_time is None:
438+
return self.align(traj_ref, correct_scale, correct_only_scale, n)
439+
440+
if n != -1:
441+
# Cannot have start_time not None or end_time not None, and n != 1
442+
raise TrajectoryException("start_time or end_time with n is not implemented")
443+
444+
with_scale = correct_scale or correct_only_scale
445+
if correct_only_scale:
446+
logger.debug("Correcting scale...")
447+
else:
448+
logger.debug("Aligning using Umeyama's method..." +
449+
(" (with scale correction)" if with_scale else ""))
450+
451+
relative_timestamps = self.timestamps - np.min(self.timestamps)
452+
453+
if start_time is None:
454+
start_index = 0
455+
elif np.all(relative_timestamps < start_time):
456+
logger.warning("Align start time ({}s) is after end of trajectory ({}s), ignoring start time"
457+
.format(start_time, np.max(relative_timestamps)))
458+
start_index = 0
459+
else:
460+
# Find first value that is less or equal to start_time
461+
start_index = np.flatnonzero(start_time <= relative_timestamps)[0]
462+
logger.debug("Start of alignment: in reference {}s, in trajectory {}s"
463+
.format(traj_ref.timestamps[start_index], self.timestamps[start_index]))
464+
465+
if end_time is None:
466+
end_index = self.positions_xyz.shape[0]
467+
elif np.all(relative_timestamps < end_time):
468+
logger.warning("Align end time ({}s) is after end of trajectory ({}s), ignoring end time"
469+
.format(end_time, np.max(relative_timestamps)))
470+
end_index = self.timestamps.shape[0]
471+
else:
472+
# Find first value that is greater or equal to end_time
473+
end_index = np.flatnonzero(end_time <= relative_timestamps)[0]
474+
logger.debug("End of alignment: in reference {}s, in trajectory {}s"
475+
.format(traj_ref.timestamps[end_index], self.timestamps[end_index]))
476+
477+
if end_index <= start_index:
478+
raise TrajectoryException("alignment is empty")
479+
480+
r_a, t_a, s = geometry.umeyama_alignment(self.positions_xyz[start_index:end_index, :].T,
481+
traj_ref.positions_xyz[start_index:end_index, :].T,
482+
with_scale)
483+
484+
if not correct_only_scale:
485+
logger.debug("Rotation of alignment:\n{}"
486+
"\nTranslation of alignment:\n{}".format(r_a, t_a))
487+
logger.debug("Scale correction: {}".format(s))
488+
489+
if correct_only_scale:
490+
self.scale(s)
491+
elif correct_scale:
492+
self.scale(s)
493+
self.transform(lie.se3(r_a, t_a))
494+
else:
495+
self.transform(lie.se3(r_a, t_a))
496+
497+
return r_a, t_a, s
498+
423499
def reduce_to_ids(
424500
self, ids: typing.Union[typing.Sequence[int], np.ndarray]) -> None:
425501
super(PoseTrajectory3D, self).reduce_to_ids(ids)

evo/main_traj.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,10 @@ def run(args):
235235

236236
if args.n_to_align != -1 and not (args.align or args.correct_scale):
237237
die("--n_to_align is useless without --align or/and --correct_scale")
238+
if args.n_to_align != -1 and (args.start_t_to_align is not None or args.end_t_to_align is not None):
239+
die("--start_t_to_align or --end_t_to_align with --n_to_align is not implemented")
240+
if (args.start_t_to_align is not None or args.end_t_to_align is not None) and not (args.align or args.correct_scale):
241+
die("--start_t_to_align and --end_t_to_align are useless without --align or/and --correct_scale")
238242

239243
# TODO: this is fugly, but is a quick solution for remembering each synced
240244
# reference when plotting pose correspondences later...
@@ -257,10 +261,11 @@ def run(args):
257261
if args.align or args.correct_scale:
258262
logger.debug(SEP)
259263
logger.debug("Aligning {} to reference.".format(name))
260-
trajectories[name].align(
264+
trajectories[name].align_on_window(
261265
ref_traj_tmp, correct_scale=args.correct_scale,
262266
correct_only_scale=args.correct_scale and not args.align,
263-
n=args.n_to_align)
267+
n=args.n_to_align, start_time=args.start_t_to_align,
268+
end_time=args.end_t_to_align)
264269
if args.align_origin:
265270
logger.debug(SEP)
266271
logger.debug("Aligning {}'s origin to reference.".format(name))

evo/main_traj_parser.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import argparse
2+
from typing import Optional
23

34
from evo.tools.settings import SETTINGS
45

@@ -20,6 +21,14 @@ def parser() -> argparse.ArgumentParser:
2021
"--n_to_align",
2122
help="the number of poses to use for Umeyama alignment, "
2223
"counted from the start (default: all)", default=-1, type=int)
24+
algo_opts.add_argument(
25+
"--start_t_to_align",
26+
help="the start of the time window to use for Umeyama alignment, "
27+
"in seconds relative to the first timestamp of the file", default=None, type=float)
28+
algo_opts.add_argument(
29+
"--end_t_to_align",
30+
help="the end of the time window to use for Umeyama alignment, "
31+
"in seconds relative to the first timestamp of the file", default=None, type=float)
2332
algo_opts.add_argument(
2433
"--sync",
2534
help="associate trajectories via matching timestamps - requires --ref",

0 commit comments

Comments
 (0)