fix(g1): let a whole-body controller replace the proprio state features

observation_features merged the 29 raw joint positions with the controller's
own proprio features, so with SONIC the G1 advertised a 93-D state (29 + 64)
while sonic_walk was trained on the 64-D token echo alone. Async inference hit
"size of tensor a (93) must match the size of tensor b (64)"; rollout only
escaped it because its .pos/.vel filter silently drops the .q joint keys.

Substitute instead of merge, matching how action_features already hands the
action space to the controller. Controllers that advertise no observation_ft
(GR00T, Holosoma, none) keep the 29 joint keys unchanged.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Martino Russi
2026-08-07 15:15:45 +02:00
parent b4ffe89c3d
commit c9cc572621
+6 -3
View File
@@ -231,9 +231,12 @@ class UnitreeG1(Robot):
@cached_property
def observation_features(self) -> dict[str, type | tuple]:
# Controllers may contribute their own proprio features (e.g. SONIC's token state).
controller_ft = getattr(self.controller, "observation_ft", {})
return {**self._motors_ft, **controller_ft, **self._cameras_ft}
# A controller advertising its own proprio state (SONIC's 64-D token echo) replaces the
# raw joint positions rather than extending them, the way action_features hands the
# action space over to the controller.
controller_ft = getattr(self.controller, "observation_ft", None)
proprio_ft = self._motors_ft if controller_ft is None else dict(controller_ft)
return {**proprio_ft, **self._cameras_ft}
@cached_property
def action_features(self) -> dict[str, type]: