Spaces:
Running
Running
egoSample: add Robot Ego · Multi-Sensor Capture category (planned) with Faber U shelf-restocking episode
Browse files- .gitattributes +7 -0
- egoSample/epdata/restock_20260916_ep000.js +3 -0
- egoSample/faberu.html +298 -0
- egoSample/index.html +7 -5
- egoSample/media/restock_20260916_ep000/head_color.mp4 +3 -0
- egoSample/media/restock_20260916_ep000/head_depth.mp4 +3 -0
- egoSample/media/restock_20260916_ep000/head_left_fisheye.mp4 +3 -0
- egoSample/media/restock_20260916_ep000/head_right_fisheye.mp4 +3 -0
- egoSample/media/restock_20260916_ep000/head_stereo_left.mp4 +3 -0
- egoSample/media/restock_20260916_ep000/head_stereo_right.mp4 +3 -0
.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=>({'&':'&','<':'<','>':'>','"':'"'}[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 (>2.3 m) and >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">▶</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 — 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 · <a href="${HFDIR}" target="_blank" rel="noopener" style="text-transform:none;color:#7fe0a0">full episode on HuggingFace →</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?'❙❙':'▶';
|
| 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&<[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">← all datasets</a><span>Ego Sample —
|
| 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=>({'&':'&','<':'<','>':'>','"':'"'}[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">← all datasets</a><span>Ego Sample — 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=>({'&':'&','<':'<','>':'>','"':'"'}[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
|