Repository navigation
fix(showcase): seat G1 on terrain surface so the Curriculum-Showcase stops NaN-ing - #155
Merged
Merged
Conversation
…ps NaN-ing The Curriculum-Showcase G1 spawned ~0.77 m buried in the RandomRough heightfield: terrain_levels_vel (run on every reset) and the runner teleport wrote the base to the cell origin at z=0 (= terrain.pos[2]), ignoring the surface elevation and the robot's 0.76 m standing height. Feet ended at z=-0.72 under a +0.05 m surface; that interpenetration blew up the rigid contact solver — instant NaN at dt=0.005, an unconverging multi-minute grind at dt=0.002. dt was a red herring. - terrains/importer: add surface_height_at(xy) heightfield lookup. - mdp/curriculums: terrain_levels_vel reseats the base at surface + init_pos.z + spawn_clearance instead of the bare cell origin. - showcase runner: teleport reseats at standing height too, so the full 400-step demo survives its teleports. - showcase cfg: dt 0.005->0.002, decimation 4->10. The G1 is independently NaN-unstable at dt=0.005 even from a clean stance; this matches the known-good Velocity/Tracking-Flat configs. Control rate stays 50 Hz and the 2 s episode (100 control steps) is unchanged. - rl/runner: play_task caps scripted (zero/random) playback at simulation.steps, so `play TASK --agent zero --steps N` stops after N steps instead of looping forever headless (--steps was silently ignored on the RL play path). Trained playback stays unbounded. Verify: `play GeneLab-Curriculum-Showcase-v0 --agent zero --steps 5` exits 0, no NaN; full 400-step showcase exits 0 with curriculum promotion (levels 0->4). pytest: 507 passed.
KraHsu
added a commit
that referenced
this pull request
Jun 5, 2026
fix(showcase): seat G1 on terrain surface so the Curriculum-Showcase stops NaN-ing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
genelab play GeneLab-Curriculum-Showcase-v0 --agent zero --steps 5crashed with:Reproduced on RTX 5060 Ti (local) and H200 (server) — not hardware-specific.
Root cause (dt was a red herring)
A no-step diagnostic showed the G1 spawned ~0.77 m buried in the RandomRough heightfield.
terrain_levels_vel(which runs on every reset) and the runner's teleport both wrote the robot base to the terrain cell origin atz=0(=terrain.pos[2]), ignoring the surface elevation and the robot's 0.76 m standing height. Feet landed atz=−0.72under a+0.05m surface. That interpenetration exploded the rigid contact solver:dt=0.005→ instant NaN at step 1 (the reported symptom).dt=0.002→ an unconverging multi-minute solver grind (_kernel_solve_graph).The Terrain-Showcase shares the identical buried-spawn bug (out of scope here; see follow-ups).
Fix (one concept: seat the G1 on the terrain, not in it)
terrains/importer.py: addsurface_height_at(xy)— world-frame heightfield surface lookup.mdp/curriculums.py:terrain_levels_velreseats the base atsurface + init_pos.z + spawn_clearanceinstead of the bare cell origin (new optionalspawn_clearance=0.1).runner.py: teleport reseats at standing height too, so the full 400-step demo survives its teleports.env_cfg.py:dt 0.005→0.002,decimation 4→10. The G1 is independently NaN-unstable atdt=0.005even from a clean stance; this matches the known-good Velocity/Tracking-Flat configs. Control rate stays 50 Hz; the 2 s episode (100 control steps) is unchanged.rl/runner.py:play_taskcaps scripted (zero/random) playback atsimulation.steps, soplay TASK --agent zero --steps Nstops after N steps instead of looping forever headless (--stepswas silently ignored on the RL play path;max_stepsdefaulted toNone). Trained playback stays unbounded.Verification
play … --agent zero --steps 5(exact repro)play … --steps 5(showcasetask.play()path)play … --steps 400(all teleports)histogram=[8,0,0,0,8])uv run pytest -qReference audit (CLAUDE.md)
grepacrosssrc/ examples/ tests/:terrain_levels_vel— only non-test caller is the curriculum showcase cfg; signature change is additive (new optionalspawn_clearance). No breakage. Behavior tests intests/test_curriculum_terrain.pyupdated to the corrected (seated) spawn.surface_height_at— new method; callers areterrain_levels_veland the showcase runner (both in this PR).reset_root_state_on_terrain— a reset-event approach explored mid-development was fully removed (the curriculum overwrites root pose after events, so the fix had to live interrain_levels_vel);grepconfirms zero references remain.No
plans/files touched.