Skip to content

fix(augmentation): name-based gait-phase channel mirror for symmetry aug - #184

Open
tkevinbest wants to merge 1 commit into
mainfrom
dev/tkbest/augmentation-gait-phase-mirror
Open

fix(augmentation): name-based gait-phase channel mirror for symmetry aug#184
tkevinbest wants to merge 1 commit into
mainfrom
dev/tkbest/augmentation-gait-phase-mirror

Conversation

@tkevinbest

@tkevinbest tkevinbest commented Aug 10, 2026

Copy link
Copy Markdown

What

Symmetry-based data augmentation mirrors observations across the robot's sagittal plane. The gait-phase signals sin_phase and cos_phase are separate observation terms, each carrying one channel per foot, ordered to match env.feet_indices. Under a left-right mirror, each foot's phase channel must be swapped with its mirror foot's channel.

The old code did neither a full mirror nor a swap:

sin_phase[..., 0] = -sin_phase[..., 0]

It only ever touched channel 0 and negated it. So:

  • Every foot channel other than channel 0 was left untouched — for a biped, the second foot's phase was never mirrored at all. This affects the standard left-first g1 case, not just unusual orderings.
  • Negating channel 0 is not a consistent left-right leg swap even for that one channel.

The result is an inconsistent mirrored gait phase, which corrupts the symmetry-augmented transitions used in training (observed as a wide-stance gait when augmentation was enabled).

Fix

Mirror the gait phase by swapping each foot's channel with its left-right mirror foot's channel, derived by name:

  • For each foot channel, find the foot whose name is its left/right mirror and map the channel to that foot's index (in env.feet_indices order). This gives a permutation p where mirrored_phase[..., i] = phase[..., p[i]]. For g1 this resolves to [1, 0] — a genuine left/right swap.
  • Mirror resolution uses a new helper _mirror_body_name, which swaps side tokens (left/right, and l_/r_ prefixes or _l_/_r_ infixes) only at word boundaries — so a mid-word letter like the l in ankle_roll is never misread as a side marker.
  • If a channel's mirror can't be resolved by name, it falls back to identity (maps to itself), so behavior degrades to "unchanged" rather than "wrong".

Correct for any foot ordering and any number of feet, bipeds and quadrupeds.

Behavior change

This changes the mirrored phase for every robot, including the existing left-first biped case — it is not a no-op refactor. Old vs new for a left-first biped:

  • Old: [-sin(φ0), sin(φ1)] (negate channel 0, ignore channel 1)
  • New: [ sin(φ1), sin(φ0)] (swap the two feet)

The new behavior is the intended consistent leg swap. Any policy trained with symmetry augmentation on the old code was augmenting against a corrupted phase mirror.

Scope

Single file: agents/modules/augmentation_utils.py (+63 / −23). Isolated to the augmentation path.

The gait-phase observation mirror hard-coded "negate channel 0", which is only
(partially) correct when channel 0 is the LEFT foot (left-first body_names). For
right-first orderings or >2 feet it produces an inconsistent mirrored
phase, corrupting symmetry-augmented transitions.

Derive the phase-channel permutation by NAME instead: map each foot's phase
channel to its left/right mirror foot's channel (env.feet_indices order), with a
word-boundary-safe _mirror_body_name so mid-word letters (the 'l' in 'roll') are
never read as a side token. Identity fallback when a mirror can't be resolved, so
behavior degrades to "unchanged" rather than wrong. Correct for any foot ordering
and any number of feet.
@tkevinbest
tkevinbest requested a review from Juyue August 10, 2026 20:04
@tkevinbest
tkevinbest marked this pull request as ready for review August 10, 2026 20:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant