Skip to content

feat: Introduce G1 Energy Benchmark Environment (Fluidity Index)#1

Open
ngoiyaeric wants to merge 6 commits into
mainfrom
g1-energy-benchmark-3027240647789121151
Open

feat: Introduce G1 Energy Benchmark Environment (Fluidity Index)#1
ngoiyaeric wants to merge 6 commits into
mainfrom
g1-energy-benchmark-3027240647789121151

Conversation

@ngoiyaeric

@ngoiyaeric ngoiyaeric commented Mar 1, 2026

Copy link
Copy Markdown
Collaborator

This PR introduces a new energy benchmark environment for the Unitree G1 robot (Isaac-Velocity-Energy-G1-v0). The environment tracks battery and token states directly in a custom subclass of ManagerBasedRLEnv by overriding step, load_managers, and _reset_idx. It utilizes a custom token earning mechanism and charging logic at the world origin, and feeds these states back to the policy via custom observations. This acts as a foundation for energy-efficient humanoid RL research.


PR created automatically by Jules for task 3027240647789121151 started by @ngoiyaeric

Summary by CodeRabbit

  • New Features
    • G1 energy-aware locomotion task: new environment with battery, token economy, charging stations, and energy-aware step/reset logic.
    • New configuration exposing energy parameters and policy observations (battery level, token count), plus related rewards and empty-battery termination hooks.
    • MDP utilities (observations, rewards, terminations) added and exported, and the environment is registered and exposed at the parent package level.

Co-authored-by: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com>
@google-labs-jules

Copy link
Copy Markdown

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@qodo-code-review

Copy link
Copy Markdown

Review Summary by Qodo

Add G1 Energy Benchmark environment with battery and token mechanics

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Introduces G1 Energy Benchmark environment with battery and token tracking
• Implements custom energy drain based on robot torque consumption
• Adds token earning mechanism tied to velocity tracking quality
• Includes charging station logic at world origin with token cost
• Provides custom observations and rewards for energy-aware RL training
Diagram
flowchart LR
  A["G1FlatEnvCfg"] -->|extends| B["G1EnergyEnvCfg"]
  C["ManagerBasedRLEnv"] -->|extends| D["G1EnergyEnv"]
  B -->|configures| D
  D -->|tracks| E["Battery State"]
  D -->|tracks| F["Token State"]
  E -->|drains via| G["Torque-based Energy"]
  F -->|earned via| H["Velocity Tracking Quality"]
  E -->|recharged at| I["Charging Station"]
  I -->|costs| F
  D -->|provides| J["Custom Observations"]
  D -->|provides| K["Custom Rewards"]
  D -->|provides| L["Termination Conditions"]
Loading

Grey Divider

File Changes

1. source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/__init__.py ⚙️ Configuration changes +1/-0

Export G1 energy configuration module

• Added import statement for g1_energy module to expose configurations

source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/init.py


2. source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/__init__.py ⚙️ Configuration changes +18/-0

Register G1 energy environment with Gymnasium

• Registers new Gymnasium environment Isaac-Velocity-Energy-G1-v0
• Imports G1EnergyEnv and G1EnergyEnvCfg classes
• Configures environment entry points and RSL-RL agent configuration

source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/init.py


3. source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/env.py ✨ Enhancement +153/-0

Custom environment with energy and token mechanics

• Implements custom G1EnergyEnv class extending ManagerBasedRLEnv
• Overrides step() to track battery drain based on applied torques
• Implements token earning mechanism tied to velocity tracking error
• Adds charging station logic at world origin with token cost
• Overrides _reset_idx() to reset battery and token buffers on episode reset
• Logs average battery and token metrics to extras

source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/env.py


View more (5)
4. source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/env_cfg.py ⚙️ Configuration changes +57/-0

Configuration for G1 energy environment parameters

• Extends G1FlatEnvCfg with energy-specific configuration parameters
• Defines battery capacity, drain rate, token earn rate, and charging costs
• Configures command ranges for X, Y velocity and yaw control
• Extends episode length to 60 seconds for longer charging cycles
• Adds custom termination, reward, and observation terms via MDP module
• Registers charging station event mode

source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/env_cfg.py


5. source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/mdp/__init__.py Miscellaneous +8/-0

MDP module exports for energy environment

• Exports observation, reward, and termination term functions

source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/mdp/init.py


6. source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/mdp/observations.py ✨ Enhancement +20/-0

Custom observation functions for battery and tokens

• Implements battery_level() observation returning normalized battery state
• Implements token_count() observation returning current token count
• Both return tensors with shape (num_envs, 1) for policy input

source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/mdp/observations.py


7. source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/mdp/rewards.py ✨ Enhancement +24/-0

Reward functions for battery state penalties

• Implements battery_penalty() returning linear penalty based on battery level
• Implements empty_battery_penalty() returning heavy penalty when battery depletes
• Penalties scale from 0 (full battery) to -1 or -10 (empty battery)

source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/mdp/rewards.py


8. source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/mdp/terminations.py ✨ Enhancement +13/-0

Termination condition for empty battery

• Implements battery_empty() termination condition
• Terminates episode when battery level reaches or falls below 0

source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/mdp/terminations.py


Grey Divider

Qodo Logo

@qodo-code-review

qodo-code-review Bot commented Mar 1, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (5) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider


Action required

1. Obs init before buffers🐞 Bug ✓ Correctness
Description
G1EnergyEnv.load_managers() calls super().load_managers() before initializing
battery_buf/tokens_buf and max_battery, but ObservationManager calls observation terms during
initialization to infer shapes. This will crash env creation when the new observation terms access
these uninitialized fields.
Code

source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/env.py[R27-38]

+    def load_managers(self):
+        super().load_managers()
+        # Initialize the buffers now that the number of environments is known
+        self.battery_buf = torch.ones(self.num_envs, device=self.device)
+        self.tokens_buf = torch.zeros(self.num_envs, device=self.device)
+
+        # Parameters for energy / tokens
+        self.max_battery = self.cfg.battery_capacity
+        self.battery_drain_rate = self.cfg.battery_drain_rate
+        self.token_earn_rate = self.cfg.token_earn_rate
+        self.charge_token_cost = self.cfg.charge_token_cost
+        self.charging_station_radius = self.cfg.charging_station_radius
Evidence
ObservationManager infers observation term dimensions by calling each term function once during
manager initialization. The new observation terms read env.battery_buf and env.max_battery, but
those are only set after super().load_managers() returns, so they are None/missing at the time of
the call.

source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/env.py[27-38]
source/isaaclab/isaaclab/managers/observation_manager.py[480-486]
source/isaaclab/isaaclab/managers/observation_manager.py[554-559]
source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/mdp/observations.py[11-15]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`G1EnergyEnv.load_managers()` currently initializes `battery_buf`/`tokens_buf` and `max_battery` after calling `super().load_managers()`. However, ObservationManager calls observation terms during initialization to infer tensor shapes, and the new observation terms access these fields, causing env creation to crash.
## Issue Context
ObservationManager infers observation dimensions by calling each observation term once during initialization.
## Fix Focus Areas
- source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/env.py[27-38]
- source/isaaclab/isaaclab/managers/observation_manager.py[554-559]
- source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/mdp/observations.py[11-15]
## Implementation notes
- Reorder `load_managers()` to set `self.max_battery` and allocate `battery_buf`/`tokens_buf` before `super().load_managers()`.
- Ensure the buffers are on `self.device` and match `self.num_envs`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Invalid mdp import🐞 Bug ✓ Correctness
Description
G1EnergyEnvCfg imports custom_mdp via import source.isaaclab_tasks.isaaclab_tasks..., which
doesn’t match the package’s import structure used elsewhere. This is very likely to raise
ModuleNotFoundError when configs are imported for gym registration.
Code

source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/env_cfg.py[16]

+import source.isaaclab_tasks.isaaclab_tasks.manager_based.locomotion.velocity.config.g1_energy.mdp as custom_mdp
Evidence
The tasks package is imported as isaaclab_tasks and recursively imports subpackages for
environment registration. Other env cfg files import their MDP modules using isaaclab_tasks....,
not source...., indicating source is not part of the runtime package name.

source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/env_cfg.py[12-17]
source/isaaclab_tasks/isaaclab_tasks/manager_based/classic/humanoid/humanoid_env_cfg.py[19-20]
source/isaaclab_tasks/isaaclab_tasks/init.py[33-39]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The env config imports `custom_mdp` through an invalid module path prefixed with `source.` and duplicating `isaaclab_tasks`, which will fail in normal package imports.
## Issue Context
Other configs import MDP modules using `isaaclab_tasks....` and the package auto-import mechanism imports under the `isaaclab_tasks` namespace.
## Fix Focus Areas
- source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/env_cfg.py[16-16]
## Implementation notes
- Use either:
- `import isaaclab_tasks.manager_based.locomotion.velocity.config.g1_energy.mdp as custom_mdp`, or
- `from . import mdp as custom_mdp` (preferred, since it’s within the same package).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Battery empty is timeout🐞 Bug ✓ Correctness
Description
Battery depletion termination is configured with time_out=True, which makes it a truncation
instead of a true terminal condition. This breaks RL semantics (bootstrapping/episode stats) because
battery empty is a task failure, not a time-limit.
Code

source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/env_cfg.py[R42-44]

+        # Terminations
+        self.terminations.battery_empty = DoneTerm(func=custom_mdp.battery_empty, time_out=True)
+
Evidence
TerminationManager routes termination terms with time_out=True into the truncated buffer and only
non-timeout terms into the terminated buffer. Setting battery empty as a timeout will therefore
surface as reset_time_outs instead of reset_terminated.

source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/env_cfg.py[42-44]
source/isaaclab/isaaclab/managers/termination_manager.py[167-173]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Battery depletion is incorrectly marked as a timeout (`time_out=True`), causing it to be treated as a truncation rather than a terminal failure.
## Issue Context
TerminationManager separates `time_outs` vs `terminated` based on the `time_out` flag.
## Fix Focus Areas
- source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/env_cfg.py[42-44]
- source/isaaclab/isaaclab/managers/termination_manager.py[167-173]
## Implementation notes
- Change to: `DoneTerm(func=custom_mdp.battery_empty, time_out=False)` (or omit `time_out` entirely).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (1)
4. Charging uses world origin🐞 Bug ✓ Correctness
Description
Charging station distance is computed against world (0,0) using root_pos_w, but vectorized
environments are typically offset by scene.env_origins. This makes charging effectively impossible
for most parallel env instances unless they happen to be near the world origin.
Code

source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/env.py[R84-88]

+        # Charging logic
+        # Check distance to charging station (origin 0,0)
+        dist_to_station = torch.norm(robot.data.root_pos_w[:, :2], dim=1)
+        at_station = dist_to_station < self.charging_station_radius
+        can_charge = self.tokens_buf >= self.charge_token_cost
Evidence
Other tasks convert world positions to per-env local coordinates by subtracting scene.env_origins.
Without this adjustment, positions for envs placed with spacing will be far from (0,0), so
at_station will almost always be false.

source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/env.py[84-88]
source/isaaclab_tasks/isaaclab_tasks/direct/automate/assembly_env.py[276-283]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Charging checks distance to the world origin using `root_pos_w`, which breaks in vectorized scenes where each environment is offset by `scene.env_origins`.
## Issue Context
Other tasks subtract `scene.env_origins` to compute per-environment local positions.
## Fix Focus Areas
- source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/env.py[84-88]
- source/isaaclab_tasks/isaaclab_tasks/direct/automate/assembly_env.py[276-283]
## Implementation notes
- Use something like:
- `pos_local_xy = robot.data.root_pos_w[:, :2] - self.scene.env_origins[:, :2]`
- `dist_to_station = torch.norm(pos_local_xy, dim=1)`
- Alternatively, if the station is global by design, place/compute it consistently for all env instances.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

5. Battery init not full 🐞 Bug ⛯ Reliability
Description
Battery starts at 1.0 regardless of battery_capacity, while reset sets it to max_battery. For
non-default capacities this creates inconsistent semantics (initial state not “full”, but resets
are).
Code

source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/env.py[R29-35]

+        # Initialize the buffers now that the number of environments is known
+        self.battery_buf = torch.ones(self.num_envs, device=self.device)
+        self.tokens_buf = torch.zeros(self.num_envs, device=self.device)
+
+        # Parameters for energy / tokens
+        self.max_battery = self.cfg.battery_capacity
+        self.battery_drain_rate = self.cfg.battery_drain_rate
Evidence
The config exposes a battery_capacity parameter and the env stores it in max_battery, but
initial buffer allocation uses torch.ones(...). Meanwhile _reset_idx resets battery to
self.max_battery, so initial episodes can start at a different battery level than subsequent
episodes after resets when capacity != 1.0.

source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/env.py[29-35]
source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/env_cfg.py[23-28]
source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/env.py[144-146]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`battery_buf` starts at 1.0 irrespective of `battery_capacity`, but resets restore to `max_battery`. For capacities != 1.0 this makes initial episodes start with a different battery level than later episodes.
## Issue Context
`battery_capacity` is configurable and is stored as `self.max_battery`.
## Fix Focus Areas
- source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/env.py[29-35]
- source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/env.py[144-146]
## Implementation notes
- Prefer: `self.battery_buf = torch.full((self.num_envs,), self.max_battery, device=self.device)`
- Update comments to reflect the chosen battery units (absolute vs normalized).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

@coderabbitai

coderabbitai Bot commented Mar 1, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Adds a new G1 energy-aware locomotion task: env class, env config, MDP utilities (observations, rewards, terminations), package initializers, and Gymnasium registration exposing "Isaac-Velocity-Energy-G1-v0".

Changes

Cohort / File(s) Summary
Package init & registration
source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/__init__.py, source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/__init__.py, source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/mdp/__init__.py
config now re-exports g1_energy; g1_energy registers Gymnasium env id "Isaac-Velocity-Energy-G1-v0" on import; g1_energy/mdp re-exports observations/rewards/terminations.
Environment implementation
source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/env.py
New G1EnergyEnv (subclass of ManagerBasedRLEnv) with battery_buf and tokens_buf, energy drain computation, token earning, charging-station logic, step loop, reset behavior, and integration with managers.
Environment config
source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/env_cfg.py
New G1EnergyEnvCfg (extends G1FlatEnvCfg): energy parameters (capacity, drain/earn rates, token cost, station radius, vel_error_scale), overrides command ranges and episode length, adds battery/token observations and reward/termination wiring.
MDP observations
source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/mdp/observations.py
Adds battery_level(env) and token_count(env, max_expected_tokens=1.0) returning normalized torch tensors.
MDP rewards
source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/mdp/rewards.py
Adds battery_penalty(env) (linear penalty) and empty_battery_penalty(env) (heavy penalty when battery nearly empty).
MDP terminations
source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/mdp/terminations.py
Adds battery_empty(env) termination predicate signaling episode end when battery depleted.

Sequence Diagram

sequenceDiagram
    participant Agent as RL Agent
    participant Env as G1EnergyEnv
    participant Physics as Physics Engine
    participant Energy as Energy System
    participant Charging as Charging Station
    participant Managers as Reward/Termination Managers

    Agent->>Env: step(action)
    Env->>Env: apply action & record pre-step data

    loop decimation steps
        Env->>Physics: simulate physics step
        Physics-->>Env: sensor/torque data
    end

    Env->>Energy: compute energy drain from torques
    Energy-->>Env: update battery_buf (clamped)

    Env->>Energy: compute token earnings from tracking error
    Energy-->>Env: update tokens_buf

    Env->>Charging: check proximity & token availability
    alt within radius AND tokens >= cost
        Charging->>Energy: refill battery_buf (deduct tokens)
        Charging->>Env: emit at_charging_station event
    end

    Env->>Managers: compute reward & termination
    Managers-->>Env: reward, done flags

    alt reset required
        Env->>Env: _reset_idx -> reset battery_buf, tokens_buf, env state
    end

    Env->>Agent: return (obs, reward, terminated, truncated, extras)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 I hop through code with battery bright,
Tokens jingling, charging by night,
G1 strides on with energy care,
Physics hums — we leap through air,
Recharged, I twitch my whiskers with delight.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 45.45% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes the main change: introducing a new G1 Energy Benchmark Environment, which aligns with the primary objective of adding a custom energy-aware environment for the Unitree G1 robot.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch g1-energy-benchmark-3027240647789121151

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (4)
source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/__init__.py (1)

6-8: Consider reordering imports per PEP 8.

Standard library imports (gymnasium) should come before local imports. This is a minor style nit.

♻️ Suggested import order
+import gymnasium as gym
+
 from .env import G1EnergyEnv
 from .env_cfg import G1EnergyEnvCfg
-import gymnasium as gym
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/__init__.py`
around lines 6 - 8, Reorder the imports in this module to follow PEP 8 by
placing the third-party/standard import before the local package imports: import
gymnasium as gym should come before the relative imports of G1EnergyEnv and
G1EnergyEnvCfg (the symbols to look for are G1EnergyEnv, G1EnergyEnvCfg, and
gym).
source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/mdp/observations.py (1)

17-20: Consider normalizing token_count for training stability.

Unlike battery_level, token_count returns unbounded raw values. This could lead to large variance in observations as tokens accumulate over time, potentially affecting training stability. Consider normalizing by a maximum expected token value or applying a transform (e.g., tanh).

♻️ Example normalization approach
+MAX_EXPECTED_TOKENS = 10.0  # Tune based on expected accumulation
+
 def token_count(env: ManagerBasedRLEnv) -> torch.Tensor:
     """Returns the current token count of the robot."""
     # (num_envs, 1)
-    return env.tokens_buf.unsqueeze(-1)
+    # Normalize using tanh for bounded output
+    return torch.tanh(env.tokens_buf / MAX_EXPECTED_TOKENS).unsqueeze(-1)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/mdp/observations.py`
around lines 17 - 20, The token_count observation currently returns raw,
unbounded values from env.tokens_buf via token_count(env: ManagerBasedRLEnv);
normalize it to improve training stability by transforming the tensor before
returning—e.g., divide by a configurable max_expected_tokens (or apply
torch.tanh) and keep the (num_envs, 1) shape; ensure the normalization uses a
clear parameter name (e.g., max_expected_tokens or token_scale) accessible where
observations are constructed and document that token_count returns the
normalized tensor so it remains consistent with battery_level scaling.
source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/env.py (1)

17-25: Remove redundant _cfg assignment.

The parent class ManagerBasedRLEnv.__init__ already stores the config as self.cfg. The self._cfg = cfg assignment is redundant and could cause confusion about which attribute to use.

♻️ Proposed fix
     def __init__(self, cfg, **kwargs):
         # 1. Allocate custom buffers before calling super().__init__()
         # These will be initialized to 1.0 (full battery) and 0.0 (no tokens)
         self.battery_buf = None
         self.tokens_buf = None
-        self._cfg = cfg

         # Super init will call load_managers, so we will initialize the buffers inside load_managers
         super().__init__(cfg, **kwargs)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/env.py`
around lines 17 - 25, In the __init__ of the environment class remove the
redundant self._cfg = cfg assignment (the parent ManagerBasedRLEnv.__init__
already stores the config as self.cfg) and rely on self.cfg throughout; keep the
custom buffer attributes (battery_buf and tokens_buf) initialization and the
super().__init__(cfg, **kwargs) call, and update any references to use self.cfg
instead of self._cfg if present.
source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/mdp/rewards.py (1)

18-24: Avoid creating tensors inside the reward function hot path.

Creating torch.tensor() objects on every step incurs overhead from device allocation. Use tensor multiplication or torch.where with fill values directly.

♻️ Proposed fix using scalar multiplication
 def empty_battery_penalty(env: ManagerBasedRLEnv) -> torch.Tensor:
     """Heavily penalize when battery hits exactly 0."""
-    return torch.where(
-        env.battery_buf <= 0.01,
-        torch.tensor(-10.0, device=env.device),
-        torch.tensor(0.0, device=env.device),
-    )
+    return (env.battery_buf <= 0.01).float() * -10.0
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/mdp/rewards.py`
around lines 18 - 24, The reward function empty_battery_penalty currently
allocates new tensors each step via torch.tensor(..., device=env.device);
replace those allocations by using tensor operations on existing buffers — e.g.
build the mask = env.battery_buf <= 0.01 and return
mask.to(env.battery_buf.dtype) * -10.0 or use torch.where(mask,
torch.full_like(env.battery_buf, -10.0), torch.zeros_like(env.battery_buf)) so
no per-step torch.tensor() device allocations occur; update
empty_battery_penalty to use env.battery_buf, mask, and full_like/zeros_like or
scalar-multiplication to keep tensors on the correct device and dtype.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/__init__.py`:
- Around line 8-10: Update the stale comment in __init__.py to reflect that this
package now re-exports symbols from g1_energy; replace the lines that claim the
file is empty and doesn't expose configs with a short comment indicating that
g1_energy is intentionally re-exported (e.g., "Re-export g1_energy configs for
parent-package imports") and keep the existing from .g1_energy import *
statement unchanged so imports continue to work.

In
`@source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/env_cfg.py`:
- Line 16: The import in env_cfg.py uses the repository prefix "source." which
breaks installed package imports; change the import of custom_mdp to the
package-relative path (drop the leading "source."), e.g., import
source.isaaclab_tasks.isaaclab_tasks... -> import
isaaclab_tasks.manager_based.locomotion.velocity.config.g1_energy.mdp as
custom_mdp (locate the import statement referencing custom_mdp in env_cfg.py and
update it to the correct package path).
- Line 43: The DoneTerm for battery depletion is incorrectly marked as a
timeout; update the termination created at self.terminations.battery_empty
(DoneTerm(func=custom_mdp.battery_empty, ...)) so it is treated as a failure
termination rather than a time_out—i.e., remove or set time_out to False so
battery_empty contributes to reset_terminated (not reset_time_outs).

In
`@source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/env.py`:
- Around line 141-153: The current _reset_idx implementation logs avg_battery
and avg_tokens after the buffers are reset, so episode-end stats are lost; to
fix, sample the battery_buf and tokens_buf values for the provided env_ids
before you call super()._reset_idx (or before you overwrite them), compute their
means, and store those pre-reset values into
self.extras["log"]["Metrics/avg_battery"] and
self.extras["log"]["Metrics/avg_tokens"] instead of the post-reset values; keep
the subsequent buffer reset (self.battery_buf[env_ids] = self.max_battery,
self.tokens_buf[env_ids] = 0.0) and ensure this change is made inside the
_reset_idx method.
- Around line 114-118: The current reset index extraction uses
self.reset_buf.nonzero(as_tuple=False).squeeze(-1), which can yield a 0-D tensor
when there is a single environment and break the downstream _reset_idx call;
replace the squeeze(-1) with .flatten() to guarantee a 1-D tensor of indices
(e.g., assign reset_env_ids = self.reset_buf.nonzero(as_tuple=False).flatten()),
then keep the existing check and calls to
self.recorder_manager.record_pre_reset(reset_env_ids) and
self._reset_idx(reset_env_ids).

---

Nitpick comments:
In
`@source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/__init__.py`:
- Around line 6-8: Reorder the imports in this module to follow PEP 8 by placing
the third-party/standard import before the local package imports: import
gymnasium as gym should come before the relative imports of G1EnergyEnv and
G1EnergyEnvCfg (the symbols to look for are G1EnergyEnv, G1EnergyEnvCfg, and
gym).

In
`@source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/env.py`:
- Around line 17-25: In the __init__ of the environment class remove the
redundant self._cfg = cfg assignment (the parent ManagerBasedRLEnv.__init__
already stores the config as self.cfg) and rely on self.cfg throughout; keep the
custom buffer attributes (battery_buf and tokens_buf) initialization and the
super().__init__(cfg, **kwargs) call, and update any references to use self.cfg
instead of self._cfg if present.

In
`@source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/mdp/observations.py`:
- Around line 17-20: The token_count observation currently returns raw,
unbounded values from env.tokens_buf via token_count(env: ManagerBasedRLEnv);
normalize it to improve training stability by transforming the tensor before
returning—e.g., divide by a configurable max_expected_tokens (or apply
torch.tanh) and keep the (num_envs, 1) shape; ensure the normalization uses a
clear parameter name (e.g., max_expected_tokens or token_scale) accessible where
observations are constructed and document that token_count returns the
normalized tensor so it remains consistent with battery_level scaling.

In
`@source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/mdp/rewards.py`:
- Around line 18-24: The reward function empty_battery_penalty currently
allocates new tensors each step via torch.tensor(..., device=env.device);
replace those allocations by using tensor operations on existing buffers — e.g.
build the mask = env.battery_buf <= 0.01 and return
mask.to(env.battery_buf.dtype) * -10.0 or use torch.where(mask,
torch.full_like(env.battery_buf, -10.0), torch.zeros_like(env.battery_buf)) so
no per-step torch.tensor() device allocations occur; update
empty_battery_penalty to use env.battery_buf, mask, and full_like/zeros_like or
scalar-multiplication to keep tensors on the correct device and dtype.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e173128 and cea2d03.

📒 Files selected for processing (8)
  • source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/__init__.py
  • source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/__init__.py
  • source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/env.py
  • source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/env_cfg.py
  • source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/mdp/__init__.py
  • source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/mdp/observations.py
  • source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/mdp/rewards.py
  • source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/mdp/terminations.py

@coderabbitai

coderabbitai Bot commented Mar 1, 2026

Copy link
Copy Markdown

Note

Docstrings generation - SUCCESS
Generated docstrings for this pull request at #2

@coderabbitai

coderabbitai Bot commented Mar 1, 2026

Copy link
Copy Markdown

Note

Unit test generation is a public access feature. Expect some limitations and changes as we gather feedback and continue to improve it.


Generating unit tests... This may take up to 20 minutes.

coderabbitai Bot added a commit that referenced this pull request Mar 1, 2026
Docstrings generation was requested by @ngoiyaeric.

* #1 (comment)

The following files were modified:

* `source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/env.py`
* `source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/env_cfg.py`
* `source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/mdp/observations.py`
* `source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/mdp/rewards.py`
* `source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/mdp/terminations.py`
@coderabbitai

coderabbitai Bot commented Mar 1, 2026

Copy link
Copy Markdown

✅ Created PR with unit tests: #3

Co-authored-by: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/env.py (1)

78-80: Consider making velocity error scaling configurable.

The hardcoded 0.5 divisor in torch.exp(-vel_error / 0.5) controls how strictly velocity tracking affects token earning. This tuning parameter could be exposed in G1EnergyEnvCfg for easier experimentation.

♻️ Proposed refactor

In env_cfg.py:

 charge_token_cost: float = 1.0
 charging_station_radius: float = 1.0
+vel_error_scale: float = 0.5  # Sensitivity of token earning to velocity tracking error

In env.py:

-job_quality = torch.exp(-vel_error / 0.5)
+job_quality = torch.exp(-vel_error / self.cfg.vel_error_scale)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/env.py`
around lines 78 - 80, The hardcoded velocity-error scaling (0.5) inside
job_quality = torch.exp(-vel_error / 0.5) should be made configurable via
G1EnergyEnvCfg so tuning is easier; add a new field (e.g., vel_error_scale or
vel_tracking_scale) to G1EnergyEnvCfg with a sensible default, update any env
config parsing, and replace the literal 0.5 in env.py with
self.cfg.vel_error_scale (or the chosen name) when computing job_quality and
tokens_earned so the behavior is driven by the config rather than a magic
number.
source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/env_cfg.py (1)

57-58: Consider replacing stub lambda with a named no-op function.

The lambda with unused arguments is a placeholder for the at_charging_station event. While functional, a named function improves clarity and avoids the Ruff ARG005 warnings.

♻️ Proposed refactor

Add a no-op function at module level or in the mdp package:

def _noop_event(env, env_ids):
    """No-op event handler for charging station proximity."""
    pass

Then use it:

-        self.events.at_charging_station = EventTerm(func=lambda env, env_ids: None, mode="at_charging_station")
+        self.events.at_charging_station = EventTerm(func=_noop_event, mode="at_charging_station")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/env_cfg.py`
around lines 57 - 58, Replace the inline lambda used for
self.events.at_charging_station with a named no-op function to improve clarity
and silence Ruff ARG005; add a module-level function (e.g., def _noop_event(env,
env_ids): pass) or import one from the mdp package, then instantiate
EventTerm(func=_noop_event, mode="at_charging_station") instead of the lambda so
the EventTerm call and symbol self.events.at_charging_station remain unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/env_cfg.py`:
- Around line 57-58: Replace the inline lambda used for
self.events.at_charging_station with a named no-op function to improve clarity
and silence Ruff ARG005; add a module-level function (e.g., def _noop_event(env,
env_ids): pass) or import one from the mdp package, then instantiate
EventTerm(func=_noop_event, mode="at_charging_station") instead of the lambda so
the EventTerm call and symbol self.events.at_charging_station remain unchanged.

In
`@source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/env.py`:
- Around line 78-80: The hardcoded velocity-error scaling (0.5) inside
job_quality = torch.exp(-vel_error / 0.5) should be made configurable via
G1EnergyEnvCfg so tuning is easier; add a new field (e.g., vel_error_scale or
vel_tracking_scale) to G1EnergyEnvCfg with a sensible default, update any env
config parsing, and replace the literal 0.5 in env.py with
self.cfg.vel_error_scale (or the chosen name) when computing job_quality and
tokens_earned so the behavior is driven by the config rather than a magic
number.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cea2d03 and 8db47b0.

📒 Files selected for processing (6)
  • source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/__init__.py
  • source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/__init__.py
  • source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/env.py
  • source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/env_cfg.py
  • source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/mdp/observations.py
  • source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/g1_energy/mdp/rewards.py

Co-authored-by: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com>
@ngoiyaeric ngoiyaeric changed the title feat: Introduce G1 Energy Benchmark Environment feat: Introduce G1 Energy Benchmark Environment (Fluidity Index) Mar 1, 2026
Co-authored-by: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com>
@QueueLab QueueLab deleted a comment from google-labs-jules Bot Mar 1, 2026
@ngoiyaeric

Copy link
Copy Markdown
Collaborator Author

grok-video-81b989a1-c83e-4d20-9193-b0dc645c90c6.mp4

FIX.mp4

@google-labs-jules

Copy link
Copy Markdown

grok-video-81b989a1-c83e-4d20-9193-b0dc645c90c6.mp4

Uploading FIX.mp4…

Acknowledged.

Co-authored-by: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com>
@ngoiyaeric

Copy link
Copy Markdown
Collaborator Author
grok-video-81b989a1-c83e-4d20-9193-b0dc645c90c6.mp4

@google-labs-jules

Copy link
Copy Markdown
grok-video-81b989a1-c83e-4d20-9193-b0dc645c90c6.mp4

Acknowledged.

Co-authored-by: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com>
@ngoiyaeric

Copy link
Copy Markdown
Collaborator Author

Synchronize the main branch without merge issues

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