File size: 9,433 Bytes
a6aac55
 
42b547e
a6aac55
 
 
 
 
 
 
 
 
 
 
 
 
42b547e
 
 
 
 
a6aac55
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42b547e
a6aac55
 
 
42b547e
a6aac55
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42b547e
a6aac55
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42b547e
a6aac55
 
 
42b547e
 
a6aac55
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42b547e
a6aac55
 
 
 
 
 
 
 
 
 
 
42b547e
a6aac55
 
 
 
42b547e
a6aac55
 
 
 
 
 
 
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
175
176
177
178
179
# rSkill manifest β€” OpenRAL packaging format V1 (CLAUDE.md Β§6.4)
#
# Wraps: OpenRAL/rskill-molmoact2-multi-so101-nf4 (NF4-quantized from
#        allenai/MolmoAct2-SO100_101 via tools/quantize_rskill.py)
# Base:  allenai/MolmoAct2 (Ai2 action reasoning model, Molmo2-ER VLM +
#        flow-matching action expert) β€” arXiv:2605.02881
#
# Why NF4: bf16 MolmoAct2 (~5.5 B params, ~11 GiB) OOMs an 8 GiB consumer
# GPU. NF4 quantization of every Linear with >=4M weight elements brings
# the working set to ~3.5 GiB, comfortably under the 8 GiB ceiling while
# preserving paper-faithful behaviour (Molmo2-ER backbone + DiT-style
# action expert). The OpenRAL molmoact2 adapter detects the upstream
# repo's quantization_metadata.json sentinel and loads the pre-quantized
# weights via load_prequantized_state_for_rskill (no on-the-fly
# quantization cost at startup).
#
# Provenance (lerobot 0.6.0): the model graph is built from lerobot's in-tree
# MolmoAct2ForConditionalGeneration class (this NF4 pack loads into it
# key-for-key) β€” NOT via a trust_remote_code AutoModel. source_repo below
# supplies only the config + processor + norm_stats.json.
#
# norm_tag: this checkpoint requires norm_tag="so100_so101_molmoact2" (the
# adapter's bare default is "libero", which this checkpoint rejects). It is
# declared below under image_preprocessing.norm_tag and propagated to the
# adapter by resolve_image_preprocessing, so SimEnvironment configs need NOT
# set it β€” a SimEnvironment YAML may still override it via vla.extra.norm_tag.
#
# UNITS β€” degrees: this checkpoint was trained on LeRobot SO-100/101 teleop
# recorded in joint DEGREES (norm_stats.json spans Β±270, control_mode
# "absolute joint pose"). MuJoCo scenes are radian-native, so a sim config
# driving this rSkill MUST select the degree convention, e.g. the so101_box
# scene's `scene.backend_options.joint_units: degrees`. The env then converts
# its proprio state (rad→deg) and the returned action (deg→rad) at the policy
# boundary. NOTE: the LeRobot SO-101 servo-degree calibration does not share a
# zero with the MuJoCo `so101_new_calib` URDF (e.g. trained shoulder_lift sits
# ~45–186Β°, the MJCF joint maxes at 100Β°), so a units conversion alone aligns
# magnitudes but not the per-joint zero reference; full task fidelity needs the
# dataset's calibration offsets. See scenes/sim/so101_tube_insertion.yaml.
#
# LICENSE (CLAUDE.md Β§7.4): Apache-2.0 (code + weights). MolmoAct2 is a
# fully open release from Ai2 β€” weights, training code, and data are all
# Apache-2.0. Commercial use is permitted.

# ── Identity ───────────────────────────────────────────────────────────────
schema_version: "0.1"
name: "OpenRAL/rskill-molmoact2-multi-so101-nf4"
version: "0.1.0"
license: "apache-2.0"
role: "s1"
kind: "vla"  # rSkill kind discriminator. "vla" = learnable Vision-Language-Action policy.

# ── Policy identity ────────────────────────────────────────────────────────
model_family: "molmoact2"

# ── Compatibility contract ─────────────────────────────────────────────────
# MolmoAct2-SO100_101 was finetuned on a mixture of SO-100 and SO-101
# teleop data with identical 6-DoF kinematics (Feetech STS3215 servo chain).
# Both embodiment tags are claimed because the training distribution covers
# both and the 6-DoF absolute joint-position contract is identical.
embodiment_tags:
  - "so100_follower"
  - "so101_follower"

# MolmoAct2-SO100_101 consumes two RGB camera streams. The training data
# used overhead ("top") and side-mounted ("side") Realsense views; the
# aliases below map from the canonical SO101 scene camera names to the
# model's expected feature keys.
sensors_required:
  - modality: "rgb"
    vla_feature_key: "observation.images.camera1"
    min_width: 224
    min_height: 224
  - modality: "rgb"
    vla_feature_key: "observation.images.camera2"
    min_width: 224
    min_height: 224

# Output side. For the canonical so101_follower embodiment the
# loader auto-fills n_dof + vla_action_key from robots/so101_follower/robot.yaml.
# The checkpoint emits absolute joint-position targets.
actuators_required:
  - kind: "joint_position"
    control_mode_semantics:
      mode: "absolute"

# ── Runtime / weights ──────────────────────────────────────────────────────
runtime: "pytorch"
quantization:
  # ``int4`` is the OpenRAL ``QuantizationDtype`` enum value that matches
  # bitsandbytes NF4. The exact bnb scheme (nf4 + compute_dtype bf16, applied
  # to every Linear with >=4M weight elements) is recorded in the upstream
  # ``quantization_metadata.json`` sentinel that the molmoact2 adapter probes
  # via ``detect_prequantized_nf4`` and loads via ``install_prequantized_linears``.
  dtype: "int4"
  backend: "pytorch"
# Informational VRAM ceilings, used by `openral rskill check` / `openral doctor`.
# The NF4 packed weights are ~3.5 GiB and fit comfortably under the 8 GiB
# consumer-GPU ceiling. bf16 ceiling matches the base Molmo2-ER model (~5.5B
# params Γ— 2 bytes = ~11 GiB weights only; peak inference higher).
min_vram_gb:
  fp32: 22.0
  bf16: 11.0
  int4: 4.0
weights_uri: "hf://OpenRAL/rskill-molmoact2-multi-so101-nf4"

# ── Preprocessing (all knobs needed to interpret IO) ───────────────────────
processors:
  preprocessor_uri: "hf://OpenRAL/rskill-molmoact2-multi-so101-nf4/policy_preprocessor.json"
  postprocessor_uri: "hf://OpenRAL/rskill-molmoact2-multi-so101-nf4/policy_postprocessor.json"
# SO-100/101 teleop data is recorded upright β€” no rotation applied.
# Aliases map from canonical SO101 scene camera keys (so101_box scene emits
# ``oak_top`` + ``wrist``) to the model's training feature keys (``top`` /
# ``side``). Adjust per-scene in vla.extra if your camera names differ.
image_preprocessing:
  flip_180: false
  norm_tag: "so100_so101_molmoact2"
  # Secondary activation lever: caps the image processor's tile count (each extra
  # 378px crop adds ~182 pooled image tokens with quadratic attention cost).
  # MEASURED on an 8 GiB RTX 4070 (transformers 5.x): this does NOT by itself
  # decide the 8 GiB fit β€” the inference peak (~7.63 GiB) is set by the LM token
  # embedding, not the vision crops, and transformers 5.x's fast image processor
  # does not honour max_crops the way the slow one did. The actual 8 GiB enabler
  # is the CUDA expandable-segments allocator, which the molmoact2 adapter turns
  # on automatically (see policies/molmoact2.py::_enable_expandable_segments).
  # The pin is kept conservative for the slow-processor path / larger frames;
  # raise it via vla.extra.image_max_crops if you have VRAM headroom.
  image_max_crops: 4
  aliases:
    top: "top"
    wrist: "side"
state_contract:
  dim: 6

# ── Execution semantics ────────────────────────────────────────────────────
chunk_size: 10
# n_action_steps omitted β€” equals chunk_size (full chunk replay, MolmoAct2 default).
latency_budget:
  # MolmoAct2's adaptive-depth reasoning runs a single action call in ~180 ms
  # (base) to ~790 ms (with depth reasoning); a half-chunk replan budget of
  # 1000 ms leaves headroom for the flow-matching sampling (10 steps default).
  per_chunk_ms: 1000.0

# ── Provenance ─────────────────────────────────────────────────────────────
paper_url: "https://arxiv.org/abs/2605.02881"
source_repo: "hf://allenai/MolmoAct2-SO100_101"

description: >
  MolmoAct2 (Ai2) finetuned on the SO-100/SO-101 teleop mixture, NF4-quantized
  for 8 GB GPUs. Emits 6-DoF absolute joint-position chunks (size 10) for the
  SO-100/SO-101 follower arm. Flow-matching action expert on Molmo2-ER VLM.
  Apache-2.0. norm_tag="so100_so101_molmoact2" travels in the manifest's
  image_preprocessing block (overridable via vla.extra.norm_tag).

# Action vocabulary surfaced to the reasoner LLM tool palette so it
# can pick this skill by what it does (action verb + object + scene), not just
# by its slug.
actions:
  - "pick"
  - "place"
  - "pick_and_place"
  - "grasp"
objects: []
scenes:
  - "tabletop"

# Per-checkpoint action contract (consumed by the dataset bridge to
# bind the LeRobot v3 `action` feature shape). SO-100/101 uses a 6-D absolute
# joint-position action (5 arm joints + 1 gripper).
action_contract:
  dim: 6
  # SO-101 emits absolute joint positions (5 arm joints + 1 gripper).
  representation: "joint_positions"
  # EXPLICIT joint units β€” trained on LeRobot SO-100/101 teleop, which records in
  # DEGREES (verified from norm_stats.json metadata_by_tag/so100_so101_molmoact2:
  # state_stats span βˆ’270…+250, q99β‰ˆ185 β€” far outside radians). The runner
  # converts deg↔rad at the policy boundary; see the header UNITS note. Same
  # convention as the smolvla-so101-pen reference.
  joint_units: "degrees"