Skip to content

fix: imu_omega shape inconsistency, duplicate SDK import, task_registry cleanup#87

Open
dcol91863 wants to merge 4 commits intounitreerobotics:mainfrom
dcol91863:fix/more-bugs
Open

fix: imu_omega shape inconsistency, duplicate SDK import, task_registry cleanup#87
dcol91863 wants to merge 4 commits intounitreerobotics:mainfrom
dcol91863:fix/more-bugs

Conversation

@dcol91863
Copy link

Summary

Four independent fixes found during a second audit pass.


1. ang_vel shape inconsistency + imu_omega[0] wrong indexing (paired)

Files: deploy/deploy_real/deploy_real.py · deploy/deploy_real/common/rotation_helper.py

deploy_real.py line 162 created ang_vel with shape (1, 3) due to an extra list wrapper:

# Before — shape (1, 3)
ang_vel = np.array([self.low_state.imu_state.gyroscope], dtype=np.float32)

# After — shape (3,)
ang_vel = np.array(self.low_state.imu_state.gyroscope, dtype=np.float32)

rotation_helper.py compensated with imu_omega[0] (extracting the inner row), but this masked the issue rather than fixing it. In the pelvis-IMU path (G1, which skips transform_imu_data), ang_vel remained shape (1, 3), causing a latent shape mismatch when it was multiplied by ang_vel_scale and assigned into obs[:3].

The two changes go together:

  • deploy_real.py: remove outer [...] so ang_vel is always shape (3,)
  • rotation_helper.py: change imu_omega[0]imu_omega to rotate the full 3-vector

2. Duplicate ChannelFactoryInitialize import

File: deploy/deploy_real/deploy_real.py

# Before — imported twice from the same module
from unitree_sdk2py.core.channel import ChannelPublisher, ChannelFactoryInitialize
from unitree_sdk2py.core.channel import ChannelSubscriber, ChannelFactoryInitialize

# After — merged into one line
from unitree_sdk2py.core.channel import ChannelPublisher, ChannelSubscriber, ChannelFactoryInitialize

3. task_registry.py unused imports removed

File: legged_gym/utils/task_registry.py

  • import numpy as npnp is never referenced anywhere in the file
  • import syssys is never referenced anywhere in the file
  • Fixed docstring typo nammename (appeared in both make_env and make_alg_runner)

4. Docstring typo in _resample_commands

File: legged_gym/envs/base/legged_robot.py

'Randommly''Randomly'

Test plan

  • Deploy H1 (torso IMU path): confirm transform_imu_data returns a (3,) angular velocity vector as before
  • Deploy G1 (pelvis IMU path): confirm ang_vel is shape (3,) and obs[:3] assignment works correctly
  • Verify task_registry.make_env() and make_alg_runner() still work after removing the unused imports

…or heightfield/trimesh

Closes unitreerobotics#81

The `_update_terrain_curriculum(env_ids)` method was referenced in the
docstring of `reset_idx` but was never implemented, making terrain
curriculum training a no-op even when `cfg.terrain.curriculum = True`.

Changes
-------

legged_gym/envs/base/legged_robot.py:

1. _get_env_origins() — heightfield / trimesh branch (new)
   Previously the method only created a flat grid regardless of terrain
   type, so `custom_origins` was always False and robots were never
   placed on the actual heightfield platforms.  Now:
   - Sets `custom_origins = True` for heightfield and trimesh terrains.
   - Converts `terrain.env_origins` (numpy, rows=levels, cols=types) to
     a GPU tensor `self.terrain_origins` for fast index lookups.
   - Assigns each environment a random terrain type (column).
   - Initialises each environment to a random level in
     [0, max_init_terrain_level].
   - Sets `self.env_origins` from the lookup table.

2. _update_terrain_curriculum(env_ids) — new method
   Promotes robots that traversed more than half a platform length
   (env_length / 2) during the episode to the next harder level, and
   demotes robots that fell short by one level.  Levels are clamped to
   [0, num_rows - 1].  env_origins is refreshed for the reset envs.
   The method is a no-op on the very first call (before init_done) so
   the initial level assignment from _get_env_origins is preserved.

3. reset_idx() — wired up
   Added call to `_update_terrain_curriculum(env_ids)` when
   `cfg.terrain.curriculum` is True (placed before robot repositioning
   so the updated origin is used immediately).  Also logs the mean
   terrain level to `extras["episode"]["terrain_level"]` for monitoring.
…terrain, helpers, BaseTask ABC

Fixes found during a broad audit of the codebase. Each change is
independent; grouped here to avoid PR noise.

---

legged_gym/envs/base/legged_robot.py
  _push_robots() velocity corruption (critical)
  - The previous code wrote random XY velocities into self.root_states[:]
    (all num_envs rows) but only committed push_env_ids rows back to the
    simulator. This left stale random values in the root_states tensor for
    non-pushed environments, corrupting base_lin_vel / base_ang_vel
    observations on the same step.
  - Fix: index the write with push_env_ids, matching the indexed set call.

  Duplicate / unused imports removed
  - LEGGED_GYM_ROOT_DIR was imported twice (lines 1 and 14).
  - envs (line 1), WarningMessage (line 3), and Tensor (line 11) were
    imported but never referenced anywhere in the file.

---

deploy/deploy_mujoco/deploy_mujoco.py + configs/{g1,h1,h1_2}.yaml
  Missing max_cmd factor — sim2real observation mismatch (critical)
  - deploy_real builds cmd obs as: cmd * cmd_scale * max_cmd
  - deploy_mujoco built it as:    cmd * cmd_scale   (max_cmd missing)
  - The policy was trained with the former, so the MuJoCo deployment was
    feeding a different numerical range for the command slice, leading to
    degraded or incorrect behaviour.
  - Fix: read max_cmd from config and apply it; add max_cmd field to all
    three MuJoCo YAML configs (values match deploy_real configs).

---

legged_gym/utils/terrain.py
  selected_terrain() three bugs fixed
  - length= was set to width_per_env_pixels instead of length_per_env_pixels,
    producing square sub-terrains even when env_length != env_width.
  - vertical_scale= and horizontal_scale= referenced self.vertical_scale /
    self.horizontal_scale, attributes that do not exist on Terrain; the
    correct references are self.cfg.vertical_scale / self.cfg.horizontal_scale
    (consistent with how make_terrain() accesses the same values).
  - eval(terrain_type)(terrain, **self.cfg.terrain_kwargs.terrain_kwargs)
    had a double .terrain_kwargs; changed to **self.cfg.terrain_kwargs.

  curiculum → curriculum spelling fix
  - Method name curiculum() and its call site corrected to curriculum().

---

legged_gym/utils/helpers.py
  get_load_path() bare except and month-boundary sort bug
  - Bare except: caught all exceptions including KeyboardInterrupt and
    MemoryError, swallowing the original error and replacing it with a
    misleading message. Now uses except OSError and chains the cause.
  - Empty-directory case was an IndexError (runs[-1]) silently converted
    to the same misleading message; now checked explicitly.
  - Lexicographic sort breaks at month/year boundaries (e.g. Oct > Nov
    alphabetically). Now sorts by mtime so the most-recent run is always
    picked correctly regardless of name format.

---

legged_gym/envs/base/base_task.py
  BaseTask should be abstract
  - BaseTask had reset_idx() and step() that raised NotImplementedError at
    runtime. A subclass that forgot to implement either would only fail when
    the method was actually called.
  - Added ABC base class and @AbstractMethod decorators so Python raises
    TypeError at instantiation time instead.
… H1/H1_2/G1 env files

The three humanoid robot environments (H1, H1_2, G1) were near-complete
copies of each other — 10 methods, ~120 lines, were pasted verbatim in
every file. The only code that legitimately differed was the hip-joint
DOF indices used in _reward_hip_pos().

Changes
-------
legged_gym/envs/base/humanoid_robot.py  (new file)
  Introduces HumanoidRobot(LeggedRobot) — a shared base class that owns:
  - _init_foot() / _init_buffers()      (rigid-body foot state setup)
  - update_feet_state()                 (per-step foot state refresh)
  - _post_physics_step_callback()       (gait phase computation)
  - _get_noise_scale_vec()              (observation noise scaling)
  - compute_observations()              (obs + privileged_obs assembly)
  - _reward_contact()                   (phase-gated contact reward)
  - _reward_feet_swing_height()         (swing foot height penalty)
  - _reward_alive()                     (constant survival reward)
  - _reward_contact_no_vel()            (contact-while-moving penalty)
  - _reward_hip_pos()                   (uses self.hip_indices — set per subclass)

legged_gym/envs/h1/h1_env.py
legged_gym/envs/h1_2/h1_2_env.py
legged_gym/envs/g1/g1_env.py
  Each reduced to a 3-line subclass that sets hip_indices.
  Zero behaviour change — the logic is identical, just deduplicated.

legged_gym/scripts/play.py
  Removed duplicate imports of sys and LEGGED_GYM_ROOT_DIR (lines 1-5
  contained each import twice). Removed unused RECORD_FRAMES and
  MOVE_CAMERA variables (set in __main__ but never read anywhere).

legged_gym/envs/base/legged_robot_config.py + legged_gym/utils/terrain.py
  Corrected long-standing spelling mistake: slope_treshold → slope_threshold
  (both the config field definition and the call site in terrain.py).
deploy/deploy_real/deploy_real.py + deploy/deploy_real/common/rotation_helper.py
  ang_vel shape inconsistency fixed (paired change)

  deploy_real.py line 162 constructed ang_vel as:
    np.array([self.low_state.imu_state.gyroscope], dtype=np.float32)
  The extra list wrapper produced shape (1, 3) instead of (3,). This was
  masked in the torso-IMU path by rotation_helper.py using imu_omega[0]
  to extract the inner (3,) row, but left ang_vel with shape (1, 3) in the
  pelvis-IMU path (G1), causing a latent shape mismatch in subsequent ops.

  Fix (two changes that must go together):
  - deploy_real.py: remove the outer [...] so ang_vel is always shape (3,)
  - rotation_helper.py: change imu_omega[0] → imu_omega so the full
    3-element vector is rotated, not just one scalar element

deploy/deploy_real/deploy_real.py
  Duplicate import of ChannelFactoryInitialize merged

  Lines 7-8 both imported ChannelFactoryInitialize from the same module.
  The two separate from-import lines have been merged into one.

legged_gym/utils/task_registry.py
  - Removed unused imports: numpy (np) and sys — neither is referenced
    anywhere in task_registry.py
  - Fixed docstring typo: 'namme' → 'name' (appeared twice, in make_env
    and make_alg_runner)

legged_gym/envs/base/legged_robot.py
  - Fixed docstring typo in _resample_commands: 'Randommly' → 'Randomly'
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.

2 participants