File size: 13,687 Bytes
c5a7b09
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>LeRobot Diffusion Policy - PushT-v0</title>
    <script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
    <link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css" rel="stylesheet" />
    <style>
        body {
            font-family: 'Inter', sans-serif;
        }
        pre, code {
            font-family: 'JetBrains Mono', monospace !important;
        }
    </style>
</head>
<body class="bg-slate-950 text-slate-100 min-h-screen selection:bg-indigo-500 selection:text-white">

    <header class="border-b border-slate-800 bg-slate-900/50 backdrop-blur sticky top-0 z-50">
        <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 h-16 flex items-center justify-between">
            <div class="flex items-center space-x-3">
                <span class="flex h-3 w-3 relative">
                    <span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-emerald-400 opacity-75"></span>
                    <span class="relative inline-flex rounded-full h-3 w-3 bg-emerald-500"></span>
                </span>
                <h1 class="text-lg font-semibold tracking-tight text-white">LeRobot PushT Evaluation</h1>
            </div>
            <div class="flex items-center space-x-2">
                <span class="px-2.5 py-1 text-xs font-medium rounded-md bg-indigo-500/10 text-indigo-400 border border-indigo-500/20">Diffusion Policy</span>
                <span class="px-2.5 py-1 text-xs font-medium rounded-md bg-slate-800 text-slate-400 border border-slate-700">Gymnasium</span>
            </div>
        </div>
    </header>

    <main class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-10 space-y-12">
        
        <div class="grid grid-cols-1 lg:grid-cols-12 gap-8 items-start">
            
            <div class="lg:col-span-5 space-y-4">
                <div class="bg-slate-900 border border-slate-800 rounded-xl overflow-hidden shadow-2xl p-4">
                    <div class="flex items-center justify-between mb-3 px-1">
                        <span class="text-xs font-medium uppercase tracking-wider text-slate-400">Policy Rollout (300 Steps)</span>
                        <span class="text-xs text-slate-500">pusht_policy.mp4</span>
                    </div>
                    <div class="relative aspect-square rounded-lg overflow-hidden bg-white border border-slate-700">
                        <video class="w-full h-full object-contain" autoplay loop muted controls playsinline>
                            <source src="pusht_policy.mp4" type="video/mp4">
                            Your browser does not support the video tag.
                        </video>
                    </div>
                </div>
            </div>

            <div class="lg:col-span-7 space-y-6 lg:pt-2">
                <div>
                    <h2 class="text-3xl font-bold tracking-tight text-white mb-3">Autonomous Multi-Modal Manipulation</h2>
                    <p class="text-slate-400 leading-relaxed">
                        This Space showcases a trained <strong>Diffusion Policy</strong> operating within the <code>PushT-v0</code> simulation environment using Hugging Face's <strong>LeRobot</strong> ecosystem. The agent learns multi-modal trajectories to effectively guide the gray T-shaped block completely into the target green silhouette zone.
                    </p>
                </div>

                <div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
                    <div class="p-4 bg-slate-900/60 border border-slate-800 rounded-xl">
                        <h3 class="text-xs font-semibold text-slate-400 uppercase tracking-wider mb-2">Observation Space</h3>
                        <p class="text-sm font-medium text-slate-200">Pixels &amp; Agent Position</p>
                        <p class="text-xs text-slate-500 mt-1">Image Shape: (3, 384, 384)</p>
                    </div>
                    <div class="p-4 bg-slate-900/60 border border-slate-800 rounded-xl">
                        <h3 class="text-xs font-semibold text-slate-400 uppercase tracking-wider mb-2">Action Space</h3>
                        <p class="text-sm font-medium text-slate-200">2D Continuous Control</p>
                        <p class="text-xs text-slate-500 mt-1">End-effector delta position</p>
                    </div>
                    <div class="p-4 bg-slate-900/60 border border-slate-800 rounded-xl">
                        <h3 class="text-xs font-semibold text-slate-400 uppercase tracking-wider mb-2">Model Source</h3>
                        <p class="text-sm font-medium text-slate-200">lerobot/diffusion_pusht</p>
                        <p class="text-xs text-slate-500 mt-1">Pre-trained Checkpoint via HF Hub</p>
                    </div>
                    <div class="p-4 bg-slate-900/60 border border-slate-800 rounded-xl">
                        <h3 class="text-xs font-semibold text-slate-400 uppercase tracking-wider mb-2">Pipeline Optimization</h3>
                        <p class="text-sm font-medium text-emerald-400">Dynamic Buffer Patching</p>
                        <p class="text-xs text-slate-500 mt-1">Overrides state dict pos_grid mismatch</p>
                    </div>
                </div>

                <div class="p-4 bg-amber-500/5 border border-amber-500/20 rounded-xl flex space-x-3">
                    <svg class="h-5 w-5 text-amber-500 shrink-0 mt-0.5" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
                        <path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z" />
                    </svg>
                    <div class="text-xs text-slate-400 leading-relaxed">
                        <strong class="text-slate-200 font-medium">Engineering Note:</strong> The default environment instantiation sets the visual input size to 384×384 pixels, forcing the model's position grid (<code class="text-amber-400">pos_grid</code>) to shape <code class="text-amber-400">[144, 2]</code>. To preserve checkpoint compatibility, the execution engine explicitly overwrites and re-registers the pre-trained token configuration <code class="text-slate-300">[9, 2]</code> buffer layout right after model initialization.
                    </div>
                </div>
            </div>
        </div>

        <hr class="border-slate-800" />

        <div class="space-y-4">
            <div>
                <h2 class="text-xl font-bold text-white tracking-tight">Deployment & Rollout Script</h2>
                <p class="text-sm text-slate-400 mt-1">The clean implementation pipeline used to compile dataset statistics, patch the architecture shapes, step through environment dynamics, and generate the rollout asset.</p>
            </div>

            <div class="relative bg-slate-900 border border-slate-800 rounded-xl overflow-hidden shadow-xl max-h-[600px] overflow-y-auto">
                <div class="sticky top-0 bg-slate-900 border-b border-slate-800 px-4 py-2 flex items-center justify-between text-xs text-slate-400 z-10">
                    <span class="font-mono">run_pusht.py</span>
                    <button id="copyBtn" class="hover:text-white transition flex items-center space-x-1 cursor-pointer">
                        <svg class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
                            <path stroke-linecap="round" stroke-linejoin="round" d="M15.75 17.25v3.375c0 .621-.504 1.125-1.125 1.125h-9.75a1.125 1.125 0 0 1-1.125-1.125V7.875c0-.621.504-1.125 1.125-1.125H6.75a9.06 9.06 0 0 1 1.5.124m7.5 10.376A8.965 8.965 0 0 0 12 12.75a8.965 8.965 0 0 0-3.75 4.625M18 4.75V3.375c0-.621-.504-1.125-1.125-1.125h-9.75a1.125 1.125 0 0 0-1.125 1.125V4.75m12.75 0V19.5a1.125 1.125 0 0 1-1.125 1.125H18M9 4.75v1.5a1.125 1.125 0 0 0 1.125 1.125h3.75A1.125 1.125 0 0 0 15 6.25v-1.5M9 4.75h6" />
                        </svg>
                        <span>Copy Code</span>
                    </button>
                </div>
                <pre class="m-0 p-4 bg-slate-900 text-sm"><code class="language-python" id="codeBlock">import os
import gymnasium as gym
import gym_pusht
import torch
import imageio
from huggingface_hub import hf_hub_download
import safetensors.torch
from lerobot.policies.diffusion.modeling_diffusion import DiffusionPolicy
from lerobot.configs.policies import PreTrainedConfig
from lerobot.policies.factory import make_pre_post_processors
from lerobot.envs.utils import preprocess_observation

def main():
    # 1. Download checkpoint and load config
    print("Downloading config from lerobot/diffusion_pusht...")
    cfg = PreTrainedConfig.from_pretrained('lerobot/diffusion_pusht')
    
    # We override the observation.image feature shape to (3, 384, 384) to match the environment defaults,
    # which instantiates the model's pos_grid as [144, 2] instead of [9, 2] (checkpoint size).
    cfg.input_features['observation.image'].shape = (3, 384, 384)

    # Build the DiffusionPolicy
    print("Building DiffusionPolicy...")
    policy = DiffusionPolicy(cfg)
    print("Initial pos_grid shape in model:", policy.diffusion.rgb_encoder.pool.pos_grid.shape)

    # Load weights with strict=False
    print("Downloading and loading safetensors model weights...")
    model_file = hf_hub_download(repo_id='lerobot/diffusion_pusht', filename='model.safetensors')
    state_dict = safetensors.torch.load_file(model_file)
    policy.load_state_dict(state_dict, strict=False)

    # 2. Patch the pos_grid shape mismatch so inference works
    print("Patching the pos_grid shape mismatch...")
    checkpoint_pos_grid = state_dict['diffusion.rgb_encoder.pool.pos_grid']
    policy.diffusion.rgb_encoder.pool.register_buffer('pos_grid', checkpoint_pos_grid)
    print("Patched pos_grid shape in model:", policy.diffusion.rgb_encoder.pool.pos_grid.shape)

    # Move policy to correct device and set to eval mode
    policy.to(cfg.device)
    policy.eval()

    # 3. Create preprocessor / postprocessor with the extracted dataset stats
    print("Creating preprocessor and postprocessor...")
    dataset_stats = {
        'observation.image': {
            'mean': state_dict['normalize_inputs.buffer_observation_image.mean'],
            'std': state_dict['normalize_inputs.buffer_observation_image.std'],
        },
        'observation.state': {
            'max': state_dict['normalize_inputs.buffer_observation_state.max'],
            'min': state_dict['normalize_inputs.buffer_observation_state.min'],
        },
        'action': {
            'max': state_dict['normalize_targets.buffer_action.max'],
            'min': state_dict['normalize_targets.buffer_action.min'],
        }
    }
    preprocessor, postprocessor = make_pre_post_processors(cfg, dataset_stats=dataset_stats)

    # 4. Instantiate the gym environment
    print("Creating PushT environment...")
    env = gym.make('gym_pusht/PushT-v0', render_mode='rgb_array', obs_type='pixels_agent_pos')
    
    # Reset env and cache initial frame
    policy.reset()
    obs, info = env.reset()
    frames = [env.render()]

    # Run rollout for 300 steps
    print("Running 300 steps rollout...")
    for step in range(300):
        # Format observations to LeRobot format
        obs_t = preprocess_observation(obs)
        obs_t = preprocessor(obs_t)

        # Select action
        with torch.no_grad():
            action = policy.select_action(obs_t)
        action = postprocessor(action)

        # Extract numpy action and apply to env (drop batch dimension)
        action_numpy = action.to("cpu").numpy()[0]
        obs, reward, terminated, truncated, info = env.step(action_numpy)
        
        # Render frame
        frame = env.render()
        frames.append(frame)

        if terminated or truncated:
            obs, info = env.reset()

    # Close env
    env.close()

    # 5. Save the frames as pusht_policy.mp4
    print("Saving video to pusht_policy.mp4...")
    imageio.mimsave("pusht_policy.mp4", frames, fps=10)
    print("Done! Video saved successfully.")

if __name__ == "__main__":
    main()</code></pre>
            </div>
        </div>
    </main>

    <footer class="text-center py-8 text-xs text-slate-600 border-t border-slate-900 mt-12">
        Powered by LeRobot, Gymnasium, and Hugging Face Static Spaces.
    </footer>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-core.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/autoloader/prism-autoloader.min.js"></script>
    
    <script>
        document.getElementById('copyBtn').addEventListener('click', () => {
            const code = document.getElementById('codeBlock').innerText;
            navigator.clipboard.writeText(code).then(() => {
                const btnSpan = document.querySelector('#copyBtn span');
                btnSpan.textContent = 'Copied!';
                setTimeout(() => { btnSpan.textContent = 'Copy Code'; }, 2000);
            });
        });
    </script>
</body>
</html>