use_mean_std is plumbed and serialised but never consumed
Summary
use_mean_std has a complete producer chain — model config, both setup.py construction paths, the processor constructor, save_pretrained, and the from_pretrained override list — and no consumer anywhere in gr00t/. Setting it has no effect. Its docstring says "Use mean/std normalization instead of min/max", so the name promises a normalization mode that never engages.
Meanwhile the mechanism that does implement mean/std normalization, ModalityConfig.mean_std_embedding_keys, is not populated by any embodiment in gr00t/configs/data/embodiment_configs.py. So mean/std normalization is currently unreachable through either route.
Evidence
Every occurrence on main (b9955401) is a write; there is no read:
gr00t/configs/model/gr00t_n1d7.py:120 declaration
gr00t/model/gr00t_n1d7/setup.py:182,211 plumbed from model config
gr00t/model/gr00t_n1d7/processing_gr00t_n1d7.py:245 constructor parameter
gr00t/model/gr00t_n1d7/processing_gr00t_n1d7.py:262 stored on self
gr00t/model/gr00t_n1d7/processing_gr00t_n1d7.py:784 serialised by save_pretrained
gr00t/model/gr00t_n1d7/processing_gr00t_n1d7.py:874 listed in from_pretrained overridesThe omission is visible in one place. Gr00tN1d7Processor.__init__ forwards its sibling normalization flags to StateActionProcessor and then stores them; use_mean_std is only stored:
self.state_action_processor = StateActionProcessor(
modality_configs=modality_configs,
statistics=statistics,
use_percentiles=use_percentiles,
clip_outliers=clip_outliers,
apply_sincos_state_encoding=apply_sincos_state_encoding,
use_relative_action=use_relative_action,
)
# Save state action processor settings
self.use_percentiles = use_percentiles
self.use_mean_std = use_mean_std # <- forwarded nowhereStateActionProcessor.__init__ has no use_mean_std parameter at all, so wiring it is not a one-line forward.
git log -S "use_mean_std" shows the flag arrived in 23ace64f (N1.7 Release) and never had a reader — it is dead from birth, not a regression.
For contrast, use_percentiles two lines up is forwarded and consumed at gr00t/data/state_action/state_action_processor.py:157.
Reproduce
rg -n "use_mean_std" gr00t/ # writes only, no reads
rg -n "use_mean_std" gr00t/data/ # empty: never reaches the normalizer
rg -n "mean_std_embedding_keys" gr00t/configs/data/embodiment_configs.py # emptyWhy this matters
use_mean_std is serialised into every processor_config.json, so checkpoints record a normalization mode that was never applied. Anyone reading a checkpoint config to reconstruct preprocessing — including the deployment and ONNX paths — will see a flag that does not describe the data.
Resolution needs a decision from the team
Two options, and I do not have the context to pick:
- Wire it. Add the parameter to
StateActionProcessorand make the global flag imply mean/std for all keys, presumably as a default thatmean_std_embedding_keyscan refine. This changes normalization math for anyone who has been setting the flag expecting it to work. - Delete it. If
mean_std_embedding_keysis the intended and only interface, drop the flag from the model config, the processor, andsave_pretrained. This is a config-schema change affecting savedprocessor_config.jsonfiles, so it needs a read path that tolerates the key's absence and presence.
Happy to send a PR for whichever direction you prefer.
Note
Found while working on #744, which fixes a different failure in the same area (kwargs dropped in transit between setup.py and from_pretrained). The two are independent: use_mean_std was already in the old allowlist, so #744's diff has no effect on its behaviour.
Source: NVIDIA/Isaac-GR00T