Repository navigation
feat: G1 per-joint kp/kd domain randomization (issue #129) #259
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -22,6 +22,15 @@ | |||||||||||||||||||||||||||||||||
| from unilab.envs.locomotion.g1.base import G1BaseCfg, G1BaseEnv | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| @dataclass | ||||||||||||||||||||||||||||||||||
| class G1DomainRandConfig(DomainRandConfig): | ||||||||||||||||||||||||||||||||||
| randomize_kp: bool = True | ||||||||||||||||||||||||||||||||||
| kp_multiplier_range: list[float] = field(default_factory=lambda: [0.9, 1.1]) | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| randomize_kd: bool = True | ||||||||||||||||||||||||||||||||||
| kd_multiplier_range: list[float] = field(default_factory=lambda: [0.9, 1.1]) | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| @dataclass | ||||||||||||||||||||||||||||||||||
| class InitState: | ||||||||||||||||||||||||||||||||||
| pos = [0.0, 0.0, 0.754] | ||||||||||||||||||||||||||||||||||
|
|
@@ -107,12 +116,19 @@ class G1JoystickPPOCfg(G1BaseCfg): | |||||||||||||||||||||||||||||||||
| init_state: InitState = field(default_factory=InitState) | ||||||||||||||||||||||||||||||||||
| commands: Commands = field(default_factory=Commands) | ||||||||||||||||||||||||||||||||||
| reward_config: RewardConfigPPO | None = None | ||||||||||||||||||||||||||||||||||
| domain_rand: DomainRandConfig = field(default_factory=DomainRandConfig) | ||||||||||||||||||||||||||||||||||
| domain_rand: G1DomainRandConfig = field(default_factory=G1DomainRandConfig) | ||||||||||||||||||||||||||||||||||
| gait_phase_init_mode: str = "offset_phase" | ||||||||||||||||||||||||||||||||||
| reset_base_qvel_limit: float = 0.5 | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| class G1JoystickDomainRandomizationProvider(LocomotionDRProvider): | ||||||||||||||||||||||||||||||||||
| def __init__(self, *, base_kp: np.ndarray | None = None, base_kd: np.ndarray | None = None): | ||||||||||||||||||||||||||||||||||
| self._base_kp = base_kp | ||||||||||||||||||||||||||||||||||
| self._base_kd = base_kd | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| def _get_base_actuator_gains(self, env: Any) -> tuple[np.ndarray | None, np.ndarray | None]: | ||||||||||||||||||||||||||||||||||
| return self._base_kp, self._base_kd | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| def _get_qvel_limit(self, env: Any) -> float: | ||||||||||||||||||||||||||||||||||
| return float(env.cfg.reset_base_qvel_limit) | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
|
|
@@ -173,7 +189,12 @@ def __init__(self, cfg: G1JoystickPPOCfg, num_envs=1, backend_type="mujoco"): | |||||||||||||||||||||||||||||||||
| self._upper_body_pose_weights = build_upper_body_pose_weights(self._reward_cfg.pose_weights) | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| self._init_reward_functions() | ||||||||||||||||||||||||||||||||||
| self._init_domain_randomization(G1JoystickDomainRandomizationProvider()) | ||||||||||||||||||||||||||||||||||
| if cfg.domain_rand.randomize_kp or cfg.domain_rand.randomize_kd: | ||||||||||||||||||||||||||||||||||
| base_kp, base_kd = backend.get_actuator_gains() | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
| base_kp, base_kd = backend.get_actuator_gains() | |
| base_kp, base_kd = self._backend.get_actuator_gains() |
Copilot
AI
Apr 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The domain-randomization provider selection logic is duplicated here and in g1/joystick_sac.py. To reduce the chance of future drift (e.g., adding a new DR term in one env but not the other), consider factoring this into a shared helper (or a small method on the base env) that returns the correctly-configured provider.
| if cfg.domain_rand.randomize_kp or cfg.domain_rand.randomize_kd: | |
| base_kp, base_kd = backend.get_actuator_gains() | |
| dr_provider = G1JoystickDomainRandomizationProvider(base_kp=base_kp, base_kd=base_kd) | |
| else: | |
| dr_provider = G1JoystickDomainRandomizationProvider() | |
| self._init_domain_randomization(dr_provider) | |
| dr_provider = self._create_domain_randomization_provider(backend) | |
| self._init_domain_randomization(dr_provider) | |
| def _create_domain_randomization_provider( | |
| self, backend: Any | |
| ) -> G1JoystickDomainRandomizationProvider: | |
| if self._cfg.domain_rand.randomize_kp or self._cfg.domain_rand.randomize_kd: | |
| base_kp, base_kd = backend.get_actuator_gains() | |
| return G1JoystickDomainRandomizationProvider(base_kp=base_kp, base_kd=base_kd) | |
| return G1JoystickDomainRandomizationProvider() |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -14,10 +14,10 @@ | |||||||||||||||||||||||||||||||||||||||||
| from unilab.base.dtype_config import get_global_dtype | ||||||||||||||||||||||||||||||||||||||||||
| from unilab.envs.locomotion.common import rewards | ||||||||||||||||||||||||||||||||||||||||||
| from unilab.envs.locomotion.common.commands import Commands | ||||||||||||||||||||||||||||||||||||||||||
| from unilab.envs.locomotion.common.domain_rand import DomainRandConfig | ||||||||||||||||||||||||||||||||||||||||||
| from unilab.envs.locomotion.common.rewards import RewardContext | ||||||||||||||||||||||||||||||||||||||||||
| from unilab.envs.locomotion.g1.base import G1BaseCfg, G1BaseEnv | ||||||||||||||||||||||||||||||||||||||||||
| from unilab.envs.locomotion.g1.joystick import ( | ||||||||||||||||||||||||||||||||||||||||||
| G1DomainRandConfig, | ||||||||||||||||||||||||||||||||||||||||||
| G1JoystickDomainRandomizationProvider, | ||||||||||||||||||||||||||||||||||||||||||
| G1JoystickPPO, | ||||||||||||||||||||||||||||||||||||||||||
| InitState, | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -55,7 +55,7 @@ class G1JoystickSACCfg(G1BaseCfg): | |||||||||||||||||||||||||||||||||||||||||
| init_state: InitState = field(default_factory=InitState) | ||||||||||||||||||||||||||||||||||||||||||
| commands: Commands = field(default_factory=Commands) | ||||||||||||||||||||||||||||||||||||||||||
| control_config: ControlConfigSAC = field(default_factory=ControlConfigSAC) # type: ignore[assignment] | ||||||||||||||||||||||||||||||||||||||||||
| domain_rand: DomainRandConfig = field(default_factory=DomainRandConfig) | ||||||||||||||||||||||||||||||||||||||||||
| domain_rand: G1DomainRandConfig = field(default_factory=G1DomainRandConfig) | ||||||||||||||||||||||||||||||||||||||||||
| gait_phase_init_mode: str = "offset_phase" | ||||||||||||||||||||||||||||||||||||||||||
| reset_base_qvel_limit: float = 0.5 | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -96,7 +96,12 @@ def __init__(self, cfg: G1JoystickSACCfg, num_envs=1, backend_type="mujoco"): | |||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| self._init_reward_functions() | ||||||||||||||||||||||||||||||||||||||||||
| self._init_domain_randomization(G1JoystickDomainRandomizationProvider()) | ||||||||||||||||||||||||||||||||||||||||||
| if cfg.domain_rand.randomize_kp or cfg.domain_rand.randomize_kd: | ||||||||||||||||||||||||||||||||||||||||||
| base_kp, base_kd = backend.get_actuator_gains() | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
| base_kp, base_kd = backend.get_actuator_gains() | |
| base_kp, base_kd = self._backend.get_actuator_gains() |
Copilot
AI
Apr 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The domain-randomization provider selection logic is duplicated with g1/joystick.py. Consider extracting a shared helper so PPO/SAC variants stay in sync when DR behavior changes (e.g., additional reset terms or different gain-caching rules).
| if cfg.domain_rand.randomize_kp or cfg.domain_rand.randomize_kd: | |
| base_kp, base_kd = backend.get_actuator_gains() | |
| dr_provider = G1JoystickDomainRandomizationProvider(base_kp=base_kp, base_kd=base_kd) | |
| else: | |
| dr_provider = G1JoystickDomainRandomizationProvider() | |
| self._init_domain_randomization(dr_provider) | |
| dr_provider = self._create_domain_randomization_provider(cfg, backend) | |
| self._init_domain_randomization(dr_provider) | |
| def _create_domain_randomization_provider(self, cfg: G1JoystickSACCfg, backend): | |
| """Create the DR provider while staying aligned with PPO/shared logic when available.""" | |
| parent_factory = getattr(super(), "_create_domain_randomization_provider", None) | |
| if callable(parent_factory): | |
| return parent_factory(cfg, backend) | |
| if cfg.domain_rand.randomize_kp or cfg.domain_rand.randomize_kd: | |
| base_kp, base_kd = backend.get_actuator_gains() | |
| return G1JoystickDomainRandomizationProvider(base_kp=base_kp, base_kd=base_kd) | |
| return G1JoystickDomainRandomizationProvider() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mujoco_uni.batch_env
pool.get_field(env_id, name) -> np.ndarray
提供了批量获取DR参数属性的功能,替换成这个接口