dreamzero / docs /TREX_TRACK_FORCE_MODEL.md
zhicao's picture
Upload dreamzero source code (no model checkpoints)
fbd9366 verified
|
Raw
History Blame Contribute Delete
13.1 kB
# T-Rex Track-Force:16-step、20 Hz 动作与 5 Hz 触觉模型
本文描述独立的 `trex_track_force` 模型、数据契约、embedding、注意力、两阶段
flow matching、训练和在线执行。它不会修改原 DreamZero 的模型调用路径。
## 1. 固定时序契约
- 每个 action chunk 含 **16 个动作**,动作频率为 **20 Hz**
- 一个 chunk 覆盖 `16 / 20 = 0.8 s`;动作时间戳跨度为 `0.75 s`
- 触觉频率为 **5 Hz**,因此每 **4 个动作步**刷新一次。
- 一个 chunk 内的触觉刷新 offset 固定为 `[0, 4, 8, 12]`
- 每次刷新只重新去噪尚未执行的 action suffix;已经执行的前缀保持不变。
- `max_chunk_size=4` 保留 DreamZero 原有的 autoregressive block memory。
这里的 `max_chunk_size` 是跨 block 的记忆窗口,不是单个 action chunk 的长度。
- 一个训练 sample 仍由 4 个 autoregressive blocks 组成,因此共有 64 个动作;
只有每 block 的 action 长度从 24 改为 16。
- 三路视频保持 DreamZero 的 33 帧布局:1 帧 clean conditioning observation,
后接每 block 8 帧、共 32 帧 10 Hz future-video targets。
- 每个 block 都有 16 帧过去 track 和 16 步 target track;target 的第 0 步就是
当前 anchor 帧,与 OpenPI 的 track window 契约一致。
## 2. 总体架构
```mermaid
flowchart LR
V["三视角 RGB\nhead + left wrist + right wrist"] --> GRID["三视角拼图\n160 × 320"]
GRID --> VAE["Wan2.2 VAE38\n48-channel latent"]
GRID --> CLIP["Wan CLIP\n图像条件"]
TXT["任务文本"] --> T5["UMT5\n文本条件"]
TRK["250 点 track\npast clean + future noisy"] --> TE["TrackEncoder\n几何 + 可见性 + 身份 embedding"]
ACT["16 × 62D delta-base action\n补零到 64D"] --> AE["Action encoder"]
ST["62D 当前 EEF/hand state"] --> SE["State encoder"]
VAE --> WAN["CausalWanTrackForceModel\nWan2.2-TI2V-5B · 30 blocks"]
CLIP --> WAN
T5 --> WAN
TE --> WAN
AE --> WAN
SE --> WAN
WAN --> COARSE["τ: 1 → 0.4\n6-step coarse action"]
WAN --> TFLOW["未来 track flow"]
WAN --> VFLOW["未来 video flow"]
WAN --> MEM["τ=0.4 coarse memory"]
RAW["10 fingers × 6D force\n16-sample history"] --> VQ["Per-finger force VQ-VAE\n64 codes · 256D"]
FRESH["当前 10 × 6D force"] --> FT["Force-only Transformer\n6 layers · width 768"]
VQ --> FT
MEM --> FT
COARSE --> FT
FT --> FINE["τ: 0.4 → 0\n4-step tactile action suffix"]
```
粗阶段负责从视觉、状态、语言和 track 中得到动作的大尺度结构;独立的
force-only transformer 只使用力信号和粗阶段 memory 完成剩余去噪,使触觉
负责接触后的细粒度修正。
## 3. 62DoF 动作空间
每只手臂占 31 维,两侧共 62 维:
- 手腕/末端位姿:`xyz + rotation-6D`,共 9 维。
- 手部关节目标:22 维。
训练动作不是世界坐标绝对位姿。loader 以 chunk 起始状态为 reference,把每个
目标手腕位姿转换为 **delta-base**
- 平移在 chunk 起始手腕坐标系中表示。
- 旋转为 `R_reference^-1 × R_target`,再编码为 rotation-6D。
- 手部 22 维仍是绝对目标。
模型内部把 62 个物理维补零为 64 维;采样的两个 padding 维在初始化和每个
Euler step 后都会重新置零。输出先按 relative-action 统计量反归一化,再用
chunk 起始 state 恢复绝对 EEF 位姿。
## 4. Track 数据和点身份
250 个点具有固定、可验证的顺序:
- `0:50`:头部视角左手及手臂。
- `50:100`:头部视角右手及手臂。
- `100:125`:左腕视角 5×5 背景点。
- `125:175`:左腕视角手掌点。
- `175:200`:右腕视角 5×5 背景点。
- `200:250`:右腕视角手掌点。
SAM2 只在 episode 第 0 帧根据固定 prompt 产生手/臂 mask;点从 mask 中采样,
随后由 CoTracker 跟踪整段视频。保存字段为:
- `observation.track_xy``[250, 2]`,坐标归一化到 `[0, 1]`
- `observation.track_visibility``[250]`
- metadata 中同时记录 view、hand、role、point index 和各 segment 边界。
## 5. Embedding 设计
### 5.1 Video、语言和图像条件
- 三视角拼入 2×2 canvas 的三个有效格,再统一缩放到 `160×320`
- Wan2.2 VAE38 生成 48-channel latent。
- `patch_size=[1,2,2]`,每 latent frame 形成 50 个 video tokens。
- video token 使用 Wan 原生 3D RoPE,编码时间、高度和宽度。
- UMT5 输出 4096D 文本条件。
- Wan CLIP 输出 1280D 图像条件,并经 `img_emb` 投影。
- CLIP/T5 cross-attention 只作用于 observation/action query,不向 track query
泄漏视觉或语言信息。
### 5.2 Action 和 state
- action:每个 62D 动作补零到 64D,经线性层投影到 Wan hidden dim 3072。
- 一个 block 有 16 个 action tokens;位置由 1D action RoPE 编码。
- state:62D 当前状态补到 64D,经线性层得到一个 state token。
- flow 时间 `τ` 经 sinusoidal embedding 和 MLP 后参与 Wan modulation。
### 5.3 TrackEncoder
每个点分别产生一个 `past token` 和一个 `future token`。时间序列输入特征为:
```text
[x, y, visibility, Δx, Δy]
```
不可见时刻的坐标和 motion 先清零,随后完整的 `16×5` 时序按固定顺序展平,再经
`Linear → SiLU → Linear` 投影。不能在时间维求平均:future flow 的每个时刻含有
独立噪声,平均会丢失“哪一个噪声属于哪一个 timestep”,使 16-step 重建不可解。
最终每个点 token 是下列 embedding 的和,再经 LayerNorm:
```text
trajectory
+ view(head / left_wrist / right_wrist)
+ hand(none / left / right)
+ role(head_hand / wrist_background / wrist_hand)
+ canonical point id(0...249)
+ autoregressive block id
+ temporal role(past / future)
```
左右手不只通过点序号区分,还具有显式 hand embedding。背景点使用
`hand=none` 和独立 role embedding。
与 OpenPI 一致,track target 始终是 `[0,1]` 内的绝对归一化 XY;target window
从当前帧开始,因此第 0 步等于 GT anchor。`TrackDecoder` 对 250 个
future-track hidden token 分别执行 `LayerNorm → Linear(16×2)`,预测 CFM
velocity `noise-clean_xy`。Euler 积分后的结果已经是绝对坐标,不做空间
`cumsum`,也不再额外加 anchor。
### 5.4 Force-only VQ-VAE
输入只包含 force/wrench,不包含 deformation map:
```text
[batch, history=16, fingers=10, wrench=6]
```
- 两只手共享同一套时序卷积 encoder/decoder。
- 每只手内部加入 5 个 finger identity embeddings。
- 输出每根手指一个 256D latent,共 10 个 tactile history tokens。
- EMA codebook 大小为 64,带 commitment loss、perplexity 统计和 dead-code
revival。
- 左右手通过 side embedding 区分,手指位置通过 finger embedding 区分。
- 训练可直接输入 raw 16-step history;推理也可输入预计算的 10 个离散 codes。
- episode 前缀缺失的历史填为归一化中性值,并用 validity mask 从 VQ 重建
loss 中排除 padding 步。
### 5.5 Force-only Transformer
输入 token 包括:
- 16 个 noisy action tokens。
- 10 个当前 force tokens。
- 10 个 VQ history tokens。
- 从 Wan `τ=0.4` hidden state 提取的 coarse memory tokens。
action token 叠加 action position、5Hz force slot、token type、当前 refresh
offset 和 `τ` embedding。force/history token叠加 finger position、token type 和
refresh offset。独立 transformer 为 6 层、12 heads、hidden dim 768。
## 6. 非对称 attention 契约
每个 autoregressive block 的 packed 顺序为:
```text
[obs, action, state, track_past, track_future]
```
其可见性严格为:
- observation query 和 action query 在同一 block 内互相可见。
- observation/action query 可读取同 block 的 state、past track 和 noisy future
track,也可读取有限 AR 窗口内的历史 obs/action/track。
- state query 只读取自己的 state token,避免把多模态信息反向带给 track。
- past-track query 读取历史 block 的 track 和当前 past track,但不能读取当前
noisy future track。
- future-track query 只读取当前及历史 track。
- **任何 track query 都不能读取 observation、action、state、CLIP 或文本。**
- 任何 query 都不能读取未来 block。
因此实现了要求的方向性:`obs/action → track` 表示 obs/action 可以把 track
作为条件;反方向被 mask 禁止。
## 7. 两阶段 flow matching
使用线性插值:
```text
x_τ = τ · noise + (1 - τ) · clean
target flow = noise - clean
x_next = x_τ + (τ_next - τ) · predicted_flow
```
### 粗阶段:Wan,`τ=1 → 0.4`
- 训练时 action/Wan expert 遵循原始 T-Rex,在完整 `(0,1]` 上按
`Beta(1.5,1.0)` 采样;其均值约为 `0.6`,但这不是 split timestep。
- Wan 联合预测 action flow、future-track flow 和 future-video flow。
- 推理按总计 10 个、`Δτ=-0.1` 的 Euler 网格运行前 6 步,到达 `τ=0.4`
- 在精确的 `τ=0.4` 再执行一次 Wan,生成给触觉 transformer 使用的 detached
coarse memory。
### 精阶段:触觉,`τ=0.4 → 0`
- 训练时 `τ_tactile = 0.4 × Beta(1.5,1.0)`,覆盖 `(0,0.4]`
- 每个训练 chunk 对 offset `0/4/8/12` 分别提供对应的 force 与 16-sample
history。
- offset 为 `k` 时,loss 只覆盖 action `k:16`
- 推理运行剩余 4 个 Euler updates;每次 5Hz 刷新只写入未执行 suffix。
- runtime controller 保存之前已经发出的 prefix,确保后续触觉刷新不能改写历史
command。
## 8. 训练 loss
总 loss 由以下部分加权求和:
- `dynamics_loss`:未来视频 latent flow MSE。
- `action_loss`:粗阶段 62 个物理动作维的 flow MSE。
- `track_loss`:可见性 mask 后的绝对 XY CFM velocity MSE。
- `force_loss`:按 refresh offset mask 后的动作 suffix flow MSE。
- `vq_loss`:仅在有效历史步计算的 force history reconstruction loss。
- `commitment_loss`:VQ commitment loss,默认系数 0.25。
训练使用 Wan2.2-TI2V-5B 共享权重和 LoRA。LoRA 注入
`q,k,v,o,k_img,v_img,ffn.0,ffn.2`;新建的 action/state/track/force、video token
投影和 decoder 完整训练。checkpoint 保存 LoRA、新模块参数以及 VQ EMA buffers。
## 9. 在线执行
1. 收集三视角 conditioning observation(在线接口也可编码多帧历史)、当前
62D state、16 帧 past track、当前 force 和 16-sample force history。
2. Wan 对 action、future track 和 future video 运行 6 步,从 `τ=1`
`τ=0.4`
3. offset 0 的触觉运行剩余 4 步,把完整 16-step action 去噪到 `τ=0`
4. 以 20 Hz 执行动作。
5. 执行 4、8、12 步后,各接收一次新的 5Hz 触觉;每次从缓存的 coarse state
重新去噪剩余 suffix,同时保留已执行 prefix。
6. 输出 delta-base 动作反归一化并恢复为绝对手腕 `xyz + rotation-6D`
## 10. 入口
训练:
```bash
cd /scratch1/home/zhicao/dreamzero
bash scripts/train/trex_track_force_training_wan22.sh
```
训练脚本直接使用已经构建完成的 `data/trex_small`,启动前只执行 schema
完整性校验,不会再次运行 SAM2/CoTracker。
日志、checkpoint 与原 T-Rex 训练共用同一套 Trainer 回调。每隔
`wandb_video_reconstruction_steps` 个 global step 会分别保存两个监控视频:
不带标记的原始重建视频位于
`OUTPUT_DIR/eval_videos/train_step_XXXXXX.mp4`,20Hz 预测 track motion
叠加到 10Hz head/left-wrist/right-wrist 三面板后的版本位于
`OUTPUT_DIR/eval_track_videos/train_step_XXXXXX.mp4`。原始预测与目标
track 同时写入 `OUTPUT_DIR/eval_tracks/train_step_XXXXXX.npz`。两个视频
分别记录到 W&B `eval/predicted_video``eval/predicted_track_video`
`dynamics/action/track/force/VQ/commitment` loss 和触觉 codebook
perplexity/active-code 指标同时写入 W&B 与 `OUTPUT_DIR/loss_log.jsonl`
两个视频的首帧都是精确 GT conditioning frame;track target/prediction 的第 0
步在模型链路内就是当前 GT anchor。可视化不再对整段预测做事后平移。
训练可视化默认使用
`wandb_video_reconstruction_inference_steps=1`:先运行一次 coarse WAN
Euler update,再在 `tau=0.4` 用一次边界预测恢复 clean video/track;不会直接
解码半噪声状态,并跳过不会改变这两个输出的 action-only 触觉精修。
这不会改变训练或正式在线推理的 6+4 两阶段 schedule;如需更高质量的监控视频,
可以单独提高该值,但耗时近似按 inference steps 线性增长。
离线 NPZ 推理:
```bash
python scripts/eval/trex_track_force_inference.py \
--checkpoint checkpoints/trex_track_force_wan22_lora/checkpoint-8000 \
--dataset-root data/trex_small \
--input sample_input.npz \
--output prediction.npz
```
核心实现位于 `groot/vla/model/trex_track_force/`;独立 Hydra 配置为
`model=trex_track_force/vla``data=dreamzero/trex_track_force_wan22`