Skip to content

Introduction

Retargeting experiments begin with observations, not synchronized files and not robot-specific dictionaries.

sensor/video/pose sources
-> native recordings
-> observation recipe
-> SceneObservation
-> retargeting recipe + RobotSpec
-> RetargetingProblem
-> RetargetingResult

Native Data

Every modality keeps its own timeline and frame:

from retarget import GvhmrOutputSource, ViconRecordingSource
from retarget.recipes.skateboarding import GVHMR_SCHEMA, VICON_SCHEMA

mocap = ViconRecordingSource(vicon_path, VICON_SCHEMA, name=demo)
human_pose = GvhmrOutputSource(
    gvhmr_path,
    GVHMR_SCHEMA,
    fps=59.942,
    name=demo,
)

Loading these sources produces MocapRecording and HumanPoseRecording. Nothing claims they are synchronized. Tracks carry enum roles, validity masks, and provenance.

Observation Processing

An ObservationRecipe declares the modalities and fusion policy:

from retarget.recipes.skateboarding import SkateboardingObservationRecipe

observation_recipe = SkateboardingObservationRecipe(
    mocap=mocap,
    human_pose=human_pose,
)
observation = observation_recipe.observe()

For skateboarding this estimates a ClockTransform, crops to valid overlap, resamples each track according to its type, converts frames, registers the actor spatially, reconstructs the board, and derives semantic contacts. Failed quality gates raise AlignmentError with an AlignmentReport.

SceneObservation is independent of the target robot. It can be inspected or reused:

for report in observation.alignment_reports:
    print(report.strategy, report.score, report.accepted)

observation.save_npz("inspection_checkpoint.npz")

The save call is optional and explicit. The standard path remains in memory.

Robot Adaptation

RobotSpec declares a robot-role vocabulary and binds roles to concrete joints, links, and link groups. An adaptation recipe maps semantic observation roles to those robot roles:

from retarget.recipes.skateboarding import SkateboardingRetargetingRecipe

recipe = SkateboardingRetargetingRecipe()
problem = recipe.build_problem(observation, robot)

Only this stage creates ContactPlan, LinkTargetPlan, source-to-robot mappings, objectives, and constraints. Semantic contacts never contain robot link names.

Experiment Orchestration

RetargetingExperiment composes both stages:

from retarget import RetargetingExperiment

experiment = RetargetingExperiment(
    observation=observation_recipe,
    recipe=recipe,
    robot=robot,
)
result = experiment.run()

Pass an existing SceneObservation instead of observation_recipe to reuse capture processing with another robot or profile.

Holosoma climbing follows the same hierarchy with one mocap source and no temporal fusion:

from retarget.recipes.holosoma import (
    HolosomaClimbObservationRecipe,
    HolosomaClimbRetargetingRecipe,
)

It is a strict subset of the architecture, not a separate adapter system.

Declarative Frontend

Run configs serialize the same object graph:

[observation]
kind = "skateboarding"
vicon = "/data/vicon/demo"
gvhmr = "/data/gvhmr/demo"
video_fps = 59.942

[recipe]
kind = "skateboarding"

RetargetingRunConfig.build_experiment() returns the public experiment object. There is no synchronization command or staged archive required by the CLI.