Motion Reference¶
Motion tracking tasks score a robot against reference motion. Eden serves that motion along two paths, and this page documents the classes of both. The motion tracking guide explains the data schema, the multi-actor pattern and which tracking term reads which field.
- The manager path —
MimicRLEnvBaseowns aMotionManagerwhoseMotionTermloads a dataset, reads the frame atfloor((start + t) * fps)(no interpolation at lookup), serves a window of future frames and runs the motion-id and start-time curricula. Tracking terms read it asenv.motion_manager.data[<entity_name>]. This is the training path. - The command path —
MotionCommand(a command term) plays.npzclips verbatim on a plainRLEnvBase, for frozen-policy inference and single-clip tracking. It is documented with the command terms.
Both expose the same world-frame vocabulary: links_*_w for the tracked links and base_*_w for the floating
base. Quaternions are wxyz.
Motion manager and MotionTerm¶
Motion manager and MotionTerm base: motion-reference data for imitation tasks.
A :class:MotionTerm behaves like a PyTorch Dataset of motion clips (some generate
references on the fly), and the :class:MotionManager fills per-env reference buffers
each step for :class:~eden.envs.base.MimicRLEnvBase.
Under DDP, each rank is assigned only a subset of trajectories (data_source and the
_local_to_global map shrink per rank) while the motion-weight and outcome buffers
stay sized to the full motion-id space, so cross-rank all_reduce stays
shape-symmetric.
Attributes¶
MOTION_TERM_REGISTRY
module-attribute
¶
MOTION_TERM_REGISTRY = TermRegistry('MOTION_TERM', MotionTerm)
Classes¶
MotionTerm
¶
MotionTerm(env: EnvBase, options: MotionTermOptions)
Bases: ConfigurableTermBase[MotionTermOptions]
Base class for motion terms, which serve a similar role to Dataset in PyTorch.
Some of the motion terms may return the motion reference by generating it on the fly.
| PARAMETER | DESCRIPTION |
|---|---|
dataset_options
|
The options for the dataset.
|
entities_map
|
The mapping from the entity name in the environment to the entity name in the dataset. - key: entity name in the environment - value: motion sequence key in the dataset
|
target_dofs_name
|
The names of the degrees of freedom to target.
|
target_links_name
|
The names of the links to target.
|
env_motion_groups
|
Per-env clip groups keyed on a heterogeneous entity's variant;
TYPE:
|
Source code in eden/managers/motion_manager.py
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 | |
Attributes¶
target_dofs_name
class-attribute
instance-attribute
¶
target_links_name
class-attribute
instance-attribute
¶
env_motion_groups
class-attribute
instance-attribute
¶
env_motion_groups: MotionGroupOptions | None = None
env_starting_stub_sampling_strategy
class-attribute
instance-attribute
¶
env_starting_stub_sampling_strategy: Literal['independent', 'concat_motion_bins'] = 'independent'
motion_start_from_middle_range
class-attribute
instance-attribute
¶
adaptive_failure_rate_max_over_mean
class-attribute
instance-attribute
¶
adaptive_failure_rate_max_over_mean: float = 10.0
motion_weight_success_discount
class-attribute
instance-attribute
¶
motion_weight_success_discount: float = 0.999
motion_weight_failure_amplify
class-attribute
instance-attribute
¶
motion_weight_failure_amplify: float = 2.0
motion_weight_update_mode
class-attribute
instance-attribute
¶
motion_weight_update_mode: Literal['compound', 'snapshot'] = 'snapshot'
motion_weight_uniform_mix
class-attribute
instance-attribute
¶
motion_weight_uniform_mix: float = 0.0
motion_weight_failure_rate_max_over_mean
class-attribute
instance-attribute
¶
motion_weight_failure_rate_max_over_mean: float = 200.0
motion_weight_update_every_n_iters
class-attribute
instance-attribute
¶
motion_weight_update_every_n_iters: int = 1
start_at_timestep_zero_prob
class-attribute
instance-attribute
¶
start_at_timestep_zero_prob: float = 0.0
freeze_at_timestep_zero_prob
class-attribute
instance-attribute
¶
freeze_at_timestep_zero_prob: float = 0.0
compute_per_motion_metrics
class-attribute
instance-attribute
¶
compute_per_motion_metrics: bool = False
metric_anchor_link_name
class-attribute
instance-attribute
¶
metric_anchor_link_name: str | None = None
log_motion_id_to_filename
class-attribute
instance-attribute
¶
log_motion_id_to_filename: bool = False
data_source
instance-attribute
¶
data_source: DatasetBase = DATASET_REGISTRY.get(options.dataset_options.name)(options.dataset_options, device=self.device)
num_trajectories
cached
property
¶
num_trajectories: int
Return the number of trajectories in the motion buffer.
For a generative motion buffer, this just returns a placeholder number.
num_assigned_envs
property
¶
num_assigned_envs: int
The number of envs assigned to this motion buffer.
complete_motion_lengths
property
¶
The full motion reference lengths for each assigned env. the unit is in seconds.
assigned_motion_lengths
property
¶
The motion reference lengths for each assigned env. the unit is in seconds.
Methods:¶
set_envs_idx_assignments
¶
set_envs_idx_assignments(envs_idx: slice) -> None
Assign the environment indices this motion buffer is responsible for.
The motion buffer is expected to fill the motion reference data for the assigned envs only.
| PARAMETER | DESCRIPTION |
|---|---|
envs_idx
|
The environment IDs to assign to this motion buffer.
TYPE:
|
Source code in eden/managers/motion_manager.py
enable_trajectories
¶
enable_trajectories(traj_ids: Tensor | slice | None = None) -> None
Enable only the selected motion reference trajectories.
Assuming all motion reference trajectories are stored in a sequential way, the motion manager will call this function to enable only the selected trajectories, depending on how multi-processing is implemented. By "enabled", it means the motion buffer will generate the motion data for only the enabled trajectories.
_motion_weights and the per-motion outcome buffers stay sized to the
full pre-subset motion-id space on every rank — only data_source and
the new _local_to_global map shrink. This is what lets DDP
all_reduce stay shape-symmetric while each rank holds only its
assigned trajectory slice in VRAM.
| PARAMETER | DESCRIPTION |
|---|---|
traj_ids
|
The trajectory IDs to enable. start from 0 to
TYPE:
|
Source code in eden/managers/motion_manager.py
rewind_to_start
¶
Rewind playback to frame 0 of each env's currently-assigned clip (no resampling).
Zeros this term's per-env start offset. The per-term half of
:meth:MotionManager.rewind_to_start; public so callers needn't poke
_motion_buffer_start_time_s (allocated in :meth:set_envs_idx_assignments at
manager construction, so it always exists by the time playback is rewound).
Source code in eden/managers/motion_manager.py
reset
¶
Reset the motion buffer for the specified envs_idx.
| PARAMETER | DESCRIPTION |
|---|---|
envs_idx
|
The environment IDs to reset the motion buffer, which is among the entire num_envs. If None, reset all buffers.
TYPE:
|
symmetric_augmentation_mask_buffer
|
The buffer to store the symmetric augmentation mask for each env. The motion buffer is expected to sample and fill the symmetric augmentation mask for each env. Because some of the motions do or do not support symmetric augmentation. NOTE: the motion buffer does not apply the symmetric augmentation; it only updates the mask.
TYPE:
|
Source code in eden/managers/motion_manager.py
fill_init_reference_state
¶
fill_init_reference_state(motion_sequence_key: str, envs_idx: Sequence[int] | Tensor, env_origins: Tensor | None, state_buffer: MotionReferenceState) -> None
Fill the initial reference state into the buffer for the specified envs only.
| PARAMETER | DESCRIPTION |
|---|---|
motion_sequence_key
|
The motion sequence key to fill the initial reference state from.
TYPE:
|
envs_idx
|
The environment IDs to fill the initial reference state. |
env_origins
|
The origins for each robot/env, which is also affecting the motion reference data, shape (num_envs, 3). None: every env's origin is the world origin.
TYPE:
|
state_buffer
|
The MotionReferenceState to fill the initial reference state, which is a buffer for all envs, shape (num_envs, ...).
TYPE:
|
Source code in eden/managers/motion_manager.py
compute
¶
compute(motion_sequence_key: str, envs_idx: slice | Sequence[int] | Tensor, sample_timestamp: Tensor, env_origins: Tensor | None, data_buffer: MotionReferenceData) -> None
Fill the motion data into the buffer for the specified envs only.
This should not modify any other data in the MotionReferenceData.
| PARAMETER | DESCRIPTION |
|---|---|
motion_sequence_key
|
The motion sequence key to sample the motion data from.
TYPE:
|
envs_idx
|
The environment IDs to fill the motion data. A slice (the per-step fill of every env) is gathered straight into the buffer, with no intermediate copy. |
sample_timestamp
|
The time (s) to sample from the start of each env, shape (num_envs_idx, num_frames).
TYPE:
|
env_origins
|
The origins for each robot/env, which is also affecting the motion reference data, shape (num_envs, 3). None: every env's origin is the world origin.
TYPE:
|
data_buffer
|
The MotionReferenceData to fill the motion data, which is a buffer for all envs, shape (num_envs, num_frames, ...).
TYPE:
|
Source code in eden/managers/motion_manager.py
assigned_ids_from_envs_idx
¶
Local int ids in [0, num_assigned_envs) for an int-like env selector.
Accepts None (all assigned envs), a slice, or a long/int tensor (or
Sequence[int]) of global env ids. A bool mask is not accepted
here — its assigned-space form is a bool mask, not int ids; use
:meth:assigned_mask_from_envs_mask, or :meth:envs_idx_to_assigned_ids
when the selector dtype is only known at runtime.
Source code in eden/managers/motion_manager.py
assigned_mask_from_envs_mask
¶
Local bool mask over [0, num_assigned_envs) for a global bool mask.
The assigned-space form is the global mask sliced to the assigned env range, kept as a bool mask (not int ids) so downstream resampling can over-sample the full buffer and mask-write without a GPU→CPU sync.
Source code in eden/managers/motion_manager.py
envs_idx_to_assigned_ids
¶
Dispatch a runtime-typed env selector to its assigned-space form.
Returns a local bool mask when envs_idx is a bool tensor, otherwise
local int ids. This dual return shape is load-bearing: a bool selector
stays a bool mask so callers can mask-write (no GPU→CPU sync), while
int/slice/None selectors become int ids that index local buffers
directly. When the selector dtype is statically known, call
:meth:assigned_ids_from_envs_idx or :meth:assigned_mask_from_envs_mask
directly instead of going through this dispatcher.
Source code in eden/managers/motion_manager.py
envs_idx_is_assigned
¶
envs_idx_is_assigned(envs_idx: slice | Tensor) -> Tensor
Check if the envs_idx are assigned to this motion buffer.
| PARAMETER | DESCRIPTION |
|---|---|
envs_idx
|
The environment IDs to check.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Tensor
|
A boolean tensor indicating assignment. |
Source code in eden/managers/motion_manager.py
get_current_motion_weights
¶
get_current_motion_weights(envs_idx: slice | Tensor | None = None) -> Tensor
Get the current motion weights for the specified envs.
The motion weights are used to determine the probability of selecting a motion reference for each env.
_assigned_env_motion_selection holds local motion ids; the
canonical _motion_weights is global-sized, so we go through
_local_to_global to index correctly.
| PARAMETER | DESCRIPTION |
|---|---|
envs_idx
|
The environment IDs (or bool mask) to get weights for; None for every assigned env.
TYPE:
|
Source code in eden/managers/motion_manager.py
match_scene
¶
match_scene(entity_name: str)
Let the motion buffer match the scene.
For example a scene-interactive motion may need to match the motion starting point to the terrain.
Recommended to call scene.terrain.terrain_origins and scene.terrain.subterrain_specific_cfgs
to get some structured terrain information.
| PARAMETER | DESCRIPTION |
|---|---|
entity_name
|
The name of the entity whose motion should be matched to the scene.
TYPE:
|
Source code in eden/managers/motion_manager.py
summarize_per_motion_metrics
¶
Per-motion-id tensors for CSV dump; {} when metrics are disabled.
Public accessor for external dump tooling (see
MotionTermOptions.compute_per_motion_metrics); delegates to the
PerMotionMetrics collaborator that owns the buffers.
Source code in eden/managers/motion_manager.py
MotionManager
¶
MotionManager(env: EnvBase, options: MotionManagerOptions)
Bases: FlatManagerBase[MotionManagerOptions], SymmetricAugmentationMixin
The manager to handle all motion references.
Source code in eden/managers/motion_manager.py
Attributes¶
require_frame_interval_s_resample
instance-attribute
¶
require_frame_interval_s_resample = options.frame_interval_s_range[1] - options.frame_interval_s_range[0] > 0
data
property
¶
The motion reference data for all entities, keyed by entity name.
Filled by :meth:compute at the start of each MimicRLEnvBase.step()
and refilled per-env by :meth:resetting in :meth:MimicRLEnvBase._reset_idx.
Fresh for all envs during reward, termination, action, metric, and
observation computation in RLEnvBase.step().
Stale window: :meth:reset is called inside
MimicRLEnvBase._reset_idx before super()._reset_idx and only
resamples motion selection / rolls back _timestamp — it does
not refill the buffer. :meth:resetting, which brackets the scene
reset and the RESET events, does: at the first init-state request inside
it (data_start_from="current_time"), else on its exit. A RESET-mode
event may therefore see stale data for reset envs (last step's
compute fill); use :meth:get_reset_reference_state instead — it is the
one ReferenceStateInitialization uses.
Raises inside the manager resets (:meth:managers_resetting): the reset
envs' window already holds the new episode there, so a reset() reading
it to grade the ended episode would grade the wrong one, silently.
Grade the ended episode off :meth:ended_episode_validity; read the new
episode's reference in post_reset().
reference_frame
property
¶
The reference state of all entities at current time step, keyed by entity name.
env_origins
property
¶
The env's env_origins (num_envs, 3), which shift the reference; None for an env without them.
None, not zeros, so that a fill does not add zeros to every position field of every entity.
has_adaptive_motion_weights
property
¶
has_adaptive_motion_weights: bool
True iff at least one motion term enables hard-motion mining.
Methods:¶
summary
¶
Get the tabular information of the motion reference managed buffer.
Source code in eden/managers/motion_manager.py
reset
¶
Reset the motion reference manager.
Resamples motion selection, rolls back _timestamp[envs_idx] to 0,
and updates adaptive-sampling bookkeeping. Does not refill
self._data — callers that need fresh per-step data must call
:meth:fill_data themselves. MimicRLEnvBase does it through
:meth:resetting, around the scene reset and RESET events (so the
refill picks up any env_origins update from scene.reset /
RESET events, as the reference-state seeding does).
Source code in eden/managers/motion_manager.py
post_reset
¶
post_reset(envs_idx: slice | Tensor | None = None) -> None
Fan out to motion terms, restricted to each term's assigned environments.
Overrides the flat fan-out (which would hand every term the full reset index)
because a motion term owns a contiguous env slice and its buffers are sized to
that slice — the same per-term narrowing :meth:compute and :meth:fill_data
do. Terms are unique per name here, so the base class's dedupe is not needed.
Source code in eden/managers/motion_manager.py
collect_per_motion_distributions
¶
Per-term per-motion histograms for the wandb-direct emit path.
Kept off the _collect_log_extras / extras["log"] channel: those
flow through rsl_rl's Logger.ep_extras which iterates and prints
every key per iter — at AMASS scale that loop is the per-step
bottleneck. The wandb-direct mixin path (see
MotionWeightCurriculumMixin) calls this method once per learning
iter and merges the result into the same wandb.log() payload.
Empty when no term has log_per_motion_metrics=True.
Source code in eden/managers/motion_manager.py
compute
¶
compute(dt: float)
Advance _timestamp and refill self._data for all envs.
Called once per MimicRLEnvBase.step (before reward / termination
/ observation compute). The reset path fills the reset-env subset
through :meth:resetting (see :meth:fill_data).
When any motion term sets freeze_at_timestep_zero_prob > 0, envs
currently at _timestamp == 0 and assigned to that term are
per-step-randomly held at 0 (re-rolled every step — see
:class:MotionTermOptions.freeze_at_timestep_zero_prob).
Source code in eden/managers/motion_manager.py
rewind_to_start
¶
Rewind playback to frame 0 for envs_idx WITHOUT resampling.
Distinct from :meth:reset: reset re-samples motion selection and start phase
(the per-episode randomized restart), whereas this replays each env's CURRENTLY-assigned
clip deterministically from the beginning (e.g. a deploy starting from a stand). Zeros
the manager timestamp and fans out to each term's :meth:MotionTerm.rewind_to_start.
Does not refill the reference buffer — call :meth:fill_data afterwards.
Source code in eden/managers/motion_manager.py
fill_data
¶
Refill self._data[entity][envs_idx] from self._timestamp[envs_idx]; None fills every env.
Called by :meth:compute (per step, every env) and by :meth:resetting (reset envs only),
around the scene reset and RESET events of MimicRLEnvBase._reset_idx. Bool-mask input
becomes int ids through :func:~eden.utils.misc.env_indices — free for the auto-reset's mask,
whose ids the env took once for the whole reset.
Only the filled envs' rows are written. A fill of every env (None) hands each term its assignment as a slice, which the term gathers straight into the buffer; the ids of every env write the same rows, through the ids.
Source code in eden/managers/motion_manager.py
get_target_dofs_name
¶
Return the resolved DOF names that the motion reference exposes for entity_name.
These are the names that index into reference_frame[entity_name].dofs_pos
along the last axis. Returns None when no motion term targets the entity.
When multiple terms target the same entity, the first term is used; the
:class:MotionManager already requires consistent num_dofs across terms.
Source code in eden/managers/motion_manager.py
get_motion_phase
¶
get_motion_phase(entity_name: str) -> Tensor
Per-env normalized playback phase of the clip tracking entity_name.
frame / (clip_length - 1) in [0, 1] at the CURRENT reference frame
(the same floor-of-timestamp frame selection as :meth:MotionTerm._select_frames),
so mid-clip RSI starts report their true position in the clip rather than
the episode-step phase. Envs not assigned to a term tracking the entity
report 0. Shape (num_envs,).
Raises if no term tracks entity_name at all: that is a config error
(a misspelled entity), and returning the all-zero buffer would hand a
consumer a silently-constant signal forever.
Source code in eden/managers/motion_manager.py
get_init_reference_state
¶
get_init_reference_state(envs_idx: Sequence[int] | Tensor | None = None, entity_names: Sequence[str] | None = None) -> dict[str, MotionReferenceState]
Get the initial reference state for the given envs_idx, keyed by entity name.
entity_names limits the gather to those entities (None: every tracked one). Each
entity costs one gather per field, so the reset path asks only for the entities it seeds
rather than refilling all of them once per RSI term. Valid for any envs, at any time;
a RESET event seeding its own envs_idx calls :meth:get_reset_reference_state, which
saves the gather.
Source code in eden/managers/motion_manager.py
get_reset_reference_state
¶
get_reset_reference_state(envs_idx: Sequence[int] | Tensor | None = None, entity_names: Sequence[str] | None = None) -> dict[str, MotionReferenceState]
:meth:get_init_reference_state for a RESET event seeding its own envs_idx.
Inside :meth:resetting with data_start_from="current_time", the window's first frame is
each reset env's init state, so the first call fills the reset envs' window and every call
reads the state off its frame 0: the reset gathers each reference row once, not once for the
window and again for reference-state initialization. Anywhere else it is
:meth:get_init_reference_state.
envs_idx must be envs of the reset in progress, which a RESET event's envs_idx always
is (a throttled event's is a subset). Frame 0 of any other env's window is a mid-episode
reference row, and it is not checked here, since that would cost a GPU->CPU sync every reset:
call :meth:get_init_reference_state for envs that need not be resetting.
Source code in eden/managers/motion_manager.py
record_ended_episode_validity
¶
record_ended_episode_validity(entity_name: str) -> None
Keep, from now on, entity_name's :meth:ended_episode_validity (call it in a term's build()).
For a term that grades the episode a reset ends: the manager resets run after the reset envs'
window has been refilled for the new episode (see :meth:resetting), so they cannot read the
ended one off data.
Source code in eden/managers/motion_manager.py
ended_episode_validity
¶
ended_episode_validity(entity_name: str) -> Tensor
validity[:, 0] of entity_name's window as each env's last episode ended, (num_envs,).
False where that episode played its clip out. Taken by :meth:reset, before the window moves
on; an env that never ran an episode reads False, the empty window's validity.
Source code in eden/managers/motion_manager.py
resetting
¶
resetting(envs_idx: Tensor) -> Iterator[None]
Bracket the scene reset and RESET events of a reset of envs_idx, and leave its window filled.
Enter after :meth:reset; on a normal exit data holds the reset envs' new window, filled
by :meth:fill_data once, so every manager reset after it sees the new episode (the ended one's
clip state is :meth:ended_episode_validity). With data_start_from="current_time" the
window's first frame is each reset env's init state, so the first
:meth:get_reset_reference_state call inside fills the window there and every call reads the
state off it. Otherwise, and when nothing asks, the window is filled on exit, after the
RESET events. The window reads env_origins when it is filled: at the first such call, or
on exit.
Source code in eden/managers/motion_manager.py
managers_resetting
¶
managers_resetting() -> Iterator[None]
Bracket the manager resets that follow :meth:resetting: reading :attr:data inside raises.
The reset envs' window holds the new episode by then, so a manager reset() reading it to
grade the episode that ended would silently grade the new one.
Source code in eden/managers/motion_manager.py
update_motion_weight_curriculum
¶
Run one curriculum step across all adaptive motion terms.
Called once per PPO iteration by the rsl-rl runner mixin (see
eden.envs.wrappers.rsl_rl_env). Terms that haven't yet reached
their per-term motion_weight_update_every_n_iters cadence are
cheap no-ops; terms that have reached cadence drain their counts,
all-reduce them across DDP ranks (so every rank applies the same
update — a hard requirement for keeping _motion_weights
byte-identical), then rewrite _motion_weights per
motion_weight_update_mode.
DDP shape symmetry holds because every rank's outcome buffers and
_motion_weights are sized to the GLOBAL motion-id space,
independent of mp_split_method or each rank's local trajectory
slice. Per-rank contributions land in the same global slots; after
the SUM all_reduce every rank applies the same update and
the resulting _motion_weights is byte-identical across ranks.
Source code in eden/managers/motion_manager.py
Functions:¶
sample_start_times
¶
sample_start_times(motion_ids: Tensor, motion_bin_weights: ConcatBatchTensor, buffer_device: device, motion_bin_length_s: float | None, data_source: 'DatasetBase | None' = None, uniform_bins: bool = False) -> Tensor
Sample a start time (seconds) per env from the bin-weight distribution.
uniform_bins=True is a fast path for when the bin weights are known to be
uniform (i.e. adaptive_sampling is off, so the torch.ones init is never
rewritten): a multinomial over uniform weights is exactly a uniform draw
over each motion's valid bins, so we draw floor(rand * n_bins) directly and
skip the padded get_with_mask gather + multinomial launch. This is
bit-for-bit the same DISTRIBUTION as the weighted path (verified in
tests/managers/test_motion_manager.py); the weighted path would dominate
the motion-end resync's per-step cost, which calls this on every clip wrap.
When data_source is provided, results are clamped per-env to
(buffer_length[motion_id] - 1) / framerate[motion_id]. This is the
correct behavior whenever any clip is shorter than motion_bin_length_s
— without the clamp, the (after-clamp(min=1)) single bin spans the
full motion_bin_length_s window, so the intra-bin uniform noise can
return a start time past the end of the clip. fill_init_reference_state
then reads base_quat_w at an out-of-range frame index, ConcatBatchTensor
zero-fills it to (0,0,0,0), and the first genesis quaternion op on that
degenerate quat — the rotation math normalizes by q·q, so a zero quat is a
divide-by-zero — NaN-poisons the policy observation on the first reset.
The sibling _sample_as_concat_motion_bins path applies the same clamp
inline (see max_start_time_s below). Keeping data_source optional
preserves the existing call signature for tests that build a CBT directly
without a full DatasetBase; production callers should pass it.
Source code in eden/managers/motion_manager.py
MotionTerm collaborators¶
The pieces MotionTerm delegates to: adaptive start-time bins, the per-motion outcome curriculum, per-motion
tracking-error metrics, and the opt-in resync that keeps an episode running past a clip's end.
Intra-motion start-time bin curriculum (adaptive start-time sampling).
Split out of MotionTerm. Owns the per-motion temporal bin
weights (a ConcatBatchTensor of per-bin sampling probabilities) and, when
adaptive_sampling is on, the death-bin failure-count EMA + the vectorised weight-update
that biases start-time sampling toward the run-up to hard moments.
One instance per term that sets motion_bin_length_s (created/recreated in
MotionTerm.enable_trajectories whenever the motion library changes). The bin
weights exist whenever motion_bin_length_s is set — they drive weighted
start-time sampling even without the curriculum; the adaptive EMA layer activates
only when adaptive_sampling=True, so record_bin_outcomes / blend_failure_ema
/ _update_adaptive_bin_weights keep their internal adaptive guards.
The term's start-time samplers (_sample_env_motion_start_time /
_sample_as_concat_motion_bins) read the distribution via :attr:weights.
Term env-assignment state (_assigned_env_motion_selection,
_motion_buffer_start_time_s, envs_idx_to_assigned_ids) is read through the
back-reference.
Classes¶
AdaptiveBinSampler
¶
AdaptiveBinSampler(term: MotionTerm)
Owns the temporal bin weights and the adaptive start-bin curriculum.
Initialize temporal bin weights for weighted start-time sampling.
Source code in eden/managers/motion/bin_sampler.py
Attributes¶
group_bin_mask
property
¶
(G, total_bins) membership of every start bin in each per-env motion group (env_motion_groups).
Built on first use from the term's per-rank (G, M) clip mask, and cached: the bin layout is fixed for this
library, and the sampler is rebuilt whenever the library changes.
weights
property
¶
Per-motion temporal bin probabilities (read by the term's start-time samplers).
Methods:¶
record_bin_outcomes
¶
Accumulate per-episode failure counts for resetting envs.
Failures are keyed by the bin the episode died in
(floor((start_time + timestamps) / bin_length)) and accumulated as
raw counts in _current_bin_failed for the terminated subset only.
A failure rate keyed by the start bin instead starves the easy early
frames a cold policy needs to bootstrap on multi-clip LAFAN1 and
collapses tracking; the death-bin failure count is the curriculum
signal that lets it learn.
terminated_mask / timestamps are aligned with envs_idx.
Source code in eden/managers/motion/bin_sampler.py
blend_failure_ema
¶
Blend _current_bin_failed into _bin_failure_ema, then zero it.
Source code in eden/managers/motion/bin_sampler.py
Per-motion-id outcome curriculum (hard-motion mining).
Split out of MotionTerm: owns the per-motion success/failure count
buffers, the drain cadence, the weight-rewrite dynamics (snapshot / opt-in
compound + uniform-mix regulariser), and the wandb summary/histogram reporting.
One instance per term that sets adaptive_motion_weights=True, created in
MotionTerm.__init__.
_motion_weights itself stays on the term — it is core sampling state read by
_local_motion_weights / get_current_motion_weights. This curriculum
rewrites it in place (through the back-reference) from the drained outcome
rates; everything else it touches (counts, summaries) it owns.
Cadence: MotionTerm._update_motion_weight_curriculum calls update once per
PPO iter; every motion_weight_update_every_n_iters-th call drains, optionally
all-reduces across DDP, and rewrites the weights. The term piggybacks the
per-motion metric drain on the same cadence.
Classes¶
MotionOutcomeCurriculum
¶
MotionOutcomeCurriculum(term: MotionTerm)
Owns the per-motion-id outcome counts and the weight-rewrite pipeline.
Source code in eden/managers/motion/outcome_curriculum.py
Attributes¶
last_curriculum_summary
property
¶
Cached per-iter outcome scalars (MotionOutcome/*); empty until first drain.
Methods:¶
record_motion_outcomes
¶
record_motion_outcomes(success_envs_idx: Tensor, failure_envs_idx: Tensor, success_timestamps: Tensor, failure_timestamps: Tensor, env_dt: float) -> None
Increment per-motion success / failure counts for envs that just reset.
Classification is driven by motion completion, not by which cohort
an env arrived in (matches ProtoMotions's done_clip: a clip is
"done" when motion_times + env_dt >= end_times, regardless of why
the episode reset).
- Completed (
start_time + episode_time + env_dt >= motion_length) → success. This includes terminated envs whose motion happened to play to its natural end. - Terminated AND not completed → failure.
- Not terminated AND not completed (mid-motion timeout) → dropped.
Both *_envs_idx accept the same forms as envs_idx_to_assigned_ids:
a bool mask of length num_envs (preferred — avoids GPU sync) or an
int-index tensor of global env ids. *_timestamps are compact tensors
aligned with the corresponding cohort.
Source code in eden/managers/motion/outcome_curriculum.py
update
¶
update(all_reduce_fn=None) -> bool
Drain outcome counts, optionally reduce across DDP ranks, rewrite weights.
Called once per PPO iteration via MotionTerm._update_motion_weight_curriculum.
The first motion_weight_update_every_n_iters - 1 calls just bump the
internal iter counter and return False (counts keep accumulating);
the K-th call drains, optionally all-reduces, and applies the curriculum,
returning True (which the term uses to gate the metric drain onto the
same cadence).
all_reduce_fn — if provided, applied in-place to both count tensors
before they drive the curriculum (the manager supplies a
dist.all_reduce lambda for DDP; tests pass None).
Source code in eden/managers/motion/outcome_curriculum.py
summarize_motion_weights
¶
Read the cached scalar summary of the motion-weights distribution.
Returns the dict populated by _compute_motion_weights_summary,
refreshed only when _motion_weights actually changes (init, plus each
update call that drains). The hot reset path reads this cache rather than
recomputing from the GPU tensor (which would cost 5 GPU→CPU syncs).
Source code in eden/managers/motion/outcome_curriculum.py
collect_per_motion_distributions
¶
Build per-motion histograms for the wandb-direct emit path.
Returns wandb.Histogram objects keyed under MotionPerMotion/* when
log_per_motion_metrics=True; an empty dict otherwise. Built from the
cached numpy arrays _compute_motion_weights_summary /
_record_curriculum_summary populate — one snapshot per drain, not per
env step. Histograms (not per-id scalars) keep the fan-out constant-cost
at AMASS / SEED scale.
Source code in eden/managers/motion/outcome_curriculum.py
Per-motion tracking-error metrics (MPJPE / G-MPJPE / root error).
Split out of MotionTerm: a self-contained collaborator owning the
metric buffers + link-index cache and the record → scatter → drain → summary
pipeline. One instance per opted-in term, created in
MotionTerm.set_envs_idx_assignments when compute_per_motion_metrics is
set; it reads the term's env-assignment / DDP id state through a back-reference
(the term stays the owner of that state).
Pipeline (and why each stage runs when it does):
record_step— everycompute()step: per-env error vs the reference, added into the per-env running sums. No CPU↔GPU sync.scatter_at_reset— at episode end: mean each finished env's sums over its episode and scatter into the per-motion accumulators, sized to the GLOBAL motion-id space so DDP all_reduce stays shape-symmetric (mirrors the curriculum counts).drain— once per curriculum drain: reduce across DDP and cache the aggregate summary surfaced viaMotionManager._collect_log_extras. Shares the curriculum'sall_reduce_fnso it pays one extra collective.
Classes¶
PerMotionMetrics
¶
PerMotionMetrics(term: MotionTerm)
Owns the per-motion tracking-error buffers and their accumulation pipeline.
Source code in eden/managers/motion/per_motion_metrics.py
Methods:¶
record_step
¶
Compute one step of per-env tracking error for entity and add it
into the running sums.
Per-entity no-op when the reference lacks world-frame link/root fields
(e.g. dof-only motion data) or the term tracks no links for this entity.
A get_links_pos (two with an anchor) + one get_pos/get_quat
and per-env reductions, all on the GPU with no sync.
Source code in eden/managers/motion/per_motion_metrics.py
accumulate
¶
accumulate(mpjpe: Tensor, root_pos_err: Tensor, root_quat_err: Tensor, g_mpjpe: Tensor | None = None) -> None
Add one step of per-env error into the running sums.
Inputs are [num_assigned_envs] float tensors on the term's device.
g_mpjpe must be passed iff metric_anchor_link_name is set on the
term — a mismatch is a config bug and raises.
Source code in eden/managers/motion/per_motion_metrics.py
scatter_at_reset
¶
Drain per-env running sums into the per-motion accumulators at episode end.
episode_lengths is the full-size per-env episode_length_buf
([num_envs]); we slice it to this term's assigned_env_slice FIRST
so all downstream indexing operates in the LOCAL assigned-env space —
envs_idx_to_assigned_ids returns local-space indices, so indexing the
global episode_lengths with them silently reads the wrong env's
length when assigned_env_slice.start > 0.
Supports both int-index and bool-mask envs_idx (the contract of
record_motion_outcomes). Envs at their bootstrap reset (length 0) are
filtered out — they have no real outcome. Maps motion ids through
_local_to_global so per-rank contributions land in shape-compatible
tensors for the downstream DDP all_reduce.
Source code in eden/managers/motion/per_motion_metrics.py
drain
¶
Drain per-motion accumulators, reduce across DDP, refresh summary cache.
Called from MotionTerm._update_motion_weight_curriculum right after
the success/failure drain so both share one DDP cadence. After this
returns, summary() holds the aggregate scalars surfaced via
MotionManager._collect_log_extras.
Source code in eden/managers/motion/per_motion_metrics.py
summary
¶
per_motion_tensors
¶
Per-motion-id tensors (CPU dict views) suitable for CSV dump.
Returns the raw per-motion sum tensors and the episode count; callers
compute the per-motion mean as sum / count.clamp_min(1) once they
hold the tensors locally. Tensors are copied to CPU so the caller can
iterate without holding GPU memory.
Source code in eden/managers/motion/per_motion_metrics.py
Functions:¶
Motion-end resync (opt-in): keep an episode running past a clip's end.
When MotionManagerOptions.resync_on_motion_end is set, an env whose reference
reaches the end of its clip is soft-reset mid-episode — a fresh start phase is
resampled, its playback clock restarted, and the robot teleported onto the new
reference start — WITHOUT ending the episode. This mirrors the clip-wrap behavior
of MotionCommand (the beyondmimic example); the default (off) freezes the
reference at the clip's last frame.
It is folded into the single per-step fill_data(all): :meth:pre_fill
detects wrapped envs from the just-advanced clock (before the fill) and resamples
them, so the one fill_data computes their new-phase reference (no second
fill); :meth:post_fill then teleports them onto that freshly-filled reference.
One instance per :class:~eden.managers.motion_manager.MotionManager, created in
its __init__; it reads the manager's state through a back-reference.
Hot-path cost contract: no GPU->CPU sync. Whether any env wrapped is never
read on the host: every step resamples and teleports through the wrap mask, every
env's row with the mask picking the wrapped ones (torch.where / masked_fill_,
and Genesis' masked forward kinematics), so a step where nothing wrapped writes
nothing — it only runs the same few kernels over all envs. That fixed cost does
not outweigh the sync it replaces: BeyondMimic at 4096 envs (L40S) stepped in
24.12 ms against 24.16 ms with the nonzero() gate. (MotionCommand keeps its
.any() gate: its reset path adds pose noise and full-batch draws, ~1 ms.)
Classes¶
MotionEndResync
¶
MotionEndResync(manager: MotionManager)
Owns the opt-in motion-end resync operation for a MotionManager.
Source code in eden/managers/motion/resync.py
Attributes¶
Methods:¶
validate
¶
Build-time check (a post-build hook, once the event manager exists): resolve
the RSI teleport terms now so a missing one raises when resync_on_motion_end
is first honored — at env construction — instead of at the first mid-episode
wrap. The resolution is cached, so the first wrap pays nothing.
Source code in eden/managers/motion/resync.py
pre_fill
¶
Before fill_data(all): detect wrapped envs and resample them so the
single fill computes their new-phase reference. Returns the (num_envs,)
wrap mask (to hand back to :meth:post_fill), or None when disabled.
The mask is not read on the host, so it may select no env: everything below
writes through it, which then writes nothing.
Source code in eden/managers/motion/resync.py
post_fill
¶
After fill_data(all): teleport the wrapped envs onto the freshly
filled reference. With EDEN_RESYNC_DEBUG=1, first assert the pre-fill wrap
detection matched fill_data's validity (no env still past-end).
Source code in eden/managers/motion/resync.py
Options¶
MotionManagerOptions, MotionTermOptions and MotionGroupOptions are on the
manager options page.
Data containers and lookup helpers¶
The motion data containers live in eden/utils/motion_reference/, which has no __init__.py, so the API generator
cannot render them here. Read their docstrings in the source:
| Module | Contents |
|---|---|
motion_data.py |
MotionSequence (one clip, [num_frames, ...] arrays), BatchMotionSequence, MotionReferenceData, MotionReferenceState |
lookup.py |
index resolution for tracking terms: get_motion_link_idx, get_motion_dofs_idx, current_motion_frame, assert_motion_field_present, get_anchor_state |
utils.py |
robot-vs-reference differences (get_link_position_distance, get_dofs_position_difference, ...) and interpolation (interpolate_motion, slerp_rows) |
transforms.py |
anchor-relative link transforms |
augmentation_mixin.py, filter_mixin.py |
SymmetricAugmentationMixin, FilterMixin |
bipedal_ik.py |
start-transition IK used in preprocessing |