Principle:Isaac sim IsaacGymEnvs Skeletal Motion Representation
| Knowledge Sources | |
|---|---|
| Domains | Character_Animation, Skeleton_Representation |
| Last Updated | 2026-02-15 11:00 GMT |
Overview
Skeletal motion representation models articulated body motion as a hierarchical tree of joints (SkeletonTree) with associated poses (SkeletonState) and temporal sequences (SkeletonMotion), supporting local and global transformations, motion retargeting between different skeleton topologies, and serialization from multiple file formats.
Description
The foundation of skeletal motion representation is the SkeletonTree, a data structure that encodes the topology of an articulated body. Each node in the tree represents a joint, and edges represent bones connecting parent joints to child joints. The tree stores joint names, parent indices, and local offset vectors (the rest-pose displacement from parent to child). This topological description is independent of any specific pose or motion, serving as the structural blueprint for the character.
A SkeletonState associates a concrete pose with a SkeletonTree. Each joint in the tree is assigned a local rotation (quaternion) and optionally a local translation relative to its parent. Through forward kinematics -- propagating transformations from the root down through the hierarchy -- the system computes global rotations and global translations for every joint. The root joint additionally carries a global position and orientation that places the entire skeleton in world space. SkeletonState supports both local-to-global and global-to-local conversion, enabling flexible manipulation of poses at either level of the hierarchy.
A SkeletonMotion extends SkeletonState with a temporal dimension, representing a sequence of poses over time at a given frame rate (fps). Beyond position and rotation per frame, SkeletonMotion can compute derived quantities such as linear velocity, angular velocity, and root velocity through finite differencing of successive frames. Motion retargeting maps motion data from one skeleton topology to another -- for example, transferring motion captured on a human skeleton to a differently-proportioned humanoid robot. Retargeting preserves joint angles while adapting to different bone lengths and joint orderings. Serialization supports loading skeleton and motion data from FBX files (via an FBX backend), MJCF robot description files, and compressed NPZ archives, enabling interoperability with diverse data sources.
Usage
Use SkeletonTree when defining the structure of an articulated character or robot. Use SkeletonState for representing and manipulating individual poses -- for example, computing fingertip positions from joint angles. Use SkeletonMotion for working with motion clips -- loading reference animations, computing velocities for reward functions, or sampling transitions for adversarial training. Use motion retargeting when reference motions are captured on a different skeleton than the simulated agent.
Theoretical Basis
Forward kinematics:
T_global(joint_i) = T_global(parent_i) * T_local(joint_i)
Local transform from rotation and translation:
T_local = [R_local | t_local; 0 | 1]
Velocity via finite differencing:
v(t) = (p(t+1) - p(t)) * fps
Angular velocity from quaternions:
omega(t) = 2 * quat_mul(q(t+1), q_inv(t)) * fps
# Abstract Skeletal Motion Representation (pseudo-code)
class SkeletonTree:
def __init__(self, joint_names, parent_indices, local_offsets):
self.joint_names = joint_names # list of joint name strings
self.parent_indices = parent_indices # parent index per joint (-1 for root)
self.local_offsets = local_offsets # rest-pose offset vectors
def num_joints(self):
return len(self.joint_names)
class SkeletonState:
def __init__(self, skeleton_tree, local_rotations, root_translation):
self.skeleton_tree = skeleton_tree
self.local_rotations = local_rotations # quaternion per joint
self.root_translation = root_translation # 3D position of root
def compute_global_transforms(self):
"""Forward kinematics: propagate transforms root-to-leaf."""
global_rotations = zeros(num_joints, 4)
global_translations = zeros(num_joints, 3)
for joint_idx in topological_order(self.skeleton_tree):
parent_idx = self.skeleton_tree.parent_indices[joint_idx]
if parent_idx == -1:
global_rotations[joint_idx] = self.local_rotations[joint_idx]
global_translations[joint_idx] = self.root_translation
else:
global_rotations[joint_idx] = quat_mul(
global_rotations[parent_idx],
self.local_rotations[joint_idx]
)
global_translations[joint_idx] = (
global_translations[parent_idx]
+ quat_apply(global_rotations[parent_idx],
self.skeleton_tree.local_offsets[joint_idx])
)
return global_rotations, global_translations
class SkeletonMotion:
def __init__(self, skeleton_states, fps):
self.states = skeleton_states # sequence of SkeletonState
self.fps = fps
def compute_velocities(self):
"""Finite difference velocity computation."""
linear_vel = (positions[1:] - positions[:-1]) * self.fps
angular_vel = 2.0 * quat_mul(rotations[1:], quat_inv(rotations[:-1])) * self.fps
return linear_vel, angular_vel
def retarget_motion(source_motion, source_skeleton, target_skeleton, joint_mapping):
"""Map motion from source skeleton to target skeleton."""
retargeted_frames = []
for frame in source_motion.states:
target_rotations = zeros(target_skeleton.num_joints(), 4)
for src_joint, tgt_joint in joint_mapping.items():
target_rotations[tgt_joint] = frame.local_rotations[src_joint]
retargeted_frames.append(
SkeletonState(target_skeleton, target_rotations, frame.root_translation)
)
return SkeletonMotion(retargeted_frames, source_motion.fps)