Guard GR00T relative action stepwise decode

This commit is contained in:
Andy Wrenn
2026-06-23 09:18:33 -07:00
parent 2ed55d2a77
commit 6126a85d60
3 changed files with 71 additions and 0 deletions
@@ -468,6 +468,14 @@ class GrootPolicy(PreTrainedPolicy):
@torch.no_grad()
def select_action(self, batch: dict[str, Tensor]) -> Tensor:
"""Select single action from action queue."""
if getattr(self.config, "use_relative_actions", False):
raise NotImplementedError(
"GrootPolicy.select_action does not support relative-action policies because cached "
"relative chunk actions can be decoded against newer observation states. Use "
"predict_action_chunk and postprocess the full chunk before queuing actions, or use "
"the RTC/chunked rollout inference path."
)
self.eval()
if len(self._action_queue) == 0:
@@ -2158,6 +2158,12 @@ class GrootN17ActionDecodeStep(ProcessorStep):
return transition
action_np = action.detach().cpu().float().numpy()
if self.use_relative_action and action_np.ndim != 3:
raise NotImplementedError(
"GrootN17ActionDecodeStep cannot decode native relative actions one step at a time. "
"Decode the full action chunk returned by predict_action_chunk while the matching "
"GrootN17PackInputsStep state is still cached, then queue the decoded absolute actions."
)
# The sync action queue postprocesses popped actions as (B, D); decode
# them as single-step (B, 1, D) chunks and squeeze the horizon back at
# the end so both ranks share the chunk decode logic below.
+57
View File
@@ -1324,6 +1324,55 @@ def test_groot_n1_7_action_decode_truncates_to_valid_horizon_for_relative_stats(
torch.testing.assert_close(decoded[..., 5], torch.full((1, 16), 5.0))
def test_groot_n1_7_action_decode_rejects_stepwise_native_relative_actions():
raw_stats = {
"state": {
"single_arm": _stats([0.0] * 5),
"gripper": _stats([0.0]),
},
"action": {
"single_arm": _stats([0.0] * 5),
"gripper": _stats([0.0]),
},
"relative_action": {
"single_arm": _stats([0.0] * 5),
},
}
modality_config = {
"state": {
"modality_keys": ["single_arm", "gripper"],
},
"action": {
"modality_keys": ["single_arm", "gripper"],
"action_configs": [
{"rep": "RELATIVE", "type": "NON_EEF", "format": "DEFAULT", "state_key": None},
{"rep": "ABSOLUTE", "type": "NON_EEF", "format": "DEFAULT", "state_key": None},
],
},
}
pack_step = GrootN17PackInputsStep(
raw_stats=raw_stats,
modality_config=modality_config,
normalize_min_max=False,
)
pack_step(
{
TransitionKey.OBSERVATION: {OBS_STATE: torch.zeros(1, 6)},
TransitionKey.COMPLEMENTARY_DATA: {},
}
)
decode_step = GrootN17ActionDecodeStep(
env_action_dim=6,
raw_stats=raw_stats,
modality_config=modality_config,
use_relative_action=True,
pack_step=pack_step,
)
with pytest.raises(NotImplementedError, match="cannot decode native relative actions one step at a time"):
decode_step({TransitionKey.ACTION: torch.zeros(1, 6)})
def test_groot_n1_7_action_decode_requires_gripper_key_for_libero_transform():
step = GrootN17ActionDecodeStep(
env_action_dim=1,
@@ -2324,6 +2373,14 @@ def test_groot_n1_7_libero_execution_horizon_uses_core_eight_action_cadence(tmp_
assert infer_groot_n1_7_action_execution_horizon(model_path, "libero_sim") == 8
def test_groot_select_action_rejects_relative_action_policies():
policy = object.__new__(GrootPolicy)
object.__setattr__(policy, "config", SimpleNamespace(use_relative_actions=True))
with pytest.raises(NotImplementedError, match="select_action does not support relative-action policies"):
policy.select_action({})
def test_groot_n1_7_select_action_uses_checkpoint_valid_horizon(tmp_path, monkeypatch):
from lerobot.policies.groot.groot_n1_7 import GR00TN17