Source code for protomotions.utils.motion_interpolation_utils

# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

"""Motion interpolation utilities.

Provides functions for smoothly interpolating between motion frames,
including linear position interpolation and spherical quaternion interpolation (SLERP).
"""

import torch
from protomotions.utils import rotations


[docs] def interpolate_pos(pos0, pos1, blend): """Linear interpolation between two position tensors. Args: pos0: Starting positions [batch, ...] or [batch, bodies, 3] pos1: Ending positions [batch, ...] or [batch, bodies, 3] blend: Blend factor [batch] where 0=pos0, 1=pos1 Returns: Interpolated positions with same shape as pos0/pos1 """ if pos1.dim() == 2: blend = blend.unsqueeze(-1) elif pos1.dim() == 3: blend = blend.unsqueeze(-1).unsqueeze(-1) else: raise ValueError(f"pos1 has {pos1.dim()} dimensions, expected 2 or 3") return (1.0 - blend) * pos0 + blend * pos1
[docs] def interpolate_quat(rot0, rot1, blend): """Spherical linear interpolation (SLERP) between quaternions. Args: rot0: Starting quaternions [batch, 4] or [batch, bodies, 4] rot1: Ending quaternions [batch, 4] or [batch, bodies, 4] blend: Blend factor [batch] where 0=rot0, 1=rot1 Returns: Interpolated quaternions with same shape as rot0/rot1 """ if rot1.dim() == 2: blend = blend.unsqueeze(-1) elif rot1.dim() == 3: blend = blend.unsqueeze(-1).unsqueeze(-1) else: raise ValueError(f"rot1 has {rot1.dim()} dimensions, expected 2 or 3") return rotations.slerp(rot0, rot1, blend)
[docs] def calc_frame_blend(time, length, num_frames, dt): """ Calculate frame indices and blend factor for interpolation. Args: time (torch.Tensor): Current time. length (torch.Tensor): Length of the motion sequence in seconds. num_frames (torch.Tensor): Number of frames in the motion sequence. dt (torch.Tensor): Time step between frames. Returns: Tuple[torch.Tensor, torch.Tensor, torch.Tensor]: Frame index 0, frame index 1, and blend factor. """ time = torch.minimum(torch.clamp_min(time, 0.0), length) phase = time / length phase = torch.clip(phase, 0.0, 1.0) frame_idx0 = (phase * (num_frames - 1)).long() frame_idx1 = torch.min(frame_idx0 + 1, num_frames - 1) blend = torch.clip((time - frame_idx0 * dt) / dt, 0.0, 1.0) return frame_idx0, frame_idx1, blend