File size: 7,196 Bytes
77d4e50
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
---
license: apache-2.0
library_name: jax
pipeline_tag: video-to-video
tags:
  - robotics
  - world-model
  - dreamer
  - dreamer-v4
  - yam
  - lerobot
  - jax
  - flax
datasets:
  - Dimios45/yam-pick-duster-200
  - Dimios45/yam-pick-duster
  - Dimios45/yam_towel_fold
  - Dimios45/molmo_eval_90cmtopcam
  - Dimios45/molmo_eval_hitl
---

# YAM World Model β€” a Dreamer-4 style action-conditioned video world model

An action-conditioned latent video world model for the **YAM** robot arm
(single-arm 7-DoF and bimanual 14-DoF), trained from scratch on a single
**RTX 4090**. Given a few frames of context and a stream of joint commands, it
imagines the future.

Built on the [visionary](https://github.com/james0248/visionary) Dreamer-4
implementation, retargeted from SO-101 to YAM.

<p align="center">
  <img src="assets/yam_action_control.webp" alt="The same clip imagined under true, shuffled, and zero actions" width="800">
</p>

## The model actually obeys its actions

This is the property that matters and the one that is easy to fake. A world model
trained on a single repetitive scene learns to predict the future from pixels
alone and quietly ignores the action input. The control is to re-run the same
clip, same seed, and change **only** the actions:

| `--action_source` | PSNR | SSIM | rollout motion | vs ground truth |
| --- | --- | --- | --- | --- |
| `true` | **15.03** | **0.6675** | 3.684 | 1.6x |
| `shuffled` (another episode's actions) | 14.55 | 0.6484 | 5.311 | **2.3x** |
| `zero` | 17.85 | 0.7459 | 1.264 | 0.55x |
| ground truth | β€” | β€” | 2.295 | 1.0x |

* `true` beats `shuffled` on both metrics.
* Wrong actions produce **wrong motion** β€” the arm thrashes at 2.3x ground truth.
* Zero actions nearly **freeze** the arm, which is correct for "no commanded
  movement".

> ⚠️ **`zero` scores highest on PSNR, and that is a metric artifact.** On a mostly
> static scene a near-frozen prediction beats sharp-but-slightly-misaligned motion,
> because pixel metrics reward blur. PSNR alone would have scored this model as
> ignoring its actions. Pair the action control with a motion statistic.

## Reconstruction

<p align="center">
  <img src="assets/yam_reconstruction.webp" alt="Tokenizer reconstruction against ground truth" width="800">
</p>

| stage | metric | value |
| --- | --- | --- |
| tokenizer | held-out PSNR | **25.6 dB** |
| tokenizer | mse / lpips | 0.0009 / 0.0166 |
| tokenizer | dead latent channels | **0 / 16** |
| dynamics | rollout PSNR / SSIM | **24.82 / 0.913** |
| dynamics | `flow_mse` (baseline 1.0) | 0.0048 |

Rollout quality saturates at the tokenizer's own reconstruction ceiling β€” past
that, the dynamics model is limited by what the tokenizer can decode.

## Architecture

Two stages, both JAX/Flax, following Dreamer 4.

**Video tokenizer** β€” masked-autoencoder spatiotemporal transformer. 144Γ—192
frames, patch 16, β†’ 96 latent tokens Γ— 16 channels per frame, `tanh` bottleneck.
Video only; never sees actions. 8 encoder + 8 decoder layers, dim 512. MSE + LPIPS,
Muon optimizer, 12,000 steps.

**Dynamics** β€” shortcut/flow-matching transformer over the frozen latents,
conditioned on a continuous action vector through a **per-embodiment** MLP
projection plus an embodiment embedding. 8 layers, dim 768, 24-frame context
(4.8 s at 5 Hz). 6,000 steps, EMA 0.999 (exports carry EMA weights).

Two embodiments share one video backbone: `yam_absolute_joint_7d_v1` (7-D) and
`bi_yam_absolute_joint_14d_v1` (14-D), `max_action_dim: 14`.

## Usage

```python
from predict import WorldModel   # scripts/robot/predict.py

wm = WorldModel(
    tokenizer_dir="yam_tokenizer",
    dynamics_dir="yam_dynamics",
    latent_stats="latent_stats.json",
    dynamics_step=6000,
)
# context_frames: (T0, H, W, 3) uint8   actions: (T0+horizon, 7) normalized to [-1,1]
frames = wm.imagine(context_frames, actions, embodiment_id=0)
```

**5.4 fps warm** on an RTX 4090 (first call ~28 s of JIT compile; 4.4 s per
24-frame rollout thereafter). Trained at 5 Hz, so it imagines at roughly the rate
the data was recorded β€” enough for single-trajectory lookahead in a 5 Hz control
loop, not for dense multi-rollout MPC.

## Training data

| Corpus | Robot | Action | Episodes | Frames |
| --- | --- | --- | --- | --- |
| `yam-pick-duster` + `-200` | YAM | 7-D joint, **radians** | 250 | ~104k |
| `yam_towel_fold` | bi-YAM | 14-D joint, radians | 11 | 9,468 |
| `molmo_eval_90cmtopcam` + `_hitl` | molmoact dual-arm | 14-D joint, radians | 5 | 13,003 |

266 packed records / **118,982 frames**, 0 invalid. All are LeRobot **v3.0**. Only
verified-fixed cameras are kept; wrist cameras are dropped. Episodes are trimmed to
their non-idle span, actions normalized q01–q99 β†’ [-1,1], video packed at 240Γ—320
and decoded to 144Γ—192 at 5 Hz.

## Limitations

* **Not a policy.** This is a world model β€” it imagines, it does not act. No
  planning or RL is included.
* **Never drives a real arm.** Model output has not been executed on hardware.
  Check your robot's calibration convention before attempting it.
* **Single fixed viewpoint per episode**, and the scene diversity is modest
  (duster picking, towel folding, two eval sets). Expect degradation on unseen
  scenes, lighting, or camera placements.
* **Bimanual is the weaker embodiment** β€” 16 episodes against 250 for the
  single-arm side.
* **Rollouts drift over long horizons**, as flow-matching video models do. Results
  here are for a 32-frame (6.4 s) horizon from 4 context frames.

### A negative result worth publishing

Adding 39 episodes from [`rerun/abc-130k`](https://huggingface.co/buckets/rerun/abc-130k)
(a Rerun conversion of the gated [`XDOF/ABC-130k`](https://huggingface.co/datasets/XDOF/ABC-130k))
more than doubled the corpus to 260,731 frames and turned the starved bimanual
embodiment into the larger one β€” and **made the model worse**. The single-arm side
*lost* its action-conditioning (`shuffled` began outscoring `true`) and the
bimanual side never gained any.

<p align="center">
  <img src="assets/abc_domain_gap.webp" alt="Combined tokenizer: YAM 25.6 dB versus ABC-130k 19.0 dB" width="800">
</p>

Two unseparated causes: the two bimanual robots were merged into one embodiment
(same action *convention*, different **kinematics** β€” identical joint angles
produce different pixel motion on a different robot), and the combined tokenizer
reconstructs ABC at only 19.0 dB against YAM's 25.6, a gap that *widened* with
training as ABC's dense two-arm scenes saturated the 96-latent bottleneck.

**The weights published here are the single-corpus YAM model, which passes the
action control.**

## Reproducing

Full recipe, per-stage commands, and the failure modes encountered are documented
in [`docs/yam_world_model.md`](https://github.com/Dimios45/visionary/blob/main/docs/yam_world_model.md).

## Acknowledgements

* [Dreamer 4](https://danijar.com/project/dreamer4) β€” the architecture.
* [visionary](https://github.com/james0248/visionary) β€” the implementation this builds on.
* [Dimios45](https://huggingface.co/Dimios45) β€” the YAM datasets.
* [Rerun](https://huggingface.co/rerun) and the ABC-130k authors β€” the bimanual corpus.