faradayfuture commited on
Commit
c8f934b
·
verified ·
1 Parent(s): 2718498

egoSample: add Robot Ego · Multi-Sensor Capture category (planned) with Faber U shelf-restocking episode

Browse files
.gitattributes CHANGED
@@ -222,3 +222,10 @@ teleopSample/ep5/video/chest_right_color.mp4 filter=lfs diff=lfs merge=lfs -text
222
  teleopSample/faberU/**/*.mp4 filter=lfs diff=lfs merge=lfs -text
223
  egoSample/media/craft_20260629_sticker_ep000/head_stereo_left.mp4 filter=lfs diff=lfs merge=lfs -text
224
  egoSample/media/craft_20260629_sticker_ep000/head_stereo_right.mp4 filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
222
  teleopSample/faberU/**/*.mp4 filter=lfs diff=lfs merge=lfs -text
223
  egoSample/media/craft_20260629_sticker_ep000/head_stereo_left.mp4 filter=lfs diff=lfs merge=lfs -text
224
  egoSample/media/craft_20260629_sticker_ep000/head_stereo_right.mp4 filter=lfs diff=lfs merge=lfs -text
225
+ egoSample/epdata/restock_20260916_ep000.js filter=lfs diff=lfs merge=lfs -text
226
+ egoSample/media/restock_20260916_ep000/head_color.mp4 filter=lfs diff=lfs merge=lfs -text
227
+ egoSample/media/restock_20260916_ep000/head_depth.mp4 filter=lfs diff=lfs merge=lfs -text
228
+ egoSample/media/restock_20260916_ep000/head_left_fisheye.mp4 filter=lfs diff=lfs merge=lfs -text
229
+ egoSample/media/restock_20260916_ep000/head_right_fisheye.mp4 filter=lfs diff=lfs merge=lfs -text
230
+ egoSample/media/restock_20260916_ep000/head_stereo_left.mp4 filter=lfs diff=lfs merge=lfs -text
231
+ egoSample/media/restock_20260916_ep000/head_stereo_right.mp4 filter=lfs diff=lfs merge=lfs -text
egoSample/epdata/restock_20260916_ep000.js ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:13b04d774382e13289e7f06164a08f3f7f0d1a8b5099f7c818c17e9f5a865df6
3
+ size 11833114
egoSample/faberu.html ADDED
@@ -0,0 +1,298 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+ <html lang="en"><head><meta charset="utf-8">
3
+ <meta name="viewport" content="width=device-width,initial-scale=1">
4
+ <title>Faber U Teleop Viewer</title>
5
+ <style>
6
+ :root{color-scheme:dark}
7
+ *{box-sizing:border-box}
8
+ html,body{margin:0;height:100%}
9
+ body{background:#07090d;color:#e6e8ee;font:14px/1.5 -apple-system,"Helvetica Neue",Arial,sans-serif}
10
+ a{color:#6f92c8}
11
+ .main{height:100vh;overflow-y:auto}
12
+ header{padding:14px 20px 10px}
13
+ header h1{margin:0 0 3px;font-size:19px}
14
+ .sub{color:#8890a0;font-size:12.5px}.sub code{color:#c7ccd6;background:#12151c;padding:1px 5px;border-radius:4px}
15
+ .badges{display:flex;flex-wrap:wrap;gap:6px;margin-top:7px}
16
+ .bdg{background:#12151c;border:1px solid #1c2028;border-radius:999px;padding:2px 9px;font-size:11px;color:#9aa1ad}
17
+ .bdg b{color:#e6e8ee}
18
+ .bdg.tag{color:#07090d;background:#6f9fe0;border-color:#6f9fe0;font-weight:700}
19
+ .layout{display:grid;grid-template-columns:minmax(0,1fr) 320px;gap:14px;padding:0 20px}
20
+ @media(max-width:1360px){.layout{grid-template-columns:1fr}}
21
+ .panel{background:#0e1118;border:1px solid #1c2028;border-radius:8px}
22
+ .vgrid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px;padding:8px}
23
+ @media(max-width:700px){.vgrid{grid-template-columns:1fr}}
24
+ .vp{position:relative;background:#000;border-radius:4px;overflow:hidden;aspect-ratio:5/4;min-width:0}
25
+ .vp.big{grid-column:span 2;grid-row:span 2}
26
+ .vp video{width:100%;height:100%;object-fit:contain;display:block}
27
+ .vp .lbl{position:absolute;top:5px;left:6px;font:10.5px ui-monospace,monospace;color:#bcd2e6;background:rgba(0,0,0,.5);padding:1px 5px;border-radius:3px;pointer-events:none}
28
+ .vp .rd{position:absolute;bottom:5px;right:6px;font:11px ui-monospace,monospace;color:#fff;background:rgba(0,0,0,.6);padding:1px 6px;border-radius:3px;pointer-events:none}
29
+ .three{position:relative;height:clamp(260px,38vh,420px);margin:0 8px 8px;background:#0b0d12;border:1px solid #1c2028;border-radius:6px;overflow:hidden}
30
+ .three canvas{display:block}
31
+ .three .lbl{position:absolute;top:6px;left:9px;font:12px ui-monospace,monospace;color:#9aa1ad;pointer-events:none;z-index:1}
32
+ .three .hint{position:absolute;bottom:6px;right:9px;font-size:10px;color:#556;pointer-events:none;z-index:1}
33
+ .chk{display:flex;flex-wrap:wrap;gap:4px 14px;padding:0 12px 8px;font-size:11.5px;color:#9aa1ad}
34
+ .chk label{cursor:pointer;display:flex;gap:4px;align-items:center}
35
+ .spans-head{padding:12px 14px 8px;font-size:12px;letter-spacing:.04em;color:#9aa1ad;text-transform:uppercase}
36
+ .spans-head b{color:#6f9fe0}
37
+ .tl{height:9px;border-radius:4px;margin:0 14px 6px;display:flex;overflow:hidden;cursor:pointer;background:#12151c}
38
+ .tl i{height:100%}
39
+ .chips{display:flex;flex-wrap:wrap;gap:5px;padding:0 14px 10px}
40
+ .chip{min-width:24px;height:21px;padding:0 5px;display:flex;align-items:center;justify-content:center;border-radius:5px;font-size:10.5px;font-weight:700;cursor:pointer;color:#07090d}
41
+ .chip.on{outline:2px solid #fff}
42
+ .slist{max-height:560px;overflow-y:auto}
43
+ .si{border-left:3px solid transparent;border-radius:4px;padding:7px 10px;margin:0 10px 5px;cursor:pointer;background:#12151c}
44
+ .si:hover{background:#171b24}.si.on{background:#182339;border-left-color:#6f9fe0}
45
+ .si .n{display:inline-flex;min-width:17px;height:17px;padding:0 3px;border-radius:9px;color:#07090d;font-size:10px;font-weight:700;align-items:center;justify-content:center;margin-right:6px}
46
+ .si .k{font-size:13px}.si .m{font-size:10.5px;color:#6f92c8;font-family:ui-monospace,monospace;margin-top:2px}
47
+ .si .lb{font-size:11px;color:#8890a0}
48
+ .legend{display:flex;flex-wrap:wrap;gap:6px 12px;padding:10px 20px 0;color:#8890a0;font-size:11.5px}
49
+ .legend .it{display:flex;align-items:center;gap:5px}
50
+ .sw{width:11px;height:11px;border-radius:3px;display:inline-block}
51
+ .controls{display:flex;align-items:center;gap:12px;padding:9px 16px;margin:14px 20px 0}
52
+ .controls button{background:#1b2740;color:#cfe0ff;border:1px solid #2b3654;border-radius:6px;padding:5px 13px;cursor:pointer;font-size:15px}
53
+ .controls input[type=range]{flex:1;min-width:0}
54
+ .controls .t{font-variant-numeric:tabular-nums;color:#c7ccd6;font-size:13px;white-space:nowrap}
55
+ .controls select{background:#12151c;color:#c7ccd6;border:1px solid #2b3654;border-radius:5px;font-size:12px;padding:3px 4px}
56
+ .note{margin:10px 20px 0;padding:9px 14px;font-size:12px;color:#9aa1ad;border-left:3px solid #b8860b;background:#14130c;border-radius:4px}
57
+ .note b{color:#d9c48a}
58
+ .readout{margin:14px 20px 0;padding:13px 18px}
59
+ .rt{font-size:11px;letter-spacing:.04em;color:#9aa1ad;text-transform:uppercase;margin-bottom:9px}
60
+ .rg{display:grid;grid-template-columns:repeat(3,1fr);gap:8px 26px}
61
+ @media(max-width:900px){.rg{grid-template-columns:1fr 1fr}}
62
+ .ro{display:flex;justify-content:space-between;border-bottom:1px dashed #1c2028;padding-bottom:4px;gap:8px}
63
+ .ro .k{color:#9aa1ad}.ro .v{font-variant-numeric:tabular-nums;text-align:right}.ro .v.b{color:#7fb2ff}.ro .v.hi{color:#6fe08a}
64
+ .charts{margin:12px 20px 0;display:grid;grid-template-columns:1fr 1fr;gap:8px}
65
+ @media(max-width:1100px){.charts{grid-template-columns:1fr}}
66
+ .chp{padding:6px 14px 8px}
67
+ .chp .l{font-size:11px;color:#9aa1ad}.chp .u{color:#5a6373;font-size:10px;margin-left:6px}
68
+ .chp canvas{width:100%;height:84px;display:block;cursor:pointer}
69
+ .leg{display:inline-flex;flex-wrap:wrap;gap:4px 9px;margin-left:8px;font-size:10.5px}
70
+ .leg i{width:9px;height:9px;border-radius:2px;display:inline-block;margin-right:3px;vertical-align:-1px}
71
+ .files{display:flex;flex-wrap:wrap;gap:7px}
72
+ .fc{background:#12151c;border:1px solid #1c2028;border-radius:5px;padding:4px 9px;font:11.5px ui-monospace,monospace;color:#c7ccd6;text-decoration:none;display:inline-block}
73
+ .fc.dl{color:#7fe0a0;border-color:#24402f}.fc.dl:hover{background:#16211a;border-color:#3d9970}
74
+ .cpy{background:#12151c;border:1px solid #2b3654;color:#9fc0ff;border-radius:5px;padding:4px 9px;font:11.5px ui-monospace,monospace;cursor:pointer}
75
+ </style>
76
+ <script type="importmap">{"imports":{"three":"https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js","three/addons/":"https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/"}}</script>
77
+ </head>
78
+ <body>
79
+ <div class="main" id="main"></div>
80
+ <script id="D" type="application/json">{"eps": [{"slug": "restock_20260916_ep000", "dataset_dir": "faberu_restock_20260916_ep000", "task": "restock_20260916", "leaf": "Restocking a retail shelf", "leaf_instr": "Fetch a bottle from the table and restock the shelf", "mode": "VR teleoperation, mobile manipulation", "perspective": "Robot egocentric", "planned": true, "rationale": "We can collect data from robots deployed in real-world working scenes. This episode is included in Ego Sample to showcase that sensor capability: a deployed robot captures rich, synchronized multi-sensor data (stereo, fisheye and color cameras, metric depth, dual lidar, IMU, odometry, joint and teleop streams). Robot-egocentric capture is a planned data line.", "ep": 0, "task_name": "Retail shelf restocking (mobile manipulation)", "instruction": "Drive from the shelf to the table, pick up the bottle with the right gripper, carry it back and place it on the shelf", "duration": 80.4, "nframes": 2412, "fps": 30, "hw": "Faber U", "vendor": "FF", "region": "US", "tags": ["US"], "taxo": {"environment_l1": "Retail and Consumer Goods", "environment_l2": "Grocery Store", "environment_l3": "Shelf Aisle", "naics_primary_code": "445110", "naics_primary_title": "Supermarkets and Other Grocery Retailers", "taxonomy_source": "OpenAI Robotics Data Requirements 08-21-2026 (Environment/Skill Group/Jobs taxonomies)", "collection_setup": "staged retail shelf in a lab space", "skill_group": "Inventory & Stock Management", "job_family": "Stock clerk", "task_difficulty": "Medium"}, "n_segments": 22, "skills": {"Navigate": 7, "Wait": 3, "Rotate": 3, "Open": 2, "Retract": 2, "Close": 1, "Pick": 1, "Lift": 1, "Reach": 1, "Place": 1}, "skill_list": ["Close", "Lift", "Navigate", "Open", "Pick", "Place", "Reach", "Retract", "Rotate", "Wait"], "qc": {"status": "pass", "total": 10, "passed": 8, "warnings": 2, "failures": 0}, "state_dim": 27, "action_dim": 19, "depth_valid": 0.847, "lidar_scans": [810, 810], "segments": [{"skill": "Wait", "label": "The robot stands in front of the retail shelf while the teleoperator gets ready", "ts": 0.0, "te": 13.0, "mistake": false}, {"skill": "Close", "label": "The right gripper closes to its travel position", "ts": 13.0, "te": 14.25, "mistake": false}, {"skill": "Navigate", "label": "The robot backs away from the shelf", "ts": 14.25, "te": 16.75, "mistake": false}, {"skill": "Rotate", "label": "The robot turns about 95 degrees right to face the work table", "ts": 16.75, "te": 20.0, "mistake": false}, {"skill": "Wait", "label": "The robot pauses facing the table", "ts": 20.0, "te": 21.25, "mistake": false}, {"skill": "Navigate", "label": "The robot drives forward toward the table", "ts": 21.25, "te": 23.25, "mistake": false}, {"skill": "Rotate", "label": "The robot corrects its heading toward the table", "ts": 23.25, "te": 25.25, "mistake": false}, {"skill": "Navigate", "label": "The robot drives up to the table edge and stops", "ts": 25.25, "te": 32.0, "mistake": false}, {"skill": "Open", "label": "The right gripper opens in front of the bottle on the table", "ts": 32.0, "te": 34.0, "mistake": false}, {"skill": "Pick", "label": "The right arm reaches the bottle on the table and the gripper closes on it", "ts": 34.0, "te": 40.25, "mistake": false}, {"skill": "Lift", "label": "The right arm lifts the bottle and retracts it close to the body", "ts": 40.25, "te": 44.25, "mistake": false}, {"skill": "Navigate", "label": "The robot drives back toward the shelf while holding the bottle", "ts": 44.25, "te": 48.5, "mistake": false}, {"skill": "Rotate", "label": "The robot turns about 95 degrees left to face the shelf", "ts": 48.5, "te": 52.0, "mistake": false}, {"skill": "Navigate", "label": "The robot aligns itself with the shelf", "ts": 52.0, "te": 54.75, "mistake": false}, {"skill": "Reach", "label": "The right arm raises the bottle toward the middle shelf", "ts": 54.75, "te": 61.0, "mistake": false}, {"skill": "Navigate", "label": "The robot repositions sideways along the shelf to reach a free slot", "ts": 61.0, "te": 65.75, "mistake": false}, {"skill": "Place", "label": "The right arm moves the bottle onto the middle shelf", "ts": 65.75, "te": 70.25, "mistake": false}, {"skill": "Open", "label": "The right gripper opens to release the bottle on the shelf", "ts": 70.25, "te": 71.5, "mistake": false}, {"skill": "Retract", "label": "The right arm withdraws from the shelf", "ts": 71.5, "te": 74.0, "mistake": false}, {"skill": "Navigate", "label": "The robot backs away from the shelf and straightens up", "ts": 74.0, "te": 76.75, "mistake": false}, {"skill": "Retract", "label": "Both arms return to the home pose", "ts": 76.75, "te": 78.5, "mistake": false}, {"skill": "Wait", "label": "The robot stops; episode ends", "ts": 78.5, "te": 80.4, "mistake": false}]}], "skc": {"Clip": "#4f83cc", "Close": "#e0913a", "CloseBox": "#5aa469", "Cut": "#c0504d", "Dip": "#8064a2", "Drop": "#4bacc6", "Flip": "#d98594", "Fold": "#9b8f2e", "HandOver": "#7f8fa6", "Hang": "#674ea7", "Insert": "#3d9970", "Knead": "#b8860b", "Open": "#a0522d", "Paint": "#2e8b8b", "Pick": "#b5651d", "Place": "#6a5acd", "Plug": "#556b2f", "Pour": "#8b3a62", "Press": "#487eb0", "PressButton": "#cc8e35", "Pull": "#6b8e23", "Rinse": "#9370db", "Rotate": "#20b2aa", "Scan": "#cd853f", "Scoop": "#4f83cc", "Screw (tighten)": "#e0913a", "Smear": "#5aa469", "Stack": "#c0504d", "Stir": "#8064a2", "Straighten": "#4bacc6", "Takedown": "#d98594", "Takeout (remove)": "#9b8f2e", "Tear": "#7f8fa6", "Wipe": "#674ea7", "dip": "#3d9970", "plug": "#b8860b", "Wait": "#5d6574", "Navigate": "#3fa7d6", "Lift": "#e07a5f", "Reach": "#81b29a", "Retract": "#b388eb"}, "hf": "https://huggingface.co/datasets/faradayfuture/egoSample"}</script>
81
+ <script>
82
+ const D=JSON.parse(document.getElementById('D').textContent);
83
+ const EPS=D.eps, SKC=D.skc, HF=D.hf;
84
+ const CAMS=['head_stereo_left','head_color','head_stereo_right','head_left_fisheye','head_depth','head_right_fisheye'];
85
+ const AX={x:'#e0616f',y:'#41c76b',z:'#4f8ff7'};
86
+ const PAL=['#e0616f','#41c76b','#4f8ff7','#e0a33a','#b57be0','#3fc1c9','#d98594'];
87
+ let EP=null, SIG=null, vids=[], master=null, playing=false, raf=0, syncT=0, _lastCi=-2, _toggle=null, setCloudT=null;
88
+ const esc=s=>String(s??'').replace(/[&<>"]/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[c]));
89
+ const fmt=t=>{t=Math.max(0,t);const m=Math.floor(t/60),s=t%60;return m+':'+s.toFixed(2).padStart(5,'0');};
90
+ const skc=s=>SKC[s]||'#666';
91
+
92
+ function select(i){ EP=EPS[i]; SIG=null; cancelAnimationFrame(raf); clearInterval(syncT); playing=false; render(); loadSig(EP.slug); }
93
+ function loadSig(slug){ window.EPDATA=window.EPDATA||{};
94
+ const done=()=>{ SIG=window.EPDATA[slug]; if(!SIG) return; sizeCharts(); drawCharts(); init3D(); updateUI(master.currentTime||0);
95
+ const go=()=>{ if(_toggle&&!playing) _toggle(); }; if(master.readyState>=3) go(); else master.addEventListener('canplay',go,{once:true}); };
96
+ if(window.EPDATA[slug]){ done(); return; }
97
+ const s=document.createElement('script'); s.src='epdata/'+slug+'.js'; s.onload=done; document.body.appendChild(s); }
98
+
99
+ function render(){
100
+ const e=EP, dur=e.duration, segs=e.segments, T=e.taxo;
101
+ const tile=(c,big)=>`<div class="vp${big?' big':''}" ><video data-c="${c}" src="media/${e.slug}/${c}.mp4" muted playsinline preload="auto"></video><span class="lbl">${c}${c==='head_depth'?' · filtered preview · raw 16-bit mm in dataset':''}</span>${c==='head_depth'?'<span class="rd" id="drd">hover for depth</span>':''}</div>`;
102
+ const chips=segs.map((s,i)=>`<div class="chip" data-i="${i}" style="background:${skc(s.skill)}" title="${esc(s.skill)} — ${esc(s.label)}">${i+1}</div>`).join('');
103
+ const slist=segs.map((s,i)=>`<div class="si" data-i="${i}"><div><span class="n" style="background:${skc(s.skill)}">${i+1}</span><span class="k">${esc(s.skill)}</span><span class="lb"> · ${s.ts.toFixed(2)}–${s.te.toFixed(2)}s</span></div><div class="m">${esc(s.label)}</div></div>`).join('');
104
+ const legend=Object.keys(e.skills).sort().map(s=>`<span class="it"><span class="sw" style="background:${skc(s)}"></span>${esc(s)} <b style="color:#e6e8ee">${e.skills[s]}</b></span>`).join('');
105
+ const HFDIR=HF+'/tree/main/'+e.dataset_dir, raw=f=>HF+'/resolve/main/'+e.dataset_dir+'/'+f;
106
+ const camDl=CAMS.map(c=>`<a class="fc dl" href="media/${e.slug}/${c}.mp4" download="${e.slug}_${c}.mp4">${c}.mp4</a>`).join('');
107
+ const refs=['data/file-000.parquet','meta/info.json','meta/ff/segments.parquet','meta/ff/episode_meta.parquet','meta/ff/quality.json','meta/ff/camera_params.json','depth/head_depth/file-000.mkv','pointclouds/lidar_front/file-000.parquet','pointclouds/lidar_back/file-000.parquet','sensors/imu_chassis.parquet','sensors/odom.parquet','sensors/vr_teleop_device.parquet']
108
+ .map(f=>`<a class="fc dl" href="${raw(f)}" target="_blank" rel="noopener">${f}</a>`).join('');
109
+ document.getElementById('main').innerHTML=`
110
+ <header>
111
+ <h1>${esc(e.task_name)}</h1>
112
+ <div class="sub"><code>${esc(e.task)}</code> · ${esc(e.vendor)} ${esc(e.hw)} · <b style="color:#e0a33a">robot egocentric</b> · ${esc(e.mode)} · episode ${e.ep} · ${e.duration}s @ ${e.fps}fps</div>
113
+ <div class="badges">${e.tags.map(t=>`<span class="bdg tag">${esc(t)}</span>`).join('')}<span class="bdg tag" style="background:#e0a33a;border-color:#e0a33a">Robot egocentric</span>${e.planned?'<span class="bdg tag" style="background:#b388eb;border-color:#b388eb">Planned</span>':''}<span class="bdg">instruction: <b>${esc(e.instruction)}</b></span></div>
114
+ <div class="badges"><span class="bdg">environment <b>${esc(T.environment_l1)} › ${esc(T.environment_l2)} › ${esc(T.environment_l3)}</b></span><span class="bdg">skill group <b>${esc(T.skill_group)}</b></span><span class="bdg">job <b>${esc(T.job_family)}</b></span><span class="bdg">difficulty <b>${esc(T.task_difficulty)}</b></span><span class="bdg">NAICS <b>${esc(T.naics_primary_code)}</b></span></div>
115
+ </header>
116
+ <div class="layout">
117
+ <div class="panel">
118
+ <div class="vgrid">${CAMS.map(c=>tile(c)).join('')}</div>
119
+ <div class="chk" id="chk"></div>
120
+ <div class="three" id="three"><span class="lbl">world (odom) · robot links · lidar front <span style="color:#ff8a70">■</span> back <span style="color:#6ea8ff">■</span> · base path · ceiling (&gt;2.3 m) and &gt;10 m hidden</span><span class="hint">drag to orbit · scroll to zoom</span></div>
121
+ </div>
122
+ <div class="panel">
123
+ <div class="spans-head">temporal spans (${e.n_segments}) · <b>${esc(e.task_name)}</b></div>
124
+ <div class="tl" id="tl"></div><div class="chips" id="chips">${chips}</div><div class="slist" id="slist">${slist}</div>
125
+ </div>
126
+ </div>
127
+ <div class="legend">${legend}</div>
128
+ <div class="panel controls"><button id="play">&#9654;</button><input type="range" id="seek" min="0" max="10000" value="0"><span class="t" id="tlab">0:00.00 / ${fmt(dur)}</span>
129
+ <select id="rate"><option value="1">1×</option><option value="2">2×</option><option value="4">4×</option></select></div>
130
+ <div class="badges" style="padding:0 20px;margin-top:8px">
131
+ <span class="bdg"><b>${e.nframes}</b> frames</span><span class="bdg"><b>${e.n_segments}</b> skill spans</span><span class="bdg">state dim <b>${e.state_dim}</b> · action dim <b>${e.action_dim}</b></span>
132
+ <span class="bdg">5 RGB cams + depth</span><span class="bdg">2 lidars · ${e.lidar_scans[0]}+${e.lidar_scans[1]} scans</span><span class="bdg">IMU 200 Hz</span>
133
+ <span class="bdg">depth valid <b>${(e.depth_valid*100).toFixed(0)}%</b></span>
134
+ <span class="bdg">QC <b style="color:${e.qc.failures?'#ff5c5c':'#6fe08a'}">${e.qc.passed}/${e.qc.total}</b>${e.qc.warnings?` · ${e.qc.warnings} warn`:''}</span></div>
135
+ <div class="note"><b>Why a robot episode in Ego Sample.</b> ${esc(e.rationale||'')}</div>
136
+ <div class="panel readout"><div class="rt">current-time readout</div><div class="rg">
137
+ <div class="ro"><span class="k">time</span><span class="v" id="roT">–</span></div>
138
+ <div class="ro"><span class="k">current skill</span><span class="v b" id="roSk">–</span></div>
139
+ <div class="ro"><span class="k">span</span><span class="v" id="roSp">–</span></div>
140
+ <div class="ro"><span class="k">base x / y / yaw</span><span class="v" id="roB">–</span></div>
141
+ <div class="ro"><span class="k">base speed (odom)</span><span class="v" id="roV">–</span></div>
142
+ <div class="ro"><span class="k">gripper L / R (rad)</span><span class="v" id="roG">–</span></div>
143
+ <div class="ro"><span class="k">teleop active</span><span class="v hi" id="roA">–</span></div>
144
+ <div class="ro"><span class="k">right end xyz (base_link)</span><span class="v" id="roE">–</span></div>
145
+ <div class="ro"><span class="k">max |target − measured| arm</span><span class="v" id="roTr">–</span></div></div></div>
146
+ <div class="charts">
147
+ <div class="panel chp"><div class="l">left arm joints <span class="u">rad · solid measured, dim target</span></div><canvas id="chL"></canvas></div>
148
+ <div class="panel chp"><div class="l">right arm joints <span class="u">rad · solid measured, dim target</span></div><canvas id="chR"></canvas></div>
149
+ <div class="panel chp"><div class="l">grippers <span class="u">rad</span><span class="leg"><span><i style="background:#e0616f"></i>L pos</span><span><i style="background:#41c76b"></i>R pos</span><span><i style="background:#7a3a42"></i>L cmd</span><span><i style="background:#2a6b3e"></i>R cmd</span></span></div><canvas id="chG"></canvas></div>
150
+ <div class="panel chp"><div class="l">base velocity <span class="u">m/s, rad/s · solid odom, dim teleop cmd</span><span class="leg"><span><i style="background:${AX.x}"></i>vx</span><span><i style="background:${AX.y}"></i>vy</span><span><i style="background:${AX.z}"></i>wz</span></span></div><canvas id="chV"></canvas></div>
151
+ <div class="panel chp"><div class="l">chassis IMU · gyro <span class="u">rad/s</span><span class="leg"><span><i style="background:${AX.x}"></i>x</span><span><i style="background:${AX.y}"></i>y</span><span><i style="background:${AX.z}"></i>z</span></span></div><canvas id="chI"></canvas></div>
152
+ <div class="panel chp"><div class="l">end-effector wrench · force <span class="u">N</span><span class="leg"><span><i style="background:#e0616f"></i>L fx fy fz</span><span><i style="background:#41c76b"></i>R fx fy fz</span></span></div><canvas id="chW"></canvas></div>
153
+ </div>
154
+ <div class="panel" style="margin:14px 20px 30px;padding:11px 16px">
155
+ <div class="rt">episode clips &mdash; click to download (preview encodes)</div><div class="files" style="padding:0 0 6px">${camDl}</div>
156
+ <div class="rt" style="margin-top:8px">dataset files &middot; <a href="${HFDIR}" target="_blank" rel="noopener" style="text-transform:none;color:#7fe0a0">full episode on HuggingFace &rarr;</a>
157
+ <button class="cpy" id="cpy" style="margin-left:8px">📋 copy: hf download (all files)</button></div>
158
+ <div class="files">${refs}</div></div>`;
159
+ wire();
160
+ }
161
+
162
+ function wire(){
163
+ const e=EP, dur=e.duration, segs=e.segments;
164
+ vids=[...document.querySelectorAll('.vp video')]; master=vids[0];
165
+ const playBtn=document.getElementById('play'), seek=document.getElementById('seek'), rate=document.getElementById('rate');
166
+ function toggle(){ playing=!playing; playBtn.innerHTML=playing?'&#10073;&#10073;':'&#9654;';
167
+ if(playing){ vids.forEach(v=>{ v.playbackRate=+rate.value; v.play().catch(()=>{}); }); tick(); clearInterval(syncT); syncT=setInterval(()=>syncSlaves(false),250); }
168
+ else { vids.forEach(v=>v.pause()); clearInterval(syncT); syncSlaves(true); } }
169
+ playBtn.onclick=toggle; _toggle=toggle;
170
+ rate.onchange=()=>vids.forEach(v=>v.playbackRate=+rate.value);
171
+ seek.oninput=()=>setTime(seek.value/10000*dur);
172
+ const tl=document.getElementById('tl'); tl.innerHTML='';
173
+ segs.forEach(s=>{ const b=document.createElement('i'); b.style.width=((s.te-s.ts)/dur*100)+'%'; b.style.background=skc(s.skill); b.title=`${s.skill} — ${s.label}`;
174
+ b.onclick=ev=>{ ev.stopPropagation(); setTime(s.ts+0.01); }; tl.appendChild(b); });
175
+ document.querySelectorAll('#chips .chip,#slist .si').forEach(c=>c.onclick=()=>setTime(segs[+c.dataset.i].ts+0.01));
176
+ ['chL','chR','chG','chV','chI','chW'].forEach(id=>{ const c=document.getElementById(id); c.onclick=ev=>{ const r=c.getBoundingClientRect(); setTime((ev.clientX-r.left)/r.width*dur); }; });
177
+ master.addEventListener('seeked',()=>syncSlaves(true));
178
+ document.getElementById('cpy').onclick=()=>{ const cmd=`hf download ${HF.replace('https://huggingface.co/datasets/','')} --repo-type dataset --include "${e.dataset_dir}/*" --local-dir .`;
179
+ navigator.clipboard.writeText(cmd).then(()=>{ document.getElementById('cpy').textContent='✓ copied'; setTimeout(()=>document.getElementById('cpy').textContent='📋 copy: hf download (all files)',1500); }); };
180
+ document.onkeydown=ev=>{ if(ev.target.tagName==='SELECT') return;
181
+ if(ev.code==='Space'){ ev.preventDefault(); toggle(); }
182
+ else if(ev.code==='ArrowRight'){ ev.preventDefault(); const n=segs.find(s=>s.ts>master.currentTime+0.05); if(n) setTime(n.ts+0.01); }
183
+ else if(ev.code==='ArrowLeft'){ ev.preventDefault(); const p=[...segs].reverse().find(s=>s.ts<master.currentTime-0.3); setTime(p?p.ts+0.01:0); } };
184
+ // depth hover readout
185
+ const dv=vids.find(v=>v.dataset.c==='head_depth'), rd=document.getElementById('drd'), cache={};
186
+ dv.parentElement.addEventListener('mousemove',ev=>{ if(!SIG) return; const r=dv.getBoundingClientRect(), ar=640/400; let w=r.width,h=w/ar; if(h>r.height){h=r.height;w=h*ar;}
187
+ const x=(ev.clientX-r.left-(r.width-w)/2)/w, y=(ev.clientY-r.top-(r.height-h)/2)/h; if(x<0||x>1||y<0||y>1){ rd.textContent='—'; return; }
188
+ const [gw,gh]=SIG.depth_grid_wh, fi=Math.min(SIG.depth_grid.length-1,Math.round(dv.currentTime*30/SIG.depth_grid_every));
189
+ const g=cache[fi]||(cache[fi]=new Uint16Array(Uint8Array.from(atob(SIG.depth_grid[fi]),c=>c.charCodeAt(0)).buffer)); const v=g[Math.floor(y*gh)*gw+Math.floor(x*gw)];
190
+ rd.textContent=v?`${(v/1000).toFixed(3)} m`:'invalid'; });
191
+ dv.parentElement.addEventListener('mouseleave',()=>rd.textContent='hover for depth');
192
+ setTime(0);
193
+ }
194
+ function inBuf(v,t){ const b=v.buffered; for(let i=0;i<b.length;i++) if(t>=b.start(i)-0.2&&t<=b.end(i)+0.2) return true; return false; }
195
+ function syncSlaves(hard){ const t=master.currentTime, base=+document.getElementById('rate').value;
196
+ for(const v of vids){ if(v===master||v.readyState<1) continue;
197
+ if(hard){ v.playbackRate=base; if(!v.seeking&&Math.abs(v.currentTime-t)>0.04){ try{v.currentTime=t;}catch(e){} } continue; }
198
+ if(!playing) continue; if(v.paused&&!v.ended) v.play().catch(()=>{});
199
+ const d=v.currentTime-t, ad=Math.abs(d);
200
+ if(ad>0.75){ if(!v.seeking&&inBuf(v,t)){ try{v.currentTime=t;}catch(e){} } v.playbackRate=base; }
201
+ else v.playbackRate=ad>0.035?base*Math.max(.5,Math.min(1.6,1-d*3)):base; } }
202
+ function setTime(t){ t=Math.max(0,Math.min(EP.duration-0.01,t)); try{ if(Math.abs(master.currentTime-t)>0.03) master.currentTime=t; }catch(e){} syncSlaves(true); updateUI(t); }
203
+ function tick(){ if(!playing) return; const t=master.currentTime; updateUI(t);
204
+ if(t>=EP.duration-0.05){ try{master.currentTime=0;}catch(e){} syncSlaves(true); vids.forEach(v=>v.play().catch(()=>{})); }
205
+ raf=requestAnimationFrame(tick); }
206
+ function curSpan(t){ const s=EP.segments; for(let i=0;i<s.length;i++) if(t>=s[i].ts&&t<s[i].te) return i; return -1; }
207
+ function updateUI(t){
208
+ const dur=EP.duration, segs=EP.segments;
209
+ document.getElementById('seek').value=t/dur*10000; document.getElementById('tlab').textContent=fmt(t)+' / '+fmt(dur);
210
+ document.getElementById('roT').textContent=t.toFixed(2)+'s';
211
+ const ci=curSpan(t); document.getElementById('roSk').textContent=ci>=0?segs[ci].skill:'–'; document.getElementById('roSp').textContent=ci>=0?`${ci+1} / ${segs.length}`:'–';
212
+ if(ci!==_lastCi){ const sl=document.querySelectorAll('#slist .si'); sl.forEach((el,k)=>el.classList.toggle('on',k===ci));
213
+ document.querySelectorAll('#chips .chip').forEach((el,k)=>el.classList.toggle('on',k===ci));
214
+ if(ci>=0){ const box=document.getElementById('slist'), el=sl[ci], top=el.offsetTop-box.offsetTop; if(top<box.scrollTop||top+el.offsetHeight>box.scrollTop+box.clientHeight) box.scrollTop=top-box.clientHeight/2; }
215
+ _lastCi=ci; }
216
+ if(!SIG) return;
217
+ const j=Math.min(SIG.n-1,Math.round(t*SIG.fps)), b=SIG.base[j], bv=SIG.base_vel[j], g=SIG.grip[j], ee=SIG.ee[j];
218
+ document.getElementById('roB').textContent=`${b[0].toFixed(2)} / ${b[1].toFixed(2)} m / ${(b[2]*180/Math.PI).toFixed(0)}°`;
219
+ document.getElementById('roV').textContent=`${Math.hypot(bv[0],bv[1]).toFixed(2)} m/s · ${(bv[2]*180/Math.PI).toFixed(0)}°/s`;
220
+ document.getElementById('roG').textContent=`${g[0].toFixed(2)} / ${g[1].toFixed(2)}`;
221
+ document.getElementById('roA').textContent=SIG.active[j]?'yes':'no';
222
+ document.getElementById('roE').textContent=ee.slice(7,10).map(v=>v.toFixed(2)).join(', ');
223
+ document.getElementById('roTr').textContent=Math.max(...SIG.arm[j].map((v,k)=>Math.abs(v-SIG.arm_target[j][k]))).toFixed(3)+' rad';
224
+ drawPlayheads(t); if(setCloudT) setCloudT(t);
225
+ }
226
+
227
+ /* ---------- charts ---------- */
228
+ const CH={};
229
+ function sizeCharts(){ document.querySelectorAll('.chp canvas').forEach(c=>{ c.width=c.clientWidth*devicePixelRatio; c.height=84*devicePixelRatio; }); }
230
+ function plot(id,series){ const c=document.getElementById(id); if(!c) return; const g=c.getContext('2d'), w=c.width, h=c.height, dpr=devicePixelRatio;
231
+ g.fillStyle='#0b0e13'; g.fillRect(0,0,w,h); let lo=Infinity,hi=-Infinity; series.forEach(s=>s.d.forEach(v=>{ if(v<lo)lo=v; if(v>hi)hi=v; })); if(lo===hi){lo-=1;hi+=1;}
232
+ const n=series[0].d.length, Y=v=>h-4*dpr-(v-lo)/(hi-lo)*(h-8*dpr);
233
+ if(lo<0&&hi>0){ g.strokeStyle='#1c2028'; g.beginPath(); g.moveTo(0,Y(0)); g.lineTo(w,Y(0)); g.stroke(); }
234
+ series.forEach(s=>{ g.strokeStyle=s.c; g.globalAlpha=s.a||1; g.lineWidth=dpr; g.beginPath(); s.d.forEach((v,i)=>{ const x=i/(n-1)*w; i?g.lineTo(x,Y(v)):g.moveTo(x,Y(v)); }); g.stroke(); });
235
+ g.globalAlpha=1; g.fillStyle='#5a6373'; g.font=`${10*dpr}px ui-monospace`; g.fillText(hi.toFixed(2),4*dpr,11*dpr); g.fillText(lo.toFixed(2),4*dpr,h-3*dpr);
236
+ CH[id]=g.getImageData(0,0,w,h); }
237
+ const colOf=(a,k)=>a.map(r=>r[k]);
238
+ function drawCharts(){ if(!SIG) return; sizeCharts();
239
+ const arm=(off)=>{ const s=[]; for(let k=0;k<7;k++){ s.push({d:colOf(SIG.arm_target,off+k),c:PAL[k],a:.3}); s.push({d:colOf(SIG.arm,off+k),c:PAL[k]}); } return s; };
240
+ plot('chL',arm(0)); plot('chR',arm(7));
241
+ plot('chG',[{d:colOf(SIG.grip_cmd,0),c:'#7a3a42'},{d:colOf(SIG.grip_cmd,1),c:'#2a6b3e'},{d:colOf(SIG.grip,0),c:'#e0616f'},{d:colOf(SIG.grip,1),c:'#41c76b'}]);
242
+ plot('chV',[{d:colOf(SIG.base_cmd,0),c:AX.x,a:.35},{d:colOf(SIG.base_cmd,1),c:AX.y,a:.35},{d:colOf(SIG.base_cmd,2),c:AX.z,a:.35},{d:colOf(SIG.base_vel,0),c:AX.x},{d:colOf(SIG.base_vel,1),c:AX.y},{d:colOf(SIG.base_vel,2),c:AX.z}]);
243
+ plot('chI',[3,4,5].map((k,i)=>({d:colOf(SIG.imu,k),c:[AX.x,AX.y,AX.z][i]})));
244
+ plot('chW',[0,1,2].map(k=>({d:colOf(SIG.wrench,k),c:'#e0616f',a:.5+k*.25})).concat([6,7,8].map(k=>({d:colOf(SIG.wrench,k),c:'#41c76b',a:.5+(k-6)*.25}))));
245
+ drawPlayheads(master?master.currentTime:0); }
246
+ function drawPlayheads(t){ const f=t/EP.duration; Object.entries(CH).forEach(([id,img])=>{ const c=document.getElementById(id); if(!c) return; const g=c.getContext('2d'); g.putImageData(img,0,0);
247
+ g.strokeStyle='rgba(255,255,255,.6)'; g.lineWidth=devicePixelRatio; g.beginPath(); g.moveTo(f*c.width,0); g.lineTo(f*c.width,c.height); g.stroke(); }); }
248
+ window.addEventListener('resize',()=>{ clearTimeout(window._rz); window._rz=setTimeout(drawCharts,150); });
249
+
250
+ /* ---------- 3D world view ---------- */
251
+ async function init3D(){
252
+ const THREE=await import('three'); const {OrbitControls}=await import('three/addons/controls/OrbitControls.js');
253
+ const el=document.getElementById('three'); el.querySelectorAll('canvas').forEach(c=>c.remove());
254
+ const R=new THREE.WebGLRenderer({antialias:true}); R.setPixelRatio(devicePixelRatio); el.appendChild(R.domElement);
255
+ const scene=new THREE.Scene(); scene.background=new THREE.Color(0x0b0d12);
256
+ const cam=new THREE.PerspectiveCamera(50,1,.05,200); cam.up.set(0,0,1);
257
+ const B0=SIG.base; let cx=0,cy=0; B0.forEach(b=>{cx+=b[0];cy+=b[1];}); cx/=B0.length; cy/=B0.length;
258
+ let x0=1e9,x1=-1e9,y0=1e9,y1=-1e9; B0.forEach(b=>{x0=Math.min(x0,b[0]);x1=Math.max(x1,b[0]);y0=Math.min(y0,b[1]);y1=Math.max(y1,b[1]);}); const span=Math.max(x1-x0,y1-y0,2)+3;
259
+ cam.position.set(cx-span*.75,cy-span*.75,span*.7); const ctl=new OrbitControls(cam,R.domElement); ctl.target.set(cx,cy,.6); ctl.update();
260
+ const grid=new THREE.GridHelper(16,32,0x2a3a5a,0x1a2030); grid.rotation.x=Math.PI/2; grid.position.set(cx,cy,0); scene.add(grid);
261
+ // base path
262
+ const path=new THREE.Line(new THREE.BufferGeometry().setFromPoints(B0.map(b=>new THREE.Vector3(b[0],b[1],.01))),new THREE.LineBasicMaterial({color:0x6ea8ff})); scene.add(path);
263
+ const robot=new THREE.Group(); scene.add(robot);
264
+ const body=new THREE.LineSegments(new THREE.BufferGeometry(),new THREE.LineBasicMaterial({color:0xd6dbe6})); robot.add(body);
265
+ const joints=new THREE.Points(new THREE.BufferGeometry(),new THREE.PointsMaterial({color:0xffc857,size:.045})); robot.add(joints);
266
+ const foot=new THREE.Mesh(new THREE.BoxGeometry(.62,.52,.25),new THREE.MeshBasicMaterial({color:0x4a5570,wireframe:true})); foot.position.set(.1,0,.14); robot.add(foot);
267
+ const ax=new THREE.AxesHelper(.35); robot.add(ax);
268
+ const L=SIG.links, idx=n=>L.indexOf(n);
269
+ const bones=[['chassis_link','body_link1'],['body_link1','body_link2'],['body_link2','body_link3'],['body_link3','body_link5'],['body_link5','head_link3'],['body_link5','arm_base_link']];
270
+ ['l','r'].forEach(s=>{ bones.push(['arm_base_link',`arm_${s}_link1`]); for(let i=1;i<7;i++) bones.push([`arm_${s}_link${i}`,`arm_${s}_link${i+1}`]); bones.push([`arm_${s}_link7`,`arm_${s}_end_link`],[`arm_${s}_end_link`,`gripper_${s}_center_link`]); });
271
+ const clouds={}; const CC={lidar_front:[1,.54,.44],lidar_back:[.43,.66,1]};
272
+ Object.keys(SIG.clouds).forEach(k=>{ const p=new THREE.Points(new THREE.BufferGeometry(),new THREE.PointsMaterial({size:.03,vertexColors:true})); scene.add(p); clouds[k]={p,cur:-1}; });
273
+ document.getElementById('chk').innerHTML=Object.keys(clouds).map(k=>`<label><input type="checkbox" data-k="${k}" checked>${k}</label>`).join('')+`<label><input type="checkbox" data-k="__path" checked>base path</label><label><input type="checkbox" data-k="__follow">camera follows robot</label>`;
274
+ let follow=false;
275
+ document.querySelectorAll('#chk input').forEach(i=>i.onchange=()=>{ const k=i.dataset.k; if(k==='__path') path.visible=i.checked; else if(k==='__follow') follow=i.checked; else clouds[k].p.visible=i.checked; });
276
+ const dec=b=>Uint8Array.from(atob(b),c=>c.charCodeAt(0));
277
+ let last=null;
278
+ setCloudT=t=>{ const j=Math.min(SIG.n-1,Math.round(t*SIG.fps)), b=SIG.base[j];
279
+ robot.position.set(b[0],b[1],0); robot.rotation.set(0,0,b[2]);
280
+ if(follow&&last){ const dx=b[0]-last[0], dy=b[1]-last[1]; cam.position.x+=dx; cam.position.y+=dy; ctl.target.x+=dx; ctl.target.y+=dy; } last=b;
281
+ const lt=SIG.link_t; let li=0; while(li<lt.length-1&&lt[li+1]<=t) li++; const P=SIG.link_p[li];
282
+ const seg=[]; bones.forEach(([a,c])=>{ const ia=idx(a), ic=idx(c); if(ia>=0&&ic>=0) seg.push(...P[ia],...P[ic]); });
283
+ body.geometry.setAttribute('position',new THREE.Float32BufferAttribute(seg,3)); joints.geometry.setAttribute('position',new THREE.Float32BufferAttribute(P.flat(),3));
284
+ Object.entries(clouds).forEach(([k,C])=>{ const fr=SIG.clouds[k]; let i=0; while(i<fr.length-1&&fr[i+1].t<=t) i++; if(i===C.cur) return; C.cur=i;
285
+ const f=fr[i], q=new Int16Array(dec(f.xyz).buffer), I=dec(f.i), n=I.length, bj=Math.max(0,Math.min(SIG.n-1,Math.round(f.t*SIG.fps))), bb=SIG.base[bj], cs=Math.cos(bb[2]), sn=Math.sin(bb[2]);
286
+ const pos=new Float32Array(n*3), col=new Float32Array(n*3), base=CC[k]||[.8,.8,.8];
287
+ for(let m=0;m<n;m++){ const x=q[3*m]/1000,y=q[3*m+1]/1000,z=q[3*m+2]/1000; pos[3*m]=bb[0]+cs*x-sn*y; pos[3*m+1]=bb[1]+sn*x+cs*y; pos[3*m+2]=z;
288
+ const s=.35+.65*I[m]/255; col[3*m]=base[0]*s; col[3*m+1]=base[1]*s; col[3*m+2]=base[2]*s; }
289
+ C.p.geometry.setAttribute('position',new THREE.BufferAttribute(pos,3)); C.p.geometry.setAttribute('color',new THREE.BufferAttribute(col,3)); C.p.geometry.computeBoundingSphere(); }); };
290
+ const resize=()=>{ const w=el.clientWidth,h=el.clientHeight; if(!w) return; R.setSize(w,h); cam.aspect=w/h; cam.updateProjectionMatrix(); };
291
+ new ResizeObserver(resize).observe(el); resize();
292
+ (function loop(){ requestAnimationFrame(loop); ctl.update(); R.render(scene,cam); })();
293
+ setCloudT(master.currentTime||0);
294
+ }
295
+ function fromHash(){ const s=decodeURIComponent(location.hash.slice(1)); const i=EPS.findIndex(e=>e.slug===s); select(i>=0?i:0); }
296
+ window.addEventListener('hashchange',fromHash); fromHash();
297
+ </script>
298
+ </body></html>
egoSample/index.html CHANGED
@@ -23,6 +23,8 @@ body{background:#07090d;color:#e6e8ee;font:14px/1.5 -apple-system,"Helvetica Neu
23
  .leaf .lt{font-size:12.5px;font-weight:600}
24
  .leaf .lm{font-size:10.5px;color:#7f8896;margin-top:2px;display:flex;gap:7px;align-items:center}
25
  .rig{font:9.5px ui-monospace,monospace;color:#6f92c8;background:#12151c;border:1px solid #1c2028;border-radius:4px;padding:0 4px}
 
 
26
  .rig.tag{color:#07090d;background:#6f9fe0;border-color:#6f9fe0;font-weight:700}
27
  .main{flex:1;display:flex;flex-direction:column;min-width:0}
28
  .bar{display:flex;align-items:center;gap:10px;padding:9px 16px;border-bottom:1px solid #1c2028;background:#0b0e13;min-height:44px}
@@ -33,7 +35,7 @@ iframe{flex:1;width:100%;border:0;background:#07090d}
33
  </style></head><body>
34
  <div class="wrap">
35
  <nav class="side">
36
- <div class="st"><a class="back" href="../index.html">&larr; all datasets</a><span>Ego Sample &mdash; 12 tasks</span></div>
37
  <div id="nav"></div>
38
  </nav>
39
  <div class="main">
@@ -41,7 +43,7 @@ iframe{flex:1;width:100%;border:0;background:#07090d}
41
  <iframe id="stage" allow="autoplay" src="about:blank"></iframe>
42
  </div>
43
  </div>
44
- <script id="D" type="application/json">[["Retail & Fresh Food", [["Fruit retail & cutting", {"kind": "mego", "src": "mego.html#task_15634_ep003", "slug": "task_15634_ep003", "dur": 30.97, "rig": "MEgo 5-cam", "instr": "Place the pears from the table into the plate and arrange them neatly"}], ["Fruit cutting station", {"kind": "mego", "src": "mego.html#task_15365_ep000", "slug": "task_15365_ep000", "dur": 38.98, "rig": "MEgo 5-cam", "instr": "Sort yellow fruits and pack them into plastic boxes"}], ["Arranging fruits on retail shelf", {"kind": "ego", "src": "ego-legacy/demo09/viewer.html", "dur": 55.0, "rig": "PICO stereo", "instr": ""}], ["Dicing watermelon for storage", {"kind": "ego", "src": "ego-legacy/demo07/viewer.html", "dur": 373.0, "rig": "PICO stereo", "instr": ""}]]], ["Office & Documents", [["Document scanning & archiving", {"kind": "mego", "src": "mego.html#task_15638_ep001", "slug": "task_15638_ep001", "dur": 37.98, "rig": "MEgo 5-cam", "instr": "Organize the documents on the table"}], ["Document printing & binding", {"kind": "mego", "src": "mego.html#task_15659_ep004", "slug": "task_15659_ep004", "dur": 38.98, "rig": "MEgo 5-cam", "instr": "Organize and bind the documents on the table."}], ["Wiping office desk surface", {"kind": "ego", "src": "ego-legacy/demo08/viewer.html", "dur": 85.8, "rig": "PICO stereo", "instr": ""}]]], ["Assembly & Manufacturing", [["Mouse & keyboard assembly", {"kind": "mego", "src": "mego.html#task_15648_ep002", "slug": "task_15648_ep002", "dur": 39.98, "rig": "MEgo 5-cam", "instr": "Place the white keycap on the keyboard and press it"}], ["Trimming threads from garment", {"kind": "ego", "src": "ego-legacy/demo10/viewer.html", "dur": 22.5, "rig": "PICO stereo", "instr": ""}]]], ["Home & Organizing", [["Organizing refrigerator items", {"kind": "ego", "src": "ego-legacy/demo02/viewer.html", "dur": 41.7, "rig": "PICO stereo", "instr": ""}], ["Organizing fan power cord", {"kind": "ego", "src": "ego-legacy/demo05/viewer.html", "dur": 72.5, "rig": "PICO stereo", "instr": ""}]]], ["Creative Workshops", [["Photo keychain crafting", {"kind": "jizhi", "src": "jizhi.html#craft_20260629_sticker_ep000", "slug": "craft_20260629_sticker_ep000", "dur": 79.0, "rig": "Jizhi Stereo", "tags": ["US"], "instr": "Trim photo stickers, mount them in acrylic keychain frames and round badge cases, and attach key rings"}]]]]</script>
45
  <script>
46
  const TREE = JSON.parse(document.getElementById('D').textContent);
47
  const esc = s => (s||'').replace(/[&<>"]/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[c]));
@@ -49,15 +51,15 @@ const fmtDur = d => d==null ? '' : (d>=60 ? (d/60).toFixed(1)+' min' : d.toFixed
49
 
50
  function build(){
51
  const box = document.getElementById('nav');
52
- TREE.forEach(([cat, leaves], ci)=>{
53
  const c = document.createElement('div'); c.className = 'cat' + (ci===0?' open':'');
54
- c.innerHTML = `<div class="ch"><span class="ct">${esc(cat)}</span><span class="cc">${leaves.length}</span></div><div class="cbody"></div>`;
55
  c.querySelector('.ch').onclick = ()=>c.classList.toggle('open');
56
  const body = c.querySelector('.cbody');
57
  leaves.forEach(([lbl, m])=>{
58
  const el = document.createElement('div'); el.className = 'leaf';
59
  el.innerHTML = `<div class="lt">${esc(lbl)}</div>
60
- <div class="lm"><span class="rig">${m.rig}</span>${(m.tags||[]).map(t=>`<span class="rig tag">${esc(t)}</span>`).join('')}${fmtDur(m.dur)}${m.instr?' · '+esc(m.instr):''}</div>`;
61
  el.onclick = ()=>open(m, lbl, el);
62
  body.appendChild(el);
63
  });
 
23
  .leaf .lt{font-size:12.5px;font-weight:600}
24
  .leaf .lm{font-size:10.5px;color:#7f8896;margin-top:2px;display:flex;gap:7px;align-items:center}
25
  .rig{font:9.5px ui-monospace,monospace;color:#6f92c8;background:#12151c;border:1px solid #1c2028;border-radius:4px;padding:0 4px}
26
+ .rc{display:flex;flex-direction:column;align-items:flex-start;gap:3px;flex:0 0 auto}
27
+ .pl{font-size:9px;font-weight:700;letter-spacing:.06em;color:#07090d;background:#b388eb;border-radius:3px;padding:0 4px;margin-left:4px;vertical-align:1px}
28
  .rig.tag{color:#07090d;background:#6f9fe0;border-color:#6f9fe0;font-weight:700}
29
  .main{flex:1;display:flex;flex-direction:column;min-width:0}
30
  .bar{display:flex;align-items:center;gap:10px;padding:9px 16px;border-bottom:1px solid #1c2028;background:#0b0e13;min-height:44px}
 
35
  </style></head><body>
36
  <div class="wrap">
37
  <nav class="side">
38
+ <div class="st"><a class="back" href="../index.html">&larr; all datasets</a><span>Ego Sample &mdash; 13 tasks</span></div>
39
  <div id="nav"></div>
40
  </nav>
41
  <div class="main">
 
43
  <iframe id="stage" allow="autoplay" src="about:blank"></iframe>
44
  </div>
45
  </div>
46
+ <script id="D" type="application/json">[["Retail & Fresh Food", [["Fruit retail & cutting", {"kind": "mego", "src": "mego.html#task_15634_ep003", "slug": "task_15634_ep003", "dur": 30.97, "rig": "MEgo 5-cam", "instr": "Place the pears from the table into the plate and arrange them neatly"}], ["Fruit cutting station", {"kind": "mego", "src": "mego.html#task_15365_ep000", "slug": "task_15365_ep000", "dur": 38.98, "rig": "MEgo 5-cam", "instr": "Sort yellow fruits and pack them into plastic boxes"}], ["Arranging fruits on retail shelf", {"kind": "ego", "src": "ego-legacy/demo09/viewer.html", "dur": 55.0, "rig": "PICO stereo", "instr": ""}], ["Dicing watermelon for storage", {"kind": "ego", "src": "ego-legacy/demo07/viewer.html", "dur": 373.0, "rig": "PICO stereo", "instr": ""}]]], ["Office & Documents", [["Document scanning & archiving", {"kind": "mego", "src": "mego.html#task_15638_ep001", "slug": "task_15638_ep001", "dur": 37.98, "rig": "MEgo 5-cam", "instr": "Organize the documents on the table"}], ["Document printing & binding", {"kind": "mego", "src": "mego.html#task_15659_ep004", "slug": "task_15659_ep004", "dur": 38.98, "rig": "MEgo 5-cam", "instr": "Organize and bind the documents on the table."}], ["Wiping office desk surface", {"kind": "ego", "src": "ego-legacy/demo08/viewer.html", "dur": 85.8, "rig": "PICO stereo", "instr": ""}]]], ["Assembly & Manufacturing", [["Mouse & keyboard assembly", {"kind": "mego", "src": "mego.html#task_15648_ep002", "slug": "task_15648_ep002", "dur": 39.98, "rig": "MEgo 5-cam", "instr": "Place the white keycap on the keyboard and press it"}], ["Trimming threads from garment", {"kind": "ego", "src": "ego-legacy/demo10/viewer.html", "dur": 22.5, "rig": "PICO stereo", "instr": ""}]]], ["Home & Organizing", [["Organizing refrigerator items", {"kind": "ego", "src": "ego-legacy/demo02/viewer.html", "dur": 41.7, "rig": "PICO stereo", "instr": ""}], ["Organizing fan power cord", {"kind": "ego", "src": "ego-legacy/demo05/viewer.html", "dur": 72.5, "rig": "PICO stereo", "instr": ""}]]], ["Creative Workshops", [["Photo keychain crafting", {"kind": "jizhi", "src": "jizhi.html#craft_20260629_sticker_ep000", "slug": "craft_20260629_sticker_ep000", "dur": 79.0, "rig": "Jizhi Stereo", "tags": ["US"], "instr": "Trim photo stickers, mount them in acrylic keychain frames and round badge cases, and attach key rings"}]]], ["Robot Ego \u00b7 Multi-Sensor Capture", [["Restocking a retail shelf", {"kind": "faberu", "src": "faberu.html#restock_20260916_ep000", "slug": "restock_20260916_ep000", "dur": 80.4, "rig": "Faber U", "tags": ["US", "robot ego"], "instr": "Fetch a bottle from the table and restock the shelf"}]], {"planned": true}]]</script>
47
  <script>
48
  const TREE = JSON.parse(document.getElementById('D').textContent);
49
  const esc = s => (s||'').replace(/[&<>"]/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[c]));
 
51
 
52
  function build(){
53
  const box = document.getElementById('nav');
54
+ TREE.forEach(([cat, leaves, cm], ci)=>{
55
  const c = document.createElement('div'); c.className = 'cat' + (ci===0?' open':'');
56
+ c.innerHTML = `<div class="ch"><span class="ct">${esc(cat)}${cm&&cm.planned?' <span class="pl">PLANNED</span>':''}</span><span class="cc">${leaves.length}</span></div><div class="cbody"></div>`;
57
  c.querySelector('.ch').onclick = ()=>c.classList.toggle('open');
58
  const body = c.querySelector('.cbody');
59
  leaves.forEach(([lbl, m])=>{
60
  const el = document.createElement('div'); el.className = 'leaf';
61
  el.innerHTML = `<div class="lt">${esc(lbl)}</div>
62
+ <div class="lm"><span class="rig">${m.rig}</span>${(m.tags||[]).length?`<span class="rc">${m.tags.map(t=>`<span class="rig tag">${esc(t)}</span>`).join('')}</span>`:''}<span>${fmtDur(m.dur)}${m.instr?' · '+esc(m.instr):''}</span></div>`;
63
  el.onclick = ()=>open(m, lbl, el);
64
  body.appendChild(el);
65
  });
egoSample/media/restock_20260916_ep000/head_color.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:76a7553905d0b60bfd61301a8412ec61728ce7f39e6e8cc56347e140ef0f84b1
3
+ size 6084403
egoSample/media/restock_20260916_ep000/head_depth.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:895106314ff952b5a53c21d474c3df5ec3c29e6e879b94b5beffbb91552519d9
3
+ size 13228568
egoSample/media/restock_20260916_ep000/head_left_fisheye.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:922545620b8f7e5e6799166cbe3d7d610f91b5a7b6341cee98906be7f30faa62
3
+ size 10137456
egoSample/media/restock_20260916_ep000/head_right_fisheye.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3c1c122dd74970686ea9dc95e27c06d8ba58d600b5c2fdbeeed0e50e5f5ffc14
3
+ size 10056853
egoSample/media/restock_20260916_ep000/head_stereo_left.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:375aeef30b7a59ff182b2afd64245180ea06428315d977cf7f1e401972f52f07
3
+ size 11534239
egoSample/media/restock_20260916_ep000/head_stereo_right.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:eeb1e59716670a210b311775475efe3f535ced957855222b527cf8613397355a
3
+ size 11643733