@@ -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+ "\n Translation 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 )
0 commit comments