Skip to content

feat(rlt): sync with main and align online RL with the RLT paper - #26

Merged
Vertax42 merged 23 commits into
feature/rlt-jaxfrom
feat/rlt-paper-alignment
Nov 12, 2026
Merged

Vertax42 merged 23 commits into
feature/rlt-jaxfrom
feat/rlt-paper-alignment

Conversation

@Vertax42

Copy link
Copy Markdown
Collaborator

把 feature/rlt-jax 同步到最新 main,并按 RLT 原论文(rlt.pdf)对齐在线 RL 的几处实现。

建议按 commit 逐个 review:第一个是合并 main,diff 里大部分来自 main(PR #24 等),真正的 RLT 改动集中在后面 5 个 commit。

Commits

  1. 0e1121f 合并 main
    • 冲突:cylinder_stack_1009 和 strap_tape_1010 的 VLA 配置采用 main 的版本,strap_tape 用 Xense/strap-tape-taccap-1015。
    • PR feat: add Elite/Rebot robot support, extend XTac-UMI training, and clean up JAX attention #24 删除 cuDNN attention 后要跟着改的地方:rlt/vla.py 删掉对 use_cudnn_attention 的引用(否则加载 VLA 时报 AttributeError);insert_ethernet_0928 配置删掉 cuDNN 字段,以及启动说明里会把 FSDP 梯度算错的 XLA 参数。
  2. 5468a47 采集和训练同时进行(论文 pp. 5-6)
    • 新增 BackgroundTrainer 线程,机器人一边采集一边训练,每个动作块都用最新权重。以前每轮结束后机器人要停下来等训练。
    • 权重、经验回放、随机数和计数器由一把锁保护;梯度计算在锁外进行,取 actor 输出和提交数据不会被训练卡住。
    • 每轮数据仍在轮末一次性提交,UTD 规则不变。后台训练出错时,下一个动作块会直接报错停下。
    • 开关是 rl.async_training,默认 true,resume 时可以改。
  3. 3eba8fe 动作块 C 改为 10(论文附录 B):默认值和 3 个任务配置都改了,30Hz 下一个动作块从 0.67s 缩短到 0.33s。
  4. 2739370 经验回放容量从 6000 改为 10 万条(约 1.85 小时的标注数据)
    • 6000 条只够存约 7 分钟,warmup 和早期的人工干预数据很快就被覆盖了。论文把整次运行的数据都留在同一个经验回放里。
    • 实测每条 25.6 KB,存满约占 2.6 GB 内存,每个 RL checkpoint 也会大这么多。
    • 新增统计:开始覆盖旧数据时日志会提示,W&B 记录 round/replay_evicted。
  5. 44f637e RLT 测试统一用完整 float32 精度做矩阵乘法
    • 修复几个等价性测试时过时不过的问题,test_remat_matches 之前每次都失败。
    • 精度是全局设置的,因为 jax.default_matmul_precision 上下文只对当前线程生效,管不到后台训练线程。
  6. 5d6eca9 RL token 的输入去掉任务指令 token(论文 §IV-A 脚注 1)
    • 只去掉 Task: ..., 部分,保留 pi05 prompt 里离散化的 State: ...。
    • 生成 prefix 缓存和在线提取特征两处都应用,开关是 model.drop_task_tokens,默认 true。

⚠️ 兼容性

  • C 从 20 改为 10:actor 和 critic 输出层的大小随之改变,用 C=20 训练的 RL checkpoint 不能在新配置下 resume,加载时契约校验会拒绝。
  • drop_task_tokens 默认开启:
    • 已有的 prefix 缓存会被 check_identity 拒绝。需要用 precompute_prefix.py --overwrite 重新生成,再重训 token;或者设 model.drop_task_tokens: false 继续用旧缓存。
    • 旧的 token checkpoint 在在线阶段仍然可用,会按完整 prompt 处理。

没做的

  • VLA 和 RL token 一起微调:论文中 token 的重建损失对 VLA 做了 stop gradient("VLA considered frozen with respect to L_ro"),两边梯度互不影响,所以和我们先微调 VLA、再在冻结的 VLA 上训 token 等价。
  • 本体状态加速度:我们的观测里没有速度。
  • 部署时自动切换到 RL 策略:暂缓。

测试

  • RLT 全部测试(src/openpi/rlt、rlt_policy_test、rlt_mode_test)连续两次跑出 82 passed, 9 skipped,test_remat_matches 也包含在内。新增测试:
    • learner_test.py:后台训练与同步训练结果完全一致;训练进行时仍能取 actor 输出、提交数据;中途打断再 resume 不丢待训练的更新;后台出错能报出来;经验回放的覆盖统计。
    • collector_test.py:后台训练运行时的多轮采集集成测试。
    • prompt_tokens_test.py:用真实 PaliGemma tokenizer 验证,只丢掉指令、保留 State,包括指令本身含 "State" 的情况。
    • features_test.py:在线特征和缓存路径在开/关两种设置下一致。
  • 还没在真机上验证。第一次上机时请留意 chunk/feature_ms(VLA 推理和后台训练抢同一块 GPU)和经验回放占用的内存。

🤖 Generated with Claude Code

Hubo1231 and others added 23 commits October 11, 2026 16:10
…orm stats

Include Elite CS66 runtime, bench recipes, policy adapters, training config registration and tests, plus Elite egg-carton and Flexiv strap-tape training configs.

Revert UMI-specific normalization changes and import training loaders only for Torch/RLDS paths so parquet statistics work with the local lerobot-xense implementation.
Handle lerobot-xense builds without dataset_reader and filter video timestamps on the dataset for their legacy getitem path. Preserve reader filtering for implementations that delegate reads to DatasetReader.

Exercise both installed API variants, including actual frame reads with default, selected, and empty video whitelists.
Retire unsafe XLA collective launch flags and document the findings. Include the whiteboard inspection config, remove the obsolete box assembly config, and preserve current prompt formatting updates.
…ot resolution

- compute_norm_stats parquet path now applies XtacUmiStateActions and
  XtacUmiRelativeActions, so drop_head_pose / use_relative_pose_actions
  configs get 2D gripper-state and relative-action stats instead of 29D
  absolute-pose stats that Normalize silently truncated.
- Add config.resolve_dataset_root (DataConfig.dataset_root first,
  OPENPI_LEROBOT_DATASET_ROOT fallback), used by both training and
  compute_norm_stats so they read the same dataset. --dataset-root stays a
  one-off override and warns when it differs from the training root.
- Add scripts/compute_norm_stats_test.py covering both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ttention

feat: add Elite/Rebot robot support, extend XTac-UMI training, and clean up JAX attention
The lockfile was removed from the repo in 6061181, but `uv run` keeps
regenerating it in the working tree.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bring in PR #24 (cuDNN attention removal, retired XLA flags, explicit LR
schedules, Elite/Rebot/XTac-UMI work) and the follow-ups since.

Conflicts: cylinder_stack_1009 and strap_tape_1010 VLA configs take main's
version (strap_tape trains on Xense/strap-tape-taccap-1015). Fix the
cylinder_stack launch comment that named the earbuds config.

Fallout from the cuDNN removal:
- rlt/vla.py: drop the use_cudnn_attention override; the field is gone and
  FrozenVLA.load_model would raise AttributeError.
- insert_ethernet_0928 VLA config: drop use_cudnn_attention /
  cudnn_attention_dtype (config load would fail) and the retired XLA
  collective flags from the launch comment (they corrupted FSDP gradients).
- strap_tape RLT config: dataset comment now says 1015.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The learner spent each round's earned updates between rounds while the robot
idled, and the actor never improved within a round. Add a BackgroundTrainer
thread that spends the earned budget while the robot collects, so every chunk
acts with the latest weights (RLT paper, "Update", pp. 5-6).

- Learner: one RLock guards weights, optimizer states, replay, the RNG and
  the update counters. train_step() does one earned update with the gradient
  steps outside the lock, so act/commit never wait behind them; train() is
  now a loop over it. Snapshots and checkpoints take the lock and never see
  half an update.
- BackgroundTrainer: idles until commit() earns budget; a failure is
  re-raised by the next act()/mean() and by drain(), so collection does not
  continue on stale weights.
- Rows still reach replay once per round, so a discard never has to be
  undone; UTD accounting is unchanged.
- train_rl.py: rl.async_training (default true; operational, may change on
  resume). A finished run spends the remaining backlog before the final
  save; an interrupted one stops at once and resume picks the backlog up
  from the counters.

Tests: background training reproduces synchronous training exactly, acting
and committing while training, interrupt + resume keeps the backlog, trainer
failures re-raise, and a multi-round collector loop with the trainer running.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The RLT paper uses C=10 (App. B, a 0.2 s chunk at 50 Hz); TacXense's C=20 is
0.67 s at our 30 Hz and is not covered by the paper's ablation (C=1 vs 10).
C=10 makes the actor react twice as often and halves the TD bootstrap
horizon; replay_stride 2 still divides it.

Applies to the default and to the cylinder_stack, insert_ethernet and
strap_tape RLT configs. The actor and critic head widths depend on C, so RL
checkpoints trained at C=20 cannot be resumed under these configs (the
contract check refuses them); phase-one token checkpoints are unaffected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The RLT paper's replay pools the warm-up rollouts, RL data and teleoperated
interventions of the whole run (15 min to 5 h of robot data, p. 3 and
App. B). Our 6000-row FIFO held ~7 min of labeled data at 30 Hz with
replay_stride 2 (15 transitions per second), so the warm-up and the early
interventions were overwritten within a few rounds.

- Default rl.buffer_size 100k (~1.85 h of labeled data); also in the
  cylinder_stack, insert_ethernet and strap_tape configs. A row is 25.6 KB
  (measured, z_rl 2048, reference 50x20), so a full buffer costs ~2.6 GB of
  RAM and of each RL checkpoint. The arrays are allocated lazily.
- ReplayBuffer counts rows ever added; eviction is logged once when it
  starts, reported in the round summary, and logged as round/replay_evicted.
  Older checkpoints without the counter still load.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The equivalence tests (remat, batch independence, trailing-padding trim,
cache vs online features) assert ~1e-4, but on GPUs whose default float32
matmul uses TF32/bf16 passes the kernel choice varies with batch shape and
results differ by a few 1e-4. They failed intermittently, and
test_remat_matches failed every run on an RTX PRO 6000.

Set jax_default_matmul_precision=highest for the rlt tests. It is set
globally rather than with the jax.default_matmul_precision context, which is
thread-local and would miss the BackgroundTrainer thread.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The RLT paper drops the language embeddings before training the RL token,
since each task has a fixed instruction (Sec. IV-A, footnote 1). pi05 with
discrete_state_input renders "Task: {instruction}, State: {state};\nAction: ",
so the prompt slots also carry the discretized robot state: masking only the
instruction keeps that state information.

- rlt/prompt_tokens.py: mask the prompt slots before the last "▁State" token.
  The state section (digits, spaces, ";", "Action:") never tokenizes to
  "▁State", so the last occurrence is the template's even if the instruction
  says "State". Without a state in the prompt (pi0) or if it was truncated
  away, the whole prompt is dropped.
- RLTModelConfig.drop_task_tokens (default true). Applied when the prefix
  cache is built and when online features are extracted, so phase one and
  phase two see the same prefix.
- The cache identity and the token checkpoint metadata record the setting.
  Existing caches and token checkpoints (without the key) count as kept, so
  a cache built before this change is refused under the new default instead
  of silently mixing the two, and old token checkpoints keep working online
  with the full prompt.

Migration: rebuild prefix caches (precompute_prefix.py --overwrite) and
retrain the token, or set model.drop_task_tokens: false to keep using them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Vertax42
Vertax42 merged commit de6a235 into feature/rlt-jax Nov 12, 2026
@Vertax42
Vertax42 deleted the feat/rlt-paper-alignment branch November 12, 2026 05:20
AtticusZeller pushed a commit to AtticusZeller/xense-openpi that referenced this pull request Nov 13, 2026
The pull_request filter "*" does not match "/", so PRs into branches like
feature/rlt-jax (e.g. XenseRobotics-AI#26) got no checks at all. "**" matches every branch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

2 participants