jbilcke-hf commited on
Commit
0c5ee34
·
verified ·
1 Parent(s): f291b72

Upload opentrack.ipynb

Browse files
Files changed (1) hide show
  1. samples/opentrack.ipynb +493 -0
samples/opentrack.ipynb ADDED
@@ -0,0 +1,493 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {},
6
+ "source": [
7
+ "# OpenTrack - Humanoid Motion Tracking Demo\n",
8
+ "\n",
9
+ "This notebook demonstrates OpenTrack, an open-source humanoid motion tracking system using MuJoCo.\n",
10
+ "\n",
11
+ "**Note**: Training can be resource-intensive. We'll use debug mode for quick testing."
12
+ ]
13
+ },
14
+ {
15
+ "cell_type": "markdown",
16
+ "metadata": {},
17
+ "source": [
18
+ "## 1. Setup Environment"
19
+ ]
20
+ },
21
+ {
22
+ "cell_type": "code",
23
+ "execution_count": null,
24
+ "metadata": {},
25
+ "outputs": [],
26
+ "source": [
27
+ "import os\n",
28
+ "import subprocess\n",
29
+ "import time\n",
30
+ "import glob\n",
31
+ "from pathlib import Path\n",
32
+ "from IPython.display import Video, display, HTML\n",
33
+ "import threading\n",
34
+ "import queue\n",
35
+ "\n",
36
+ "print(\"Environment setup complete!\")"
37
+ ]
38
+ },
39
+ {
40
+ "cell_type": "markdown",
41
+ "metadata": {},
42
+ "source": [
43
+ "## 2. Clone OpenTrack Repository"
44
+ ]
45
+ },
46
+ {
47
+ "cell_type": "code",
48
+ "execution_count": null,
49
+ "metadata": {},
50
+ "outputs": [],
51
+ "source": [
52
+ "# Clone the repository if not already cloned\n",
53
+ "if not os.path.exists('OpenTrack'):\n",
54
+ " !git clone https://github.com/GalaxyGeneralRobotics/OpenTrack.git\n",
55
+ " print(\"✓ Repository cloned successfully\")\n",
56
+ "else:\n",
57
+ " print(\"✓ Repository already exists\")\n",
58
+ "\n",
59
+ "os.chdir('OpenTrack')\n",
60
+ "print(f\"Current directory: {os.getcwd()}\")"
61
+ ]
62
+ },
63
+ {
64
+ "cell_type": "markdown",
65
+ "metadata": {},
66
+ "source": [
67
+ "## 3. Install Dependencies"
68
+ ]
69
+ },
70
+ {
71
+ "cell_type": "code",
72
+ "execution_count": null,
73
+ "metadata": {},
74
+ "outputs": [],
75
+ "source": [
76
+ "# Install PyTorch (CPU version for compatibility)\n",
77
+ "!pip install -q torch==2.5.1 torchvision==0.20.1 torchaudio==2.5.1 --index-url https://download.pytorch.org/whl/cpu\n",
78
+ "\n",
79
+ "# Install OpenTrack requirements\n",
80
+ "!pip install -q -r requirements.txt\n",
81
+ "\n",
82
+ "# Install additional packages for video handling\n",
83
+ "!pip install -q imageio imageio-ffmpeg\n",
84
+ "\n",
85
+ "print(\"✓ All dependencies installed\")"
86
+ ]
87
+ },
88
+ {
89
+ "cell_type": "markdown",
90
+ "metadata": {},
91
+ "source": [
92
+ "## 4. Download Motion Capture Data"
93
+ ]
94
+ },
95
+ {
96
+ "cell_type": "code",
97
+ "execution_count": null,
98
+ "metadata": {},
99
+ "outputs": [],
100
+ "source": [
101
+ "from huggingface_hub import snapshot_download\n",
102
+ "\n",
103
+ "# Create directory structure\n",
104
+ "mocap_dir = Path(\"data/mocap/lafan1/UnitreeG1\")\n",
105
+ "mocap_dir.mkdir(parents=True, exist_ok=True)\n",
106
+ "\n",
107
+ "repo_id = \"robfiras/loco-mujoco-datasets\"\n",
108
+ "\n",
109
+ "print(\"Downloading all mocap data from Lafan1/mocap/UnitreeG1...\")\n",
110
+ "print(\"This will download all .npz files concurrently.\\n\")\n",
111
+ "\n",
112
+ "try:\n",
113
+ " # Use snapshot_download with allow_patterns to download only the files we need\n",
114
+ " # This is much more efficient than downloading files one by one\n",
115
+ " snapshot_path = snapshot_download(\n",
116
+ " repo_id=repo_id,\n",
117
+ " repo_type=\"dataset\",\n",
118
+ " allow_patterns=\"Lafan1/mocap/UnitreeG1/*.npz\",\n",
119
+ " local_dir=\".\",\n",
120
+ " local_dir_use_symlinks=False\n",
121
+ " )\n",
122
+ " \n",
123
+ " print(f\"\\n✓ Download complete! Files saved to: {snapshot_path}\")\n",
124
+ " \n",
125
+ " # Verify files\n",
126
+ " npz_files = list(mocap_dir.glob(\"*.npz\"))\n",
127
+ " print(f\"✓ Found {len(npz_files)} .npz files in {mocap_dir}\")\n",
128
+ " \n",
129
+ " if npz_files:\n",
130
+ " print(\"\\nSample files:\")\n",
131
+ " for f in sorted(npz_files)[:10]: # Show first 10 files\n",
132
+ " print(f\" - {f.name}\")\n",
133
+ " if len(npz_files) > 10:\n",
134
+ " print(f\" ... and {len(npz_files) - 10} more files\")\n",
135
+ " \n",
136
+ "except Exception as e:\n",
137
+ " print(f\"⚠ Error downloading mocap data: {e}\")\n",
138
+ " print(\"\\nYou may need to download manually from:\")\n",
139
+ " print(\"https://huggingface.co/datasets/robfiras/loco-mujoco-datasets/tree/main/Lafan1/mocap/UnitreeG1\")\n",
140
+ " print(\"\\nOr check if you need to authenticate:\")\n",
141
+ " print(\" huggingface-cli login\")"
142
+ ]
143
+ },
144
+ {
145
+ "cell_type": "markdown",
146
+ "metadata": {},
147
+ "source": [
148
+ "## 5. Helper Functions for Background Process Management"
149
+ ]
150
+ },
151
+ {
152
+ "cell_type": "code",
153
+ "execution_count": null,
154
+ "metadata": {},
155
+ "outputs": [],
156
+ "source": [
157
+ "def run_command_with_output(cmd, description=\"Running command\"):\n",
158
+ " \"\"\"\n",
159
+ " Run a command and display output in real-time\n",
160
+ " \"\"\"\n",
161
+ " print(f\"\\n{'='*60}\")\n",
162
+ " print(f\"{description}\")\n",
163
+ " print(f\"Command: {' '.join(cmd)}\")\n",
164
+ " print(f\"{'='*60}\\n\")\n",
165
+ " \n",
166
+ " process = subprocess.Popen(\n",
167
+ " cmd,\n",
168
+ " stdout=subprocess.PIPE,\n",
169
+ " stderr=subprocess.STDOUT,\n",
170
+ " text=True,\n",
171
+ " bufsize=1,\n",
172
+ " universal_newlines=True\n",
173
+ " )\n",
174
+ " \n",
175
+ " # Read output line by line\n",
176
+ " for line in process.stdout:\n",
177
+ " print(line, end='')\n",
178
+ " \n",
179
+ " process.wait()\n",
180
+ " \n",
181
+ " if process.returncode == 0:\n",
182
+ " print(f\"\\n✓ {description} completed successfully\")\n",
183
+ " else:\n",
184
+ " print(f\"\\n⚠ {description} exited with code {process.returncode}\")\n",
185
+ " \n",
186
+ " return process.returncode\n",
187
+ "\n",
188
+ "\n",
189
+ "def find_latest_experiment(exp_name_pattern):\n",
190
+ " \"\"\"\n",
191
+ " Find the latest experiment folder matching the pattern\n",
192
+ " \"\"\"\n",
193
+ " logs_dir = Path(\"logs\")\n",
194
+ " if not logs_dir.exists():\n",
195
+ " return None\n",
196
+ " \n",
197
+ " # Find all matching experiments\n",
198
+ " experiments = sorted(\n",
199
+ " [d for d in logs_dir.iterdir() if d.is_dir() and exp_name_pattern in d.name],\n",
200
+ " key=lambda x: x.stat().st_mtime,\n",
201
+ " reverse=True\n",
202
+ " )\n",
203
+ " \n",
204
+ " return experiments[0].name if experiments else None\n",
205
+ "\n",
206
+ "\n",
207
+ "def find_generated_videos(output_dir=\"videos\"):\n",
208
+ " \"\"\"\n",
209
+ " Find all generated video files\n",
210
+ " \"\"\"\n",
211
+ " video_dir = Path(output_dir)\n",
212
+ " if not video_dir.exists():\n",
213
+ " # Try alternative locations\n",
214
+ " alternative_dirs = [\".\", \"logs\", \"outputs\"]\n",
215
+ " for alt_dir in alternative_dirs:\n",
216
+ " alt_path = Path(alt_dir)\n",
217
+ " videos = list(alt_path.glob(\"**/*.mp4\")) + list(alt_path.glob(\"**/*.gif\"))\n",
218
+ " if videos:\n",
219
+ " return sorted(videos, key=lambda x: x.stat().st_mtime, reverse=True)\n",
220
+ " return []\n",
221
+ " \n",
222
+ " videos = list(video_dir.glob(\"*.mp4\")) + list(video_dir.glob(\"*.gif\"))\n",
223
+ " return sorted(videos, key=lambda x: x.stat().st_mtime, reverse=True)\n",
224
+ "\n",
225
+ "print(\"✓ Helper functions loaded\")"
226
+ ]
227
+ },
228
+ {
229
+ "cell_type": "markdown",
230
+ "metadata": {},
231
+ "source": [
232
+ "## 6. Quick Training (Debug Mode)\n",
233
+ "\n",
234
+ "We'll run a quick training session in debug mode. This won't produce a well-trained model but will verify everything works."
235
+ ]
236
+ },
237
+ {
238
+ "cell_type": "code",
239
+ "execution_count": null,
240
+ "metadata": {},
241
+ "outputs": [],
242
+ "source": [
243
+ "# Run quick training\n",
244
+ "cmd = [\n",
245
+ " 'python', 'train_policy.py',\n",
246
+ " '--exp_name', 'debug',\n",
247
+ " '--terrain_type', 'flat_terrain'\n",
248
+ "]\n",
249
+ "\n",
250
+ "return_code = run_command_with_output(cmd, \"Training OpenTrack (debug mode)\")\n",
251
+ "\n",
252
+ "# Find the experiment folder\n",
253
+ "exp_folder = find_latest_experiment('debug')\n",
254
+ "if exp_folder:\n",
255
+ " print(f\"\\n✓ Training completed! Experiment: {exp_folder}\")\n",
256
+ "else:\n",
257
+ " print(\"\\n⚠ Could not find experiment folder\")"
258
+ ]
259
+ },
260
+ {
261
+ "cell_type": "markdown",
262
+ "metadata": {},
263
+ "source": [
264
+ "## 7. Convert Checkpoint (Brax → PyTorch)"
265
+ ]
266
+ },
267
+ {
268
+ "cell_type": "code",
269
+ "execution_count": null,
270
+ "metadata": {},
271
+ "outputs": [],
272
+ "source": [
273
+ "# Get the latest experiment name\n",
274
+ "exp_folder = find_latest_experiment('debug')\n",
275
+ "\n",
276
+ "if exp_folder:\n",
277
+ " print(f\"Converting checkpoint for: {exp_folder}\")\n",
278
+ " \n",
279
+ " cmd = [\n",
280
+ " 'python', 'brax2torch.py',\n",
281
+ " '--exp_name', exp_folder\n",
282
+ " ]\n",
283
+ " \n",
284
+ " return_code = run_command_with_output(cmd, \"Converting Brax checkpoint to PyTorch\")\n",
285
+ "else:\n",
286
+ " print(\"⚠ No experiment found. Run training first.\")"
287
+ ]
288
+ },
289
+ {
290
+ "cell_type": "markdown",
291
+ "metadata": {},
292
+ "source": [
293
+ "## 8. Generate Videos (Headless Rendering)\n",
294
+ "\n",
295
+ "This will run the policy and generate videos using MuJoCo's headless renderer."
296
+ ]
297
+ },
298
+ {
299
+ "cell_type": "code",
300
+ "execution_count": null,
301
+ "metadata": {},
302
+ "outputs": [],
303
+ "source": [
304
+ "# Get the latest experiment name\n",
305
+ "exp_folder = find_latest_experiment('debug')\n",
306
+ "\n",
307
+ "if exp_folder:\n",
308
+ " print(f\"Generating videos for: {exp_folder}\")\n",
309
+ " \n",
310
+ " # Use --use_renderer for headless video generation (NOT --use_viewer)\n",
311
+ " cmd = [\n",
312
+ " 'python', 'play_policy.py',\n",
313
+ " '--exp_name', exp_folder,\n",
314
+ " '--use_renderer',\n",
315
+ " # '--play_ref_motion', # Uncomment to also show reference motion\n",
316
+ " ]\n",
317
+ " \n",
318
+ " return_code = run_command_with_output(cmd, \"Generating videos with MuJoCo renderer\")\n",
319
+ " \n",
320
+ " print(\"\\n\" + \"=\"*60)\n",
321
+ " print(\"Searching for generated videos...\")\n",
322
+ " print(\"=\"*60)\n",
323
+ " \n",
324
+ " # Wait a moment for files to be written\n",
325
+ " time.sleep(2)\n",
326
+ " \n",
327
+ " # Find generated videos\n",
328
+ " videos = find_generated_videos()\n",
329
+ " \n",
330
+ " if videos:\n",
331
+ " print(f\"\\n✓ Found {len(videos)} video(s):\")\n",
332
+ " for video in videos:\n",
333
+ " print(f\" - {video}\")\n",
334
+ " else:\n",
335
+ " print(\"\\n⚠ No videos found. Checking alternative locations...\")\n",
336
+ " # Search more broadly\n",
337
+ " all_videos = list(Path(\".\").rglob(\"*.mp4\")) + list(Path(\".\").rglob(\"*.gif\"))\n",
338
+ " if all_videos:\n",
339
+ " print(f\"Found {len(all_videos)} video(s) in project:\")\n",
340
+ " for video in all_videos[:10]: # Show first 10\n",
341
+ " print(f\" - {video}\")\n",
342
+ "else:\n",
343
+ " print(\"⚠ No experiment found. Run training first.\")"
344
+ ]
345
+ },
346
+ {
347
+ "cell_type": "markdown",
348
+ "metadata": {},
349
+ "source": [
350
+ "## 9. Display Generated Videos"
351
+ ]
352
+ },
353
+ {
354
+ "cell_type": "code",
355
+ "execution_count": null,
356
+ "metadata": {},
357
+ "outputs": [],
358
+ "source": [
359
+ "# Find and display all generated videos\n",
360
+ "videos = find_generated_videos()\n",
361
+ "\n",
362
+ "if not videos:\n",
363
+ " # Try alternative search\n",
364
+ " videos = list(Path(\".\").rglob(\"*.mp4\")) + list(Path(\".\").rglob(\"*.gif\"))\n",
365
+ "\n",
366
+ "if videos:\n",
367
+ " print(f\"Displaying {len(videos)} video(s):\\n\")\n",
368
+ " \n",
369
+ " for i, video_path in enumerate(videos[:5]): # Display first 5 videos\n",
370
+ " print(f\"\\n{'='*60}\")\n",
371
+ " print(f\"Video {i+1}: {video_path.name}\")\n",
372
+ " print(f\"{'='*60}\")\n",
373
+ " \n",
374
+ " try:\n",
375
+ " if video_path.suffix == '.mp4':\n",
376
+ " display(Video(str(video_path), width=800, embed=True))\n",
377
+ " elif video_path.suffix == '.gif':\n",
378
+ " display(HTML(f'<img src=\"{video_path}\" width=\"800\">'))\n",
379
+ " except Exception as e:\n",
380
+ " print(f\"⚠ Error displaying video: {e}\")\n",
381
+ " print(f\"You can download it from: {video_path}\")\n",
382
+ "else:\n",
383
+ " print(\"⚠ No videos found.\")\n",
384
+ " print(\"\\nPossible reasons:\")\n",
385
+ " print(\"1. Training didn't complete successfully\")\n",
386
+ " print(\"2. Checkpoint conversion failed\")\n",
387
+ " print(\"3. Video generation failed\")\n",
388
+ " print(\"\\nCheck the output of previous cells for errors.\")"
389
+ ]
390
+ },
391
+ {
392
+ "cell_type": "markdown",
393
+ "metadata": {},
394
+ "source": [
395
+ "## 10. Optional: Generate Rough Terrain\n",
396
+ "\n",
397
+ "If you want to test on rough terrain, run this cell first to generate terrain data."
398
+ ]
399
+ },
400
+ {
401
+ "cell_type": "code",
402
+ "execution_count": null,
403
+ "metadata": {},
404
+ "outputs": [],
405
+ "source": [
406
+ "# Generate rough terrain using Perlin noise\n",
407
+ "cmd = ['python', 'generate_terrain.py']\n",
408
+ "return_code = run_command_with_output(cmd, \"Generating rough terrain\")\n",
409
+ "\n",
410
+ "print(\"\\n✓ Terrain generated! You can now train with --terrain_type rough_terrain\")"
411
+ ]
412
+ },
413
+ {
414
+ "cell_type": "markdown",
415
+ "metadata": {},
416
+ "source": [
417
+ "## 11. Optional: Full Training (Takes Much Longer)\n",
418
+ "\n",
419
+ "⚠️ Warning: This will take significant time and resources. Only run if you have GPU access and time."
420
+ ]
421
+ },
422
+ {
423
+ "cell_type": "code",
424
+ "execution_count": null,
425
+ "metadata": {},
426
+ "outputs": [],
427
+ "source": [
428
+ "# Full training (uncomment to run)\n",
429
+ "# cmd = [\n",
430
+ "# 'python', 'train_policy.py',\n",
431
+ "# '--exp_name', 'flat_terrain_full',\n",
432
+ "# '--terrain_type', 'flat_terrain'\n",
433
+ "# ]\n",
434
+ "# \n",
435
+ "# return_code = run_command_with_output(cmd, \"Full training on flat terrain\")\n",
436
+ "# \n",
437
+ "# # Then convert and play\n",
438
+ "# exp_folder = find_latest_experiment('flat_terrain_full')\n",
439
+ "# if exp_folder:\n",
440
+ "# !python brax2torch.py --exp_name {exp_folder}\n",
441
+ "# !python play_policy.py --exp_name {exp_folder} --use_renderer\n",
442
+ "\n",
443
+ "print(\"Full training cell ready (currently commented out)\")"
444
+ ]
445
+ },
446
+ {
447
+ "cell_type": "markdown",
448
+ "metadata": {},
449
+ "source": [
450
+ "## Summary\n",
451
+ "\n",
452
+ "This notebook demonstrates:\n",
453
+ "1. ✅ Setting up OpenTrack in a Jupyter environment\n",
454
+ "2. ✅ Running training (debug mode for quick testing)\n",
455
+ "3. ✅ Converting Brax checkpoints to PyTorch\n",
456
+ "4. ✅ Generating videos using headless MuJoCo renderer\n",
457
+ "5. ✅ Displaying videos in the notebook\n",
458
+ "\n",
459
+ "**Next Steps:**\n",
460
+ "- Download more mocap files for better training\n",
461
+ "- Run full training with GPU support\n",
462
+ "- Test on rough terrain\n",
463
+ "- Experiment with reference motion playback\n",
464
+ "\n",
465
+ "**Troubleshooting:**\n",
466
+ "- If videos aren't generated, check that `--use_renderer` flag is used (not `--use_viewer`)\n",
467
+ "- Ensure MuJoCo can run headless (may need `xvfb` on some systems)\n",
468
+ "- Check `logs/` directory for experiment outputs"
469
+ ]
470
+ }
471
+ ],
472
+ "metadata": {
473
+ "kernelspec": {
474
+ "display_name": "Python 3",
475
+ "language": "python",
476
+ "name": "python3"
477
+ },
478
+ "language_info": {
479
+ "codemirror_mode": {
480
+ "name": "ipython",
481
+ "version": 3
482
+ },
483
+ "file_extension": ".py",
484
+ "mimetype": "text/x-python",
485
+ "name": "python",
486
+ "nbconvert_exporter": "python",
487
+ "pygments_lexer": "ipython3",
488
+ "version": "3.12.0"
489
+ }
490
+ },
491
+ "nbformat": 4,
492
+ "nbformat_minor": 4
493
+ }