Spaces:
Running
Running
Merge pull request #26 from huggingface/feat/speedup_visulization
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .github/workflows/deploy-release.yml +12 -1
- .gitignore +3 -1
- README.md +11 -2
- bun.lock +117 -0
- next.config.ts +7 -0
- package.json +6 -1
- public/urdf/openarm/assets/body_link0.dae +3 -0
- public/urdf/openarm/assets/body_link0.stl +3 -0
- public/urdf/openarm/assets/finger.dae +3 -0
- public/urdf/openarm/assets/finger.stl +3 -0
- public/urdf/openarm/assets/hand.dae +3 -0
- public/urdf/openarm/assets/hand.stl +3 -0
- public/urdf/openarm/assets/link0.dae +3 -0
- public/urdf/openarm/assets/link0.stl +3 -0
- public/urdf/openarm/assets/link1.dae +3 -0
- public/urdf/openarm/assets/link1.stl +3 -0
- public/urdf/openarm/assets/link2.dae +3 -0
- public/urdf/openarm/assets/link2.stl +3 -0
- public/urdf/openarm/assets/link3.dae +3 -0
- public/urdf/openarm/assets/link3.stl +3 -0
- public/urdf/openarm/assets/link4.dae +3 -0
- public/urdf/openarm/assets/link4.stl +3 -0
- public/urdf/openarm/assets/link5.dae +3 -0
- public/urdf/openarm/assets/link5.stl +3 -0
- public/urdf/openarm/assets/link6.dae +3 -0
- public/urdf/openarm/assets/link6.stl +3 -0
- public/urdf/openarm/assets/link7.dae +3 -0
- public/urdf/openarm/assets/link7.stl +3 -0
- public/urdf/openarm/openarm_bimanual.urdf +495 -0
- public/urdf/so101/assets/base_motor_holder_so101_v1.stl +3 -0
- public/urdf/so101/assets/base_so101_v2.stl +3 -0
- public/urdf/so101/assets/motor_holder_so101_base_v1.stl +3 -0
- public/urdf/so101/assets/motor_holder_so101_wrist_v1.stl +3 -0
- public/urdf/so101/assets/moving_jaw_so101_v1.stl +3 -0
- public/urdf/so101/assets/rotation_pitch_so101_v1.stl +3 -0
- public/urdf/so101/assets/sts3215_03a_no_horn_v1.stl +3 -0
- public/urdf/so101/assets/sts3215_03a_v1.stl +3 -0
- public/urdf/so101/assets/under_arm_so101_v1.stl +3 -0
- public/urdf/so101/assets/upper_arm_so101_v1.stl +3 -0
- public/urdf/so101/assets/waveshare_mounting_plate_so101_v2.stl +3 -0
- public/urdf/so101/assets/wrist_roll_follower_so101_v1.stl +3 -0
- public/urdf/so101/assets/wrist_roll_pitch_so101_v2.stl +3 -0
- public/urdf/so101/so100.urdf +406 -0
- public/urdf/so101/so101_new_calib.urdf +453 -0
- src/app/.well-known/appspecific/[...slug]/route.ts +5 -0
- src/app/[org]/[dataset]/[episode]/actions.ts +65 -0
- src/app/[org]/[dataset]/[episode]/episode-viewer.tsx +397 -89
- src/app/[org]/[dataset]/[episode]/fetch-data.ts +1326 -259
- src/app/[org]/[dataset]/[episode]/page.tsx +1 -1
- src/app/explore/page.tsx +3 -3
.github/workflows/deploy-release.yml
CHANGED
|
@@ -16,7 +16,18 @@ jobs:
|
|
| 16 |
with:
|
| 17 |
fetch-depth: 0
|
| 18 |
lfs: true
|
|
|
|
| 19 |
- name: Push to hub
|
| 20 |
env:
|
| 21 |
HF_TOKEN: ${{ secrets.HF_TOKEN }}
|
| 22 |
-
run:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
with:
|
| 17 |
fetch-depth: 0
|
| 18 |
lfs: true
|
| 19 |
+
ref: main
|
| 20 |
- name: Push to hub
|
| 21 |
env:
|
| 22 |
HF_TOKEN: ${{ secrets.HF_TOKEN }}
|
| 23 |
+
run: |
|
| 24 |
+
set -euo pipefail
|
| 25 |
+
|
| 26 |
+
git lfs install --local
|
| 27 |
+
|
| 28 |
+
git remote remove hf 2>/dev/null || true
|
| 29 |
+
git remote add hf "https://mishig:${HF_TOKEN}@huggingface.co/spaces/lerobot/visualize_dataset"
|
| 30 |
+
|
| 31 |
+
# Push large files first, then refs.
|
| 32 |
+
git lfs push hf main
|
| 33 |
+
git push hf main -f
|
.gitignore
CHANGED
|
@@ -2,6 +2,7 @@
|
|
| 2 |
|
| 3 |
# dependencies
|
| 4 |
/node_modules
|
|
|
|
| 5 |
/.pnp
|
| 6 |
.pnp.*
|
| 7 |
.yarn/*
|
|
@@ -16,7 +17,8 @@
|
|
| 16 |
# next.js
|
| 17 |
/.next/
|
| 18 |
/out/
|
| 19 |
-
/public
|
|
|
|
| 20 |
|
| 21 |
# production
|
| 22 |
/build
|
|
|
|
| 2 |
|
| 3 |
# dependencies
|
| 4 |
/node_modules
|
| 5 |
+
package-lock.json
|
| 6 |
/.pnp
|
| 7 |
.pnp.*
|
| 8 |
.yarn/*
|
|
|
|
| 17 |
# next.js
|
| 18 |
/.next/
|
| 19 |
/out/
|
| 20 |
+
/public/*
|
| 21 |
+
!/public/urdf/
|
| 22 |
|
| 23 |
# production
|
| 24 |
/build
|
README.md
CHANGED
|
@@ -11,7 +11,7 @@ license: apache-2.0
|
|
| 11 |
|
| 12 |
# LeRobot Dataset Visualizer
|
| 13 |
|
| 14 |
-
LeRobot Dataset Visualizer is a web application for interactive exploration and visualization of robotics datasets, particularly those in the LeRobot format. It enables users to browse, view, and analyze episodes from large-scale robotics datasets, combining synchronized video playback with rich, interactive data graphs.
|
| 15 |
|
| 16 |
## Project Overview
|
| 17 |
|
|
@@ -20,13 +20,20 @@ This tool is designed to help robotics researchers and practitioners quickly ins
|
|
| 20 |
- Navigating between organizations, datasets, and episodes
|
| 21 |
- Watching episode videos
|
| 22 |
- Exploring synchronized time-series data with interactive charts
|
|
|
|
|
|
|
| 23 |
- Paginating through large datasets efficiently
|
| 24 |
|
| 25 |
## Key Features
|
| 26 |
|
| 27 |
- **Dataset & Episode Navigation:** Quickly jump between organizations, datasets, and episodes using a sidebar and navigation controls.
|
| 28 |
- **Synchronized Video & Data:** Video playback is synchronized with interactive data graphs for detailed inspection of sensor and control signals.
|
| 29 |
-
- **
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
- **Responsive UI:** Built with React, Next.js, and Tailwind CSS for a fast, modern user experience.
|
| 31 |
|
| 32 |
## Technologies Used
|
|
@@ -34,6 +41,8 @@ This tool is designed to help robotics researchers and practitioners quickly ins
|
|
| 34 |
- **Next.js** (App Router)
|
| 35 |
- **React**
|
| 36 |
- **Recharts** (for data visualization)
|
|
|
|
|
|
|
| 37 |
- **hyparquet** (for reading Parquet files)
|
| 38 |
- **Tailwind CSS** (styling)
|
| 39 |
|
|
|
|
| 11 |
|
| 12 |
# LeRobot Dataset Visualizer
|
| 13 |
|
| 14 |
+
LeRobot Dataset Tool and Visualizer is a web application for interactive exploration and visualization of robotics datasets, particularly those in the LeRobot format. It enables users to browse, view, and analyze episodes from large-scale robotics datasets, combining synchronized video playback with rich, interactive data graphs.
|
| 15 |
|
| 16 |
## Project Overview
|
| 17 |
|
|
|
|
| 20 |
- Navigating between organizations, datasets, and episodes
|
| 21 |
- Watching episode videos
|
| 22 |
- Exploring synchronized time-series data with interactive charts
|
| 23 |
+
- Analyzing action quality and identifying problematic episodes
|
| 24 |
+
- Visualizing robot poses in 3D using URDF models
|
| 25 |
- Paginating through large datasets efficiently
|
| 26 |
|
| 27 |
## Key Features
|
| 28 |
|
| 29 |
- **Dataset & Episode Navigation:** Quickly jump between organizations, datasets, and episodes using a sidebar and navigation controls.
|
| 30 |
- **Synchronized Video & Data:** Video playback is synchronized with interactive data graphs for detailed inspection of sensor and control signals.
|
| 31 |
+
- **Overview Panel:** At-a-glance summary of dataset metadata, camera info, and episode details.
|
| 32 |
+
- **Statistics Panel:** Dataset-level statistics including episode count, total recording time, frames-per-second, and an episode-length histogram.
|
| 33 |
+
- **Action Insights Panel:** Data-driven analysis tools to guide training configuration — includes autocorrelation, state-action alignment, speed distribution, and cross-episode variance heatmap.
|
| 34 |
+
- **Filtering Panel:** Identify and flag problematic episodes (low movement, jerky motion, outlier length) for removal. Exports flagged episode IDs as a ready-to-run LeRobot CLI command.
|
| 35 |
+
- **3D URDF Viewer:** Visualize robot joint poses frame-by-frame in an interactive 3D scene, with end-effector trail rendering. Supports SO-100, SO-101, and OpenArm bimanual robots.
|
| 36 |
+
- **Efficient Data Loading:** Uses parquet and JSON loading for large dataset support, with pagination, chunking, and lazy-loaded panels for fast initial load.
|
| 37 |
- **Responsive UI:** Built with React, Next.js, and Tailwind CSS for a fast, modern user experience.
|
| 38 |
|
| 39 |
## Technologies Used
|
|
|
|
| 41 |
- **Next.js** (App Router)
|
| 42 |
- **React**
|
| 43 |
- **Recharts** (for data visualization)
|
| 44 |
+
- **Three.js** + **@react-three/fiber** + **@react-three/drei** (for 3D URDF visualization)
|
| 45 |
+
- **urdf-loader** (for parsing URDF robot models)
|
| 46 |
- **hyparquet** (for reading Parquet files)
|
| 47 |
- **Tailwind CSS** (styling)
|
| 48 |
|
bun.lock
CHANGED
|
@@ -5,12 +5,17 @@
|
|
| 5 |
"": {
|
| 6 |
"name": "lerobot-viewer",
|
| 7 |
"dependencies": {
|
|
|
|
|
|
|
|
|
|
| 8 |
"hyparquet": "^1.12.1",
|
| 9 |
"next": "15.3.6",
|
| 10 |
"react": "^19.0.0",
|
| 11 |
"react-dom": "^19.0.0",
|
| 12 |
"react-icons": "^5.5.0",
|
| 13 |
"recharts": "^2.15.3",
|
|
|
|
|
|
|
| 14 |
},
|
| 15 |
"devDependencies": {
|
| 16 |
"@eslint/eslintrc": "^3",
|
|
@@ -31,6 +36,8 @@
|
|
| 31 |
|
| 32 |
"@babel/runtime": ["@babel/runtime@7.28.6", "", {}, "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA=="],
|
| 33 |
|
|
|
|
|
|
|
| 34 |
"@emnapi/core": ["@emnapi/core@1.8.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" } }, "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg=="],
|
| 35 |
|
| 36 |
"@emnapi/runtime": ["@emnapi/runtime@1.8.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="],
|
|
@@ -123,6 +130,10 @@
|
|
| 123 |
|
| 124 |
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
| 125 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 126 |
"@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.12", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.10.0" } }, "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ=="],
|
| 127 |
|
| 128 |
"@next/env": ["@next/env@15.3.6", "", {}, "sha512-/cK+QPcfRbDZxmI/uckT4lu9pHCfRIPBLqy88MhE+7Vg5hKrEYc333Ae76dn/cw2FBP2bR/GoK/4DU+U7by/Nw=="],
|
|
@@ -153,6 +164,10 @@
|
|
| 153 |
|
| 154 |
"@nolyfill/is-core-module": ["@nolyfill/is-core-module@1.0.39", "", {}, "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA=="],
|
| 155 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 156 |
"@rtsao/scc": ["@rtsao/scc@1.1.0", "", {}, "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g=="],
|
| 157 |
|
| 158 |
"@rushstack/eslint-patch": ["@rushstack/eslint-patch@1.15.0", "", {}, "sha512-ojSshQPKwVvSMR8yT2L/QtUkV5SXi/IfDiJ4/8d6UbTPjiHVmxZzUAzGD8Tzks1b9+qQkZa0isUOvYObedITaw=="],
|
|
@@ -191,6 +206,8 @@
|
|
| 191 |
|
| 192 |
"@tailwindcss/postcss": ["@tailwindcss/postcss@4.1.18", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.1.18", "@tailwindcss/oxide": "4.1.18", "postcss": "^8.4.41", "tailwindcss": "4.1.18" } }, "sha512-Ce0GFnzAOuPyfV5SxjXGn0CubwGcuDB0zcdaPuCSzAa/2vII24JTkH+I6jcbXLb1ctjZMZZI6OjDaLPJQL1S0g=="],
|
| 193 |
|
|
|
|
|
|
|
| 194 |
"@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
|
| 195 |
|
| 196 |
"@types/d3-array": ["@types/d3-array@3.2.2", "", {}, "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw=="],
|
|
@@ -211,6 +228,8 @@
|
|
| 211 |
|
| 212 |
"@types/d3-timer": ["@types/d3-timer@3.0.2", "", {}, "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw=="],
|
| 213 |
|
|
|
|
|
|
|
| 214 |
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
| 215 |
|
| 216 |
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
|
|
@@ -219,10 +238,20 @@
|
|
| 219 |
|
| 220 |
"@types/node": ["@types/node@20.19.33", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-Rs1bVAIdBs5gbTIKza/tgpMuG1k3U/UMJLWecIMxNdJFDMzcM5LOiLVRYh3PilWEYDIeUDv7bpiHPLPsbydGcw=="],
|
| 221 |
|
|
|
|
|
|
|
| 222 |
"@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="],
|
| 223 |
|
| 224 |
"@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="],
|
| 225 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 226 |
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.55.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.55.0", "@typescript-eslint/type-utils": "8.55.0", "@typescript-eslint/utils": "8.55.0", "@typescript-eslint/visitor-keys": "8.55.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.55.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-1y/MVSz0NglV1ijHC8OT49mPJ4qhPYjiK08YUQVbIOyu+5k862LKUHFkpKHWu//zmr7hDR2rhwUm6gnCGNmGBQ=="],
|
| 227 |
|
| 228 |
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.55.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.55.0", "@typescript-eslint/types": "8.55.0", "@typescript-eslint/typescript-estree": "8.55.0", "@typescript-eslint/visitor-keys": "8.55.0", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-4z2nCSBfVIMnbuu8uinj+f0o4qOeggYJLbjpPHka3KH1om7e+H9yLKTYgksTaHcGco+NClhhY2vyO3HsMH1RGw=="],
|
|
@@ -281,6 +310,12 @@
|
|
| 281 |
|
| 282 |
"@unrs/resolver-binding-win32-x64-msvc": ["@unrs/resolver-binding-win32-x64-msvc@1.11.1", "", { "os": "win32", "cpu": "x64" }, "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g=="],
|
| 283 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 284 |
"acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
|
| 285 |
|
| 286 |
"acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
|
|
@@ -321,10 +356,16 @@
|
|
| 321 |
|
| 322 |
"balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
|
| 323 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 324 |
"brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
|
| 325 |
|
| 326 |
"braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
|
| 327 |
|
|
|
|
|
|
|
| 328 |
"busboy": ["busboy@1.6.0", "", { "dependencies": { "streamsearch": "^1.1.0" } }, "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA=="],
|
| 329 |
|
| 330 |
"call-bind": ["call-bind@1.0.8", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="],
|
|
@@ -335,6 +376,8 @@
|
|
| 335 |
|
| 336 |
"callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
|
| 337 |
|
|
|
|
|
|
|
| 338 |
"caniuse-lite": ["caniuse-lite@1.0.30001769", "", {}, "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg=="],
|
| 339 |
|
| 340 |
"chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
|
@@ -349,6 +392,8 @@
|
|
| 349 |
|
| 350 |
"concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
|
| 351 |
|
|
|
|
|
|
|
| 352 |
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
| 353 |
|
| 354 |
"csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
|
|
@@ -393,12 +438,16 @@
|
|
| 393 |
|
| 394 |
"define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="],
|
| 395 |
|
|
|
|
|
|
|
| 396 |
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
|
| 397 |
|
| 398 |
"doctrine": ["doctrine@2.1.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw=="],
|
| 399 |
|
| 400 |
"dom-helpers": ["dom-helpers@5.2.1", "", { "dependencies": { "@babel/runtime": "^7.8.7", "csstype": "^3.0.2" } }, "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA=="],
|
| 401 |
|
|
|
|
|
|
|
| 402 |
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
|
| 403 |
|
| 404 |
"emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
|
|
@@ -471,6 +520,8 @@
|
|
| 471 |
|
| 472 |
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
|
| 473 |
|
|
|
|
|
|
|
| 474 |
"file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],
|
| 475 |
|
| 476 |
"fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
|
|
@@ -505,6 +556,8 @@
|
|
| 505 |
|
| 506 |
"globalthis": ["globalthis@1.0.4", "", { "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" } }, "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ=="],
|
| 507 |
|
|
|
|
|
|
|
| 508 |
"gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
|
| 509 |
|
| 510 |
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
|
|
@@ -523,10 +576,16 @@
|
|
| 523 |
|
| 524 |
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
|
| 525 |
|
|
|
|
|
|
|
| 526 |
"hyparquet": ["hyparquet@1.25.0", "", {}, "sha512-isJx+RplYT3aJc5yhaG5CeOZSBJecHZgYsUi7NE6P/nAbxxA0hZcyul0tUsWCQLc9QXYQ2uFyYBrk61JbJO0cg=="],
|
| 527 |
|
|
|
|
|
|
|
| 528 |
"ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
|
| 529 |
|
|
|
|
|
|
|
| 530 |
"import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="],
|
| 531 |
|
| 532 |
"imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
|
|
@@ -569,6 +628,8 @@
|
|
| 569 |
|
| 570 |
"is-number-object": ["is-number-object@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw=="],
|
| 571 |
|
|
|
|
|
|
|
| 572 |
"is-regex": ["is-regex@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="],
|
| 573 |
|
| 574 |
"is-set": ["is-set@2.0.3", "", {}, "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg=="],
|
|
@@ -593,6 +654,8 @@
|
|
| 593 |
|
| 594 |
"iterator.prototype": ["iterator.prototype@1.1.5", "", { "dependencies": { "define-data-property": "^1.1.4", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.6", "get-proto": "^1.0.0", "has-symbols": "^1.1.0", "set-function-name": "^2.0.2" } }, "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g=="],
|
| 595 |
|
|
|
|
|
|
|
| 596 |
"jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="],
|
| 597 |
|
| 598 |
"js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
|
|
@@ -617,6 +680,8 @@
|
|
| 617 |
|
| 618 |
"levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
|
| 619 |
|
|
|
|
|
|
|
| 620 |
"lightningcss": ["lightningcss@1.30.2", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.30.2", "lightningcss-darwin-arm64": "1.30.2", "lightningcss-darwin-x64": "1.30.2", "lightningcss-freebsd-x64": "1.30.2", "lightningcss-linux-arm-gnueabihf": "1.30.2", "lightningcss-linux-arm64-gnu": "1.30.2", "lightningcss-linux-arm64-musl": "1.30.2", "lightningcss-linux-x64-gnu": "1.30.2", "lightningcss-linux-x64-musl": "1.30.2", "lightningcss-win32-arm64-msvc": "1.30.2", "lightningcss-win32-x64-msvc": "1.30.2" } }, "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ=="],
|
| 621 |
|
| 622 |
"lightningcss-android-arm64": ["lightningcss-android-arm64@1.30.2", "", { "os": "android", "cpu": "arm64" }, "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A=="],
|
|
@@ -649,12 +714,18 @@
|
|
| 649 |
|
| 650 |
"loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="],
|
| 651 |
|
|
|
|
|
|
|
| 652 |
"magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
|
| 653 |
|
| 654 |
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
|
| 655 |
|
| 656 |
"merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
|
| 657 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 658 |
"micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
|
| 659 |
|
| 660 |
"minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
|
|
@@ -711,10 +782,14 @@
|
|
| 711 |
|
| 712 |
"postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
|
| 713 |
|
|
|
|
|
|
|
| 714 |
"prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
|
| 715 |
|
| 716 |
"prettier": ["prettier@3.8.1", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg=="],
|
| 717 |
|
|
|
|
|
|
|
| 718 |
"prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="],
|
| 719 |
|
| 720 |
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
|
|
@@ -733,6 +808,8 @@
|
|
| 733 |
|
| 734 |
"react-transition-group": ["react-transition-group@4.4.5", "", { "dependencies": { "@babel/runtime": "^7.5.5", "dom-helpers": "^5.0.1", "loose-envify": "^1.4.0", "prop-types": "^15.6.2" }, "peerDependencies": { "react": ">=16.6.0", "react-dom": ">=16.6.0" } }, "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g=="],
|
| 735 |
|
|
|
|
|
|
|
| 736 |
"recharts": ["recharts@2.15.4", "", { "dependencies": { "clsx": "^2.0.0", "eventemitter3": "^4.0.1", "lodash": "^4.17.21", "react-is": "^18.3.1", "react-smooth": "^4.0.4", "recharts-scale": "^0.4.4", "tiny-invariant": "^1.3.1", "victory-vendor": "^36.6.8" }, "peerDependencies": { "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-UT/q6fwS3c1dHbXv2uFgYJ9BMFHu3fwnd7AYZaEQhXuYQ4hgsxLvsUXzGdKeZrW5xopzDCvuA2N41WJ88I7zIw=="],
|
| 737 |
|
| 738 |
"recharts-scale": ["recharts-scale@0.4.5", "", { "dependencies": { "decimal.js-light": "^2.4.1" } }, "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w=="],
|
|
@@ -741,6 +818,8 @@
|
|
| 741 |
|
| 742 |
"regexp.prototype.flags": ["regexp.prototype.flags@1.5.4", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", "get-proto": "^1.0.1", "gopd": "^1.2.0", "set-function-name": "^2.0.2" } }, "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA=="],
|
| 743 |
|
|
|
|
|
|
|
| 744 |
"resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="],
|
| 745 |
|
| 746 |
"resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
|
|
@@ -785,6 +864,10 @@
|
|
| 785 |
|
| 786 |
"stable-hash": ["stable-hash@0.0.5", "", {}, "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA=="],
|
| 787 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 788 |
"stop-iteration-iterator": ["stop-iteration-iterator@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "internal-slot": "^1.1.0" } }, "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ=="],
|
| 789 |
|
| 790 |
"streamsearch": ["streamsearch@1.1.0", "", {}, "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg=="],
|
|
@@ -811,22 +894,38 @@
|
|
| 811 |
|
| 812 |
"supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="],
|
| 813 |
|
|
|
|
|
|
|
| 814 |
"tailwindcss": ["tailwindcss@4.1.18", "", {}, "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw=="],
|
| 815 |
|
| 816 |
"tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="],
|
| 817 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 818 |
"tiny-invariant": ["tiny-invariant@1.3.3", "", {}, "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="],
|
| 819 |
|
| 820 |
"tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
|
| 821 |
|
| 822 |
"to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
|
| 823 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 824 |
"ts-api-utils": ["ts-api-utils@2.4.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA=="],
|
| 825 |
|
| 826 |
"tsconfig-paths": ["tsconfig-paths@3.15.0", "", { "dependencies": { "@types/json5": "^0.0.29", "json5": "^1.0.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg=="],
|
| 827 |
|
| 828 |
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
| 829 |
|
|
|
|
|
|
|
| 830 |
"type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="],
|
| 831 |
|
| 832 |
"typed-array-buffer": ["typed-array-buffer@1.0.3", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-typed-array": "^1.1.14" } }, "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw=="],
|
|
@@ -845,10 +944,20 @@
|
|
| 845 |
|
| 846 |
"unrs-resolver": ["unrs-resolver@1.11.1", "", { "dependencies": { "napi-postinstall": "^0.3.0" }, "optionalDependencies": { "@unrs/resolver-binding-android-arm-eabi": "1.11.1", "@unrs/resolver-binding-android-arm64": "1.11.1", "@unrs/resolver-binding-darwin-arm64": "1.11.1", "@unrs/resolver-binding-darwin-x64": "1.11.1", "@unrs/resolver-binding-freebsd-x64": "1.11.1", "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", "@unrs/resolver-binding-linux-x64-musl": "1.11.1", "@unrs/resolver-binding-wasm32-wasi": "1.11.1", "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" } }, "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg=="],
|
| 847 |
|
|
|
|
|
|
|
| 848 |
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
|
| 849 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 850 |
"victory-vendor": ["victory-vendor@36.9.2", "", { "dependencies": { "@types/d3-array": "^3.0.3", "@types/d3-ease": "^3.0.0", "@types/d3-interpolate": "^3.0.1", "@types/d3-scale": "^4.0.2", "@types/d3-shape": "^3.1.0", "@types/d3-time": "^3.0.0", "@types/d3-timer": "^3.0.0", "d3-array": "^3.1.6", "d3-ease": "^3.0.1", "d3-interpolate": "^3.0.1", "d3-scale": "^4.0.2", "d3-shape": "^3.1.0", "d3-time": "^3.0.0", "d3-timer": "^3.0.1" } }, "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ=="],
|
| 851 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 852 |
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
| 853 |
|
| 854 |
"which-boxed-primitive": ["which-boxed-primitive@1.1.1", "", { "dependencies": { "is-bigint": "^1.1.0", "is-boolean-object": "^1.2.1", "is-number-object": "^1.1.1", "is-string": "^1.1.1", "is-symbol": "^1.1.1" } }, "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA=="],
|
|
@@ -863,6 +972,8 @@
|
|
| 863 |
|
| 864 |
"yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
|
| 865 |
|
|
|
|
|
|
|
| 866 |
"@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
|
| 867 |
|
| 868 |
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.8.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg=="],
|
|
@@ -903,6 +1014,12 @@
|
|
| 903 |
|
| 904 |
"sharp/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
|
| 905 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 906 |
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
|
| 907 |
}
|
| 908 |
}
|
|
|
|
| 5 |
"": {
|
| 6 |
"name": "lerobot-viewer",
|
| 7 |
"dependencies": {
|
| 8 |
+
"@react-three/drei": "^10.7.7",
|
| 9 |
+
"@react-three/fiber": "^9.5.0",
|
| 10 |
+
"@types/three": "^0.182.0",
|
| 11 |
"hyparquet": "^1.12.1",
|
| 12 |
"next": "15.3.6",
|
| 13 |
"react": "^19.0.0",
|
| 14 |
"react-dom": "^19.0.0",
|
| 15 |
"react-icons": "^5.5.0",
|
| 16 |
"recharts": "^2.15.3",
|
| 17 |
+
"three": "^0.182.0",
|
| 18 |
+
"urdf-loader": "^0.12.6",
|
| 19 |
},
|
| 20 |
"devDependencies": {
|
| 21 |
"@eslint/eslintrc": "^3",
|
|
|
|
| 36 |
|
| 37 |
"@babel/runtime": ["@babel/runtime@7.28.6", "", {}, "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA=="],
|
| 38 |
|
| 39 |
+
"@dimforge/rapier3d-compat": ["@dimforge/rapier3d-compat@0.12.0", "", {}, "sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow=="],
|
| 40 |
+
|
| 41 |
"@emnapi/core": ["@emnapi/core@1.8.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" } }, "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg=="],
|
| 42 |
|
| 43 |
"@emnapi/runtime": ["@emnapi/runtime@1.8.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="],
|
|
|
|
| 130 |
|
| 131 |
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
| 132 |
|
| 133 |
+
"@mediapipe/tasks-vision": ["@mediapipe/tasks-vision@0.10.17", "", {}, "sha512-CZWV/q6TTe8ta61cZXjfnnHsfWIdFhms03M9T7Cnd5y2mdpylJM0rF1qRq+wsQVRMLz1OYPVEBU9ph2Bx8cxrg=="],
|
| 134 |
+
|
| 135 |
+
"@monogrid/gainmap-js": ["@monogrid/gainmap-js@3.4.0", "", { "dependencies": { "promise-worker-transferable": "^1.0.4" }, "peerDependencies": { "three": ">= 0.159.0" } }, "sha512-2Z0FATFHaoYJ8b+Y4y4Hgfn3FRFwuU5zRrk+9dFWp4uGAdHGqVEdP7HP+gLA3X469KXHmfupJaUbKo1b/aDKIg=="],
|
| 136 |
+
|
| 137 |
"@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.12", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.10.0" } }, "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ=="],
|
| 138 |
|
| 139 |
"@next/env": ["@next/env@15.3.6", "", {}, "sha512-/cK+QPcfRbDZxmI/uckT4lu9pHCfRIPBLqy88MhE+7Vg5hKrEYc333Ae76dn/cw2FBP2bR/GoK/4DU+U7by/Nw=="],
|
|
|
|
| 164 |
|
| 165 |
"@nolyfill/is-core-module": ["@nolyfill/is-core-module@1.0.39", "", {}, "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA=="],
|
| 166 |
|
| 167 |
+
"@react-three/drei": ["@react-three/drei@10.7.7", "", { "dependencies": { "@babel/runtime": "^7.26.0", "@mediapipe/tasks-vision": "0.10.17", "@monogrid/gainmap-js": "^3.0.6", "@use-gesture/react": "^10.3.1", "camera-controls": "^3.1.0", "cross-env": "^7.0.3", "detect-gpu": "^5.0.56", "glsl-noise": "^0.0.0", "hls.js": "^1.5.17", "maath": "^0.10.8", "meshline": "^3.3.1", "stats-gl": "^2.2.8", "stats.js": "^0.17.0", "suspend-react": "^0.1.3", "three-mesh-bvh": "^0.8.3", "three-stdlib": "^2.35.6", "troika-three-text": "^0.52.4", "tunnel-rat": "^0.1.2", "use-sync-external-store": "^1.4.0", "utility-types": "^3.11.0", "zustand": "^5.0.1" }, "peerDependencies": { "@react-three/fiber": "^9.0.0", "react": "^19", "react-dom": "^19", "three": ">=0.159" }, "optionalPeers": ["react-dom"] }, "sha512-ff+J5iloR0k4tC++QtD/j9u3w5fzfgFAWDtAGQah9pF2B1YgOq/5JxqY0/aVoQG5r3xSZz0cv5tk2YuBob4xEQ=="],
|
| 168 |
+
|
| 169 |
+
"@react-three/fiber": ["@react-three/fiber@9.5.0", "", { "dependencies": { "@babel/runtime": "^7.17.8", "@types/webxr": "*", "base64-js": "^1.5.1", "buffer": "^6.0.3", "its-fine": "^2.0.0", "react-use-measure": "^2.1.7", "scheduler": "^0.27.0", "suspend-react": "^0.1.3", "use-sync-external-store": "^1.4.0", "zustand": "^5.0.3" }, "peerDependencies": { "expo": ">=43.0", "expo-asset": ">=8.4", "expo-file-system": ">=11.0", "expo-gl": ">=11.0", "react": ">=19 <19.3", "react-dom": ">=19 <19.3", "react-native": ">=0.78", "three": ">=0.156" }, "optionalPeers": ["expo", "expo-asset", "expo-file-system", "expo-gl", "react-dom", "react-native"] }, "sha512-FiUzfYW4wB1+PpmsE47UM+mCads7j2+giRBltfwH7SNhah95rqJs3ltEs9V3pP8rYdS0QlNne+9Aj8dS/SiaIA=="],
|
| 170 |
+
|
| 171 |
"@rtsao/scc": ["@rtsao/scc@1.1.0", "", {}, "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g=="],
|
| 172 |
|
| 173 |
"@rushstack/eslint-patch": ["@rushstack/eslint-patch@1.15.0", "", {}, "sha512-ojSshQPKwVvSMR8yT2L/QtUkV5SXi/IfDiJ4/8d6UbTPjiHVmxZzUAzGD8Tzks1b9+qQkZa0isUOvYObedITaw=="],
|
|
|
|
| 206 |
|
| 207 |
"@tailwindcss/postcss": ["@tailwindcss/postcss@4.1.18", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.1.18", "@tailwindcss/oxide": "4.1.18", "postcss": "^8.4.41", "tailwindcss": "4.1.18" } }, "sha512-Ce0GFnzAOuPyfV5SxjXGn0CubwGcuDB0zcdaPuCSzAa/2vII24JTkH+I6jcbXLb1ctjZMZZI6OjDaLPJQL1S0g=="],
|
| 208 |
|
| 209 |
+
"@tweenjs/tween.js": ["@tweenjs/tween.js@23.1.3", "", {}, "sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA=="],
|
| 210 |
+
|
| 211 |
"@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
|
| 212 |
|
| 213 |
"@types/d3-array": ["@types/d3-array@3.2.2", "", {}, "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw=="],
|
|
|
|
| 228 |
|
| 229 |
"@types/d3-timer": ["@types/d3-timer@3.0.2", "", {}, "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw=="],
|
| 230 |
|
| 231 |
+
"@types/draco3d": ["@types/draco3d@1.4.10", "", {}, "sha512-AX22jp8Y7wwaBgAixaSvkoG4M/+PlAcm3Qs4OW8yT9DM4xUpWKeFhLueTAyZF39pviAdcDdeJoACapiAceqNcw=="],
|
| 232 |
+
|
| 233 |
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
| 234 |
|
| 235 |
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
|
|
|
|
| 238 |
|
| 239 |
"@types/node": ["@types/node@20.19.33", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-Rs1bVAIdBs5gbTIKza/tgpMuG1k3U/UMJLWecIMxNdJFDMzcM5LOiLVRYh3PilWEYDIeUDv7bpiHPLPsbydGcw=="],
|
| 240 |
|
| 241 |
+
"@types/offscreencanvas": ["@types/offscreencanvas@2019.7.3", "", {}, "sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A=="],
|
| 242 |
+
|
| 243 |
"@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="],
|
| 244 |
|
| 245 |
"@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="],
|
| 246 |
|
| 247 |
+
"@types/react-reconciler": ["@types/react-reconciler@0.28.9", "", { "peerDependencies": { "@types/react": "*" } }, "sha512-HHM3nxyUZ3zAylX8ZEyrDNd2XZOnQ0D5XfunJF5FLQnZbHHYq4UWvW1QfelQNXv1ICNkwYhfxjwfnqivYB6bFg=="],
|
| 248 |
+
|
| 249 |
+
"@types/stats.js": ["@types/stats.js@0.17.4", "", {}, "sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA=="],
|
| 250 |
+
|
| 251 |
+
"@types/three": ["@types/three@0.182.0", "", { "dependencies": { "@dimforge/rapier3d-compat": "~0.12.0", "@tweenjs/tween.js": "~23.1.3", "@types/stats.js": "*", "@types/webxr": ">=0.5.17", "@webgpu/types": "*", "fflate": "~0.8.2", "meshoptimizer": "~0.22.0" } }, "sha512-WByN9V3Sbwbe2OkWuSGyoqQO8Du6yhYaXtXLoA5FkKTUJorZ+yOHBZ35zUUPQXlAKABZmbYp5oAqpA4RBjtJ/Q=="],
|
| 252 |
+
|
| 253 |
+
"@types/webxr": ["@types/webxr@0.5.24", "", {}, "sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg=="],
|
| 254 |
+
|
| 255 |
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.55.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.55.0", "@typescript-eslint/type-utils": "8.55.0", "@typescript-eslint/utils": "8.55.0", "@typescript-eslint/visitor-keys": "8.55.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.55.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-1y/MVSz0NglV1ijHC8OT49mPJ4qhPYjiK08YUQVbIOyu+5k862LKUHFkpKHWu//zmr7hDR2rhwUm6gnCGNmGBQ=="],
|
| 256 |
|
| 257 |
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.55.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.55.0", "@typescript-eslint/types": "8.55.0", "@typescript-eslint/typescript-estree": "8.55.0", "@typescript-eslint/visitor-keys": "8.55.0", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-4z2nCSBfVIMnbuu8uinj+f0o4qOeggYJLbjpPHka3KH1om7e+H9yLKTYgksTaHcGco+NClhhY2vyO3HsMH1RGw=="],
|
|
|
|
| 310 |
|
| 311 |
"@unrs/resolver-binding-win32-x64-msvc": ["@unrs/resolver-binding-win32-x64-msvc@1.11.1", "", { "os": "win32", "cpu": "x64" }, "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g=="],
|
| 312 |
|
| 313 |
+
"@use-gesture/core": ["@use-gesture/core@10.3.1", "", {}, "sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw=="],
|
| 314 |
+
|
| 315 |
+
"@use-gesture/react": ["@use-gesture/react@10.3.1", "", { "dependencies": { "@use-gesture/core": "10.3.1" }, "peerDependencies": { "react": ">= 16.8.0" } }, "sha512-Yy19y6O2GJq8f7CHf7L0nxL8bf4PZCPaVOCgJrusOeFHY1LvHgYXnmnXg6N5iwAnbgbZCDjo60SiM6IPJi9C5g=="],
|
| 316 |
+
|
| 317 |
+
"@webgpu/types": ["@webgpu/types@0.1.69", "", {}, "sha512-RPmm6kgRbI8e98zSD3RVACvnuktIja5+yLgDAkTmxLr90BEwdTXRQWNLF3ETTTyH/8mKhznZuN5AveXYFEsMGQ=="],
|
| 318 |
+
|
| 319 |
"acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
|
| 320 |
|
| 321 |
"acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
|
|
|
|
| 356 |
|
| 357 |
"balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
|
| 358 |
|
| 359 |
+
"base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
|
| 360 |
+
|
| 361 |
+
"bidi-js": ["bidi-js@1.0.3", "", { "dependencies": { "require-from-string": "^2.0.2" } }, "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw=="],
|
| 362 |
+
|
| 363 |
"brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
|
| 364 |
|
| 365 |
"braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
|
| 366 |
|
| 367 |
+
"buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="],
|
| 368 |
+
|
| 369 |
"busboy": ["busboy@1.6.0", "", { "dependencies": { "streamsearch": "^1.1.0" } }, "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA=="],
|
| 370 |
|
| 371 |
"call-bind": ["call-bind@1.0.8", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="],
|
|
|
|
| 376 |
|
| 377 |
"callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
|
| 378 |
|
| 379 |
+
"camera-controls": ["camera-controls@3.1.2", "", { "peerDependencies": { "three": ">=0.126.1" } }, "sha512-xkxfpG2ECZ6Ww5/9+kf4mfg1VEYAoe9aDSY+IwF0UEs7qEzwy0aVRfs2grImIECs/PoBtWFrh7RXsQkwG922JA=="],
|
| 380 |
+
|
| 381 |
"caniuse-lite": ["caniuse-lite@1.0.30001769", "", {}, "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg=="],
|
| 382 |
|
| 383 |
"chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
|
|
|
| 392 |
|
| 393 |
"concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
|
| 394 |
|
| 395 |
+
"cross-env": ["cross-env@7.0.3", "", { "dependencies": { "cross-spawn": "^7.0.1" }, "bin": { "cross-env": "src/bin/cross-env.js", "cross-env-shell": "src/bin/cross-env-shell.js" } }, "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw=="],
|
| 396 |
+
|
| 397 |
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
| 398 |
|
| 399 |
"csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
|
|
|
|
| 438 |
|
| 439 |
"define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="],
|
| 440 |
|
| 441 |
+
"detect-gpu": ["detect-gpu@5.0.70", "", { "dependencies": { "webgl-constants": "^1.1.1" } }, "sha512-bqerEP1Ese6nt3rFkwPnGbsUF9a4q+gMmpTVVOEzoCyeCc+y7/RvJnQZJx1JwhgQI5Ntg0Kgat8Uu7XpBqnz1w=="],
|
| 442 |
+
|
| 443 |
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
|
| 444 |
|
| 445 |
"doctrine": ["doctrine@2.1.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw=="],
|
| 446 |
|
| 447 |
"dom-helpers": ["dom-helpers@5.2.1", "", { "dependencies": { "@babel/runtime": "^7.8.7", "csstype": "^3.0.2" } }, "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA=="],
|
| 448 |
|
| 449 |
+
"draco3d": ["draco3d@1.5.7", "", {}, "sha512-m6WCKt/erDXcw+70IJXnG7M3awwQPAsZvJGX5zY7beBqpELw6RDGkYVU0W43AFxye4pDZ5i2Lbyc/NNGqwjUVQ=="],
|
| 450 |
+
|
| 451 |
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
|
| 452 |
|
| 453 |
"emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
|
|
|
|
| 520 |
|
| 521 |
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
|
| 522 |
|
| 523 |
+
"fflate": ["fflate@0.8.2", "", {}, "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A=="],
|
| 524 |
+
|
| 525 |
"file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],
|
| 526 |
|
| 527 |
"fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
|
|
|
|
| 556 |
|
| 557 |
"globalthis": ["globalthis@1.0.4", "", { "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" } }, "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ=="],
|
| 558 |
|
| 559 |
+
"glsl-noise": ["glsl-noise@0.0.0", "", {}, "sha512-b/ZCF6amfAUb7dJM/MxRs7AetQEahYzJ8PtgfrmEdtw6uyGOr+ZSGtgjFm6mfsBkxJ4d2W7kg+Nlqzqvn3Bc0w=="],
|
| 560 |
+
|
| 561 |
"gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
|
| 562 |
|
| 563 |
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
|
|
|
|
| 576 |
|
| 577 |
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
|
| 578 |
|
| 579 |
+
"hls.js": ["hls.js@1.6.15", "", {}, "sha512-E3a5VwgXimGHwpRGV+WxRTKeSp2DW5DI5MWv34ulL3t5UNmyJWCQ1KmLEHbYzcfThfXG8amBL+fCYPneGHC4VA=="],
|
| 580 |
+
|
| 581 |
"hyparquet": ["hyparquet@1.25.0", "", {}, "sha512-isJx+RplYT3aJc5yhaG5CeOZSBJecHZgYsUi7NE6P/nAbxxA0hZcyul0tUsWCQLc9QXYQ2uFyYBrk61JbJO0cg=="],
|
| 582 |
|
| 583 |
+
"ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
|
| 584 |
+
|
| 585 |
"ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
|
| 586 |
|
| 587 |
+
"immediate": ["immediate@3.0.6", "", {}, "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="],
|
| 588 |
+
|
| 589 |
"import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="],
|
| 590 |
|
| 591 |
"imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
|
|
|
|
| 628 |
|
| 629 |
"is-number-object": ["is-number-object@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw=="],
|
| 630 |
|
| 631 |
+
"is-promise": ["is-promise@2.2.2", "", {}, "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ=="],
|
| 632 |
+
|
| 633 |
"is-regex": ["is-regex@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="],
|
| 634 |
|
| 635 |
"is-set": ["is-set@2.0.3", "", {}, "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg=="],
|
|
|
|
| 654 |
|
| 655 |
"iterator.prototype": ["iterator.prototype@1.1.5", "", { "dependencies": { "define-data-property": "^1.1.4", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.6", "get-proto": "^1.0.0", "has-symbols": "^1.1.0", "set-function-name": "^2.0.2" } }, "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g=="],
|
| 656 |
|
| 657 |
+
"its-fine": ["its-fine@2.0.0", "", { "dependencies": { "@types/react-reconciler": "^0.28.9" }, "peerDependencies": { "react": "^19.0.0" } }, "sha512-KLViCmWx94zOvpLwSlsx6yOCeMhZYaxrJV87Po5k/FoZzcPSahvK5qJ7fYhS61sZi5ikmh2S3Hz55A2l3U69ng=="],
|
| 658 |
+
|
| 659 |
"jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="],
|
| 660 |
|
| 661 |
"js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
|
|
|
|
| 680 |
|
| 681 |
"levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
|
| 682 |
|
| 683 |
+
"lie": ["lie@3.3.0", "", { "dependencies": { "immediate": "~3.0.5" } }, "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ=="],
|
| 684 |
+
|
| 685 |
"lightningcss": ["lightningcss@1.30.2", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.30.2", "lightningcss-darwin-arm64": "1.30.2", "lightningcss-darwin-x64": "1.30.2", "lightningcss-freebsd-x64": "1.30.2", "lightningcss-linux-arm-gnueabihf": "1.30.2", "lightningcss-linux-arm64-gnu": "1.30.2", "lightningcss-linux-arm64-musl": "1.30.2", "lightningcss-linux-x64-gnu": "1.30.2", "lightningcss-linux-x64-musl": "1.30.2", "lightningcss-win32-arm64-msvc": "1.30.2", "lightningcss-win32-x64-msvc": "1.30.2" } }, "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ=="],
|
| 686 |
|
| 687 |
"lightningcss-android-arm64": ["lightningcss-android-arm64@1.30.2", "", { "os": "android", "cpu": "arm64" }, "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A=="],
|
|
|
|
| 714 |
|
| 715 |
"loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="],
|
| 716 |
|
| 717 |
+
"maath": ["maath@0.10.8", "", { "peerDependencies": { "@types/three": ">=0.134.0", "three": ">=0.134.0" } }, "sha512-tRvbDF0Pgqz+9XUa4jjfgAQ8/aPKmQdWXilFu2tMy4GWj4NOsx99HlULO4IeREfbO3a0sA145DZYyvXPkybm0g=="],
|
| 718 |
+
|
| 719 |
"magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
|
| 720 |
|
| 721 |
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
|
| 722 |
|
| 723 |
"merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
|
| 724 |
|
| 725 |
+
"meshline": ["meshline@3.3.1", "", { "peerDependencies": { "three": ">=0.137" } }, "sha512-/TQj+JdZkeSUOl5Mk2J7eLcYTLiQm2IDzmlSvYm7ov15anEcDJ92GHqqazxTSreeNgfnYu24kiEvvv0WlbCdFQ=="],
|
| 726 |
+
|
| 727 |
+
"meshoptimizer": ["meshoptimizer@0.22.0", "", {}, "sha512-IebiK79sqIy+E4EgOr+CAw+Ke8hAspXKzBd0JdgEmPHiAwmvEj2S4h1rfvo+o/BnfEYd/jAOg5IeeIjzlzSnDg=="],
|
| 728 |
+
|
| 729 |
"micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
|
| 730 |
|
| 731 |
"minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
|
|
|
|
| 782 |
|
| 783 |
"postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
|
| 784 |
|
| 785 |
+
"potpack": ["potpack@1.0.2", "", {}, "sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ=="],
|
| 786 |
+
|
| 787 |
"prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
|
| 788 |
|
| 789 |
"prettier": ["prettier@3.8.1", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg=="],
|
| 790 |
|
| 791 |
+
"promise-worker-transferable": ["promise-worker-transferable@1.0.4", "", { "dependencies": { "is-promise": "^2.1.0", "lie": "^3.0.2" } }, "sha512-bN+0ehEnrXfxV2ZQvU2PetO0n4gqBD4ulq3MI1WOPLgr7/Mg9yRQkX5+0v1vagr74ZTsl7XtzlaYDo2EuCeYJw=="],
|
| 792 |
+
|
| 793 |
"prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="],
|
| 794 |
|
| 795 |
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
|
|
|
|
| 808 |
|
| 809 |
"react-transition-group": ["react-transition-group@4.4.5", "", { "dependencies": { "@babel/runtime": "^7.5.5", "dom-helpers": "^5.0.1", "loose-envify": "^1.4.0", "prop-types": "^15.6.2" }, "peerDependencies": { "react": ">=16.6.0", "react-dom": ">=16.6.0" } }, "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g=="],
|
| 810 |
|
| 811 |
+
"react-use-measure": ["react-use-measure@2.1.7", "", { "peerDependencies": { "react": ">=16.13", "react-dom": ">=16.13" }, "optionalPeers": ["react-dom"] }, "sha512-KrvcAo13I/60HpwGO5jpW7E9DfusKyLPLvuHlUyP5zqnmAPhNc6qTRjUQrdTADl0lpPpDVU2/Gg51UlOGHXbdg=="],
|
| 812 |
+
|
| 813 |
"recharts": ["recharts@2.15.4", "", { "dependencies": { "clsx": "^2.0.0", "eventemitter3": "^4.0.1", "lodash": "^4.17.21", "react-is": "^18.3.1", "react-smooth": "^4.0.4", "recharts-scale": "^0.4.4", "tiny-invariant": "^1.3.1", "victory-vendor": "^36.6.8" }, "peerDependencies": { "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-UT/q6fwS3c1dHbXv2uFgYJ9BMFHu3fwnd7AYZaEQhXuYQ4hgsxLvsUXzGdKeZrW5xopzDCvuA2N41WJ88I7zIw=="],
|
| 814 |
|
| 815 |
"recharts-scale": ["recharts-scale@0.4.5", "", { "dependencies": { "decimal.js-light": "^2.4.1" } }, "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w=="],
|
|
|
|
| 818 |
|
| 819 |
"regexp.prototype.flags": ["regexp.prototype.flags@1.5.4", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", "get-proto": "^1.0.1", "gopd": "^1.2.0", "set-function-name": "^2.0.2" } }, "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA=="],
|
| 820 |
|
| 821 |
+
"require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="],
|
| 822 |
+
|
| 823 |
"resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="],
|
| 824 |
|
| 825 |
"resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
|
|
|
|
| 864 |
|
| 865 |
"stable-hash": ["stable-hash@0.0.5", "", {}, "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA=="],
|
| 866 |
|
| 867 |
+
"stats-gl": ["stats-gl@2.4.2", "", { "dependencies": { "@types/three": "*", "three": "^0.170.0" } }, "sha512-g5O9B0hm9CvnM36+v7SFl39T7hmAlv541tU81ME8YeSb3i1CIP5/QdDeSB3A0la0bKNHpxpwxOVRo2wFTYEosQ=="],
|
| 868 |
+
|
| 869 |
+
"stats.js": ["stats.js@0.17.0", "", {}, "sha512-hNKz8phvYLPEcRkeG1rsGmV5ChMjKDAWU7/OJJdDErPBNChQXxCo3WZurGpnWc6gZhAzEPFad1aVgyOANH1sMw=="],
|
| 870 |
+
|
| 871 |
"stop-iteration-iterator": ["stop-iteration-iterator@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "internal-slot": "^1.1.0" } }, "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ=="],
|
| 872 |
|
| 873 |
"streamsearch": ["streamsearch@1.1.0", "", {}, "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg=="],
|
|
|
|
| 894 |
|
| 895 |
"supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="],
|
| 896 |
|
| 897 |
+
"suspend-react": ["suspend-react@0.1.3", "", { "peerDependencies": { "react": ">=17.0" } }, "sha512-aqldKgX9aZqpoDp3e8/BZ8Dm7x1pJl+qI3ZKxDN0i/IQTWUwBx/ManmlVJ3wowqbno6c2bmiIfs+Um6LbsjJyQ=="],
|
| 898 |
+
|
| 899 |
"tailwindcss": ["tailwindcss@4.1.18", "", {}, "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw=="],
|
| 900 |
|
| 901 |
"tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="],
|
| 902 |
|
| 903 |
+
"three": ["three@0.182.0", "", {}, "sha512-GbHabT+Irv+ihI1/f5kIIsZ+Ef9Sl5A1Y7imvS5RQjWgtTPfPnZ43JmlYI7NtCRDK9zir20lQpfg8/9Yd02OvQ=="],
|
| 904 |
+
|
| 905 |
+
"three-mesh-bvh": ["three-mesh-bvh@0.8.3", "", { "peerDependencies": { "three": ">= 0.159.0" } }, "sha512-4G5lBaF+g2auKX3P0yqx+MJC6oVt6sB5k+CchS6Ob0qvH0YIhuUk1eYr7ktsIpY+albCqE80/FVQGV190PmiAg=="],
|
| 906 |
+
|
| 907 |
+
"three-stdlib": ["three-stdlib@2.36.1", "", { "dependencies": { "@types/draco3d": "^1.4.0", "@types/offscreencanvas": "^2019.6.4", "@types/webxr": "^0.5.2", "draco3d": "^1.4.1", "fflate": "^0.6.9", "potpack": "^1.0.1" }, "peerDependencies": { "three": ">=0.128.0" } }, "sha512-XyGQrFmNQ5O/IoKm556ftwKsBg11TIb301MB5dWNicziQBEs2g3gtOYIf7pFiLa0zI2gUwhtCjv9fmjnxKZ1Cg=="],
|
| 908 |
+
|
| 909 |
"tiny-invariant": ["tiny-invariant@1.3.3", "", {}, "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="],
|
| 910 |
|
| 911 |
"tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
|
| 912 |
|
| 913 |
"to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
|
| 914 |
|
| 915 |
+
"troika-three-text": ["troika-three-text@0.52.4", "", { "dependencies": { "bidi-js": "^1.0.2", "troika-three-utils": "^0.52.4", "troika-worker-utils": "^0.52.0", "webgl-sdf-generator": "1.1.1" }, "peerDependencies": { "three": ">=0.125.0" } }, "sha512-V50EwcYGruV5rUZ9F4aNsrytGdKcXKALjEtQXIOBfhVoZU9VAqZNIoGQ3TMiooVqFAbR1w15T+f+8gkzoFzawg=="],
|
| 916 |
+
|
| 917 |
+
"troika-three-utils": ["troika-three-utils@0.52.4", "", { "peerDependencies": { "three": ">=0.125.0" } }, "sha512-NORAStSVa/BDiG52Mfudk4j1FG4jC4ILutB3foPnfGbOeIs9+G5vZLa0pnmnaftZUGm4UwSoqEpWdqvC7zms3A=="],
|
| 918 |
+
|
| 919 |
+
"troika-worker-utils": ["troika-worker-utils@0.52.0", "", {}, "sha512-W1CpvTHykaPH5brv5VHLfQo9D1OYuo0cSBEUQFFT/nBUzM8iD6Lq2/tgG/f1OelbAS1WtaTPQzE5uM49egnngw=="],
|
| 920 |
+
|
| 921 |
"ts-api-utils": ["ts-api-utils@2.4.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA=="],
|
| 922 |
|
| 923 |
"tsconfig-paths": ["tsconfig-paths@3.15.0", "", { "dependencies": { "@types/json5": "^0.0.29", "json5": "^1.0.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg=="],
|
| 924 |
|
| 925 |
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
| 926 |
|
| 927 |
+
"tunnel-rat": ["tunnel-rat@0.1.2", "", { "dependencies": { "zustand": "^4.3.2" } }, "sha512-lR5VHmkPhzdhrM092lI2nACsLO4QubF0/yoOhzX7c+wIpbN1GjHNzCc91QlpxBi+cnx8vVJ+Ur6vL5cEoQPFpQ=="],
|
| 928 |
+
|
| 929 |
"type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="],
|
| 930 |
|
| 931 |
"typed-array-buffer": ["typed-array-buffer@1.0.3", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-typed-array": "^1.1.14" } }, "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw=="],
|
|
|
|
| 944 |
|
| 945 |
"unrs-resolver": ["unrs-resolver@1.11.1", "", { "dependencies": { "napi-postinstall": "^0.3.0" }, "optionalDependencies": { "@unrs/resolver-binding-android-arm-eabi": "1.11.1", "@unrs/resolver-binding-android-arm64": "1.11.1", "@unrs/resolver-binding-darwin-arm64": "1.11.1", "@unrs/resolver-binding-darwin-x64": "1.11.1", "@unrs/resolver-binding-freebsd-x64": "1.11.1", "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", "@unrs/resolver-binding-linux-x64-musl": "1.11.1", "@unrs/resolver-binding-wasm32-wasi": "1.11.1", "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" } }, "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg=="],
|
| 946 |
|
| 947 |
+
"urdf-loader": ["urdf-loader@0.12.6", "", { "peerDependencies": { "three": ">=0.152.0" } }, "sha512-EwpgOCPe6Tep2+MXoo/r13keHaKQXMcM+4s9+jX0NRxNS/QSNuP5JPdk5AIgWEoEB43AkEj9Vk+Nr53NkXgSbA=="],
|
| 948 |
+
|
| 949 |
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
|
| 950 |
|
| 951 |
+
"use-sync-external-store": ["use-sync-external-store@1.6.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w=="],
|
| 952 |
+
|
| 953 |
+
"utility-types": ["utility-types@3.11.0", "", {}, "sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw=="],
|
| 954 |
+
|
| 955 |
"victory-vendor": ["victory-vendor@36.9.2", "", { "dependencies": { "@types/d3-array": "^3.0.3", "@types/d3-ease": "^3.0.0", "@types/d3-interpolate": "^3.0.1", "@types/d3-scale": "^4.0.2", "@types/d3-shape": "^3.1.0", "@types/d3-time": "^3.0.0", "@types/d3-timer": "^3.0.0", "d3-array": "^3.1.6", "d3-ease": "^3.0.1", "d3-interpolate": "^3.0.1", "d3-scale": "^4.0.2", "d3-shape": "^3.1.0", "d3-time": "^3.0.0", "d3-timer": "^3.0.1" } }, "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ=="],
|
| 956 |
|
| 957 |
+
"webgl-constants": ["webgl-constants@1.1.1", "", {}, "sha512-LkBXKjU5r9vAW7Gcu3T5u+5cvSvh5WwINdr0C+9jpzVB41cjQAP5ePArDtk/WHYdVj0GefCgM73BA7FlIiNtdg=="],
|
| 958 |
+
|
| 959 |
+
"webgl-sdf-generator": ["webgl-sdf-generator@1.1.1", "", {}, "sha512-9Z0JcMTFxeE+b2x1LJTdnaT8rT8aEp7MVxkNwoycNmJWwPdzoXzMh0BjJSh/AEFP+KPYZUli814h8bJZFIZ2jA=="],
|
| 960 |
+
|
| 961 |
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
| 962 |
|
| 963 |
"which-boxed-primitive": ["which-boxed-primitive@1.1.1", "", { "dependencies": { "is-bigint": "^1.1.0", "is-boolean-object": "^1.2.1", "is-number-object": "^1.1.1", "is-string": "^1.1.1", "is-symbol": "^1.1.1" } }, "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA=="],
|
|
|
|
| 972 |
|
| 973 |
"yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
|
| 974 |
|
| 975 |
+
"zustand": ["zustand@5.0.11", "", { "peerDependencies": { "@types/react": ">=18.0.0", "immer": ">=9.0.6", "react": ">=18.0.0", "use-sync-external-store": ">=1.2.0" }, "optionalPeers": ["@types/react", "immer", "react", "use-sync-external-store"] }, "sha512-fdZY+dk7zn/vbWNCYmzZULHRrss0jx5pPFiOuMZ/5HJN6Yv3u+1Wswy/4MpZEkEGhtNH+pwxZB8OKgUBPzYAGg=="],
|
| 976 |
+
|
| 977 |
"@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
|
| 978 |
|
| 979 |
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.8.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg=="],
|
|
|
|
| 1014 |
|
| 1015 |
"sharp/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
|
| 1016 |
|
| 1017 |
+
"stats-gl/three": ["three@0.170.0", "", {}, "sha512-FQK+LEpYc0fBD+J8g6oSEyyNzjp+Q7Ks1C568WWaoMRLW+TkNNWmenWeGgJjV105Gd+p/2ql1ZcjYvNiPZBhuQ=="],
|
| 1018 |
+
|
| 1019 |
+
"three-stdlib/fflate": ["fflate@0.6.10", "", {}, "sha512-IQrh3lEPM93wVCEczc9SaAOvkmcoQn/G8Bo1e8ZPlY3X3bnAxWaBdvTdvM1hP62iZp0BXWDy4vTAy4fF0+Dlpg=="],
|
| 1020 |
+
|
| 1021 |
+
"tunnel-rat/zustand": ["zustand@4.5.7", "", { "dependencies": { "use-sync-external-store": "^1.2.2" }, "peerDependencies": { "@types/react": ">=16.8", "immer": ">=9.0.6", "react": ">=16.8" }, "optionalPeers": ["@types/react", "immer", "react"] }, "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw=="],
|
| 1022 |
+
|
| 1023 |
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
|
| 1024 |
}
|
| 1025 |
}
|
next.config.ts
CHANGED
|
@@ -2,6 +2,13 @@ import type { NextConfig } from "next";
|
|
| 2 |
import packageJson from "./package.json";
|
| 3 |
|
| 4 |
const nextConfig: NextConfig = {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
generateBuildId: () => packageJson.version,
|
| 6 |
};
|
| 7 |
|
|
|
|
| 2 |
import packageJson from "./package.json";
|
| 3 |
|
| 4 |
const nextConfig: NextConfig = {
|
| 5 |
+
typescript: {
|
| 6 |
+
ignoreBuildErrors: true,
|
| 7 |
+
},
|
| 8 |
+
eslint: {
|
| 9 |
+
ignoreDuringBuilds: true,
|
| 10 |
+
},
|
| 11 |
+
transpilePackages: ["three"],
|
| 12 |
generateBuildId: () => packageJson.version,
|
| 13 |
};
|
| 14 |
|
package.json
CHANGED
|
@@ -14,15 +14,20 @@
|
|
| 14 |
"validate": "bun run type-check && bun run lint && bun run format:check"
|
| 15 |
},
|
| 16 |
"dependencies": {
|
|
|
|
|
|
|
| 17 |
"hyparquet": "^1.12.1",
|
| 18 |
"next": "15.3.6",
|
| 19 |
"react": "^19.0.0",
|
| 20 |
"react-dom": "^19.0.0",
|
| 21 |
"react-icons": "^5.5.0",
|
| 22 |
-
"recharts": "^2.15.3"
|
|
|
|
|
|
|
| 23 |
},
|
| 24 |
"devDependencies": {
|
| 25 |
"@eslint/eslintrc": "^3",
|
|
|
|
| 26 |
"@tailwindcss/postcss": "^4",
|
| 27 |
"@types/node": "^20",
|
| 28 |
"@types/react": "^19",
|
|
|
|
| 14 |
"validate": "bun run type-check && bun run lint && bun run format:check"
|
| 15 |
},
|
| 16 |
"dependencies": {
|
| 17 |
+
"@react-three/drei": "^10.7.7",
|
| 18 |
+
"@react-three/fiber": "^9.5.0",
|
| 19 |
"hyparquet": "^1.12.1",
|
| 20 |
"next": "15.3.6",
|
| 21 |
"react": "^19.0.0",
|
| 22 |
"react-dom": "^19.0.0",
|
| 23 |
"react-icons": "^5.5.0",
|
| 24 |
+
"recharts": "^2.15.3",
|
| 25 |
+
"three": "^0.182.0",
|
| 26 |
+
"urdf-loader": "^0.12.6"
|
| 27 |
},
|
| 28 |
"devDependencies": {
|
| 29 |
"@eslint/eslintrc": "^3",
|
| 30 |
+
"@types/three": "^0.182.0",
|
| 31 |
"@tailwindcss/postcss": "^4",
|
| 32 |
"@types/node": "^20",
|
| 33 |
"@types/react": "^19",
|
public/urdf/openarm/assets/body_link0.dae
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:a05f59379dcf694e5ff336b1ee3a47ea504a2425a2fa774af1b14e096d9a239a
|
| 3 |
+
size 10783948
|
public/urdf/openarm/assets/body_link0.stl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:7ab0afa9a26bebbf5c6149894c41d96622caa649555ae7c5f0f2548f86148d91
|
| 3 |
+
size 7955034
|
public/urdf/openarm/assets/finger.dae
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:64492f6a80a551a7a53e3f769facc42ee4a9bce1634ff63300a9d7f5b3628cb0
|
| 3 |
+
size 6021838
|
public/urdf/openarm/assets/finger.stl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:25c115c7c55a422f30ad11581dc21576dc8fc4e09e659890772d86fe82ec04d7
|
| 3 |
+
size 432484
|
public/urdf/openarm/assets/hand.dae
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:a7a1ff18be1b603544f36645b50e9842ee1ea94d1ba8cbccdeded50946f4bce7
|
| 3 |
+
size 1549205
|
public/urdf/openarm/assets/hand.stl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:8e5d373ebbd3fd001b506058644062ad71a68f1ced5ca5d5ed0f6de20137956b
|
| 3 |
+
size 18284
|
public/urdf/openarm/assets/link0.dae
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:29037f2e51d6b34dbbdbd1eecb557abd900aa1897be4d4a30a7114e649f987b9
|
| 3 |
+
size 5356744
|
public/urdf/openarm/assets/link0.stl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c77bdc9419947088e1dfc452e29c6092cc7b02b239ff4f2f5be3d77e393af185
|
| 3 |
+
size 4148234
|
public/urdf/openarm/assets/link1.dae
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c25937f901a4c15730927e08bf5fd26a072e801c529f0bd9678ba9758330647d
|
| 3 |
+
size 7655919
|
public/urdf/openarm/assets/link1.stl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:2a182c7fd4d25226aff6d9e2dd9d1008a85fce7df8e16525722a5c5053f8b055
|
| 3 |
+
size 5741534
|
public/urdf/openarm/assets/link2.dae
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:5d2a1ec857a22eaa180034e2dcd989c54611abab6ce6659eb4999570c34cc124
|
| 3 |
+
size 6288652
|
public/urdf/openarm/assets/link2.stl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:de1b190a56c16dea14546fb8e22c86eccabc2ca5e054819630c1932592381745
|
| 3 |
+
size 4543534
|
public/urdf/openarm/assets/link3.dae
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:97d73c4bae3f3f7570bf3e28306da719aa99a6bec662060c9e358e5625b11cce
|
| 3 |
+
size 6846210
|
public/urdf/openarm/assets/link3.stl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:8d47d75d0c47a65021708ba63cb73162bf7c3b1d14e9cfc70dfa47336034ac76
|
| 3 |
+
size 4978834
|
public/urdf/openarm/assets/link4.dae
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:59eb1cbf44b1250ad9533176b30084179f9164fcb998ee2623d89b48968ec426
|
| 3 |
+
size 6644484
|
public/urdf/openarm/assets/link4.stl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:8ca8149f2ce8b1b102270ec0b1a4b75c3e6f98c09b084430a450adce808607e1
|
| 3 |
+
size 4944684
|
public/urdf/openarm/assets/link5.dae
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:96fd3516df16a772a84c268529e5b53b05e6df07da6ccfe850bc091eba947156
|
| 3 |
+
size 9034473
|
public/urdf/openarm/assets/link5.stl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:30ea7abfdd3661b315f897bb82a5f34fd966357b358e890e155e928e931ea975
|
| 3 |
+
size 6322984
|
public/urdf/openarm/assets/link6.dae
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:adcd37741f4e223bd6fe9925625e84191b525c9d27f151f9178b23989de05459
|
| 3 |
+
size 6133097
|
public/urdf/openarm/assets/link6.stl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:e49f91279f109baecb9ff54f5041eeb4514f757ba6daa65c3ea01fb1991967e4
|
| 3 |
+
size 4818434
|
public/urdf/openarm/assets/link7.dae
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:e0e1d59b1e09c5a73198cdf175dbb94844073ec067d4b491dcab8a9e1a7faade
|
| 3 |
+
size 6288812
|
public/urdf/openarm/assets/link7.stl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:a91593b67d2dec16d1dfb6f1305df3ddcd214cdef02e97d5aba30bc633e775b2
|
| 3 |
+
size 5114784
|
public/urdf/openarm/openarm_bimanual.urdf
ADDED
|
@@ -0,0 +1,495 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version='1.0' encoding='utf-8'?>
|
| 2 |
+
<robot name="openarm">
|
| 3 |
+
<link name="world" />
|
| 4 |
+
<joint name="openarm_body_world_joint" type="fixed">
|
| 5 |
+
<parent link="world" />
|
| 6 |
+
<child link="openarm_body_link0" />
|
| 7 |
+
<origin rpy="0 0 0" xyz="0 0 0" />
|
| 8 |
+
</joint>
|
| 9 |
+
<link name="openarm_body_link0">
|
| 10 |
+
<visual name="openarm_body_link0_visual">
|
| 11 |
+
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0" />
|
| 12 |
+
<geometry>
|
| 13 |
+
<mesh filename="assets/body_link0.dae" scale="0.001 0.001 0.001" />
|
| 14 |
+
</geometry>
|
| 15 |
+
</visual>
|
| 16 |
+
<collision name="openarm_body_link0_collision">
|
| 17 |
+
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0" />
|
| 18 |
+
<geometry>
|
| 19 |
+
<mesh filename="assets/body_link0.dae" scale="0.001 0.001 0.001" />
|
| 20 |
+
</geometry>
|
| 21 |
+
</collision>
|
| 22 |
+
<inertial>
|
| 23 |
+
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0" />
|
| 24 |
+
<mass value="13.89" />
|
| 25 |
+
<inertia ixx="1.653" ixy="0.0" ixz="0.0" iyy="1.653" iyz="0.0" izz="0.051" />
|
| 26 |
+
</inertial>
|
| 27 |
+
</link>
|
| 28 |
+
|
| 29 |
+
<!-- ═══ LEFT ARM ═══ -->
|
| 30 |
+
<joint name="openarm_left_openarm_body_link0_joint" type="fixed">
|
| 31 |
+
<parent link="openarm_body_link0" />
|
| 32 |
+
<child link="openarm_left_link0" />
|
| 33 |
+
<origin rpy="-1.5708 0 0" xyz="0.0 0.031 0.698" />
|
| 34 |
+
</joint>
|
| 35 |
+
<link name="openarm_left_link0">
|
| 36 |
+
<visual name="openarm_left_link0_visual">
|
| 37 |
+
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0" />
|
| 38 |
+
<geometry>
|
| 39 |
+
<mesh filename="assets/link0.dae" scale="0.001 -0.001 0.001" />
|
| 40 |
+
</geometry>
|
| 41 |
+
</visual>
|
| 42 |
+
<inertial>
|
| 43 |
+
<origin rpy="0.0 0.0 0.0" xyz="-0.0009483362816297526 -0.0001580207020448382 0.03076860287587199" />
|
| 44 |
+
<mass value="1.1432284943239561" />
|
| 45 |
+
<inertia ixx="0.001128" ixy="-4e-06" ixz="-3.3e-05" iyy="0.000962" iyz="-7e-06" izz="0.00147" />
|
| 46 |
+
</inertial>
|
| 47 |
+
</link>
|
| 48 |
+
<link name="openarm_left_link1">
|
| 49 |
+
<visual name="openarm_left_link1_visual">
|
| 50 |
+
<origin rpy="0.0 0.0 0.0" xyz="-0.0 0.0 -0.0625" />
|
| 51 |
+
<geometry>
|
| 52 |
+
<mesh filename="assets/link1.dae" scale="0.001 -0.001 0.001" />
|
| 53 |
+
</geometry>
|
| 54 |
+
</visual>
|
| 55 |
+
<inertial>
|
| 56 |
+
<origin rpy="0.0 0.0 0.0" xyz="0.0011467657911800769 -3.319987657026362e-05 0.05395284380736254" />
|
| 57 |
+
<mass value="1.1416684646202298" />
|
| 58 |
+
<inertia ixx="0.001567" ixy="-1e-06" ixz="-2.9e-05" iyy="0.001273" iyz="1e-06" izz="0.001016" />
|
| 59 |
+
</inertial>
|
| 60 |
+
</link>
|
| 61 |
+
<joint name="openarm_left_joint1" type="revolute">
|
| 62 |
+
<origin rpy="0 0 0" xyz="0.0 0.0 0.0625" />
|
| 63 |
+
<parent link="openarm_left_link0" />
|
| 64 |
+
<child link="openarm_left_link1" />
|
| 65 |
+
<axis xyz="0 0 1" />
|
| 66 |
+
<limit effort="40" lower="-3.490659" upper="1.3962629999999998" velocity="16.754666" />
|
| 67 |
+
</joint>
|
| 68 |
+
<link name="openarm_left_link2">
|
| 69 |
+
<visual name="openarm_left_link2_visual">
|
| 70 |
+
<origin rpy="0.0 0.0 0.0" xyz="0.0301 0.0 -0.1225" />
|
| 71 |
+
<geometry>
|
| 72 |
+
<mesh filename="assets/link2.dae" scale="0.001 -0.001 0.001" />
|
| 73 |
+
</geometry>
|
| 74 |
+
</visual>
|
| 75 |
+
<inertial>
|
| 76 |
+
<origin rpy="0.0 0.0 0.0" xyz="0.00839629182351943 2.0145102027597523e-08 0.03256649300522363" />
|
| 77 |
+
<mass value="0.2775092746011571" />
|
| 78 |
+
<inertia ixx="0.000359" ixy="1e-06" ixz="-0.000109" iyy="0.000376" iyz="1e-06" izz="0.000232" />
|
| 79 |
+
</inertial>
|
| 80 |
+
</link>
|
| 81 |
+
<joint name="openarm_left_joint2" type="revolute">
|
| 82 |
+
<origin rpy="-1.57079632679 0 0" xyz="-0.0301 0.0 0.06" />
|
| 83 |
+
<parent link="openarm_left_link1" />
|
| 84 |
+
<child link="openarm_left_link2" />
|
| 85 |
+
<axis xyz="-1 0 0" />
|
| 86 |
+
<limit effort="40" lower="-3.3161253267948965" upper="0.17453267320510335" velocity="16.754666" />
|
| 87 |
+
</joint>
|
| 88 |
+
<link name="openarm_left_link3">
|
| 89 |
+
<visual name="openarm_left_link3_visual">
|
| 90 |
+
<origin rpy="0.0 0.0 0.0" xyz="-0.0 -0.0 -0.18875" />
|
| 91 |
+
<geometry>
|
| 92 |
+
<mesh filename="assets/link3.dae" scale="0.001 -0.001 0.001" />
|
| 93 |
+
</geometry>
|
| 94 |
+
</visual>
|
| 95 |
+
<inertial>
|
| 96 |
+
<origin rpy="0.0 0.0 0.0" xyz="-0.002104752099628911 -0.0005549085042607548 0.09047470545721961" />
|
| 97 |
+
<mass value="1.073863338202347" />
|
| 98 |
+
<inertia ixx="0.004372" ixy="1e-06" ixz="1.1e-05" iyy="0.004319" iyz="-3.6e-05" izz="0.000661" />
|
| 99 |
+
</inertial>
|
| 100 |
+
</link>
|
| 101 |
+
<joint name="openarm_left_joint3" type="revolute">
|
| 102 |
+
<origin rpy="0 0 0" xyz="0.0301 0.0 0.06625" />
|
| 103 |
+
<parent link="openarm_left_link2" />
|
| 104 |
+
<child link="openarm_left_link3" />
|
| 105 |
+
<axis xyz="0 0 1" />
|
| 106 |
+
<limit effort="27" lower="-1.570796" upper="1.570796" velocity="5.445426" />
|
| 107 |
+
</joint>
|
| 108 |
+
<link name="openarm_left_link4">
|
| 109 |
+
<visual name="openarm_left_link4_visual">
|
| 110 |
+
<origin rpy="0.0 0.0 0.0" xyz="0.0 -0.0315 -0.3425" />
|
| 111 |
+
<geometry>
|
| 112 |
+
<mesh filename="assets/link4.dae" scale="0.001 0.001 0.001" />
|
| 113 |
+
</geometry>
|
| 114 |
+
</visual>
|
| 115 |
+
<inertial>
|
| 116 |
+
<origin rpy="0.0 0.0 0.0" xyz="-0.0029006831074562967 -0.03030575826634669 0.06339637422196209" />
|
| 117 |
+
<mass value="0.6348534566833373" />
|
| 118 |
+
<inertia ixx="0.000623" ixy="-1e-06" ixz="-1.9e-05" iyy="0.000511" iyz="3.8e-05" izz="0.000334" />
|
| 119 |
+
</inertial>
|
| 120 |
+
</link>
|
| 121 |
+
<joint name="openarm_left_joint4" type="revolute">
|
| 122 |
+
<origin rpy="0 0 0" xyz="-0.0 0.0315 0.15375" />
|
| 123 |
+
<parent link="openarm_left_link3" />
|
| 124 |
+
<child link="openarm_left_link4" />
|
| 125 |
+
<axis xyz="0 1 0" />
|
| 126 |
+
<limit effort="27" lower="0.0" upper="2.443461" velocity="5.445426" />
|
| 127 |
+
</joint>
|
| 128 |
+
<link name="openarm_left_link5">
|
| 129 |
+
<visual name="openarm_left_link5_visual">
|
| 130 |
+
<origin rpy="0.0 0.0 0.0" xyz="-0.0 -0.0 -0.438" />
|
| 131 |
+
<geometry>
|
| 132 |
+
<mesh filename="assets/link5.dae" scale="0.001 -0.001 0.001" />
|
| 133 |
+
</geometry>
|
| 134 |
+
</visual>
|
| 135 |
+
<inertial>
|
| 136 |
+
<origin rpy="0.0 0.0 0.0" xyz="-0.003049665024221911 -0.0008866902457326625 0.043079803024980934" />
|
| 137 |
+
<mass value="0.6156588026168502" />
|
| 138 |
+
<inertia ixx="0.000423" ixy="-8e-06" ixz="6e-06" iyy="0.000445" iyz="-6e-06" izz="0.000324" />
|
| 139 |
+
</inertial>
|
| 140 |
+
</link>
|
| 141 |
+
<joint name="openarm_left_joint5" type="revolute">
|
| 142 |
+
<origin rpy="0 0 0" xyz="0.0 -0.0315 0.0955" />
|
| 143 |
+
<parent link="openarm_left_link4" />
|
| 144 |
+
<child link="openarm_left_link5" />
|
| 145 |
+
<axis xyz="0 0 1" />
|
| 146 |
+
<limit effort="7" lower="-1.570796" upper="1.570796" velocity="20.943946" />
|
| 147 |
+
</joint>
|
| 148 |
+
<link name="openarm_left_link6">
|
| 149 |
+
<visual name="openarm_left_link6_visual">
|
| 150 |
+
<origin rpy="0.0 0.0 0.0" xyz="-0.0375 -0.0 -0.5585" />
|
| 151 |
+
<geometry>
|
| 152 |
+
<mesh filename="assets/link6.dae" scale="0.001 -0.001 0.001" />
|
| 153 |
+
</geometry>
|
| 154 |
+
</visual>
|
| 155 |
+
<inertial>
|
| 156 |
+
<origin rpy="0.0 0.0 0.0" xyz="-0.037136587005447405 -0.00033230528343419053 -9.498374522309838e-05" />
|
| 157 |
+
<mass value="0.475202773187987" />
|
| 158 |
+
<inertia ixx="0.000143" ixy="1e-06" ixz="1e-06" iyy="0.000157" iyz="1e-06" izz="0.000159" />
|
| 159 |
+
</inertial>
|
| 160 |
+
</link>
|
| 161 |
+
<joint name="openarm_left_joint6" type="revolute">
|
| 162 |
+
<origin rpy="0 0 0" xyz="0.0375 0.0 0.1205" />
|
| 163 |
+
<parent link="openarm_left_link5" />
|
| 164 |
+
<child link="openarm_left_link6" />
|
| 165 |
+
<axis xyz="1 0 0" />
|
| 166 |
+
<limit effort="7" lower="-0.785398" upper="0.785398" velocity="20.943946" />
|
| 167 |
+
</joint>
|
| 168 |
+
<link name="openarm_left_link7">
|
| 169 |
+
<visual name="openarm_left_link7_visual">
|
| 170 |
+
<origin rpy="0.0 0.0 0.0" xyz="0.0 -0.0 -0.5585" />
|
| 171 |
+
<geometry>
|
| 172 |
+
<mesh filename="assets/link7.dae" scale="0.001 -0.001 0.001" />
|
| 173 |
+
</geometry>
|
| 174 |
+
</visual>
|
| 175 |
+
<inertial>
|
| 176 |
+
<origin rpy="0.0 0.0 0.0" xyz="6.875510271106056e-05 -0.01266175250761268 0.06951945409987448" />
|
| 177 |
+
<mass value="0.4659771327380578" />
|
| 178 |
+
<inertia ixx="0.000639" ixy="1e-06" ixz="1e-06" iyy="0.000497" iyz="8.9e-05" izz="0.000342" />
|
| 179 |
+
</inertial>
|
| 180 |
+
</link>
|
| 181 |
+
<joint name="openarm_left_joint7" type="revolute">
|
| 182 |
+
<origin rpy="0 0 0" xyz="-0.0375 0.0 0.0" />
|
| 183 |
+
<parent link="openarm_left_link6" />
|
| 184 |
+
<child link="openarm_left_link7" />
|
| 185 |
+
<axis xyz="0 -1 0" />
|
| 186 |
+
<limit effort="7" lower="-1.570796" upper="1.570796" velocity="20.943946" />
|
| 187 |
+
</joint>
|
| 188 |
+
|
| 189 |
+
<!-- Left hand -->
|
| 190 |
+
<link name="openarm_left_hand">
|
| 191 |
+
<visual name="openarm_left_hand_visual">
|
| 192 |
+
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 -0.6585" />
|
| 193 |
+
<geometry>
|
| 194 |
+
<mesh filename="assets/hand.dae" scale="0.001 0.001 0.001" />
|
| 195 |
+
</geometry>
|
| 196 |
+
</visual>
|
| 197 |
+
<inertial>
|
| 198 |
+
<origin rpy="0 0 0" xyz="0.0 0.002 0.03" />
|
| 199 |
+
<mass value="0.35" />
|
| 200 |
+
<inertia ixx="0.0002473" ixy="1e-06" ixz="1e-06" iyy="1.763e-05" iyz="1e-06" izz="0.0002521" />
|
| 201 |
+
</inertial>
|
| 202 |
+
</link>
|
| 203 |
+
<joint name="left_openarm_hand_joint" type="fixed">
|
| 204 |
+
<parent link="openarm_left_link7" />
|
| 205 |
+
<child link="openarm_left_hand" />
|
| 206 |
+
<origin rpy="0 0 0" xyz="0 -0.0 0.1001" />
|
| 207 |
+
</joint>
|
| 208 |
+
<link name="openarm_left_hand_tcp">
|
| 209 |
+
<inertial>
|
| 210 |
+
<origin xyz="0 0 0" rpy="0 0 0" />
|
| 211 |
+
<mass value="0.001" />
|
| 212 |
+
<inertia ixx="0.000001" ixy="0.0" ixz="0.0" iyy="0.000001" iyz="0.0" izz="0.000001" />
|
| 213 |
+
</inertial>
|
| 214 |
+
</link>
|
| 215 |
+
<joint name="openarm_left_hand_tcp_joint" type="fixed">
|
| 216 |
+
<origin rpy="0 0 0" xyz="0 -0.0 0.08" />
|
| 217 |
+
<parent link="openarm_left_hand" />
|
| 218 |
+
<child link="openarm_left_hand_tcp" />
|
| 219 |
+
</joint>
|
| 220 |
+
<link name="openarm_left_left_finger">
|
| 221 |
+
<visual name="openarm_left_left_finger_visual">
|
| 222 |
+
<origin rpy="0.0 0.0 0.0" xyz="0.0 -0.05 -0.673001" />
|
| 223 |
+
<geometry>
|
| 224 |
+
<mesh filename="assets/finger.dae" scale="0.001 0.001 0.001" />
|
| 225 |
+
</geometry>
|
| 226 |
+
</visual>
|
| 227 |
+
<inertial>
|
| 228 |
+
<origin rpy="0 0 0" xyz="0.0064528 0.01702 0.0219685" />
|
| 229 |
+
<mass value="0.03602545343277134" />
|
| 230 |
+
<inertia ixx="2.3749999999999997e-06" ixy="1e-06" ixz="1e-06" iyy="2.3749999999999997e-06" iyz="1e-06" izz="7.5e-07" />
|
| 231 |
+
</inertial>
|
| 232 |
+
</link>
|
| 233 |
+
<link name="openarm_left_right_finger">
|
| 234 |
+
<visual name="openarm_left_right_finger_visual">
|
| 235 |
+
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.05 -0.673001" />
|
| 236 |
+
<geometry>
|
| 237 |
+
<mesh filename="assets/finger.dae" scale="0.001 -0.001 0.001" />
|
| 238 |
+
</geometry>
|
| 239 |
+
</visual>
|
| 240 |
+
<inertial>
|
| 241 |
+
<origin rpy="0 0 0" xyz="0.0064528 -0.01702 0.0219685" />
|
| 242 |
+
<mass value="0.03602545343277134" />
|
| 243 |
+
<inertia ixx="2.3749999999999997e-06" ixy="1e-06" ixz="1e-06" iyy="2.3749999999999997e-06" iyz="1e-06" izz="7.5e-07" />
|
| 244 |
+
</inertial>
|
| 245 |
+
</link>
|
| 246 |
+
<joint name="openarm_left_finger_joint1" type="prismatic">
|
| 247 |
+
<parent link="openarm_left_hand" />
|
| 248 |
+
<child link="openarm_left_right_finger" />
|
| 249 |
+
<origin rpy="0 0 0" xyz="0 -0.006 0.015" />
|
| 250 |
+
<axis xyz="0 -1 0" />
|
| 251 |
+
<limit effort="333" lower="0.0" upper="0.044" velocity="10.0" />
|
| 252 |
+
</joint>
|
| 253 |
+
<joint name="openarm_left_finger_joint2" type="prismatic">
|
| 254 |
+
<parent link="openarm_left_hand" />
|
| 255 |
+
<child link="openarm_left_left_finger" />
|
| 256 |
+
<origin rpy="0 0 0" xyz="0 0.006 0.015" />
|
| 257 |
+
<axis xyz="0 1 0" />
|
| 258 |
+
<limit effort="333" lower="0.0" upper="0.044" velocity="10.0" />
|
| 259 |
+
<mimic joint="openarm_left_finger_joint1" />
|
| 260 |
+
</joint>
|
| 261 |
+
|
| 262 |
+
<!-- ═══ RIGHT ARM ═══ -->
|
| 263 |
+
<joint name="openarm_right_openarm_body_link0_joint" type="fixed">
|
| 264 |
+
<parent link="openarm_body_link0" />
|
| 265 |
+
<child link="openarm_right_link0" />
|
| 266 |
+
<origin rpy="1.5708 0 0" xyz="0.0 -0.031 0.698" />
|
| 267 |
+
</joint>
|
| 268 |
+
<link name="openarm_right_link0">
|
| 269 |
+
<visual name="openarm_right_link0_visual">
|
| 270 |
+
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 0.0" />
|
| 271 |
+
<geometry>
|
| 272 |
+
<mesh filename="assets/link0.dae" scale="0.001 0.001 0.001" />
|
| 273 |
+
</geometry>
|
| 274 |
+
</visual>
|
| 275 |
+
<inertial>
|
| 276 |
+
<origin rpy="0.0 0.0 0.0" xyz="-0.0009483362816297526 0.0001580207020448382 0.03076860287587199" />
|
| 277 |
+
<mass value="1.1432284943239561" />
|
| 278 |
+
<inertia ixx="0.001128" ixy="-4e-06" ixz="-3.3e-05" iyy="0.000962" iyz="-7e-06" izz="0.00147" />
|
| 279 |
+
</inertial>
|
| 280 |
+
</link>
|
| 281 |
+
<link name="openarm_right_link1">
|
| 282 |
+
<visual name="openarm_right_link1_visual">
|
| 283 |
+
<origin rpy="0.0 0.0 0.0" xyz="-0.0 0.0 -0.0625" />
|
| 284 |
+
<geometry>
|
| 285 |
+
<mesh filename="assets/link1.dae" scale="0.001 0.001 0.001" />
|
| 286 |
+
</geometry>
|
| 287 |
+
</visual>
|
| 288 |
+
<inertial>
|
| 289 |
+
<origin rpy="0.0 0.0 0.0" xyz="0.0011467657911800769 3.319987657026362e-05 0.05395284380736254" />
|
| 290 |
+
<mass value="1.1416684646202298" />
|
| 291 |
+
<inertia ixx="0.001567" ixy="-1e-06" ixz="-2.9e-05" iyy="0.001273" iyz="1e-06" izz="0.001016" />
|
| 292 |
+
</inertial>
|
| 293 |
+
</link>
|
| 294 |
+
<joint name="openarm_right_joint1" type="revolute">
|
| 295 |
+
<origin rpy="0 0 0" xyz="0.0 0.0 0.0625" />
|
| 296 |
+
<parent link="openarm_right_link0" />
|
| 297 |
+
<child link="openarm_right_link1" />
|
| 298 |
+
<axis xyz="0 0 1" />
|
| 299 |
+
<limit effort="40" lower="-1.396263" upper="3.490659" velocity="16.754666" />
|
| 300 |
+
</joint>
|
| 301 |
+
<link name="openarm_right_link2">
|
| 302 |
+
<visual name="openarm_right_link2_visual">
|
| 303 |
+
<origin rpy="0.0 0.0 0.0" xyz="0.0301 0.0 -0.1225" />
|
| 304 |
+
<geometry>
|
| 305 |
+
<mesh filename="assets/link2.dae" scale="0.001 0.001 0.001" />
|
| 306 |
+
</geometry>
|
| 307 |
+
</visual>
|
| 308 |
+
<inertial>
|
| 309 |
+
<origin rpy="0.0 0.0 0.0" xyz="0.00839629182351943 -2.0145102027597523e-08 0.03256649300522363" />
|
| 310 |
+
<mass value="0.2775092746011571" />
|
| 311 |
+
<inertia ixx="0.000359" ixy="1e-06" ixz="-0.000109" iyy="0.000376" iyz="1e-06" izz="0.000232" />
|
| 312 |
+
</inertial>
|
| 313 |
+
</link>
|
| 314 |
+
<joint name="openarm_right_joint2" type="revolute">
|
| 315 |
+
<origin rpy="1.57079632679 0 0" xyz="-0.0301 0.0 0.06" />
|
| 316 |
+
<parent link="openarm_right_link1" />
|
| 317 |
+
<child link="openarm_right_link2" />
|
| 318 |
+
<axis xyz="-1 0 0" />
|
| 319 |
+
<limit effort="40" lower="-0.17453267320510335" upper="3.3161253267948965" velocity="16.754666" />
|
| 320 |
+
</joint>
|
| 321 |
+
<link name="openarm_right_link3">
|
| 322 |
+
<visual name="openarm_right_link3_visual">
|
| 323 |
+
<origin rpy="0.0 0.0 0.0" xyz="-0.0 -0.0 -0.18875" />
|
| 324 |
+
<geometry>
|
| 325 |
+
<mesh filename="assets/link3.dae" scale="0.001 0.001 0.001" />
|
| 326 |
+
</geometry>
|
| 327 |
+
</visual>
|
| 328 |
+
<inertial>
|
| 329 |
+
<origin rpy="0.0 0.0 0.0" xyz="-0.002104752099628911 0.0005549085042607548 0.09047470545721961" />
|
| 330 |
+
<mass value="1.073863338202347" />
|
| 331 |
+
<inertia ixx="0.004372" ixy="1e-06" ixz="1.1e-05" iyy="0.004319" iyz="-3.6e-05" izz="0.000661" />
|
| 332 |
+
</inertial>
|
| 333 |
+
</link>
|
| 334 |
+
<joint name="openarm_right_joint3" type="revolute">
|
| 335 |
+
<origin rpy="0 0 0" xyz="0.0301 0.0 0.06625" />
|
| 336 |
+
<parent link="openarm_right_link2" />
|
| 337 |
+
<child link="openarm_right_link3" />
|
| 338 |
+
<axis xyz="0 0 1" />
|
| 339 |
+
<limit effort="27" lower="-1.570796" upper="1.570796" velocity="5.445426" />
|
| 340 |
+
</joint>
|
| 341 |
+
<link name="openarm_right_link4">
|
| 342 |
+
<visual name="openarm_right_link4_visual">
|
| 343 |
+
<origin rpy="0.0 0.0 0.0" xyz="0.0 -0.0315 -0.3425" />
|
| 344 |
+
<geometry>
|
| 345 |
+
<mesh filename="assets/link4.dae" scale="0.001 0.001 0.001" />
|
| 346 |
+
</geometry>
|
| 347 |
+
</visual>
|
| 348 |
+
<inertial>
|
| 349 |
+
<origin rpy="0.0 0.0 0.0" xyz="-0.0029006831074562967 -0.03030575826634669 0.06339637422196209" />
|
| 350 |
+
<mass value="0.6348534566833373" />
|
| 351 |
+
<inertia ixx="0.000623" ixy="-1e-06" ixz="-1.9e-05" iyy="0.000511" iyz="3.8e-05" izz="0.000334" />
|
| 352 |
+
</inertial>
|
| 353 |
+
</link>
|
| 354 |
+
<joint name="openarm_right_joint4" type="revolute">
|
| 355 |
+
<origin rpy="0 0 0" xyz="-0.0 0.0315 0.15375" />
|
| 356 |
+
<parent link="openarm_right_link3" />
|
| 357 |
+
<child link="openarm_right_link4" />
|
| 358 |
+
<axis xyz="0 1 0" />
|
| 359 |
+
<limit effort="27" lower="0.0" upper="2.443461" velocity="5.445426" />
|
| 360 |
+
</joint>
|
| 361 |
+
<link name="openarm_right_link5">
|
| 362 |
+
<visual name="openarm_right_link5_visual">
|
| 363 |
+
<origin rpy="0.0 0.0 0.0" xyz="-0.0 -0.0 -0.438" />
|
| 364 |
+
<geometry>
|
| 365 |
+
<mesh filename="assets/link5.dae" scale="0.001 0.001 0.001" />
|
| 366 |
+
</geometry>
|
| 367 |
+
</visual>
|
| 368 |
+
<inertial>
|
| 369 |
+
<origin rpy="0.0 0.0 0.0" xyz="-0.003049665024221911 0.0008866902457326625 0.043079803024980934" />
|
| 370 |
+
<mass value="0.6156588026168502" />
|
| 371 |
+
<inertia ixx="0.000423" ixy="-8e-06" ixz="6e-06" iyy="0.000445" iyz="-6e-06" izz="0.000324" />
|
| 372 |
+
</inertial>
|
| 373 |
+
</link>
|
| 374 |
+
<joint name="openarm_right_joint5" type="revolute">
|
| 375 |
+
<origin rpy="0 0 0" xyz="0.0 -0.0315 0.0955" />
|
| 376 |
+
<parent link="openarm_right_link4" />
|
| 377 |
+
<child link="openarm_right_link5" />
|
| 378 |
+
<axis xyz="0 0 1" />
|
| 379 |
+
<limit effort="7" lower="-1.570796" upper="1.570796" velocity="20.943946" />
|
| 380 |
+
</joint>
|
| 381 |
+
<link name="openarm_right_link6">
|
| 382 |
+
<visual name="openarm_right_link6_visual">
|
| 383 |
+
<origin rpy="0.0 0.0 0.0" xyz="-0.0375 -0.0 -0.5585" />
|
| 384 |
+
<geometry>
|
| 385 |
+
<mesh filename="assets/link6.dae" scale="0.001 0.001 0.001" />
|
| 386 |
+
</geometry>
|
| 387 |
+
</visual>
|
| 388 |
+
<inertial>
|
| 389 |
+
<origin rpy="0.0 0.0 0.0" xyz="-0.037136587005447405 0.00033230528343419053 -9.498374522309838e-05" />
|
| 390 |
+
<mass value="0.475202773187987" />
|
| 391 |
+
<inertia ixx="0.000143" ixy="1e-06" ixz="1e-06" iyy="0.000157" iyz="1e-06" izz="0.000159" />
|
| 392 |
+
</inertial>
|
| 393 |
+
</link>
|
| 394 |
+
<joint name="openarm_right_joint6" type="revolute">
|
| 395 |
+
<origin rpy="0 0 0" xyz="0.0375 0.0 0.1205" />
|
| 396 |
+
<parent link="openarm_right_link5" />
|
| 397 |
+
<child link="openarm_right_link6" />
|
| 398 |
+
<axis xyz="1 0 0" />
|
| 399 |
+
<limit effort="7" lower="-0.785398" upper="0.785398" velocity="20.943946" />
|
| 400 |
+
</joint>
|
| 401 |
+
<link name="openarm_right_link7">
|
| 402 |
+
<visual name="openarm_right_link7_visual">
|
| 403 |
+
<origin rpy="0.0 0.0 0.0" xyz="0.0 -0.0 -0.5585" />
|
| 404 |
+
<geometry>
|
| 405 |
+
<mesh filename="assets/link7.dae" scale="0.001 0.001 0.001" />
|
| 406 |
+
</geometry>
|
| 407 |
+
</visual>
|
| 408 |
+
<inertial>
|
| 409 |
+
<origin rpy="0.0 0.0 0.0" xyz="6.875510271106056e-05 0.01266175250761268 0.06951945409987448" />
|
| 410 |
+
<mass value="0.4659771327380578" />
|
| 411 |
+
<inertia ixx="0.000639" ixy="1e-06" ixz="1e-06" iyy="0.000497" iyz="8.9e-05" izz="0.000342" />
|
| 412 |
+
</inertial>
|
| 413 |
+
</link>
|
| 414 |
+
<joint name="openarm_right_joint7" type="revolute">
|
| 415 |
+
<origin rpy="0 0 0" xyz="-0.0375 0.0 0.0" />
|
| 416 |
+
<parent link="openarm_right_link6" />
|
| 417 |
+
<child link="openarm_right_link7" />
|
| 418 |
+
<axis xyz="0 1 0" />
|
| 419 |
+
<limit effort="7" lower="-1.570796" upper="1.570796" velocity="20.943946" />
|
| 420 |
+
</joint>
|
| 421 |
+
|
| 422 |
+
<!-- Right hand -->
|
| 423 |
+
<link name="openarm_right_hand">
|
| 424 |
+
<visual name="openarm_right_hand_visual">
|
| 425 |
+
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.0 -0.6585" />
|
| 426 |
+
<geometry>
|
| 427 |
+
<mesh filename="assets/hand.dae" scale="0.001 0.001 0.001" />
|
| 428 |
+
</geometry>
|
| 429 |
+
</visual>
|
| 430 |
+
<inertial>
|
| 431 |
+
<origin rpy="0 0 0" xyz="0.0 0.002 0.03" />
|
| 432 |
+
<mass value="0.35" />
|
| 433 |
+
<inertia ixx="0.0002473" ixy="1e-06" ixz="1e-06" iyy="1.763e-05" iyz="1e-06" izz="0.0002521" />
|
| 434 |
+
</inertial>
|
| 435 |
+
</link>
|
| 436 |
+
<joint name="right_openarm_hand_joint" type="fixed">
|
| 437 |
+
<parent link="openarm_right_link7" />
|
| 438 |
+
<child link="openarm_right_hand" />
|
| 439 |
+
<origin rpy="0 0 0" xyz="0 -0.0 0.1001" />
|
| 440 |
+
</joint>
|
| 441 |
+
<link name="openarm_right_hand_tcp">
|
| 442 |
+
<inertial>
|
| 443 |
+
<origin xyz="0 0 0" rpy="0 0 0" />
|
| 444 |
+
<mass value="0.001" />
|
| 445 |
+
<inertia ixx="0.000001" ixy="0.0" ixz="0.0" iyy="0.000001" iyz="0.0" izz="0.000001" />
|
| 446 |
+
</inertial>
|
| 447 |
+
</link>
|
| 448 |
+
<joint name="openarm_right_hand_tcp_joint" type="fixed">
|
| 449 |
+
<origin rpy="0 0 0" xyz="0 -0.0 0.08" />
|
| 450 |
+
<parent link="openarm_right_hand" />
|
| 451 |
+
<child link="openarm_right_hand_tcp" />
|
| 452 |
+
</joint>
|
| 453 |
+
<link name="openarm_right_left_finger">
|
| 454 |
+
<visual name="openarm_right_left_finger_visual">
|
| 455 |
+
<origin rpy="0.0 0.0 0.0" xyz="0.0 -0.05 -0.673001" />
|
| 456 |
+
<geometry>
|
| 457 |
+
<mesh filename="assets/finger.dae" scale="0.001 0.001 0.001" />
|
| 458 |
+
</geometry>
|
| 459 |
+
</visual>
|
| 460 |
+
<inertial>
|
| 461 |
+
<origin rpy="0 0 0" xyz="0.0064528 0.01702 0.0219685" />
|
| 462 |
+
<mass value="0.03602545343277134" />
|
| 463 |
+
<inertia ixx="2.3749999999999997e-06" ixy="1e-06" ixz="1e-06" iyy="2.3749999999999997e-06" iyz="1e-06" izz="7.5e-07" />
|
| 464 |
+
</inertial>
|
| 465 |
+
</link>
|
| 466 |
+
<link name="openarm_right_right_finger">
|
| 467 |
+
<visual name="openarm_right_right_finger_visual">
|
| 468 |
+
<origin rpy="0.0 0.0 0.0" xyz="0.0 0.05 -0.673001" />
|
| 469 |
+
<geometry>
|
| 470 |
+
<mesh filename="assets/finger.dae" scale="0.001 -0.001 0.001" />
|
| 471 |
+
</geometry>
|
| 472 |
+
</visual>
|
| 473 |
+
<inertial>
|
| 474 |
+
<origin rpy="0 0 0" xyz="0.0064528 -0.01702 0.0219685" />
|
| 475 |
+
<mass value="0.03602545343277134" />
|
| 476 |
+
<inertia ixx="2.3749999999999997e-06" ixy="1e-06" ixz="1e-06" iyy="2.3749999999999997e-06" iyz="1e-06" izz="7.5e-07" />
|
| 477 |
+
</inertial>
|
| 478 |
+
</link>
|
| 479 |
+
<joint name="openarm_right_finger_joint1" type="prismatic">
|
| 480 |
+
<parent link="openarm_right_hand" />
|
| 481 |
+
<child link="openarm_right_right_finger" />
|
| 482 |
+
<origin rpy="0 0 0" xyz="0 -0.006 0.015" />
|
| 483 |
+
<axis xyz="0 -1 0" />
|
| 484 |
+
<limit effort="333" lower="0.0" upper="0.044" velocity="10.0" />
|
| 485 |
+
</joint>
|
| 486 |
+
<joint name="openarm_right_finger_joint2" type="prismatic">
|
| 487 |
+
<parent link="openarm_right_hand" />
|
| 488 |
+
<child link="openarm_right_left_finger" />
|
| 489 |
+
<origin rpy="0 0 0" xyz="0 0.006 0.015" />
|
| 490 |
+
<axis xyz="0 1 0" />
|
| 491 |
+
<limit effort="333" lower="0.0" upper="0.044" velocity="10.0" />
|
| 492 |
+
<mimic joint="openarm_right_finger_joint1" />
|
| 493 |
+
</joint>
|
| 494 |
+
</robot>
|
| 495 |
+
|
public/urdf/so101/assets/base_motor_holder_so101_v1.stl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:8cd2f241037ea377af1191fffe0dd9d9006beea6dcc48543660ed41647072424
|
| 3 |
+
size 1877084
|
public/urdf/so101/assets/base_so101_v2.stl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:bb12b7026575e1f70ccc7240051f9d943553bf34e5128537de6cd86fae33924d
|
| 3 |
+
size 471584
|
public/urdf/so101/assets/motor_holder_so101_base_v1.stl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:31242ae6fb59d8b15c66617b88ad8e9bded62d57c35d11c0c43a70d2f4caa95b
|
| 3 |
+
size 1129384
|
public/urdf/so101/assets/motor_holder_so101_wrist_v1.stl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:887f92e6013cb64ea3a1ab8675e92da1e0beacfd5e001f972523540545e08011
|
| 3 |
+
size 1052184
|
public/urdf/so101/assets/moving_jaw_so101_v1.stl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:785a9dded2f474bc1d869e0d3dae398a3dcd9c0c345640040472210d2861fa9d
|
| 3 |
+
size 1413584
|
public/urdf/so101/assets/rotation_pitch_so101_v1.stl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:9be900cc2a2bf718102841ef82ef8d2873842427648092c8ed2ca1e2ef4ffa34
|
| 3 |
+
size 883684
|
public/urdf/so101/assets/sts3215_03a_no_horn_v1.stl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:75ef3781b752e4065891aea855e34dc161a38a549549cd0970cedd07eae6f887
|
| 3 |
+
size 865884
|
public/urdf/so101/assets/sts3215_03a_v1.stl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:a37c871fb502483ab96c256baf457d36f2e97afc9205313d9c5ab275ef941cd0
|
| 3 |
+
size 954084
|
public/urdf/so101/assets/under_arm_so101_v1.stl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:d01d1f2de365651dcad9d6669e94ff87ff7652b5bb2d10752a66a456a86dbc71
|
| 3 |
+
size 1975884
|
public/urdf/so101/assets/upper_arm_so101_v1.stl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:475056e03a17e71919b82fd88ab9a0b898ab50164f2a7943652a6b2941bb2d4f
|
| 3 |
+
size 1303484
|
public/urdf/so101/assets/waveshare_mounting_plate_so101_v2.stl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:e197e24005a07d01bbc06a8c42311664eaeda415bf859f68fa247884d0f1a6e9
|
| 3 |
+
size 62784
|
public/urdf/so101/assets/wrist_roll_follower_so101_v1.stl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:4b17b410a12d64ec39554abc3e8054d8a97384b2dc4a8d95a5ecb2a93670f5f4
|
| 3 |
+
size 1439884
|
public/urdf/so101/assets/wrist_roll_pitch_so101_v2.stl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:6c7ec5525b4d8b9e397a30ab4bb0037156a5d5f38a4adf2c7d943d6c56eda5ae
|
| 3 |
+
size 2699784
|
public/urdf/so101/so100.urdf
ADDED
|
@@ -0,0 +1,406 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0"?>
|
| 2 |
+
|
| 3 |
+
<!-- Originally Generated using onshape-to-robot -->
|
| 4 |
+
<!-- Onshape
|
| 5 |
+
https://cad.onshape.com/documents/7715cc284bb430fe6dab4ffd/w/4fd0791b683777b02f8d975a/e/826c553ede3b7592eb9ca800 -->
|
| 6 |
+
<robot name="so100">
|
| 7 |
+
<!-- Materials -->
|
| 8 |
+
<material name="green">
|
| 9 |
+
<color rgba="0.06 0.4 0.1 1.0" />
|
| 10 |
+
</material>
|
| 11 |
+
<material name="black">
|
| 12 |
+
<color rgba="0.1 0.1 0.1 1.0" />
|
| 13 |
+
</material>
|
| 14 |
+
|
| 15 |
+
<!-- World link -->
|
| 16 |
+
<link name="world" />
|
| 17 |
+
|
| 18 |
+
<!-- Joint from world to base -->
|
| 19 |
+
<joint name="world_to_base" type="fixed">
|
| 20 |
+
<parent link="world" />
|
| 21 |
+
<child link="base" />
|
| 22 |
+
<origin xyz="0.163038 0.168068 -0.0300817" rpy="0 0 0" />
|
| 23 |
+
</joint>
|
| 24 |
+
|
| 25 |
+
<!-- Link base -->
|
| 26 |
+
<link name="base">
|
| 27 |
+
<inertial>
|
| 28 |
+
<origin xyz="-0.14932 -0.16812 0.065966" rpy="0 0 0" />
|
| 29 |
+
<mass value="0.147" />
|
| 30 |
+
<inertia ixx="0.000114686" ixy="-4.59787e-07" ixz="4.97151e-06" iyy="0.000136117"
|
| 31 |
+
iyz="9.75275e-08" izz="0.000130364" />
|
| 32 |
+
</inertial>
|
| 33 |
+
<!-- Part base_motor_holder_so101_v1 -->
|
| 34 |
+
<visual>
|
| 35 |
+
<origin xyz="-0.169402 -0.168167 0.0300817" rpy="1.5708 -1.67685e-15 1.5708" />
|
| 36 |
+
<geometry>
|
| 37 |
+
<mesh filename="assets/base_motor_holder_so101_v1.stl" />
|
| 38 |
+
</geometry>
|
| 39 |
+
<material name="green" />
|
| 40 |
+
</visual>
|
| 41 |
+
<collision>
|
| 42 |
+
<origin xyz="-0.169402 -0.168167 0.0300817" rpy="1.5708 -1.67685e-15 1.5708" />
|
| 43 |
+
<geometry>
|
| 44 |
+
<mesh filename="assets/base_motor_holder_so101_v1.stl" />
|
| 45 |
+
</geometry>
|
| 46 |
+
</collision>
|
| 47 |
+
<!-- Part base_so101_v2 -->
|
| 48 |
+
<visual>
|
| 49 |
+
<origin xyz="-0.169402 -0.168068 0.0300817" rpy="1.5708 -1.6144e-15 1.5708" />
|
| 50 |
+
<geometry>
|
| 51 |
+
<mesh filename="assets/base_so101_v2.stl" />
|
| 52 |
+
</geometry>
|
| 53 |
+
<material name="green" />
|
| 54 |
+
</visual>
|
| 55 |
+
<collision>
|
| 56 |
+
<origin xyz="-0.169402 -0.168068 0.0300817" rpy="1.5708 -1.6144e-15 1.5708" />
|
| 57 |
+
<geometry>
|
| 58 |
+
<mesh filename="assets/base_so101_v2.stl" />
|
| 59 |
+
</geometry>
|
| 60 |
+
</collision>
|
| 61 |
+
<!-- Part sts3215_03a_v1 -->
|
| 62 |
+
<visual>
|
| 63 |
+
<origin xyz="-0.136702 -0.168068 0.0761817" rpy="-8.21148e-16 7.84513e-18 1.249e-15" />
|
| 64 |
+
<geometry>
|
| 65 |
+
<mesh filename="assets/sts3215_03a_v1.stl" />
|
| 66 |
+
</geometry>
|
| 67 |
+
<material name="black" />
|
| 68 |
+
</visual>
|
| 69 |
+
<collision>
|
| 70 |
+
<origin xyz="-0.136702 -0.168068 0.0761817" rpy="-8.21148e-16 7.84513e-18 1.249e-15" />
|
| 71 |
+
<geometry>
|
| 72 |
+
<mesh filename="assets/sts3215_03a_v1.stl" />
|
| 73 |
+
</geometry>
|
| 74 |
+
</collision>
|
| 75 |
+
<!-- Part waveshare_mounting_plate_so101_v2 -->
|
| 76 |
+
<visual>
|
| 77 |
+
<origin xyz="-0.19402 -0.168267 0.0798817" rpy="1.5708 -1.35493e-14 1.5708" />
|
| 78 |
+
<geometry>
|
| 79 |
+
<mesh filename="assets/waveshare_mounting_plate_so101_v2.stl" />
|
| 80 |
+
</geometry>
|
| 81 |
+
<material name="green" />
|
| 82 |
+
</visual>
|
| 83 |
+
<collision>
|
| 84 |
+
<origin xyz="-0.19402 -0.168267 0.0798817" rpy="1.5708 -1.35493e-14 1.5708" />
|
| 85 |
+
<geometry>
|
| 86 |
+
<mesh filename="assets/waveshare_mounting_plate_so101_v2.stl" />
|
| 87 |
+
</geometry>
|
| 88 |
+
</collision>
|
| 89 |
+
</link>
|
| 90 |
+
<!-- Frame baseframe (dummy link + fixed joint) -->
|
| 91 |
+
<link name="baseframe">
|
| 92 |
+
<origin xyz="0 0 0" rpy="0 -0 0" />
|
| 93 |
+
<inertial>
|
| 94 |
+
<origin xyz="0 0 0" rpy="0 0 0" />
|
| 95 |
+
<mass value="1e-9" />
|
| 96 |
+
<inertia ixx="0" ixy="0" ixz="0" iyy="0" iyz="0" izz="0" />
|
| 97 |
+
</inertial>
|
| 98 |
+
</link>
|
| 99 |
+
<joint name="baseframe_frame" type="fixed">
|
| 100 |
+
<origin xyz="-0.163038 -0.168068 0.0324817" rpy="1.6144e-15 7.84513e-18 1.33799e-15" />
|
| 101 |
+
<parent link="base" />
|
| 102 |
+
<child link="baseframe" />
|
| 103 |
+
<axis xyz="0 0 0" />
|
| 104 |
+
</joint>
|
| 105 |
+
<!-- Link shoulder -->
|
| 106 |
+
<link name="shoulder">
|
| 107 |
+
<inertial>
|
| 108 |
+
<origin xyz="-0.0307604 -1.66727e-05 -0.0252713" rpy="0 0 0" />
|
| 109 |
+
<mass value="0.100006" />
|
| 110 |
+
<inertia ixx="8.3759e-05" ixy="7.55525e-08" ixz="-1.16342e-06" iyy="8.10403e-05"
|
| 111 |
+
iyz="1.54663e-07" izz="2.39783e-05" />
|
| 112 |
+
</inertial>
|
| 113 |
+
<!-- Part sts3215_03a_v1_2 -->
|
| 114 |
+
<visual>
|
| 115 |
+
<origin xyz="-0.0303992 0.000422241 -0.0417" rpy="1.5708 1.5708 0" />
|
| 116 |
+
<geometry>
|
| 117 |
+
<mesh filename="assets/sts3215_03a_v1.stl" />
|
| 118 |
+
</geometry>
|
| 119 |
+
<material name="black" />
|
| 120 |
+
</visual>
|
| 121 |
+
<collision>
|
| 122 |
+
<origin xyz="-0.0303992 0.000422241 -0.0417" rpy="1.5708 1.5708 0" />
|
| 123 |
+
<geometry>
|
| 124 |
+
<mesh filename="assets/sts3215_03a_v1.stl" />
|
| 125 |
+
</geometry>
|
| 126 |
+
</collision>
|
| 127 |
+
<!-- Part motor_holder_so101_base_v1 -->
|
| 128 |
+
<visual>
|
| 129 |
+
<origin xyz="-0.0675992 -0.000177759 0.0158499" rpy="1.5708 -1.5708 0" />
|
| 130 |
+
<geometry>
|
| 131 |
+
<mesh filename="assets/motor_holder_so101_base_v1.stl" />
|
| 132 |
+
</geometry>
|
| 133 |
+
<material name="green" />
|
| 134 |
+
</visual>
|
| 135 |
+
<collision>
|
| 136 |
+
<origin xyz="-0.0675992 -0.000177759 0.0158499" rpy="1.5708 -1.5708 0" />
|
| 137 |
+
<geometry>
|
| 138 |
+
<mesh filename="assets/motor_holder_so101_base_v1.stl" />
|
| 139 |
+
</geometry>
|
| 140 |
+
</collision>
|
| 141 |
+
<!-- Part rotation_pitch_so101_v1 -->
|
| 142 |
+
<visual>
|
| 143 |
+
<origin xyz="0.0122008 2.22413e-05 0.0464" rpy="-1.5708 2.35221e-33 0" />
|
| 144 |
+
<geometry>
|
| 145 |
+
<mesh filename="assets/rotation_pitch_so101_v1.stl" />
|
| 146 |
+
</geometry>
|
| 147 |
+
<material name="green" />
|
| 148 |
+
</visual>
|
| 149 |
+
<collision>
|
| 150 |
+
<origin xyz="0.0122008 2.22413e-05 0.0464" rpy="-1.5708 2.35221e-33 0" />
|
| 151 |
+
<geometry>
|
| 152 |
+
<mesh filename="assets/rotation_pitch_so101_v1.stl" />
|
| 153 |
+
</geometry>
|
| 154 |
+
</collision>
|
| 155 |
+
</link>
|
| 156 |
+
<!-- Link upper_arm -->
|
| 157 |
+
<link name="upper_arm">
|
| 158 |
+
<inertial>
|
| 159 |
+
<origin xyz="-0.0898471 -0.00838224 0.0184089" rpy="0 0 0" />
|
| 160 |
+
<mass value="0.103" />
|
| 161 |
+
<inertia ixx="4.08002e-05" ixy="-1.97819e-05" ixz="-4.03016e-08" iyy="0.000147318"
|
| 162 |
+
iyz="8.97326e-09" izz="0.000142487" />
|
| 163 |
+
</inertial>
|
| 164 |
+
<!-- Part sts3215_03a_v1_3 -->
|
| 165 |
+
<visual>
|
| 166 |
+
<origin xyz="-0.11257 -0.0155 0.0187" rpy="-3.14159 -5.27356e-16 -1.5708" />
|
| 167 |
+
<geometry>
|
| 168 |
+
<mesh filename="assets/sts3215_03a_v1.stl" />
|
| 169 |
+
</geometry>
|
| 170 |
+
<material name="black" />
|
| 171 |
+
</visual>
|
| 172 |
+
<collision>
|
| 173 |
+
<origin xyz="-0.11257 -0.0155 0.0187" rpy="-3.14159 -5.27356e-16 -1.5708" />
|
| 174 |
+
<geometry>
|
| 175 |
+
<mesh filename="assets/sts3215_03a_v1.stl" />
|
| 176 |
+
</geometry>
|
| 177 |
+
</collision>
|
| 178 |
+
<!-- Part upper_arm_so101_v1 -->
|
| 179 |
+
<visual>
|
| 180 |
+
<origin xyz="-0.065085 0.012 0.0182" rpy="3.14159 -0 -1.30911e-30" />
|
| 181 |
+
<geometry>
|
| 182 |
+
<mesh filename="assets/upper_arm_so101_v1.stl" />
|
| 183 |
+
</geometry>
|
| 184 |
+
<material name="green" />
|
| 185 |
+
</visual>
|
| 186 |
+
<collision>
|
| 187 |
+
<origin xyz="-0.065085 0.012 0.0182" rpy="3.14159 -0 -1.30911e-30" />
|
| 188 |
+
<geometry>
|
| 189 |
+
<mesh filename="assets/upper_arm_so101_v1.stl" />
|
| 190 |
+
</geometry>
|
| 191 |
+
</collision>
|
| 192 |
+
</link>
|
| 193 |
+
<!-- Link lower_arm -->
|
| 194 |
+
<link name="lower_arm">
|
| 195 |
+
<inertial>
|
| 196 |
+
<origin xyz="-0.0980701 0.00324376 0.0182831" rpy="0 0 0" />
|
| 197 |
+
<mass value="0.104" />
|
| 198 |
+
<inertia ixx="2.87438e-05" ixy="7.41152e-06" ixz="1.26409e-06" iyy="0.000159844"
|
| 199 |
+
iyz="-4.90188e-08" izz="0.00014529" />
|
| 200 |
+
</inertial>
|
| 201 |
+
<!-- Part under_arm_so101_v1 -->
|
| 202 |
+
<visual>
|
| 203 |
+
<origin xyz="-0.0648499 -0.032 0.0182" rpy="3.14159 -0 6.67202e-31" />
|
| 204 |
+
<geometry>
|
| 205 |
+
<mesh filename="assets/under_arm_so101_v1.stl" />
|
| 206 |
+
</geometry>
|
| 207 |
+
<material name="green" />
|
| 208 |
+
</visual>
|
| 209 |
+
<collision>
|
| 210 |
+
<origin xyz="-0.0648499 -0.032 0.0182" rpy="3.14159 -0 6.67202e-31" />
|
| 211 |
+
<geometry>
|
| 212 |
+
<mesh filename="assets/under_arm_so101_v1.stl" />
|
| 213 |
+
</geometry>
|
| 214 |
+
</collision>
|
| 215 |
+
<!-- Part motor_holder_so101_wrist_v1 -->
|
| 216 |
+
<visual>
|
| 217 |
+
<origin xyz="-0.0648499 -0.032 0.018" rpy="-3.14159 -2.55351e-15 -2.56146e-31" />
|
| 218 |
+
<geometry>
|
| 219 |
+
<mesh filename="assets/motor_holder_so101_wrist_v1.stl" />
|
| 220 |
+
</geometry>
|
| 221 |
+
<material name="green" />
|
| 222 |
+
</visual>
|
| 223 |
+
<collision>
|
| 224 |
+
<origin xyz="-0.0648499 -0.032 0.018" rpy="-3.14159 -2.55351e-15 -2.56146e-31" />
|
| 225 |
+
<geometry>
|
| 226 |
+
<mesh filename="assets/motor_holder_so101_wrist_v1.stl" />
|
| 227 |
+
</geometry>
|
| 228 |
+
</collision>
|
| 229 |
+
<!-- Part sts3215_03a_v1_4 -->
|
| 230 |
+
<visual>
|
| 231 |
+
<origin xyz="-0.1224 0.0052 0.0187" rpy="-3.14159 -7.88861e-31 -3.14159" />
|
| 232 |
+
<geometry>
|
| 233 |
+
<mesh filename="assets/sts3215_03a_v1.stl" />
|
| 234 |
+
</geometry>
|
| 235 |
+
<material name="black" />
|
| 236 |
+
</visual>
|
| 237 |
+
<collision>
|
| 238 |
+
<origin xyz="-0.1224 0.0052 0.0187" rpy="-3.14159 -7.88861e-31 -3.14159" />
|
| 239 |
+
<geometry>
|
| 240 |
+
<mesh filename="assets/sts3215_03a_v1.stl" />
|
| 241 |
+
</geometry>
|
| 242 |
+
</collision>
|
| 243 |
+
</link>
|
| 244 |
+
<!-- Link wrist -->
|
| 245 |
+
<link name="wrist">
|
| 246 |
+
<inertial>
|
| 247 |
+
<origin xyz="-0.000103312 -0.0386143 0.0281156" rpy="0 0 0" />
|
| 248 |
+
<mass value="0.079" />
|
| 249 |
+
<inertia ixx="3.68263e-05" ixy="1.7893e-08" ixz="-5.28128e-08" iyy="2.5391e-05"
|
| 250 |
+
iyz="3.6412e-06" izz="2.1e-05" />
|
| 251 |
+
</inertial>
|
| 252 |
+
<!-- Part sts3215_03a_no_horn_v1 -->
|
| 253 |
+
<visual>
|
| 254 |
+
<origin xyz="5.55112e-17 -0.0424 0.0306" rpy="1.5708 1.5708 0" />
|
| 255 |
+
<geometry>
|
| 256 |
+
<mesh filename="assets/sts3215_03a_no_horn_v1.stl" />
|
| 257 |
+
</geometry>
|
| 258 |
+
<material name="black" />
|
| 259 |
+
</visual>
|
| 260 |
+
<collision>
|
| 261 |
+
<origin xyz="5.55112e-17 -0.0424 0.0306" rpy="1.5708 1.5708 0" />
|
| 262 |
+
<geometry>
|
| 263 |
+
<mesh filename="assets/sts3215_03a_no_horn_v1.stl" />
|
| 264 |
+
</geometry>
|
| 265 |
+
</collision>
|
| 266 |
+
<!-- Part wrist_roll_pitch_so101_v2 -->
|
| 267 |
+
<visual>
|
| 268 |
+
<origin xyz="0 -0.028 0.0181" rpy="-1.5708 -1.5708 0" />
|
| 269 |
+
<geometry>
|
| 270 |
+
<mesh filename="assets/wrist_roll_pitch_so101_v2.stl" />
|
| 271 |
+
</geometry>
|
| 272 |
+
<material name="green" />
|
| 273 |
+
</visual>
|
| 274 |
+
<collision>
|
| 275 |
+
<origin xyz="0 -0.028 0.0181" rpy="-1.5708 -1.5708 0" />
|
| 276 |
+
<geometry>
|
| 277 |
+
<mesh filename="assets/wrist_roll_pitch_so101_v2.stl" />
|
| 278 |
+
</geometry>
|
| 279 |
+
</collision>
|
| 280 |
+
</link>
|
| 281 |
+
<!-- Link gripper -->
|
| 282 |
+
<link name="gripper">
|
| 283 |
+
<inertial>
|
| 284 |
+
<origin xyz="0.000213627 0.000245138 -0.025187" rpy="0 0 0" />
|
| 285 |
+
<mass value="0.087" />
|
| 286 |
+
<inertia ixx="2.75087e-05" ixy="-3.35241e-07" ixz="-5.7352e-06" iyy="4.33657e-05"
|
| 287 |
+
iyz="-5.17847e-08" izz="3.45059e-05" />
|
| 288 |
+
</inertial>
|
| 289 |
+
<!-- Part sts3215_03a_v1_5 -->
|
| 290 |
+
<visual>
|
| 291 |
+
<origin xyz="0.0077 0.0001 -0.0234" rpy="-1.5708 -5.19179e-17 -1.66533e-16" />
|
| 292 |
+
<geometry>
|
| 293 |
+
<mesh filename="assets/sts3215_03a_v1.stl" />
|
| 294 |
+
</geometry>
|
| 295 |
+
<material name="black" />
|
| 296 |
+
</visual>
|
| 297 |
+
<collision>
|
| 298 |
+
<origin xyz="0.0077 0.0001 -0.0234" rpy="-1.5708 -5.19179e-17 -1.66533e-16" />
|
| 299 |
+
<geometry>
|
| 300 |
+
<mesh filename="assets/sts3215_03a_v1.stl" />
|
| 301 |
+
</geometry>
|
| 302 |
+
</collision>
|
| 303 |
+
<!-- Part wrist_roll_follower_so101_v1 -->
|
| 304 |
+
<visual>
|
| 305 |
+
<origin xyz="0 -0.000218214 0.000949706" rpy="-3.14159 -5.55112e-17 0" />
|
| 306 |
+
<geometry>
|
| 307 |
+
<mesh filename="assets/wrist_roll_follower_so101_v1.stl" />
|
| 308 |
+
</geometry>
|
| 309 |
+
<material name="green" />
|
| 310 |
+
</visual>
|
| 311 |
+
<collision>
|
| 312 |
+
<origin xyz="0 -0.000218214 0.000949706" rpy="-3.14159 -5.55112e-17 0" />
|
| 313 |
+
<geometry>
|
| 314 |
+
<mesh filename="assets/wrist_roll_follower_so101_v1.stl" />
|
| 315 |
+
</geometry>
|
| 316 |
+
</collision>
|
| 317 |
+
</link>
|
| 318 |
+
<!-- Frame gripperframe (dummy link + fixed joint) -->
|
| 319 |
+
<link name="gripperframe">
|
| 320 |
+
<origin xyz="0 0 0" rpy="0 -0 0" />
|
| 321 |
+
<inertial>
|
| 322 |
+
<origin xyz="0 0 0" rpy="0 0 0" />
|
| 323 |
+
<mass value="1e-9" />
|
| 324 |
+
<inertia ixx="0" ixy="0" ixz="0" iyy="0" iyz="0" izz="0" />
|
| 325 |
+
</inertial>
|
| 326 |
+
</link>
|
| 327 |
+
<joint name="gripperframe_frame" type="fixed">
|
| 328 |
+
<origin xyz="-0.0079 -0.000218121 -0.0981274" rpy="-0 1.5708 0" />
|
| 329 |
+
<parent link="gripper" />
|
| 330 |
+
<child link="gripperframe" />
|
| 331 |
+
<axis xyz="0 0 0" />
|
| 332 |
+
</joint>
|
| 333 |
+
<!-- Link moving_jaw_so101_v1 -->
|
| 334 |
+
<link name="moving_jaw_so101_v1">
|
| 335 |
+
<inertial>
|
| 336 |
+
<origin xyz="-0.00157495 -0.0300244 0.0192755" rpy="0 0 0" />
|
| 337 |
+
<mass value="0.012" />
|
| 338 |
+
<inertia ixx="6.61427e-06" ixy="-3.19807e-07" ixz="-5.90717e-09" iyy="1.89032e-06"
|
| 339 |
+
iyz="-1.09945e-07" izz="5.28738e-06" />
|
| 340 |
+
</inertial>
|
| 341 |
+
<!-- Part moving_jaw_so101_v1 -->
|
| 342 |
+
<visual>
|
| 343 |
+
<origin xyz="-5.55112e-17 0 0.0189" rpy="9.53145e-17 6.93889e-18 1.24077e-24" />
|
| 344 |
+
<geometry>
|
| 345 |
+
<mesh filename="assets/moving_jaw_so101_v1.stl" />
|
| 346 |
+
</geometry>
|
| 347 |
+
<material name="green" />
|
| 348 |
+
</visual>
|
| 349 |
+
<collision>
|
| 350 |
+
<origin xyz="-5.55112e-17 0 0.0189" rpy="9.53145e-17 6.93889e-18 1.24077e-24" />
|
| 351 |
+
<geometry>
|
| 352 |
+
<mesh filename="assets/moving_jaw_so101_v1.stl" />
|
| 353 |
+
</geometry>
|
| 354 |
+
</collision>
|
| 355 |
+
</link>
|
| 356 |
+
|
| 357 |
+
<!-- Joint from base to shoulder -->
|
| 358 |
+
<joint name="Rotation" type="revolute">
|
| 359 |
+
<origin xyz="-0.124202 -0.168068 0.0948817" rpy="3.14159 0 0" />
|
| 360 |
+
<parent link="base" />
|
| 361 |
+
<child link="shoulder" />
|
| 362 |
+
<axis xyz="0 0 1" />
|
| 363 |
+
<limit effort="10" velocity="10" lower="1.22014" upper="5.05986" />
|
| 364 |
+
</joint>
|
| 365 |
+
<!-- Joint from shoulder to upper_arm -->
|
| 366 |
+
<joint name="Pitch" type="revolute">
|
| 367 |
+
<origin xyz="-0.0303992 -0.0182778 -0.0542" rpy="-1.5708 1.5692 0" />
|
| 368 |
+
<parent link="shoulder" />
|
| 369 |
+
<child link="upper_arm" />
|
| 370 |
+
<axis xyz="0 0 1" />
|
| 371 |
+
<limit effort="10" velocity="10" lower="1.39467" upper="4.88692" />
|
| 372 |
+
</joint>
|
| 373 |
+
<!-- Joint from upper_arm to lower_arm -->
|
| 374 |
+
<joint name="Elbow" type="revolute">
|
| 375 |
+
<origin xyz="-0.11257 -0.028 2.09886e-16" rpy="0 0 4.71239" />
|
| 376 |
+
<parent link="upper_arm" />
|
| 377 |
+
<child link="lower_arm" />
|
| 378 |
+
<axis xyz="0 0 1" />
|
| 379 |
+
<limit effort="10" velocity="10" lower="1.39626" upper="4.71239" />
|
| 380 |
+
</joint>
|
| 381 |
+
<!-- Joint from lower_arm to wrist -->
|
| 382 |
+
<joint name="Wrist_Pitch" type="revolute">
|
| 383 |
+
<origin xyz="-0.1349 0.0052 8.44651e-17" rpy="0 0 1.57079" />
|
| 384 |
+
<parent link="lower_arm" />
|
| 385 |
+
<child link="wrist" />
|
| 386 |
+
<axis xyz="0 0 1" />
|
| 387 |
+
<limit effort="10" velocity="10" lower="1.48353" upper="4.79965" />
|
| 388 |
+
</joint>
|
| 389 |
+
<!-- Joint from wrist to gripper -->
|
| 390 |
+
<joint name="Wrist_Roll" type="revolute">
|
| 391 |
+
<origin xyz="2.77556e-16 -0.0611 0.0181" rpy="1.5708 3.1902695 3.14159" />
|
| 392 |
+
<parent link="wrist" />
|
| 393 |
+
<child link="gripper" />
|
| 394 |
+
<axis xyz="0 0 1" />
|
| 395 |
+
<limit effort="10" velocity="10" lower="0.39774" upper="5.9828" />
|
| 396 |
+
</joint>
|
| 397 |
+
<!-- Joint from gripper to moving_jaw_so101_v1 -->
|
| 398 |
+
<joint name="Jaw" type="revolute">
|
| 399 |
+
<origin xyz="0.0202 0.0188 -0.0234" rpy="1.5708 3.315 0" />
|
| 400 |
+
<parent link="gripper" />
|
| 401 |
+
<child link="moving_jaw_so101_v1" />
|
| 402 |
+
<axis xyz="0 0 1" />
|
| 403 |
+
<limit effort="10" velocity="10" lower="3.14" upper="4.88692" />
|
| 404 |
+
</joint>
|
| 405 |
+
</robot>
|
| 406 |
+
|
public/urdf/so101/so101_new_calib.urdf
ADDED
|
@@ -0,0 +1,453 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0" encoding="utf-8"?>
|
| 2 |
+
<!-- Generated using onshape-to-robot -->
|
| 3 |
+
<!-- Onshape https://cad.onshape.com/documents/7715cc284bb430fe6dab4ffd/w/4fd0791b683777b02f8d975a/e/826c553ede3b7592eb9ca800 -->
|
| 4 |
+
<robot name="so101_new_calib">
|
| 5 |
+
|
| 6 |
+
<!-- Materials -->
|
| 7 |
+
<material name="3d_printed">
|
| 8 |
+
<color rgba="1.0 0.82 0.12 1.0"/>
|
| 9 |
+
</material>
|
| 10 |
+
<material name="sts3215">
|
| 11 |
+
<color rgba="0.1 0.1 0.1 1.0"/>
|
| 12 |
+
</material>
|
| 13 |
+
|
| 14 |
+
<!-- Link base -->
|
| 15 |
+
<link name="base_link">
|
| 16 |
+
<inertial>
|
| 17 |
+
<origin xyz="0.0137179 -5.19711e-05 0.0334843" rpy="0 0 0"/>
|
| 18 |
+
<mass value="0.147"/>
|
| 19 |
+
<inertia ixx="0.000114686" ixy="-4.59787e-07" ixz="4.97151e-06" iyy="0.000136117" iyz="9.75275e-08" izz="0.000130364"/>
|
| 20 |
+
</inertial>
|
| 21 |
+
<!-- Part base_motor_holder_so101_v1 -->
|
| 22 |
+
<visual>
|
| 23 |
+
<origin xyz="-0.00636471 -9.94414e-05 -0.0024" rpy="1.5708 -1.67685e-15 1.5708"/>
|
| 24 |
+
<geometry>
|
| 25 |
+
<mesh filename="assets/base_motor_holder_so101_v1.stl"/>
|
| 26 |
+
</geometry>
|
| 27 |
+
<material name="3d_printed"/>
|
| 28 |
+
</visual>
|
| 29 |
+
<collision>
|
| 30 |
+
<origin xyz="-0.00636471 -9.94414e-05 -0.0024" rpy="1.5708 -1.67685e-15 1.5708"/>
|
| 31 |
+
<geometry>
|
| 32 |
+
<mesh filename="assets/base_motor_holder_so101_v1.stl"/>
|
| 33 |
+
</geometry>
|
| 34 |
+
</collision>
|
| 35 |
+
<!-- Part base_so101_v2 -->
|
| 36 |
+
<visual>
|
| 37 |
+
<origin xyz="-0.00636471 -8.97657e-09 -0.0024" rpy="1.5708 -2.78073e-29 1.5708"/>
|
| 38 |
+
<geometry>
|
| 39 |
+
<mesh filename="assets/base_so101_v2.stl"/>
|
| 40 |
+
</geometry>
|
| 41 |
+
<material name="3d_printed"/>
|
| 42 |
+
</visual>
|
| 43 |
+
<collision>
|
| 44 |
+
<origin xyz="-0.00636471 -8.97657e-09 -0.0024" rpy="1.5708 -2.78073e-29 1.5708"/>
|
| 45 |
+
<geometry>
|
| 46 |
+
<mesh filename="assets/base_so101_v2.stl"/>
|
| 47 |
+
</geometry>
|
| 48 |
+
</collision>
|
| 49 |
+
<!-- Part sts3215_03a_v1 -->
|
| 50 |
+
<visual>
|
| 51 |
+
<origin xyz="0.0263353 -8.97657e-09 0.0437" rpy="-8.21148e-16 7.84513e-18 1.249e-15"/>
|
| 52 |
+
<geometry>
|
| 53 |
+
<mesh filename="assets/sts3215_03a_v1.stl"/>
|
| 54 |
+
</geometry>
|
| 55 |
+
<material name="sts3215"/>
|
| 56 |
+
</visual>
|
| 57 |
+
<collision>
|
| 58 |
+
<origin xyz="0.0263353 -8.97657e-09 0.0437" rpy="-8.21148e-16 7.84513e-18 1.249e-15"/>
|
| 59 |
+
<geometry>
|
| 60 |
+
<mesh filename="assets/sts3215_03a_v1.stl"/>
|
| 61 |
+
</geometry>
|
| 62 |
+
</collision>
|
| 63 |
+
<!-- Part waveshare_mounting_plate_so101_v2 -->
|
| 64 |
+
<visual>
|
| 65 |
+
<origin xyz="-0.0309827 -0.000199441 0.0474" rpy="1.5708 -1.35493e-14 1.5708"/>
|
| 66 |
+
<geometry>
|
| 67 |
+
<mesh filename="assets/waveshare_mounting_plate_so101_v2.stl"/>
|
| 68 |
+
</geometry>
|
| 69 |
+
<material name="3d_printed"/>
|
| 70 |
+
</visual>
|
| 71 |
+
<collision>
|
| 72 |
+
<origin xyz="-0.0309827 -0.000199441 0.0474" rpy="1.5708 -1.35493e-14 1.5708"/>
|
| 73 |
+
<geometry>
|
| 74 |
+
<mesh filename="assets/waveshare_mounting_plate_so101_v2.stl"/>
|
| 75 |
+
</geometry>
|
| 76 |
+
</collision>
|
| 77 |
+
</link>
|
| 78 |
+
|
| 79 |
+
<!-- Link shoulder -->
|
| 80 |
+
<link name="shoulder_link">
|
| 81 |
+
<inertial>
|
| 82 |
+
<origin xyz="-0.0307604 -1.66727e-05 -0.0252713" rpy="0 0 0"/>
|
| 83 |
+
<mass value="0.100006"/>
|
| 84 |
+
<inertia ixx="8.3759e-05" ixy="7.55525e-08" ixz="-1.16342e-06" iyy="8.10403e-05" iyz="1.54663e-07" izz="2.39783e-05"/>
|
| 85 |
+
</inertial>
|
| 86 |
+
<!-- Part sts3215_03a_v1_2 -->
|
| 87 |
+
<visual>
|
| 88 |
+
<origin xyz="-0.0303992 0.000422241 -0.0417" rpy="1.5708 1.5708 0"/>
|
| 89 |
+
<geometry>
|
| 90 |
+
<mesh filename="assets/sts3215_03a_v1.stl"/>
|
| 91 |
+
</geometry>
|
| 92 |
+
<material name="sts3215"/>
|
| 93 |
+
</visual>
|
| 94 |
+
<collision>
|
| 95 |
+
<origin xyz="-0.0303992 0.000422241 -0.0417" rpy="1.5708 1.5708 0"/>
|
| 96 |
+
<geometry>
|
| 97 |
+
<mesh filename="assets/sts3215_03a_v1.stl"/>
|
| 98 |
+
</geometry>
|
| 99 |
+
</collision>
|
| 100 |
+
<!-- Part motor_holder_so101_base_v1 -->
|
| 101 |
+
<visual>
|
| 102 |
+
<origin xyz="-0.0675992 -0.000177759 0.0158499" rpy="1.5708 -1.5708 0"/>
|
| 103 |
+
<geometry>
|
| 104 |
+
<mesh filename="assets/motor_holder_so101_base_v1.stl"/>
|
| 105 |
+
</geometry>
|
| 106 |
+
<material name="3d_printed"/>
|
| 107 |
+
</visual>
|
| 108 |
+
<collision>
|
| 109 |
+
<origin xyz="-0.0675992 -0.000177759 0.0158499" rpy="1.5708 -1.5708 0"/>
|
| 110 |
+
<geometry>
|
| 111 |
+
<mesh filename="assets/motor_holder_so101_base_v1.stl"/>
|
| 112 |
+
</geometry>
|
| 113 |
+
</collision>
|
| 114 |
+
<!-- Part rotation_pitch_so101_v1 -->
|
| 115 |
+
<visual>
|
| 116 |
+
<origin xyz="0.0122008 2.22413e-05 0.0464" rpy="-1.5708 2.35221e-33 0"/>
|
| 117 |
+
<geometry>
|
| 118 |
+
<mesh filename="assets/rotation_pitch_so101_v1.stl"/>
|
| 119 |
+
</geometry>
|
| 120 |
+
<material name="3d_printed"/>
|
| 121 |
+
</visual>
|
| 122 |
+
<collision>
|
| 123 |
+
<origin xyz="0.0122008 2.22413e-05 0.0464" rpy="-1.5708 2.35221e-33 0"/>
|
| 124 |
+
<geometry>
|
| 125 |
+
<mesh filename="assets/rotation_pitch_so101_v1.stl"/>
|
| 126 |
+
</geometry>
|
| 127 |
+
</collision>
|
| 128 |
+
</link>
|
| 129 |
+
|
| 130 |
+
<!-- Link upper_arm -->
|
| 131 |
+
<link name="upper_arm_link">
|
| 132 |
+
<inertial>
|
| 133 |
+
<origin xyz="-0.0898471 -0.00838224 0.0184089" rpy="0 0 0"/>
|
| 134 |
+
<mass value="0.103"/>
|
| 135 |
+
<inertia ixx="4.08002e-05" ixy="-1.97819e-05" ixz="-4.03016e-08" iyy="0.000147318" iyz="8.97326e-09" izz="0.000142487"/>
|
| 136 |
+
</inertial>
|
| 137 |
+
<!-- Part sts3215_03a_v1_3 -->
|
| 138 |
+
<visual>
|
| 139 |
+
<origin xyz="-0.11257 -0.0155 0.0187" rpy="-3.14159 -5.27356e-16 -1.5708"/>
|
| 140 |
+
<geometry>
|
| 141 |
+
<mesh filename="assets/sts3215_03a_v1.stl"/>
|
| 142 |
+
</geometry>
|
| 143 |
+
<material name="sts3215"/>
|
| 144 |
+
</visual>
|
| 145 |
+
<collision>
|
| 146 |
+
<origin xyz="-0.11257 -0.0155 0.0187" rpy="-3.14159 -5.27356e-16 -1.5708"/>
|
| 147 |
+
<geometry>
|
| 148 |
+
<mesh filename="assets/sts3215_03a_v1.stl"/>
|
| 149 |
+
</geometry>
|
| 150 |
+
</collision>
|
| 151 |
+
<!-- Part upper_arm_so101_v1 -->
|
| 152 |
+
<visual>
|
| 153 |
+
<origin xyz="-0.065085 0.012 0.0182" rpy="3.14159 -0 -1.30911e-30"/>
|
| 154 |
+
<geometry>
|
| 155 |
+
<mesh filename="assets/upper_arm_so101_v1.stl"/>
|
| 156 |
+
</geometry>
|
| 157 |
+
<material name="3d_printed"/>
|
| 158 |
+
</visual>
|
| 159 |
+
<collision>
|
| 160 |
+
<origin xyz="-0.065085 0.012 0.0182" rpy="3.14159 -0 -1.30911e-30"/>
|
| 161 |
+
<geometry>
|
| 162 |
+
<mesh filename="assets/upper_arm_so101_v1.stl"/>
|
| 163 |
+
</geometry>
|
| 164 |
+
</collision>
|
| 165 |
+
</link>
|
| 166 |
+
|
| 167 |
+
<!-- Link lower_arm -->
|
| 168 |
+
<link name="lower_arm_link">
|
| 169 |
+
<inertial>
|
| 170 |
+
<origin xyz="-0.0980701 0.00324376 0.0182831" rpy="0 0 0"/>
|
| 171 |
+
<mass value="0.104"/>
|
| 172 |
+
<inertia ixx="2.87438e-05" ixy="7.41152e-06" ixz="1.26409e-06" iyy="0.000159844" iyz="-4.90188e-08" izz="0.00014529"/>
|
| 173 |
+
</inertial>
|
| 174 |
+
<!-- Part under_arm_so101_v1 -->
|
| 175 |
+
<visual>
|
| 176 |
+
<origin xyz="-0.0648499 -0.032 0.0182" rpy="3.14159 -0 6.67202e-31"/>
|
| 177 |
+
<geometry>
|
| 178 |
+
<mesh filename="assets/under_arm_so101_v1.stl"/>
|
| 179 |
+
</geometry>
|
| 180 |
+
<material name="3d_printed"/>
|
| 181 |
+
</visual>
|
| 182 |
+
<collision>
|
| 183 |
+
<origin xyz="-0.0648499 -0.032 0.0182" rpy="3.14159 -0 6.67202e-31"/>
|
| 184 |
+
<geometry>
|
| 185 |
+
<mesh filename="assets/under_arm_so101_v1.stl"/>
|
| 186 |
+
</geometry>
|
| 187 |
+
</collision>
|
| 188 |
+
<!-- Part motor_holder_so101_wrist_v1 -->
|
| 189 |
+
<visual>
|
| 190 |
+
<origin xyz="-0.0648499 -0.032 0.018" rpy="-3.14159 -2.55351e-15 -1.83387e-30"/>
|
| 191 |
+
<geometry>
|
| 192 |
+
<mesh filename="assets/motor_holder_so101_wrist_v1.stl"/>
|
| 193 |
+
</geometry>
|
| 194 |
+
<material name="3d_printed"/>
|
| 195 |
+
</visual>
|
| 196 |
+
<collision>
|
| 197 |
+
<origin xyz="-0.0648499 -0.032 0.018" rpy="-3.14159 -2.55351e-15 -1.83387e-30"/>
|
| 198 |
+
<geometry>
|
| 199 |
+
<mesh filename="assets/motor_holder_so101_wrist_v1.stl"/>
|
| 200 |
+
</geometry>
|
| 201 |
+
</collision>
|
| 202 |
+
<!-- Part sts3215_03a_v1_4 -->
|
| 203 |
+
<visual>
|
| 204 |
+
<origin xyz="-0.1224 0.0052 0.0187" rpy="-3.14159 -7.88861e-31 -3.14159"/>
|
| 205 |
+
<geometry>
|
| 206 |
+
<mesh filename="assets/sts3215_03a_v1.stl"/>
|
| 207 |
+
</geometry>
|
| 208 |
+
<material name="sts3215"/>
|
| 209 |
+
</visual>
|
| 210 |
+
<collision>
|
| 211 |
+
<origin xyz="-0.1224 0.0052 0.0187" rpy="-3.14159 -7.88861e-31 -3.14159"/>
|
| 212 |
+
<geometry>
|
| 213 |
+
<mesh filename="assets/sts3215_03a_v1.stl"/>
|
| 214 |
+
</geometry>
|
| 215 |
+
</collision>
|
| 216 |
+
</link>
|
| 217 |
+
|
| 218 |
+
<!-- Link wrist -->
|
| 219 |
+
<link name="wrist_link">
|
| 220 |
+
<inertial>
|
| 221 |
+
<origin xyz="-0.000103312 -0.0386143 0.0281156" rpy="0 0 0"/>
|
| 222 |
+
<mass value="0.079"/>
|
| 223 |
+
<inertia ixx="3.68263e-05" ixy="1.7893e-08" ixz="-5.28128e-08" iyy="2.5391e-05" iyz="3.6412e-06" izz="2.1e-05"/>
|
| 224 |
+
</inertial>
|
| 225 |
+
<!-- Part sts3215_03a_no_horn_v1 -->
|
| 226 |
+
<visual>
|
| 227 |
+
<origin xyz="8.32667e-17 -0.0424 0.0306" rpy="1.5708 1.5708 0"/>
|
| 228 |
+
<geometry>
|
| 229 |
+
<mesh filename="assets/sts3215_03a_no_horn_v1.stl"/>
|
| 230 |
+
</geometry>
|
| 231 |
+
<material name="sts3215"/>
|
| 232 |
+
</visual>
|
| 233 |
+
<collision>
|
| 234 |
+
<origin xyz="8.32667e-17 -0.0424 0.0306" rpy="1.5708 1.5708 0"/>
|
| 235 |
+
<geometry>
|
| 236 |
+
<mesh filename="assets/sts3215_03a_no_horn_v1.stl"/>
|
| 237 |
+
</geometry>
|
| 238 |
+
</collision>
|
| 239 |
+
<!-- Part wrist_roll_pitch_so101_v2 -->
|
| 240 |
+
<visual>
|
| 241 |
+
<origin xyz="0 -0.028 0.0181" rpy="-1.5708 -1.5708 0"/>
|
| 242 |
+
<geometry>
|
| 243 |
+
<mesh filename="assets/wrist_roll_pitch_so101_v2.stl"/>
|
| 244 |
+
</geometry>
|
| 245 |
+
<material name="3d_printed"/>
|
| 246 |
+
</visual>
|
| 247 |
+
<collision>
|
| 248 |
+
<origin xyz="0 -0.028 0.0181" rpy="-1.5708 -1.5708 0"/>
|
| 249 |
+
<geometry>
|
| 250 |
+
<mesh filename="assets/wrist_roll_pitch_so101_v2.stl"/>
|
| 251 |
+
</geometry>
|
| 252 |
+
</collision>
|
| 253 |
+
</link>
|
| 254 |
+
|
| 255 |
+
<!-- Link gripper -->
|
| 256 |
+
<link name="gripper_link">
|
| 257 |
+
<inertial>
|
| 258 |
+
<origin xyz="0.000213627 0.000245138 -0.025187" rpy="0 0 0"/>
|
| 259 |
+
<mass value="0.087"/>
|
| 260 |
+
<inertia ixx="2.75087e-05" ixy="-3.35241e-07" ixz="-5.7352e-06" iyy="4.33657e-05" iyz="-5.17847e-08" izz="3.45059e-05"/>
|
| 261 |
+
</inertial>
|
| 262 |
+
<!-- Part sts3215_03a_v1_5 -->
|
| 263 |
+
<visual>
|
| 264 |
+
<origin xyz="0.0077 0.0001 -0.0234" rpy="-1.5708 -5.19179e-17 -1.66533e-16"/>
|
| 265 |
+
<geometry>
|
| 266 |
+
<mesh filename="assets/sts3215_03a_v1.stl"/>
|
| 267 |
+
</geometry>
|
| 268 |
+
<material name="sts3215"/>
|
| 269 |
+
</visual>
|
| 270 |
+
<collision>
|
| 271 |
+
<origin xyz="0.0077 0.0001 -0.0234" rpy="-1.5708 -5.19179e-17 -1.66533e-16"/>
|
| 272 |
+
<geometry>
|
| 273 |
+
<mesh filename="assets/sts3215_03a_v1.stl"/>
|
| 274 |
+
</geometry>
|
| 275 |
+
</collision>
|
| 276 |
+
<!-- Part wrist_roll_follower_so101_v1 -->
|
| 277 |
+
<visual>
|
| 278 |
+
<origin xyz="8.32667e-17 -0.000218214 0.000949706" rpy="-3.14159 -5.55112e-17 0"/>
|
| 279 |
+
<geometry>
|
| 280 |
+
<mesh filename="assets/wrist_roll_follower_so101_v1.stl"/>
|
| 281 |
+
</geometry>
|
| 282 |
+
<material name="3d_printed"/>
|
| 283 |
+
</visual>
|
| 284 |
+
<collision>
|
| 285 |
+
<origin xyz="8.32667e-17 -0.000218214 0.000949706" rpy="-3.14159 -5.55112e-17 0"/>
|
| 286 |
+
<geometry>
|
| 287 |
+
<mesh filename="assets/wrist_roll_follower_so101_v1.stl"/>
|
| 288 |
+
</geometry>
|
| 289 |
+
</collision>
|
| 290 |
+
</link>
|
| 291 |
+
|
| 292 |
+
<!-- Gripper frame (dummy link + fixed joint) -->
|
| 293 |
+
<link name="gripper_frame_link">
|
| 294 |
+
<origin xyz="0 0 0" rpy="0 -0 0"/>
|
| 295 |
+
<inertial>
|
| 296 |
+
<origin xyz="0 0 0" rpy="0 0 0"/>
|
| 297 |
+
<mass value="1e-9"/>
|
| 298 |
+
<inertia ixx="0" ixy="0" ixz="0" iyy="0" iyz="0" izz="0"/>
|
| 299 |
+
</inertial>
|
| 300 |
+
</link>
|
| 301 |
+
|
| 302 |
+
<joint name="gripper_frame_joint" type="fixed">
|
| 303 |
+
<origin xyz="-0.0079 -0.000218121 -0.0981274" rpy="0 3.14159 0"/>
|
| 304 |
+
<parent link="gripper_link"/>
|
| 305 |
+
<child link="gripper_frame_link"/>
|
| 306 |
+
<axis xyz="0 0 0"/>
|
| 307 |
+
</joint>
|
| 308 |
+
|
| 309 |
+
<!-- Link moving_jaw_so101_v1 -->
|
| 310 |
+
<link name="moving_jaw_so101_v1_link">
|
| 311 |
+
<inertial>
|
| 312 |
+
<origin xyz="-0.00157495 -0.0300244 0.0192755" rpy="0 0 0"/>
|
| 313 |
+
<mass value="0.012"/>
|
| 314 |
+
<inertia ixx="6.61427e-06" ixy="-3.19807e-07" ixz="-5.90717e-09" iyy="1.89032e-06" iyz="-1.09945e-07" izz="5.28738e-06"/>
|
| 315 |
+
</inertial>
|
| 316 |
+
<!-- Part moving_jaw_so101_v1 -->
|
| 317 |
+
<visual>
|
| 318 |
+
<origin xyz="-5.55112e-17 -5.55112e-17 0.0189" rpy="9.53145e-17 6.93889e-18 1.24077e-24"/>
|
| 319 |
+
<geometry>
|
| 320 |
+
<mesh filename="assets/moving_jaw_so101_v1.stl"/>
|
| 321 |
+
</geometry>
|
| 322 |
+
<material name="3d_printed"/>
|
| 323 |
+
</visual>
|
| 324 |
+
<collision>
|
| 325 |
+
<origin xyz="-5.55112e-17 -5.55112e-17 0.0189" rpy="9.53145e-17 6.93889e-18 1.24077e-24"/>
|
| 326 |
+
<geometry>
|
| 327 |
+
<mesh filename="assets/moving_jaw_so101_v1.stl"/>
|
| 328 |
+
</geometry>
|
| 329 |
+
</collision>
|
| 330 |
+
</link>
|
| 331 |
+
|
| 332 |
+
<!-- Joint from gripper to moving_jaw_so101_v1 -->
|
| 333 |
+
<joint name="gripper" type="revolute">
|
| 334 |
+
<origin xyz="0.0202 0.0188 -0.0234" rpy="1.5708 -5.24284e-08 -1.41553e-15"/>
|
| 335 |
+
<parent link="gripper_link"/>
|
| 336 |
+
<child link="moving_jaw_so101_v1_link"/>
|
| 337 |
+
<axis xyz="0 0 1"/>
|
| 338 |
+
<limit effort="10" velocity="10" lower="-0.174533" upper="1.74533"/>
|
| 339 |
+
</joint>
|
| 340 |
+
|
| 341 |
+
<transmission name="gripper_trans">
|
| 342 |
+
<type>transmission_interface/SimpleTransmission</type>
|
| 343 |
+
<joint name="gripper">
|
| 344 |
+
<hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
|
| 345 |
+
</joint>
|
| 346 |
+
<actuator name="motor6">
|
| 347 |
+
<hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
|
| 348 |
+
<mechanicalReduction>1</mechanicalReduction>
|
| 349 |
+
</actuator>
|
| 350 |
+
</transmission>
|
| 351 |
+
|
| 352 |
+
<!-- Joint from wrist to gripper -->
|
| 353 |
+
<joint name="wrist_roll" type="revolute">
|
| 354 |
+
<origin xyz="5.55112e-17 -0.0611 0.0181" rpy="1.5708 0.0486795 3.14159"/>
|
| 355 |
+
<parent link="wrist_link"/>
|
| 356 |
+
<child link="gripper_link"/>
|
| 357 |
+
<axis xyz="0 0 1"/>
|
| 358 |
+
<limit effort="10" velocity="10" lower="-2.74385" upper="2.84121"/>
|
| 359 |
+
</joint>
|
| 360 |
+
|
| 361 |
+
<transmission name="wrist_roll_trans">
|
| 362 |
+
<type>transmission_interface/SimpleTransmission</type>
|
| 363 |
+
<joint name="wrist_roll">
|
| 364 |
+
<hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
|
| 365 |
+
</joint>
|
| 366 |
+
<actuator name="motor5">
|
| 367 |
+
<hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
|
| 368 |
+
<mechanicalReduction>1</mechanicalReduction>
|
| 369 |
+
</actuator>
|
| 370 |
+
</transmission>
|
| 371 |
+
|
| 372 |
+
<!-- Joint from lower_arm to wrist -->
|
| 373 |
+
<joint name="wrist_flex" type="revolute">
|
| 374 |
+
<origin xyz="-0.1349 0.0052 3.62355e-17" rpy="4.02456e-15 8.67362e-16 -1.5708"/>
|
| 375 |
+
<parent link="lower_arm_link"/>
|
| 376 |
+
<child link="wrist_link"/>
|
| 377 |
+
<axis xyz="0 0 1"/>
|
| 378 |
+
<limit effort="10" velocity="10" lower="-1.65806" upper="1.65806"/>
|
| 379 |
+
</joint>
|
| 380 |
+
|
| 381 |
+
<transmission name="wrist_flex_trans">
|
| 382 |
+
<type>transmission_interface/SimpleTransmission</type>
|
| 383 |
+
<joint name="wrist_flex">
|
| 384 |
+
<hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
|
| 385 |
+
</joint>
|
| 386 |
+
<actuator name="motor4">
|
| 387 |
+
<hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
|
| 388 |
+
<mechanicalReduction>1</mechanicalReduction>
|
| 389 |
+
</actuator>
|
| 390 |
+
</transmission>
|
| 391 |
+
|
| 392 |
+
<!-- Joint from upper_arm to lower_arm -->
|
| 393 |
+
<!-- Note: 5-degree calibration offset applied to joint limits -->
|
| 394 |
+
<joint name="elbow_flex" type="revolute">
|
| 395 |
+
<origin xyz="-0.11257 -0.028 1.73763e-16" rpy="-3.63608e-16 8.74301e-16 1.5708"/>
|
| 396 |
+
<parent link="upper_arm_link"/>
|
| 397 |
+
<child link="lower_arm_link"/>
|
| 398 |
+
<axis xyz="0 0 1"/>
|
| 399 |
+
<limit effort="10" velocity="10" lower="-1.69" upper="1.69"/>
|
| 400 |
+
</joint>
|
| 401 |
+
|
| 402 |
+
<transmission name="elbow_flex_trans">
|
| 403 |
+
<type>transmission_interface/SimpleTransmission</type>
|
| 404 |
+
<joint name="elbow_flex">
|
| 405 |
+
<hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
|
| 406 |
+
</joint>
|
| 407 |
+
<actuator name="motor3">
|
| 408 |
+
<hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
|
| 409 |
+
<mechanicalReduction>1</mechanicalReduction>
|
| 410 |
+
</actuator>
|
| 411 |
+
</transmission>
|
| 412 |
+
|
| 413 |
+
<!-- Joint from shoulder to upper_arm -->
|
| 414 |
+
<joint name="shoulder_lift" type="revolute">
|
| 415 |
+
<origin xyz="-0.0303992 -0.0182778 -0.0542" rpy="-1.5708 -1.5708 0"/>
|
| 416 |
+
<parent link="shoulder_link"/>
|
| 417 |
+
<child link="upper_arm_link"/>
|
| 418 |
+
<axis xyz="0 0 1"/>
|
| 419 |
+
<limit effort="10" velocity="10" lower="-1.74533" upper="1.74533"/>
|
| 420 |
+
</joint>
|
| 421 |
+
|
| 422 |
+
<transmission name="shoulder_lift_trans">
|
| 423 |
+
<type>transmission_interface/SimpleTransmission</type>
|
| 424 |
+
<joint name="shoulder_lift">
|
| 425 |
+
<hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
|
| 426 |
+
</joint>
|
| 427 |
+
<actuator name="motor2">
|
| 428 |
+
<hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
|
| 429 |
+
<mechanicalReduction>1</mechanicalReduction>
|
| 430 |
+
</actuator>
|
| 431 |
+
</transmission>
|
| 432 |
+
|
| 433 |
+
<!-- Joint from base to shoulder -->
|
| 434 |
+
<joint name="shoulder_pan" type="revolute">
|
| 435 |
+
<origin xyz="0.0388353 -8.97657e-09 0.0624" rpy="3.14159 4.18253e-17 -3.14159"/>
|
| 436 |
+
<parent link="base_link"/>
|
| 437 |
+
<child link="shoulder_link"/>
|
| 438 |
+
<axis xyz="0 0 1"/>
|
| 439 |
+
<limit effort="10" velocity="10" lower="-1.91986" upper="1.91986"/>
|
| 440 |
+
</joint>
|
| 441 |
+
|
| 442 |
+
<transmission name="shoulder_pan_trans">
|
| 443 |
+
<type>transmission_interface/SimpleTransmission</type>
|
| 444 |
+
<joint name="shoulder_pan">
|
| 445 |
+
<hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
|
| 446 |
+
</joint>
|
| 447 |
+
<actuator name="motor1">
|
| 448 |
+
<hardwareInterface>hardware_interface/PositionJointInterface</hardwareInterface>
|
| 449 |
+
<mechanicalReduction>1</mechanicalReduction>
|
| 450 |
+
</actuator>
|
| 451 |
+
</transmission>
|
| 452 |
+
|
| 453 |
+
</robot>
|
src/app/.well-known/appspecific/[...slug]/route.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { NextResponse } from "next/server";
|
| 2 |
+
|
| 3 |
+
export function GET() {
|
| 4 |
+
return new NextResponse(null, { status: 404 });
|
| 5 |
+
}
|
src/app/[org]/[dataset]/[episode]/actions.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use server";
|
| 2 |
+
|
| 3 |
+
import { getDatasetVersionAndInfo } from "@/utils/versionUtils";
|
| 4 |
+
import type { DatasetMetadata } from "@/utils/parquetUtils";
|
| 5 |
+
import {
|
| 6 |
+
loadAllEpisodeLengthsV3,
|
| 7 |
+
loadAllEpisodeFrameInfo,
|
| 8 |
+
loadCrossEpisodeActionVariance,
|
| 9 |
+
loadEpisodeFlatChartData,
|
| 10 |
+
type EpisodeLengthStats,
|
| 11 |
+
type EpisodeFramesData,
|
| 12 |
+
type CrossEpisodeVarianceData,
|
| 13 |
+
} from "./fetch-data";
|
| 14 |
+
|
| 15 |
+
export async function fetchEpisodeLengthStats(
|
| 16 |
+
org: string,
|
| 17 |
+
dataset: string,
|
| 18 |
+
): Promise<EpisodeLengthStats | null> {
|
| 19 |
+
const repoId = `${org}/${dataset}`;
|
| 20 |
+
const { version, info } = await getDatasetVersionAndInfo(repoId);
|
| 21 |
+
if (version !== "v3.0") return null;
|
| 22 |
+
return loadAllEpisodeLengthsV3(repoId, version, info.fps);
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
export async function fetchEpisodeFrames(
|
| 26 |
+
org: string,
|
| 27 |
+
dataset: string,
|
| 28 |
+
): Promise<EpisodeFramesData> {
|
| 29 |
+
const repoId = `${org}/${dataset}`;
|
| 30 |
+
const { version, info } = await getDatasetVersionAndInfo(repoId);
|
| 31 |
+
return loadAllEpisodeFrameInfo(
|
| 32 |
+
repoId,
|
| 33 |
+
version,
|
| 34 |
+
info as unknown as DatasetMetadata,
|
| 35 |
+
);
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
export async function fetchCrossEpisodeVariance(
|
| 39 |
+
org: string,
|
| 40 |
+
dataset: string,
|
| 41 |
+
): Promise<CrossEpisodeVarianceData | null> {
|
| 42 |
+
const repoId = `${org}/${dataset}`;
|
| 43 |
+
const { version, info } = await getDatasetVersionAndInfo(repoId);
|
| 44 |
+
return loadCrossEpisodeActionVariance(
|
| 45 |
+
repoId,
|
| 46 |
+
version,
|
| 47 |
+
info as unknown as DatasetMetadata,
|
| 48 |
+
info.fps,
|
| 49 |
+
);
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
export async function fetchEpisodeChartData(
|
| 53 |
+
org: string,
|
| 54 |
+
dataset: string,
|
| 55 |
+
episodeId: number,
|
| 56 |
+
): Promise<Record<string, number>[]> {
|
| 57 |
+
const repoId = `${org}/${dataset}`;
|
| 58 |
+
const { version, info } = await getDatasetVersionAndInfo(repoId);
|
| 59 |
+
return loadEpisodeFlatChartData(
|
| 60 |
+
repoId,
|
| 61 |
+
version,
|
| 62 |
+
info as unknown as DatasetMetadata,
|
| 63 |
+
episodeId,
|
| 64 |
+
);
|
| 65 |
+
}
|
src/app/[org]/[dataset]/[episode]/episode-viewer.tsx
CHANGED
|
@@ -1,16 +1,46 @@
|
|
| 1 |
"use client";
|
| 2 |
|
| 3 |
-
import { useState, useEffect, useRef } from "react";
|
| 4 |
import { useRouter, useSearchParams } from "next/navigation";
|
| 5 |
import { postParentMessageWithParams } from "@/utils/postParentMessage";
|
| 6 |
import { SimpleVideosPlayer } from "@/components/simple-videos-player";
|
| 7 |
import DataRecharts from "@/components/data-recharts";
|
| 8 |
import PlaybackBar from "@/components/playback-bar";
|
| 9 |
import { TimeProvider, useTime } from "@/context/time-context";
|
|
|
|
| 10 |
import Sidebar from "@/components/side-nav";
|
|
|
|
|
|
|
| 11 |
import Loading from "@/components/loading-component";
|
| 12 |
-
import {
|
| 13 |
-
import
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
|
| 15 |
export default function EpisodeViewer({
|
| 16 |
data,
|
|
@@ -39,8 +69,10 @@ export default function EpisodeViewer({
|
|
| 39 |
}
|
| 40 |
|
| 41 |
return (
|
| 42 |
-
<TimeProvider duration={data.duration}>
|
| 43 |
-
<
|
|
|
|
|
|
|
| 44 |
</TimeProvider>
|
| 45 |
);
|
| 46 |
}
|
|
@@ -67,10 +99,127 @@ function EpisodeViewerInner({
|
|
| 67 |
const [chartsReady, setChartsReady] = useState(false);
|
| 68 |
const isLoading = !videosReady || !chartsReady;
|
| 69 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 70 |
const router = useRouter();
|
| 71 |
const searchParams = useSearchParams();
|
| 72 |
|
| 73 |
-
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 74 |
// Use context for time sync
|
| 75 |
const { currentTime, setCurrentTime, setIsPlaying, isPlaying } = useTime();
|
| 76 |
|
|
@@ -83,20 +232,29 @@ function EpisodeViewerInner({
|
|
| 83 |
currentPage * pageSize,
|
| 84 |
);
|
| 85 |
|
| 86 |
-
// Preload adjacent episodes' videos
|
| 87 |
useEffect(() => {
|
| 88 |
if (!org || !dataset) return;
|
|
|
|
| 89 |
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 98 |
|
| 99 |
-
|
|
|
|
|
|
|
| 100 |
}, [org, dataset, episodeId]);
|
| 101 |
|
| 102 |
// Initialize based on URL time parameter
|
|
@@ -189,86 +347,236 @@ function EpisodeViewerInner({
|
|
| 189 |
};
|
| 190 |
|
| 191 |
return (
|
| 192 |
-
<div className="flex h-screen max-h-screen bg-slate-950 text-gray-200">
|
| 193 |
-
{/*
|
| 194 |
-
<
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
|
| 218 |
-
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 227 |
>
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
</div>
|
| 236 |
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 242 |
/>
|
| 243 |
)}
|
| 244 |
|
| 245 |
-
{/*
|
| 246 |
-
|
| 247 |
-
|
| 248 |
-
|
| 249 |
-
|
| 250 |
-
Language Instruction:
|
| 251 |
-
</span>
|
| 252 |
-
</p>
|
| 253 |
-
<div className="mt-2 text-slate-300">
|
| 254 |
-
{task.split("\n").map((instruction, index) => (
|
| 255 |
-
<p key={index} className="mb-1">
|
| 256 |
-
{instruction}
|
| 257 |
-
</p>
|
| 258 |
-
))}
|
| 259 |
-
</div>
|
| 260 |
-
</div>
|
| 261 |
-
)}
|
| 262 |
|
| 263 |
-
|
| 264 |
-
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
|
| 268 |
-
|
| 269 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 270 |
|
| 271 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 272 |
</div>
|
| 273 |
</div>
|
| 274 |
);
|
|
|
|
| 1 |
"use client";
|
| 2 |
|
| 3 |
+
import { useState, useEffect, useRef, lazy, Suspense } from "react";
|
| 4 |
import { useRouter, useSearchParams } from "next/navigation";
|
| 5 |
import { postParentMessageWithParams } from "@/utils/postParentMessage";
|
| 6 |
import { SimpleVideosPlayer } from "@/components/simple-videos-player";
|
| 7 |
import DataRecharts from "@/components/data-recharts";
|
| 8 |
import PlaybackBar from "@/components/playback-bar";
|
| 9 |
import { TimeProvider, useTime } from "@/context/time-context";
|
| 10 |
+
import { FlaggedEpisodesProvider } from "@/context/flagged-episodes-context";
|
| 11 |
import Sidebar from "@/components/side-nav";
|
| 12 |
+
import StatsPanel from "@/components/stats-panel";
|
| 13 |
+
import OverviewPanel from "@/components/overview-panel";
|
| 14 |
import Loading from "@/components/loading-component";
|
| 15 |
+
import { hasURDFSupport } from "@/lib/so101-robot";
|
| 16 |
+
import {
|
| 17 |
+
getAdjacentEpisodesVideoInfo,
|
| 18 |
+
computeColumnMinMax,
|
| 19 |
+
type EpisodeData,
|
| 20 |
+
type ColumnMinMax,
|
| 21 |
+
type EpisodeLengthStats,
|
| 22 |
+
type EpisodeFramesData,
|
| 23 |
+
type CrossEpisodeVarianceData,
|
| 24 |
+
} from "./fetch-data";
|
| 25 |
+
import {
|
| 26 |
+
fetchEpisodeLengthStats,
|
| 27 |
+
fetchEpisodeFrames,
|
| 28 |
+
fetchCrossEpisodeVariance,
|
| 29 |
+
} from "./actions";
|
| 30 |
+
|
| 31 |
+
const URDFViewer = lazy(() => import("@/components/urdf-viewer"));
|
| 32 |
+
const ActionInsightsPanel = lazy(
|
| 33 |
+
() => import("@/components/action-insights-panel"),
|
| 34 |
+
);
|
| 35 |
+
const FilteringPanel = lazy(() => import("@/components/filtering-panel"));
|
| 36 |
+
|
| 37 |
+
type ActiveTab =
|
| 38 |
+
| "episodes"
|
| 39 |
+
| "statistics"
|
| 40 |
+
| "frames"
|
| 41 |
+
| "insights"
|
| 42 |
+
| "filtering"
|
| 43 |
+
| "urdf";
|
| 44 |
|
| 45 |
export default function EpisodeViewer({
|
| 46 |
data,
|
|
|
|
| 69 |
}
|
| 70 |
|
| 71 |
return (
|
| 72 |
+
<TimeProvider duration={data!.duration}>
|
| 73 |
+
<FlaggedEpisodesProvider>
|
| 74 |
+
<EpisodeViewerInner data={data!} org={org} dataset={dataset} />
|
| 75 |
+
</FlaggedEpisodesProvider>
|
| 76 |
</TimeProvider>
|
| 77 |
);
|
| 78 |
}
|
|
|
|
| 99 |
const [chartsReady, setChartsReady] = useState(false);
|
| 100 |
const isLoading = !videosReady || !chartsReady;
|
| 101 |
|
| 102 |
+
const loadStartRef = useRef(performance.now());
|
| 103 |
+
useEffect(() => {
|
| 104 |
+
if (!isLoading) {
|
| 105 |
+
console.log(
|
| 106 |
+
`[perf] Loading complete in ${(performance.now() - loadStartRef.current).toFixed(0)}ms (videos: ${videosReady ? "✓" : "…"}, charts: ${chartsReady ? "✓" : "…"})`,
|
| 107 |
+
);
|
| 108 |
+
}
|
| 109 |
+
}, [isLoading]);
|
| 110 |
+
|
| 111 |
const router = useRouter();
|
| 112 |
const searchParams = useSearchParams();
|
| 113 |
|
| 114 |
+
// Tab state & lazy stats
|
| 115 |
+
const [activeTab, setActiveTab] = useState<ActiveTab>("episodes");
|
| 116 |
+
const [, setColumnMinMax] = useState<ColumnMinMax[] | null>(null);
|
| 117 |
+
const [episodeLengthStats, setEpisodeLengthStats] =
|
| 118 |
+
useState<EpisodeLengthStats | null>(null);
|
| 119 |
+
const [statsLoading, setStatsLoading] = useState(false);
|
| 120 |
+
const statsLoadedRef = useRef(false);
|
| 121 |
+
const [episodeFramesData, setEpisodeFramesData] =
|
| 122 |
+
useState<EpisodeFramesData | null>(null);
|
| 123 |
+
const [framesLoading, setFramesLoading] = useState(false);
|
| 124 |
+
const framesLoadedRef = useRef(false);
|
| 125 |
+
const [framesFlaggedOnly, setFramesFlaggedOnly] = useState(false);
|
| 126 |
+
const [sidebarFlaggedOnly, setSidebarFlaggedOnly] = useState(false);
|
| 127 |
+
const [crossEpData, setCrossEpData] =
|
| 128 |
+
useState<CrossEpisodeVarianceData | null>(null);
|
| 129 |
+
const [insightsLoading, setInsightsLoading] = useState(false);
|
| 130 |
+
const insightsLoadedRef = useRef(false);
|
| 131 |
+
|
| 132 |
+
// Hydrate UI state from sessionStorage after mount (avoids SSR/client mismatch)
|
| 133 |
+
useEffect(() => {
|
| 134 |
+
const stored = sessionStorage.getItem("activeTab");
|
| 135 |
+
if (
|
| 136 |
+
stored &&
|
| 137 |
+
[
|
| 138 |
+
"episodes",
|
| 139 |
+
"statistics",
|
| 140 |
+
"frames",
|
| 141 |
+
"insights",
|
| 142 |
+
"filtering",
|
| 143 |
+
"urdf",
|
| 144 |
+
].includes(stored)
|
| 145 |
+
) {
|
| 146 |
+
setActiveTab(stored as ActiveTab);
|
| 147 |
+
}
|
| 148 |
+
if (sessionStorage.getItem("framesFlaggedOnly") === "true")
|
| 149 |
+
setFramesFlaggedOnly(true);
|
| 150 |
+
if (sessionStorage.getItem("sidebarFlaggedOnly") === "true")
|
| 151 |
+
setSidebarFlaggedOnly(true);
|
| 152 |
+
}, []);
|
| 153 |
+
|
| 154 |
+
// Persist UI state across episode navigations
|
| 155 |
+
useEffect(() => {
|
| 156 |
+
sessionStorage.setItem("activeTab", activeTab);
|
| 157 |
+
}, [activeTab]);
|
| 158 |
+
useEffect(() => {
|
| 159 |
+
sessionStorage.setItem("sidebarFlaggedOnly", String(sidebarFlaggedOnly));
|
| 160 |
+
}, [sidebarFlaggedOnly]);
|
| 161 |
+
useEffect(() => {
|
| 162 |
+
sessionStorage.setItem("framesFlaggedOnly", String(framesFlaggedOnly));
|
| 163 |
+
}, [framesFlaggedOnly]);
|
| 164 |
+
|
| 165 |
+
const loadStats = () => {
|
| 166 |
+
if (statsLoadedRef.current) return;
|
| 167 |
+
statsLoadedRef.current = true;
|
| 168 |
+
setStatsLoading(true);
|
| 169 |
+
setColumnMinMax(computeColumnMinMax(data.chartDataGroups));
|
| 170 |
+
if (org && dataset) {
|
| 171 |
+
fetchEpisodeLengthStats(org, dataset)
|
| 172 |
+
.then((result) => setEpisodeLengthStats(result))
|
| 173 |
+
.catch(() => {})
|
| 174 |
+
.finally(() => setStatsLoading(false));
|
| 175 |
+
} else {
|
| 176 |
+
setStatsLoading(false);
|
| 177 |
+
}
|
| 178 |
+
};
|
| 179 |
+
|
| 180 |
+
const loadFrames = () => {
|
| 181 |
+
if (framesLoadedRef.current || !org || !dataset) return;
|
| 182 |
+
framesLoadedRef.current = true;
|
| 183 |
+
setFramesLoading(true);
|
| 184 |
+
fetchEpisodeFrames(org, dataset)
|
| 185 |
+
.then(setEpisodeFramesData)
|
| 186 |
+
.catch(() => setEpisodeFramesData({ cameras: [], framesByCamera: {} }))
|
| 187 |
+
.finally(() => setFramesLoading(false));
|
| 188 |
+
};
|
| 189 |
+
|
| 190 |
+
const loadInsights = () => {
|
| 191 |
+
if (insightsLoadedRef.current || !org || !dataset) return;
|
| 192 |
+
insightsLoadedRef.current = true;
|
| 193 |
+
setInsightsLoading(true);
|
| 194 |
+
fetchCrossEpisodeVariance(org, dataset)
|
| 195 |
+
.then(setCrossEpData)
|
| 196 |
+
.catch((err) => console.error("[cross-ep] Failed:", err))
|
| 197 |
+
.finally(() => setInsightsLoading(false));
|
| 198 |
+
};
|
| 199 |
+
|
| 200 |
+
// Re-trigger data loading for the restored tab on mount
|
| 201 |
+
useEffect(() => {
|
| 202 |
+
if (activeTab === "statistics") loadStats();
|
| 203 |
+
if (activeTab === "frames") loadFrames();
|
| 204 |
+
if (activeTab === "insights") loadInsights();
|
| 205 |
+
if (activeTab === "filtering") {
|
| 206 |
+
loadStats();
|
| 207 |
+
loadInsights();
|
| 208 |
+
}
|
| 209 |
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
| 210 |
+
}, []);
|
| 211 |
+
|
| 212 |
+
const handleTabChange = (tab: ActiveTab) => {
|
| 213 |
+
setActiveTab(tab);
|
| 214 |
+
if (tab === "statistics") loadStats();
|
| 215 |
+
if (tab === "frames") loadFrames();
|
| 216 |
+
if (tab === "insights") loadInsights();
|
| 217 |
+
if (tab === "filtering") {
|
| 218 |
+
loadStats();
|
| 219 |
+
loadInsights();
|
| 220 |
+
}
|
| 221 |
+
};
|
| 222 |
+
|
| 223 |
// Use context for time sync
|
| 224 |
const { currentTime, setCurrentTime, setIsPlaying, isPlaying } = useTime();
|
| 225 |
|
|
|
|
| 232 |
currentPage * pageSize,
|
| 233 |
);
|
| 234 |
|
| 235 |
+
// Preload adjacent episodes' videos via <link rel="preload"> tags
|
| 236 |
useEffect(() => {
|
| 237 |
if (!org || !dataset) return;
|
| 238 |
+
const links: HTMLLinkElement[] = [];
|
| 239 |
|
| 240 |
+
getAdjacentEpisodesVideoInfo(org, dataset, episodeId, 2)
|
| 241 |
+
.then((adjacentVideos) => {
|
| 242 |
+
for (const ep of adjacentVideos) {
|
| 243 |
+
for (const v of ep.videosInfo) {
|
| 244 |
+
const link = document.createElement("link");
|
| 245 |
+
link.rel = "preload";
|
| 246 |
+
link.as = "video";
|
| 247 |
+
link.href = v.url;
|
| 248 |
+
document.head.appendChild(link);
|
| 249 |
+
links.push(link);
|
| 250 |
+
}
|
| 251 |
+
}
|
| 252 |
+
})
|
| 253 |
+
.catch(() => {});
|
| 254 |
|
| 255 |
+
return () => {
|
| 256 |
+
links.forEach((l) => l.remove());
|
| 257 |
+
};
|
| 258 |
}, [org, dataset, episodeId]);
|
| 259 |
|
| 260 |
// Initialize based on URL time parameter
|
|
|
|
| 347 |
};
|
| 348 |
|
| 349 |
return (
|
| 350 |
+
<div className="flex flex-col h-screen max-h-screen bg-slate-950 text-gray-200">
|
| 351 |
+
{/* Top tab bar */}
|
| 352 |
+
<div className="flex items-center border-b border-slate-700 bg-slate-900 shrink-0">
|
| 353 |
+
<button
|
| 354 |
+
className={`px-6 py-2.5 text-sm font-medium transition-colors relative ${
|
| 355 |
+
activeTab === "episodes"
|
| 356 |
+
? "text-orange-400"
|
| 357 |
+
: "text-slate-400 hover:text-slate-200"
|
| 358 |
+
}`}
|
| 359 |
+
onClick={() => handleTabChange("episodes")}
|
| 360 |
+
>
|
| 361 |
+
Episodes
|
| 362 |
+
{activeTab === "episodes" && (
|
| 363 |
+
<span className="absolute bottom-0 left-0 right-0 h-0.5 bg-orange-500" />
|
| 364 |
+
)}
|
| 365 |
+
</button>
|
| 366 |
+
<button
|
| 367 |
+
className={`px-6 py-2.5 text-sm font-medium transition-colors relative ${
|
| 368 |
+
activeTab === "statistics"
|
| 369 |
+
? "text-orange-400"
|
| 370 |
+
: "text-slate-400 hover:text-slate-200"
|
| 371 |
+
}`}
|
| 372 |
+
onClick={() => handleTabChange("statistics")}
|
| 373 |
+
>
|
| 374 |
+
Statistics
|
| 375 |
+
{activeTab === "statistics" && (
|
| 376 |
+
<span className="absolute bottom-0 left-0 right-0 h-0.5 bg-orange-500" />
|
| 377 |
+
)}
|
| 378 |
+
</button>
|
| 379 |
+
<button
|
| 380 |
+
className={`px-6 py-2.5 text-sm font-medium transition-colors relative ${
|
| 381 |
+
activeTab === "filtering"
|
| 382 |
+
? "text-orange-400"
|
| 383 |
+
: "text-slate-400 hover:text-slate-200"
|
| 384 |
+
}`}
|
| 385 |
+
onClick={() => handleTabChange("filtering")}
|
| 386 |
+
>
|
| 387 |
+
Filtering
|
| 388 |
+
{activeTab === "filtering" && (
|
| 389 |
+
<span className="absolute bottom-0 left-0 right-0 h-0.5 bg-orange-500" />
|
| 390 |
+
)}
|
| 391 |
+
</button>
|
| 392 |
+
<button
|
| 393 |
+
className={`px-6 py-2.5 text-sm font-medium transition-colors relative ${
|
| 394 |
+
activeTab === "frames"
|
| 395 |
+
? "text-orange-400"
|
| 396 |
+
: "text-slate-400 hover:text-slate-200"
|
| 397 |
+
}`}
|
| 398 |
+
onClick={() => handleTabChange("frames")}
|
| 399 |
+
>
|
| 400 |
+
Frames
|
| 401 |
+
{activeTab === "frames" && (
|
| 402 |
+
<span className="absolute bottom-0 left-0 right-0 h-0.5 bg-orange-500" />
|
| 403 |
+
)}
|
| 404 |
+
</button>
|
| 405 |
+
<button
|
| 406 |
+
className={`px-6 py-2.5 text-sm font-medium transition-colors relative ${
|
| 407 |
+
activeTab === "insights"
|
| 408 |
+
? "text-orange-400"
|
| 409 |
+
: "text-slate-400 hover:text-slate-200"
|
| 410 |
+
}`}
|
| 411 |
+
onClick={() => handleTabChange("insights")}
|
| 412 |
+
>
|
| 413 |
+
Action Insights
|
| 414 |
+
{activeTab === "insights" && (
|
| 415 |
+
<span className="absolute bottom-0 left-0 right-0 h-0.5 bg-orange-500" />
|
| 416 |
+
)}
|
| 417 |
+
</button>
|
| 418 |
+
{hasURDFSupport(datasetInfo.robot_type) &&
|
| 419 |
+
datasetInfo.codebase_version >= "v3.0" && (
|
| 420 |
+
<button
|
| 421 |
+
className={`px-6 py-2.5 text-sm font-medium transition-colors relative ${
|
| 422 |
+
activeTab === "urdf"
|
| 423 |
+
? "text-orange-400"
|
| 424 |
+
: "text-slate-400 hover:text-slate-200"
|
| 425 |
+
}`}
|
| 426 |
+
onClick={() => handleTabChange("urdf")}
|
| 427 |
>
|
| 428 |
+
3D Replay
|
| 429 |
+
{activeTab === "urdf" && (
|
| 430 |
+
<span className="absolute bottom-0 left-0 right-0 h-0.5 bg-orange-500" />
|
| 431 |
+
)}
|
| 432 |
+
</button>
|
| 433 |
+
)}
|
| 434 |
+
</div>
|
|
|
|
| 435 |
|
| 436 |
+
{/* Body: sidebar + content */}
|
| 437 |
+
<div className="flex flex-1 min-h-0">
|
| 438 |
+
{/* Sidebar — only on Episodes tab */}
|
| 439 |
+
{activeTab === "episodes" && (
|
| 440 |
+
<Sidebar
|
| 441 |
+
datasetInfo={datasetInfo}
|
| 442 |
+
paginatedEpisodes={paginatedEpisodes}
|
| 443 |
+
episodeId={episodeId}
|
| 444 |
+
totalPages={totalPages}
|
| 445 |
+
currentPage={currentPage}
|
| 446 |
+
prevPage={prevPage}
|
| 447 |
+
nextPage={nextPage}
|
| 448 |
+
showFlaggedOnly={sidebarFlaggedOnly}
|
| 449 |
+
onShowFlaggedOnlyChange={setSidebarFlaggedOnly}
|
| 450 |
/>
|
| 451 |
)}
|
| 452 |
|
| 453 |
+
{/* Main content */}
|
| 454 |
+
<div
|
| 455 |
+
className={`flex flex-col gap-4 p-4 flex-1 relative ${isLoading ? "overflow-hidden" : "overflow-y-auto"}`}
|
| 456 |
+
>
|
| 457 |
+
{isLoading && <Loading />}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 458 |
|
| 459 |
+
{activeTab === "episodes" && (
|
| 460 |
+
<>
|
| 461 |
+
<div className="flex items-center justify-start my-4">
|
| 462 |
+
<a
|
| 463 |
+
href="https://github.com/huggingface/lerobot"
|
| 464 |
+
target="_blank"
|
| 465 |
+
className="block"
|
| 466 |
+
>
|
| 467 |
+
<img
|
| 468 |
+
src="https://github.com/huggingface/lerobot/raw/main/media/readme/lerobot-logo-thumbnail.png"
|
| 469 |
+
alt="LeRobot Logo"
|
| 470 |
+
className="w-32"
|
| 471 |
+
/>
|
| 472 |
+
</a>
|
| 473 |
|
| 474 |
+
<div>
|
| 475 |
+
<a
|
| 476 |
+
href={`https://huggingface.co/datasets/${datasetInfo.repoId}`}
|
| 477 |
+
target="_blank"
|
| 478 |
+
>
|
| 479 |
+
<p className="text-lg font-semibold">
|
| 480 |
+
{datasetInfo.repoId}
|
| 481 |
+
</p>
|
| 482 |
+
</a>
|
| 483 |
+
|
| 484 |
+
<p className="font-mono text-lg font-semibold">
|
| 485 |
+
episode {episodeId}
|
| 486 |
+
</p>
|
| 487 |
+
</div>
|
| 488 |
+
</div>
|
| 489 |
+
|
| 490 |
+
{/* Videos */}
|
| 491 |
+
{videosInfo.length > 0 && (
|
| 492 |
+
<SimpleVideosPlayer
|
| 493 |
+
videosInfo={videosInfo}
|
| 494 |
+
onVideosReady={() => setVideosReady(true)}
|
| 495 |
+
/>
|
| 496 |
+
)}
|
| 497 |
+
|
| 498 |
+
{/* Language Instruction */}
|
| 499 |
+
{task && (
|
| 500 |
+
<div className="mb-6 p-4 bg-slate-800 rounded-lg border border-slate-600">
|
| 501 |
+
<p className="text-slate-300">
|
| 502 |
+
<span className="font-semibold text-slate-100">
|
| 503 |
+
Language Instruction:
|
| 504 |
+
</span>
|
| 505 |
+
</p>
|
| 506 |
+
<div className="mt-2 text-slate-300">
|
| 507 |
+
{task
|
| 508 |
+
.split("\n")
|
| 509 |
+
.map((instruction: string, index: number) => (
|
| 510 |
+
<p key={index} className="mb-1">
|
| 511 |
+
{instruction}
|
| 512 |
+
</p>
|
| 513 |
+
))}
|
| 514 |
+
</div>
|
| 515 |
+
</div>
|
| 516 |
+
)}
|
| 517 |
+
|
| 518 |
+
{/* Graph */}
|
| 519 |
+
<div className="mb-4">
|
| 520 |
+
<DataRecharts
|
| 521 |
+
data={chartDataGroups}
|
| 522 |
+
onChartsReady={() => setChartsReady(true)}
|
| 523 |
+
/>
|
| 524 |
+
</div>
|
| 525 |
+
|
| 526 |
+
<PlaybackBar />
|
| 527 |
+
</>
|
| 528 |
+
)}
|
| 529 |
+
|
| 530 |
+
{activeTab === "statistics" && (
|
| 531 |
+
<StatsPanel
|
| 532 |
+
datasetInfo={datasetInfo}
|
| 533 |
+
episodeLengthStats={episodeLengthStats}
|
| 534 |
+
loading={statsLoading}
|
| 535 |
+
/>
|
| 536 |
+
)}
|
| 537 |
+
|
| 538 |
+
{activeTab === "frames" && (
|
| 539 |
+
<OverviewPanel
|
| 540 |
+
data={episodeFramesData}
|
| 541 |
+
loading={framesLoading}
|
| 542 |
+
flaggedOnly={framesFlaggedOnly}
|
| 543 |
+
onFlaggedOnlyChange={setFramesFlaggedOnly}
|
| 544 |
+
/>
|
| 545 |
+
)}
|
| 546 |
+
|
| 547 |
+
{activeTab === "insights" && (
|
| 548 |
+
<Suspense fallback={<Loading />}>
|
| 549 |
+
<ActionInsightsPanel
|
| 550 |
+
flatChartData={data.flatChartData}
|
| 551 |
+
fps={datasetInfo.fps}
|
| 552 |
+
crossEpisodeData={crossEpData}
|
| 553 |
+
crossEpisodeLoading={insightsLoading}
|
| 554 |
+
/>
|
| 555 |
+
</Suspense>
|
| 556 |
+
)}
|
| 557 |
+
|
| 558 |
+
{activeTab === "filtering" && (
|
| 559 |
+
<Suspense fallback={<Loading />}>
|
| 560 |
+
<FilteringPanel
|
| 561 |
+
repoId={datasetInfo.repoId}
|
| 562 |
+
crossEpisodeData={crossEpData}
|
| 563 |
+
crossEpisodeLoading={insightsLoading}
|
| 564 |
+
episodeLengthStats={episodeLengthStats}
|
| 565 |
+
flatChartData={data.flatChartData}
|
| 566 |
+
onViewFlaggedEpisodes={() => {
|
| 567 |
+
setSidebarFlaggedOnly(true);
|
| 568 |
+
handleTabChange("episodes");
|
| 569 |
+
}}
|
| 570 |
+
/>
|
| 571 |
+
</Suspense>
|
| 572 |
+
)}
|
| 573 |
+
|
| 574 |
+
{activeTab === "urdf" && (
|
| 575 |
+
<Suspense fallback={<Loading />}>
|
| 576 |
+
<URDFViewer data={data} org={org} dataset={dataset} />
|
| 577 |
+
</Suspense>
|
| 578 |
+
)}
|
| 579 |
+
</div>
|
| 580 |
</div>
|
| 581 |
</div>
|
| 582 |
);
|
src/app/[org]/[dataset]/[episode]/fetch-data.ts
CHANGED
|
@@ -1,32 +1,109 @@
|
|
| 1 |
import {
|
| 2 |
-
|
| 3 |
fetchParquetFile,
|
| 4 |
formatStringWithVars,
|
| 5 |
-
readParquetColumn,
|
| 6 |
readParquetAsObjects,
|
| 7 |
} from "@/utils/parquetUtils";
|
| 8 |
import { pick } from "@/utils/pick";
|
| 9 |
-
import {
|
|
|
|
|
|
|
|
|
|
| 10 |
import { PADDING, CHART_CONFIG, EXCLUDED_COLUMNS } from "@/utils/constants";
|
| 11 |
import {
|
| 12 |
processChartDataGroups,
|
| 13 |
groupRowBySuffix,
|
| 14 |
} from "@/utils/dataProcessing";
|
| 15 |
-
import { extractLanguageInstructions } from "@/utils/languageInstructions";
|
| 16 |
import {
|
| 17 |
buildV3VideoPath,
|
| 18 |
buildV3DataPath,
|
| 19 |
buildV3EpisodesMetadataPath,
|
| 20 |
} from "@/utils/stringFormatting";
|
| 21 |
import { bigIntToNumber } from "@/utils/typeGuards";
|
| 22 |
-
import type {
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
|
| 31 |
export async function getEpisodeData(
|
| 32 |
org: string,
|
|
@@ -35,10 +112,10 @@ export async function getEpisodeData(
|
|
| 35 |
): Promise<EpisodeData> {
|
| 36 |
const repoId = `${org}/${dataset}`;
|
| 37 |
try {
|
| 38 |
-
|
| 39 |
-
const version = await
|
| 40 |
-
|
| 41 |
-
const info =
|
| 42 |
|
| 43 |
if (info.video_path === null) {
|
| 44 |
throw new Error(
|
|
@@ -46,19 +123,39 @@ export async function getEpisodeData(
|
|
| 46 |
);
|
| 47 |
}
|
| 48 |
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 55 |
} catch (err) {
|
| 56 |
console.error("Error loading episode data:", err);
|
| 57 |
throw err;
|
| 58 |
}
|
| 59 |
}
|
| 60 |
|
| 61 |
-
// Get video info for adjacent episodes (for preloading)
|
| 62 |
export async function getAdjacentEpisodesVideoInfo(
|
| 63 |
org: string,
|
| 64 |
dataset: string,
|
|
@@ -67,9 +164,8 @@ export async function getAdjacentEpisodesVideoInfo(
|
|
| 67 |
): Promise<AdjacentEpisodeVideos[]> {
|
| 68 |
const repoId = `${org}/${dataset}`;
|
| 69 |
try {
|
| 70 |
-
const version = await
|
| 71 |
-
const
|
| 72 |
-
const info = await fetchJson<DatasetMetadata>(jsonUrl);
|
| 73 |
|
| 74 |
const totalEpisodes = info.total_episodes;
|
| 75 |
const adjacentVideos: AdjacentEpisodeVideos[] = [];
|
|
@@ -142,12 +238,16 @@ async function getEpisodeDataV2(
|
|
| 142 |
): Promise<EpisodeData> {
|
| 143 |
const episode_chunk = Math.floor(0 / 1000);
|
| 144 |
|
| 145 |
-
|
| 146 |
-
const datasetInfo = {
|
| 147 |
repoId,
|
| 148 |
total_frames: info.total_frames,
|
| 149 |
total_episodes: info.total_episodes,
|
| 150 |
fps: info.fps,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 151 |
};
|
| 152 |
|
| 153 |
// Generate list of episodes
|
|
@@ -197,22 +297,17 @@ async function getEpisodeDataV2(
|
|
| 197 |
const filteredColumns = columnNames.filter(
|
| 198 |
(column) => !excludedColumns.includes(column.key),
|
| 199 |
);
|
| 200 |
-
const
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
];
|
| 204 |
-
|
| 205 |
-
const columns = filteredColumns.map(({ key }) => {
|
| 206 |
-
let column_names = info.features[key].names;
|
| 207 |
-
while (typeof column_names === "object") {
|
| 208 |
if (Array.isArray(column_names)) break;
|
| 209 |
-
column_names = Object.values(column_names
|
| 210 |
}
|
| 211 |
return {
|
| 212 |
key,
|
| 213 |
value: Array.isArray(column_names)
|
| 214 |
? column_names.map(
|
| 215 |
-
(name) => `${key}${
|
| 216 |
)
|
| 217 |
: Array.from(
|
| 218 |
{ length: columnNames.find((c) => c.key === key)?.length ?? 1 },
|
|
@@ -233,32 +328,38 @@ async function getEpisodeDataV2(
|
|
| 233 |
);
|
| 234 |
|
| 235 |
const arrayBuffer = await fetchParquetFile(parquetUrl);
|
|
|
|
| 236 |
|
| 237 |
-
// Extract task
|
| 238 |
let task: string | undefined;
|
| 239 |
-
let allData: Record<string, unknown>[] = [];
|
| 240 |
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
} catch {
|
| 245 |
-
// Could not read parquet data
|
| 246 |
-
}
|
| 247 |
|
| 248 |
-
|
| 249 |
-
|
|
|
|
| 250 |
|
| 251 |
-
|
| 252 |
-
|
| 253 |
-
|
| 254 |
-
|
| 255 |
-
|
| 256 |
-
|
| 257 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 258 |
task = allData[0].task;
|
| 259 |
}
|
| 260 |
|
| 261 |
-
// If still no task found, try loading from tasks.jsonl metadata file (v2.x format)
|
| 262 |
if (!task && allData.length > 0) {
|
| 263 |
try {
|
| 264 |
const tasksUrl = buildVersionedUrl(repoId, version, "meta/tasks.jsonl");
|
|
@@ -266,7 +367,6 @@ async function getEpisodeDataV2(
|
|
| 266 |
|
| 267 |
if (tasksResponse.ok) {
|
| 268 |
const tasksText = await tasksResponse.text();
|
| 269 |
-
// Parse JSONL format (one JSON object per line)
|
| 270 |
const tasksData = tasksText
|
| 271 |
.split("\n")
|
| 272 |
.filter((line) => line.trim())
|
|
@@ -274,13 +374,11 @@ async function getEpisodeDataV2(
|
|
| 274 |
|
| 275 |
if (tasksData && tasksData.length > 0) {
|
| 276 |
const taskIndex = allData[0].task_index;
|
| 277 |
-
|
| 278 |
-
// Convert BigInt to number for comparison
|
| 279 |
const taskIndexNum =
|
| 280 |
typeof taskIndex === "bigint" ? Number(taskIndex) : taskIndex;
|
| 281 |
-
|
| 282 |
-
|
| 283 |
-
|
| 284 |
if (taskData) {
|
| 285 |
task = taskData.task;
|
| 286 |
}
|
|
@@ -291,20 +389,25 @@ async function getEpisodeDataV2(
|
|
| 291 |
}
|
| 292 |
}
|
| 293 |
|
| 294 |
-
|
| 295 |
-
// Flatten and map to array of objects for chartData
|
| 296 |
const seriesNames = [
|
| 297 |
"timestamp",
|
| 298 |
...columns.map(({ value }) => value).flat(),
|
| 299 |
];
|
| 300 |
|
| 301 |
-
const chartData =
|
| 302 |
-
const flatRow = row.flat();
|
| 303 |
const obj: Record<string, number> = {};
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
|
| 307 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 308 |
return obj;
|
| 309 |
});
|
| 310 |
|
|
@@ -338,6 +441,7 @@ async function getEpisodeDataV2(
|
|
| 338 |
episodeId,
|
| 339 |
videosInfo,
|
| 340 |
chartDataGroups,
|
|
|
|
| 341 |
episodes,
|
| 342 |
ignoredColumns,
|
| 343 |
duration,
|
|
@@ -352,15 +456,18 @@ async function getEpisodeDataV3(
|
|
| 352 |
info: DatasetMetadata,
|
| 353 |
episodeId: number,
|
| 354 |
): Promise<EpisodeData> {
|
| 355 |
-
|
| 356 |
-
const datasetInfo = {
|
| 357 |
repoId,
|
| 358 |
total_frames: info.total_frames,
|
| 359 |
total_episodes: info.total_episodes,
|
| 360 |
fps: info.fps,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 361 |
};
|
| 362 |
|
| 363 |
-
// Generate episodes list based on total_episodes from dataset info
|
| 364 |
const episodes = Array.from({ length: info.total_episodes }, (_, i) => i);
|
| 365 |
|
| 366 |
// Load episode metadata to get timestamps for episode 0
|
|
@@ -379,25 +486,19 @@ async function getEpisodeDataV3(
|
|
| 379 |
);
|
| 380 |
|
| 381 |
// Load episode data for charts
|
| 382 |
-
const { chartDataGroups, ignoredColumns, task } =
|
| 383 |
-
repoId,
|
| 384 |
-
version,
|
| 385 |
-
info,
|
| 386 |
-
episodeMetadata,
|
| 387 |
-
);
|
| 388 |
|
| 389 |
-
|
| 390 |
-
|
| 391 |
-
|
| 392 |
-
? episodeLength / info.fps
|
| 393 |
-
: (episodeMetadata.video_to_timestamp || 0) -
|
| 394 |
-
(episodeMetadata.video_from_timestamp || 0);
|
| 395 |
|
| 396 |
return {
|
| 397 |
datasetInfo,
|
| 398 |
episodeId,
|
| 399 |
videosInfo,
|
| 400 |
chartDataGroups,
|
|
|
|
| 401 |
episodes,
|
| 402 |
ignoredColumns,
|
| 403 |
duration,
|
|
@@ -412,7 +513,8 @@ async function loadEpisodeDataV3(
|
|
| 412 |
info: DatasetMetadata,
|
| 413 |
episodeMetadata: EpisodeMetadataV3,
|
| 414 |
): Promise<{
|
| 415 |
-
chartDataGroups:
|
|
|
|
| 416 |
ignoredColumns: string[];
|
| 417 |
task?: string;
|
| 418 |
}> {
|
|
@@ -427,9 +529,11 @@ async function loadEpisodeDataV3(
|
|
| 427 |
const fullData = await readParquetAsObjects(arrayBuffer, []);
|
| 428 |
|
| 429 |
// Extract the episode-specific data slice
|
| 430 |
-
|
| 431 |
-
const
|
| 432 |
-
|
|
|
|
|
|
|
| 433 |
|
| 434 |
// Find the starting index of this parquet file by checking the first row's index
|
| 435 |
// This handles the case where episodes are split across multiple parquet files
|
|
@@ -445,29 +549,57 @@ async function loadEpisodeDataV3(
|
|
| 445 |
const episodeData = fullData.slice(localFromIndex, localToIndex);
|
| 446 |
|
| 447 |
if (episodeData.length === 0) {
|
| 448 |
-
return {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 449 |
}
|
| 450 |
|
| 451 |
// Convert to the same format as v2.x for compatibility with existing chart code
|
| 452 |
-
const { chartDataGroups, ignoredColumns } =
|
| 453 |
-
episodeData,
|
| 454 |
-
info,
|
| 455 |
-
episodeMetadata,
|
| 456 |
-
);
|
| 457 |
|
| 458 |
// First check for language_instruction fields in the data (preferred)
|
| 459 |
-
|
| 460 |
-
|
| 461 |
-
|
| 462 |
-
|
| 463 |
-
|
| 464 |
-
|
| 465 |
-
|
| 466 |
-
|
| 467 |
-
|
| 468 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 469 |
try {
|
| 470 |
-
// Load tasks metadata
|
| 471 |
const tasksUrl = buildVersionedUrl(
|
| 472 |
repoId,
|
| 473 |
version,
|
|
@@ -476,53 +608,28 @@ async function loadEpisodeDataV3(
|
|
| 476 |
const tasksArrayBuffer = await fetchParquetFile(tasksUrl);
|
| 477 |
const tasksData = await readParquetAsObjects(tasksArrayBuffer, []);
|
| 478 |
|
| 479 |
-
if (
|
| 480 |
-
|
| 481 |
-
tasksData &&
|
| 482 |
-
tasksData.length > 0 &&
|
| 483 |
-
"task_index" in episodeData[0]
|
| 484 |
-
) {
|
| 485 |
-
const taskIndex = episodeData[0].task_index;
|
| 486 |
|
| 487 |
-
|
| 488 |
-
const taskIndexNum =
|
| 489 |
-
typeof taskIndex === "bigint"
|
| 490 |
-
? Number(taskIndex)
|
| 491 |
-
: typeof taskIndex === "number"
|
| 492 |
-
? taskIndex
|
| 493 |
-
: undefined;
|
| 494 |
-
|
| 495 |
-
// Look up task by index
|
| 496 |
-
if (
|
| 497 |
-
taskIndexNum !== undefined &&
|
| 498 |
-
taskIndexNum >= 0 &&
|
| 499 |
-
taskIndexNum < tasksData.length
|
| 500 |
-
) {
|
| 501 |
const taskData = tasksData[taskIndexNum];
|
| 502 |
-
|
| 503 |
-
|
| 504 |
-
taskData &&
|
| 505 |
-
"__index_level_0__" in taskData &&
|
| 506 |
-
typeof taskData.__index_level_0__ === "string"
|
| 507 |
-
) {
|
| 508 |
-
task = taskData.__index_level_0__;
|
| 509 |
-
} else if (
|
| 510 |
-
taskData &&
|
| 511 |
-
"task" in taskData &&
|
| 512 |
-
typeof taskData.task === "string"
|
| 513 |
-
) {
|
| 514 |
-
task = taskData.task;
|
| 515 |
-
}
|
| 516 |
}
|
| 517 |
}
|
| 518 |
} catch {
|
| 519 |
-
// Could not load tasks metadata
|
| 520 |
}
|
| 521 |
}
|
| 522 |
|
| 523 |
-
return { chartDataGroups, ignoredColumns, task };
|
| 524 |
} catch {
|
| 525 |
-
return {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 526 |
}
|
| 527 |
}
|
| 528 |
|
|
@@ -531,16 +638,11 @@ function processEpisodeDataForCharts(
|
|
| 531 |
episodeData: Record<string, unknown>[],
|
| 532 |
info: DatasetMetadata,
|
| 533 |
episodeMetadata?: EpisodeMetadataV3,
|
| 534 |
-
): {
|
| 535 |
-
|
| 536 |
-
|
| 537 |
-
|
| 538 |
-
|
| 539 |
-
// ["float32", "int32"].includes(value.dtype) &&
|
| 540 |
-
// value.shape.length === 1,
|
| 541 |
-
// )
|
| 542 |
-
// .map(([key, value]) => ({ key, value }));
|
| 543 |
-
|
| 544 |
// Convert parquet data to chart format
|
| 545 |
let seriesNames: string[] = [];
|
| 546 |
|
|
@@ -576,7 +678,7 @@ function processEpisodeDataForCharts(
|
|
| 576 |
const excludedColumns = EXCLUDED_COLUMNS.V3 as readonly string[];
|
| 577 |
|
| 578 |
// Create columns structure similar to V2.1 for proper hierarchical naming
|
| 579 |
-
const columns = Object.entries(info.features)
|
| 580 |
.filter(
|
| 581 |
([key, value]) =>
|
| 582 |
["float32", "int32"].includes(value.dtype) &&
|
|
@@ -584,16 +686,16 @@ function processEpisodeDataForCharts(
|
|
| 584 |
!excludedColumns.includes(key),
|
| 585 |
)
|
| 586 |
.map(([key, feature]) => {
|
| 587 |
-
let column_names = feature.names;
|
| 588 |
-
while (typeof column_names === "object") {
|
| 589 |
if (Array.isArray(column_names)) break;
|
| 590 |
-
column_names = Object.values(column_names
|
| 591 |
}
|
| 592 |
return {
|
| 593 |
key,
|
| 594 |
value: Array.isArray(column_names)
|
| 595 |
? column_names.map(
|
| 596 |
-
(name) => `${key}${
|
| 597 |
)
|
| 598 |
: Array.from(
|
| 599 |
{ length: feature.shape[0] || 1 },
|
|
@@ -714,45 +816,6 @@ function processEpisodeDataForCharts(
|
|
| 714 |
// Process chart data into organized groups using utility function
|
| 715 |
const chartGroups = processChartDataGroups(seriesNames, chartData);
|
| 716 |
|
| 717 |
-
// Utility function to group row keys by suffix (same as V2.1)
|
| 718 |
-
function groupRowBySuffix(row: Record<string, number>): {
|
| 719 |
-
timestamp: number;
|
| 720 |
-
[key: string]: number | Record<string, number>;
|
| 721 |
-
} {
|
| 722 |
-
const result: {
|
| 723 |
-
timestamp: number;
|
| 724 |
-
[key: string]: number | Record<string, number>;
|
| 725 |
-
} = {
|
| 726 |
-
timestamp: 0,
|
| 727 |
-
};
|
| 728 |
-
const suffixGroups: Record<string, Record<string, number>> = {};
|
| 729 |
-
for (const [key, value] of Object.entries(row)) {
|
| 730 |
-
if (key === "timestamp") {
|
| 731 |
-
result.timestamp = value;
|
| 732 |
-
continue;
|
| 733 |
-
}
|
| 734 |
-
const parts = key.split(CHART_CONFIG.SERIES_NAME_DELIMITER);
|
| 735 |
-
if (parts.length === 2) {
|
| 736 |
-
const [prefix, suffix] = parts;
|
| 737 |
-
if (!suffixGroups[suffix]) suffixGroups[suffix] = {};
|
| 738 |
-
suffixGroups[suffix][prefix] = value;
|
| 739 |
-
} else {
|
| 740 |
-
result[key] = value;
|
| 741 |
-
}
|
| 742 |
-
}
|
| 743 |
-
for (const [suffix, group] of Object.entries(suffixGroups)) {
|
| 744 |
-
const keys = Object.keys(group);
|
| 745 |
-
if (keys.length === 1) {
|
| 746 |
-
// Use the full original name as the key
|
| 747 |
-
const fullName = `${keys[0]}${CHART_CONFIG.SERIES_NAME_DELIMITER}${suffix}`;
|
| 748 |
-
result[fullName] = group[keys[0]];
|
| 749 |
-
} else {
|
| 750 |
-
result[suffix] = group;
|
| 751 |
-
}
|
| 752 |
-
}
|
| 753 |
-
return result;
|
| 754 |
-
}
|
| 755 |
-
|
| 756 |
const chartDataGroups = chartGroups.map((group) =>
|
| 757 |
chartData.map((row) => {
|
| 758 |
const grouped = groupRowBySuffix(pick(row, [...group, "timestamp"]));
|
|
@@ -765,7 +828,7 @@ function processEpisodeDataForCharts(
|
|
| 765 |
}),
|
| 766 |
);
|
| 767 |
|
| 768 |
-
return { chartDataGroups, ignoredColumns };
|
| 769 |
}
|
| 770 |
|
| 771 |
// Video info extraction with segmentation for v3.0
|
|
@@ -786,18 +849,22 @@ function extractVideoInfoV3WithSegmentation(
|
|
| 786 |
key.startsWith(`videos/${videoKey}/`),
|
| 787 |
);
|
| 788 |
|
| 789 |
-
let chunkIndex
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 790 |
|
| 791 |
if (cameraSpecificKeys.length > 0) {
|
| 792 |
-
|
| 793 |
-
|
| 794 |
-
|
| 795 |
-
|
| 796 |
-
|
| 797 |
-
|
| 798 |
-
segmentEnd = episodeMetadata[`videos/${videoKey}/to_timestamp`] || 30;
|
| 799 |
} else {
|
| 800 |
-
// Fallback to generic video metadata
|
| 801 |
chunkIndex = episodeMetadata.video_chunk_index || 0;
|
| 802 |
fileIndex = episodeMetadata.video_file_index || 0;
|
| 803 |
segmentStart = episodeMetadata.video_from_timestamp || 0;
|
|
@@ -899,70 +966,83 @@ function parseEpisodeRowSimple(
|
|
| 899 |
// Check if this is v3.0 format with named keys
|
| 900 |
if ("episode_index" in row) {
|
| 901 |
// v3.0 format - use named keys
|
| 902 |
-
|
| 903 |
-
|
| 904 |
-
|
| 905 |
-
|
| 906 |
-
|
| 907 |
-
|
| 908 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 909 |
};
|
| 910 |
|
| 911 |
// Handle video metadata - look for video-specific keys
|
| 912 |
const videoKeys = Object.keys(row).filter(
|
| 913 |
(key) => key.includes("videos/") && key.includes("/chunk_index"),
|
| 914 |
);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 915 |
if (videoKeys.length > 0) {
|
| 916 |
-
|
| 917 |
-
|
| 918 |
-
|
| 919 |
-
|
| 920 |
-
|
| 921 |
-
row[`${videoBaseName}/chunk_index`],
|
| 922 |
-
0,
|
| 923 |
-
);
|
| 924 |
-
episodeData.video_file_index = bigIntToNumber(
|
| 925 |
-
row[`${videoBaseName}/file_index`],
|
| 926 |
-
0,
|
| 927 |
-
);
|
| 928 |
-
episodeData.video_from_timestamp = bigIntToNumber(
|
| 929 |
-
row[`${videoBaseName}/from_timestamp`],
|
| 930 |
-
0,
|
| 931 |
-
);
|
| 932 |
-
episodeData.video_to_timestamp = bigIntToNumber(
|
| 933 |
-
row[`${videoBaseName}/to_timestamp`],
|
| 934 |
-
0,
|
| 935 |
-
);
|
| 936 |
-
} else {
|
| 937 |
-
// Fallback video values
|
| 938 |
-
episodeData.video_chunk_index = 0;
|
| 939 |
-
episodeData.video_file_index = 0;
|
| 940 |
-
episodeData.video_from_timestamp = 0;
|
| 941 |
-
episodeData.video_to_timestamp = 30;
|
| 942 |
}
|
| 943 |
|
| 944 |
-
|
| 945 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 946 |
Object.keys(row).forEach((key) => {
|
| 947 |
if (key.startsWith("videos/")) {
|
| 948 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 949 |
}
|
| 950 |
});
|
| 951 |
|
| 952 |
return episodeData as EpisodeMetadataV3;
|
| 953 |
} else {
|
| 954 |
// Fallback to numeric keys for compatibility
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 955 |
return {
|
| 956 |
-
episode_index:
|
| 957 |
-
data_chunk_index:
|
| 958 |
-
data_file_index:
|
| 959 |
-
dataset_from_index:
|
| 960 |
-
dataset_to_index:
|
| 961 |
-
video_chunk_index:
|
| 962 |
-
video_file_index:
|
| 963 |
-
video_from_timestamp:
|
| 964 |
-
video_to_timestamp:
|
| 965 |
-
length:
|
| 966 |
};
|
| 967 |
}
|
| 968 |
}
|
|
@@ -984,6 +1064,995 @@ function parseEpisodeRowSimple(
|
|
| 984 |
return fallback;
|
| 985 |
}
|
| 986 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 987 |
// Safe wrapper for UI error display
|
| 988 |
export async function getEpisodeDataSafe(
|
| 989 |
org: string,
|
|
@@ -993,10 +2062,8 @@ export async function getEpisodeDataSafe(
|
|
| 993 |
try {
|
| 994 |
const data = await getEpisodeData(org, dataset, episodeId);
|
| 995 |
return { data };
|
| 996 |
-
} catch (err) {
|
| 997 |
-
|
| 998 |
-
|
| 999 |
-
err instanceof Error ? err.message : String(err) || "Unknown error";
|
| 1000 |
-
return { error: errorMessage };
|
| 1001 |
}
|
| 1002 |
}
|
|
|
|
| 1 |
import {
|
| 2 |
+
DatasetMetadata,
|
| 3 |
fetchParquetFile,
|
| 4 |
formatStringWithVars,
|
|
|
|
| 5 |
readParquetAsObjects,
|
| 6 |
} from "@/utils/parquetUtils";
|
| 7 |
import { pick } from "@/utils/pick";
|
| 8 |
+
import {
|
| 9 |
+
getDatasetVersionAndInfo,
|
| 10 |
+
buildVersionedUrl,
|
| 11 |
+
} from "@/utils/versionUtils";
|
| 12 |
import { PADDING, CHART_CONFIG, EXCLUDED_COLUMNS } from "@/utils/constants";
|
| 13 |
import {
|
| 14 |
processChartDataGroups,
|
| 15 |
groupRowBySuffix,
|
| 16 |
} from "@/utils/dataProcessing";
|
|
|
|
| 17 |
import {
|
| 18 |
buildV3VideoPath,
|
| 19 |
buildV3DataPath,
|
| 20 |
buildV3EpisodesMetadataPath,
|
| 21 |
} from "@/utils/stringFormatting";
|
| 22 |
import { bigIntToNumber } from "@/utils/typeGuards";
|
| 23 |
+
import type { VideoInfo, AdjacentEpisodeVideos } from "@/types";
|
| 24 |
+
|
| 25 |
+
const SERIES_NAME_DELIMITER = CHART_CONFIG.SERIES_NAME_DELIMITER;
|
| 26 |
+
|
| 27 |
+
export type CameraInfo = { name: string; width: number; height: number };
|
| 28 |
+
|
| 29 |
+
export type DatasetDisplayInfo = {
|
| 30 |
+
repoId: string;
|
| 31 |
+
total_frames: number;
|
| 32 |
+
total_episodes: number;
|
| 33 |
+
fps: number;
|
| 34 |
+
robot_type: string | null;
|
| 35 |
+
codebase_version: string;
|
| 36 |
+
total_tasks: number;
|
| 37 |
+
dataset_size_mb: number;
|
| 38 |
+
cameras: CameraInfo[];
|
| 39 |
+
};
|
| 40 |
+
|
| 41 |
+
export type ChartRow = Record<string, number | Record<string, number>>;
|
| 42 |
+
|
| 43 |
+
export type ColumnMinMax = {
|
| 44 |
+
column: string;
|
| 45 |
+
min: number;
|
| 46 |
+
max: number;
|
| 47 |
+
};
|
| 48 |
+
|
| 49 |
+
export type EpisodeLengthInfo = {
|
| 50 |
+
episodeIndex: number;
|
| 51 |
+
lengthSeconds: number;
|
| 52 |
+
frames: number;
|
| 53 |
+
};
|
| 54 |
+
|
| 55 |
+
export type EpisodeLengthStats = {
|
| 56 |
+
shortestEpisodes: EpisodeLengthInfo[];
|
| 57 |
+
longestEpisodes: EpisodeLengthInfo[];
|
| 58 |
+
allEpisodeLengths: EpisodeLengthInfo[];
|
| 59 |
+
meanEpisodeLength: number;
|
| 60 |
+
medianEpisodeLength: number;
|
| 61 |
+
stdEpisodeLength: number;
|
| 62 |
+
episodeLengthHistogram: { binLabel: string; count: number }[];
|
| 63 |
+
};
|
| 64 |
+
|
| 65 |
+
export type EpisodeFrameInfo = {
|
| 66 |
+
episodeIndex: number;
|
| 67 |
+
videoUrl: string;
|
| 68 |
+
firstFrameTime: number;
|
| 69 |
+
lastFrameTime: number | null; // null = seek to video.duration on client
|
| 70 |
+
};
|
| 71 |
+
|
| 72 |
+
export type EpisodeFramesData = {
|
| 73 |
+
cameras: string[];
|
| 74 |
+
framesByCamera: Record<string, EpisodeFrameInfo[]>;
|
| 75 |
+
};
|
| 76 |
+
|
| 77 |
+
export type EpisodeData = {
|
| 78 |
+
datasetInfo: DatasetDisplayInfo;
|
| 79 |
+
episodeId: number;
|
| 80 |
+
videosInfo: VideoInfo[];
|
| 81 |
+
chartDataGroups: ChartRow[][];
|
| 82 |
+
flatChartData: Record<string, number>[];
|
| 83 |
+
episodes: number[];
|
| 84 |
+
ignoredColumns: string[];
|
| 85 |
+
duration: number;
|
| 86 |
+
task?: string;
|
| 87 |
+
};
|
| 88 |
+
|
| 89 |
+
type EpisodeMetadataV3 = {
|
| 90 |
+
episode_index: number;
|
| 91 |
+
data_chunk_index: number;
|
| 92 |
+
data_file_index: number;
|
| 93 |
+
dataset_from_index: number;
|
| 94 |
+
dataset_to_index: number;
|
| 95 |
+
video_chunk_index: number;
|
| 96 |
+
video_file_index: number;
|
| 97 |
+
video_from_timestamp: number;
|
| 98 |
+
video_to_timestamp: number;
|
| 99 |
+
length: number;
|
| 100 |
+
[key: string]: string | number;
|
| 101 |
+
};
|
| 102 |
+
|
| 103 |
+
type ColumnDef = {
|
| 104 |
+
key: string;
|
| 105 |
+
value: string[];
|
| 106 |
+
};
|
| 107 |
|
| 108 |
export async function getEpisodeData(
|
| 109 |
org: string,
|
|
|
|
| 112 |
): Promise<EpisodeData> {
|
| 113 |
const repoId = `${org}/${dataset}`;
|
| 114 |
try {
|
| 115 |
+
console.time(`[perf] getDatasetVersionAndInfo`);
|
| 116 |
+
const { version, info: rawInfo } = await getDatasetVersionAndInfo(repoId);
|
| 117 |
+
console.timeEnd(`[perf] getDatasetVersionAndInfo`);
|
| 118 |
+
const info = rawInfo as unknown as DatasetMetadata;
|
| 119 |
|
| 120 |
if (info.video_path === null) {
|
| 121 |
throw new Error(
|
|
|
|
| 123 |
);
|
| 124 |
}
|
| 125 |
|
| 126 |
+
console.time(`[perf] getEpisodeData (${version})`);
|
| 127 |
+
const result =
|
| 128 |
+
version === "v3.0"
|
| 129 |
+
? await getEpisodeDataV3(repoId, version, info, episodeId)
|
| 130 |
+
: await getEpisodeDataV2(repoId, version, info, episodeId);
|
| 131 |
+
console.timeEnd(`[perf] getEpisodeData (${version})`);
|
| 132 |
+
|
| 133 |
+
// Extract camera resolutions from features
|
| 134 |
+
const cameras: CameraInfo[] = Object.entries(rawInfo.features)
|
| 135 |
+
.filter(([, f]) => f.dtype === "video" && f.shape.length >= 2)
|
| 136 |
+
.map(([name, f]) => ({ name, height: f.shape[0], width: f.shape[1] }));
|
| 137 |
+
|
| 138 |
+
result.datasetInfo = {
|
| 139 |
+
...result.datasetInfo,
|
| 140 |
+
robot_type: rawInfo.robot_type ?? null,
|
| 141 |
+
codebase_version: rawInfo.codebase_version,
|
| 142 |
+
total_tasks: rawInfo.total_tasks ?? 0,
|
| 143 |
+
dataset_size_mb:
|
| 144 |
+
Math.round(
|
| 145 |
+
((rawInfo.data_files_size_in_mb ?? 0) +
|
| 146 |
+
(rawInfo.video_files_size_in_mb ?? 0)) *
|
| 147 |
+
10,
|
| 148 |
+
) / 10,
|
| 149 |
+
cameras,
|
| 150 |
+
};
|
| 151 |
+
|
| 152 |
+
return result;
|
| 153 |
} catch (err) {
|
| 154 |
console.error("Error loading episode data:", err);
|
| 155 |
throw err;
|
| 156 |
}
|
| 157 |
}
|
| 158 |
|
|
|
|
| 159 |
export async function getAdjacentEpisodesVideoInfo(
|
| 160 |
org: string,
|
| 161 |
dataset: string,
|
|
|
|
| 164 |
): Promise<AdjacentEpisodeVideos[]> {
|
| 165 |
const repoId = `${org}/${dataset}`;
|
| 166 |
try {
|
| 167 |
+
const { version, info: rawInfo } = await getDatasetVersionAndInfo(repoId);
|
| 168 |
+
const info = rawInfo as unknown as DatasetMetadata;
|
|
|
|
| 169 |
|
| 170 |
const totalEpisodes = info.total_episodes;
|
| 171 |
const adjacentVideos: AdjacentEpisodeVideos[] = [];
|
|
|
|
| 238 |
): Promise<EpisodeData> {
|
| 239 |
const episode_chunk = Math.floor(0 / 1000);
|
| 240 |
|
| 241 |
+
const datasetInfo: DatasetDisplayInfo = {
|
|
|
|
| 242 |
repoId,
|
| 243 |
total_frames: info.total_frames,
|
| 244 |
total_episodes: info.total_episodes,
|
| 245 |
fps: info.fps,
|
| 246 |
+
robot_type: null,
|
| 247 |
+
codebase_version: version,
|
| 248 |
+
total_tasks: 0,
|
| 249 |
+
dataset_size_mb: 0,
|
| 250 |
+
cameras: [],
|
| 251 |
};
|
| 252 |
|
| 253 |
// Generate list of episodes
|
|
|
|
| 297 |
const filteredColumns = columnNames.filter(
|
| 298 |
(column) => !excludedColumns.includes(column.key),
|
| 299 |
);
|
| 300 |
+
const columns: ColumnDef[] = filteredColumns.map(({ key }) => {
|
| 301 |
+
let column_names: unknown = info.features[key].names;
|
| 302 |
+
while (typeof column_names === "object" && column_names !== null) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 303 |
if (Array.isArray(column_names)) break;
|
| 304 |
+
column_names = Object.values(column_names)[0];
|
| 305 |
}
|
| 306 |
return {
|
| 307 |
key,
|
| 308 |
value: Array.isArray(column_names)
|
| 309 |
? column_names.map(
|
| 310 |
+
(name: string) => `${key}${SERIES_NAME_DELIMITER}${name}`,
|
| 311 |
)
|
| 312 |
: Array.from(
|
| 313 |
{ length: columnNames.find((c) => c.key === key)?.length ?? 1 },
|
|
|
|
| 328 |
);
|
| 329 |
|
| 330 |
const arrayBuffer = await fetchParquetFile(parquetUrl);
|
| 331 |
+
const allData = await readParquetAsObjects(arrayBuffer, []);
|
| 332 |
|
| 333 |
+
// Extract task from language_instruction fields, task field, or tasks.jsonl
|
| 334 |
let task: string | undefined;
|
|
|
|
| 335 |
|
| 336 |
+
if (allData.length > 0) {
|
| 337 |
+
const firstRow = allData[0];
|
| 338 |
+
const languageInstructions: string[] = [];
|
|
|
|
|
|
|
|
|
|
| 339 |
|
| 340 |
+
if (typeof firstRow.language_instruction === "string") {
|
| 341 |
+
languageInstructions.push(firstRow.language_instruction);
|
| 342 |
+
}
|
| 343 |
|
| 344 |
+
let instructionNum = 2;
|
| 345 |
+
while (
|
| 346 |
+
typeof firstRow[`language_instruction_${instructionNum}`] === "string"
|
| 347 |
+
) {
|
| 348 |
+
languageInstructions.push(
|
| 349 |
+
firstRow[`language_instruction_${instructionNum}`] as string,
|
| 350 |
+
);
|
| 351 |
+
instructionNum++;
|
| 352 |
+
}
|
| 353 |
+
|
| 354 |
+
if (languageInstructions.length > 0) {
|
| 355 |
+
task = languageInstructions.join("\n");
|
| 356 |
+
}
|
| 357 |
+
}
|
| 358 |
+
|
| 359 |
+
if (!task && allData.length > 0 && typeof allData[0].task === "string") {
|
| 360 |
task = allData[0].task;
|
| 361 |
}
|
| 362 |
|
|
|
|
| 363 |
if (!task && allData.length > 0) {
|
| 364 |
try {
|
| 365 |
const tasksUrl = buildVersionedUrl(repoId, version, "meta/tasks.jsonl");
|
|
|
|
| 367 |
|
| 368 |
if (tasksResponse.ok) {
|
| 369 |
const tasksText = await tasksResponse.text();
|
|
|
|
| 370 |
const tasksData = tasksText
|
| 371 |
.split("\n")
|
| 372 |
.filter((line) => line.trim())
|
|
|
|
| 374 |
|
| 375 |
if (tasksData && tasksData.length > 0) {
|
| 376 |
const taskIndex = allData[0].task_index;
|
|
|
|
|
|
|
| 377 |
const taskIndexNum =
|
| 378 |
typeof taskIndex === "bigint" ? Number(taskIndex) : taskIndex;
|
| 379 |
+
const taskData = tasksData.find(
|
| 380 |
+
(t: Record<string, unknown>) => t.task_index === taskIndexNum,
|
| 381 |
+
);
|
| 382 |
if (taskData) {
|
| 383 |
task = taskData.task;
|
| 384 |
}
|
|
|
|
| 389 |
}
|
| 390 |
}
|
| 391 |
|
| 392 |
+
// Build chart data from already-parsed allData (no second parquet parse)
|
|
|
|
| 393 |
const seriesNames = [
|
| 394 |
"timestamp",
|
| 395 |
...columns.map(({ value }) => value).flat(),
|
| 396 |
];
|
| 397 |
|
| 398 |
+
const chartData = allData.map((row) => {
|
|
|
|
| 399 |
const obj: Record<string, number> = {};
|
| 400 |
+
obj["timestamp"] = Number(row.timestamp);
|
| 401 |
+
for (const col of columns) {
|
| 402 |
+
const rawVal = row[col.key];
|
| 403 |
+
if (Array.isArray(rawVal)) {
|
| 404 |
+
rawVal.forEach((v: unknown, i: number) => {
|
| 405 |
+
if (i < col.value.length) obj[col.value[i]] = Number(v);
|
| 406 |
+
});
|
| 407 |
+
} else if (rawVal !== undefined) {
|
| 408 |
+
obj[col.value[0]] = Number(rawVal);
|
| 409 |
+
}
|
| 410 |
+
}
|
| 411 |
return obj;
|
| 412 |
});
|
| 413 |
|
|
|
|
| 441 |
episodeId,
|
| 442 |
videosInfo,
|
| 443 |
chartDataGroups,
|
| 444 |
+
flatChartData: chartData,
|
| 445 |
episodes,
|
| 446 |
ignoredColumns,
|
| 447 |
duration,
|
|
|
|
| 456 |
info: DatasetMetadata,
|
| 457 |
episodeId: number,
|
| 458 |
): Promise<EpisodeData> {
|
| 459 |
+
const datasetInfo: DatasetDisplayInfo = {
|
|
|
|
| 460 |
repoId,
|
| 461 |
total_frames: info.total_frames,
|
| 462 |
total_episodes: info.total_episodes,
|
| 463 |
fps: info.fps,
|
| 464 |
+
robot_type: null,
|
| 465 |
+
codebase_version: version,
|
| 466 |
+
total_tasks: 0,
|
| 467 |
+
dataset_size_mb: 0,
|
| 468 |
+
cameras: [],
|
| 469 |
};
|
| 470 |
|
|
|
|
| 471 |
const episodes = Array.from({ length: info.total_episodes }, (_, i) => i);
|
| 472 |
|
| 473 |
// Load episode metadata to get timestamps for episode 0
|
|
|
|
| 486 |
);
|
| 487 |
|
| 488 |
// Load episode data for charts
|
| 489 |
+
const { chartDataGroups, flatChartData, ignoredColumns, task } =
|
| 490 |
+
await loadEpisodeDataV3(repoId, version, info, episodeMetadata);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 491 |
|
| 492 |
+
const duration = episodeMetadata.length
|
| 493 |
+
? episodeMetadata.length / info.fps
|
| 494 |
+
: episodeMetadata.video_to_timestamp - episodeMetadata.video_from_timestamp;
|
|
|
|
|
|
|
|
|
|
| 495 |
|
| 496 |
return {
|
| 497 |
datasetInfo,
|
| 498 |
episodeId,
|
| 499 |
videosInfo,
|
| 500 |
chartDataGroups,
|
| 501 |
+
flatChartData,
|
| 502 |
episodes,
|
| 503 |
ignoredColumns,
|
| 504 |
duration,
|
|
|
|
| 513 |
info: DatasetMetadata,
|
| 514 |
episodeMetadata: EpisodeMetadataV3,
|
| 515 |
): Promise<{
|
| 516 |
+
chartDataGroups: ChartRow[][];
|
| 517 |
+
flatChartData: Record<string, number>[];
|
| 518 |
ignoredColumns: string[];
|
| 519 |
task?: string;
|
| 520 |
}> {
|
|
|
|
| 529 |
const fullData = await readParquetAsObjects(arrayBuffer, []);
|
| 530 |
|
| 531 |
// Extract the episode-specific data slice
|
| 532 |
+
const fromIndex = bigIntToNumber(episodeMetadata.dataset_from_index, 0);
|
| 533 |
+
const toIndex = bigIntToNumber(
|
| 534 |
+
episodeMetadata.dataset_to_index,
|
| 535 |
+
fullData.length,
|
| 536 |
+
);
|
| 537 |
|
| 538 |
// Find the starting index of this parquet file by checking the first row's index
|
| 539 |
// This handles the case where episodes are split across multiple parquet files
|
|
|
|
| 549 |
const episodeData = fullData.slice(localFromIndex, localToIndex);
|
| 550 |
|
| 551 |
if (episodeData.length === 0) {
|
| 552 |
+
return {
|
| 553 |
+
chartDataGroups: [],
|
| 554 |
+
flatChartData: [],
|
| 555 |
+
ignoredColumns: [],
|
| 556 |
+
task: undefined,
|
| 557 |
+
};
|
| 558 |
}
|
| 559 |
|
| 560 |
// Convert to the same format as v2.x for compatibility with existing chart code
|
| 561 |
+
const { chartDataGroups, flatChartData, ignoredColumns } =
|
| 562 |
+
processEpisodeDataForCharts(episodeData, info, episodeMetadata);
|
|
|
|
|
|
|
|
|
|
| 563 |
|
| 564 |
// First check for language_instruction fields in the data (preferred)
|
| 565 |
+
let task: string | undefined;
|
| 566 |
+
if (episodeData.length > 0) {
|
| 567 |
+
const languageInstructions: string[] = [];
|
| 568 |
+
|
| 569 |
+
const extractInstructions = (row: Record<string, unknown>) => {
|
| 570 |
+
if (typeof row.language_instruction === "string") {
|
| 571 |
+
languageInstructions.push(row.language_instruction);
|
| 572 |
+
}
|
| 573 |
+
let num = 2;
|
| 574 |
+
while (typeof row[`language_instruction_${num}`] === "string") {
|
| 575 |
+
languageInstructions.push(
|
| 576 |
+
row[`language_instruction_${num}`] as string,
|
| 577 |
+
);
|
| 578 |
+
num++;
|
| 579 |
+
}
|
| 580 |
+
};
|
| 581 |
+
|
| 582 |
+
extractInstructions(episodeData[0]);
|
| 583 |
+
|
| 584 |
+
// If no instructions in first row, check middle and last rows
|
| 585 |
+
if (languageInstructions.length === 0 && episodeData.length > 1) {
|
| 586 |
+
for (const idx of [
|
| 587 |
+
Math.floor(episodeData.length / 2),
|
| 588 |
+
episodeData.length - 1,
|
| 589 |
+
]) {
|
| 590 |
+
extractInstructions(episodeData[idx]);
|
| 591 |
+
if (languageInstructions.length > 0) break;
|
| 592 |
+
}
|
| 593 |
+
}
|
| 594 |
+
|
| 595 |
+
if (languageInstructions.length > 0) {
|
| 596 |
+
task = languageInstructions.join("\n");
|
| 597 |
+
}
|
| 598 |
+
}
|
| 599 |
+
|
| 600 |
+
// Fall back to tasks metadata parquet
|
| 601 |
+
if (!task && episodeData.length > 0) {
|
| 602 |
try {
|
|
|
|
| 603 |
const tasksUrl = buildVersionedUrl(
|
| 604 |
repoId,
|
| 605 |
version,
|
|
|
|
| 608 |
const tasksArrayBuffer = await fetchParquetFile(tasksUrl);
|
| 609 |
const tasksData = await readParquetAsObjects(tasksArrayBuffer, []);
|
| 610 |
|
| 611 |
+
if (tasksData.length > 0) {
|
| 612 |
+
const taskIndexNum = bigIntToNumber(episodeData[0].task_index, -1);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 613 |
|
| 614 |
+
if (taskIndexNum >= 0 && taskIndexNum < tasksData.length) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 615 |
const taskData = tasksData[taskIndexNum];
|
| 616 |
+
const rawTask = taskData.__index_level_0__ ?? taskData.task;
|
| 617 |
+
task = typeof rawTask === "string" ? rawTask : undefined;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 618 |
}
|
| 619 |
}
|
| 620 |
} catch {
|
| 621 |
+
// Could not load tasks metadata
|
| 622 |
}
|
| 623 |
}
|
| 624 |
|
| 625 |
+
return { chartDataGroups, flatChartData, ignoredColumns, task };
|
| 626 |
} catch {
|
| 627 |
+
return {
|
| 628 |
+
chartDataGroups: [],
|
| 629 |
+
flatChartData: [],
|
| 630 |
+
ignoredColumns: [],
|
| 631 |
+
task: undefined,
|
| 632 |
+
};
|
| 633 |
}
|
| 634 |
}
|
| 635 |
|
|
|
|
| 638 |
episodeData: Record<string, unknown>[],
|
| 639 |
info: DatasetMetadata,
|
| 640 |
episodeMetadata?: EpisodeMetadataV3,
|
| 641 |
+
): {
|
| 642 |
+
chartDataGroups: ChartRow[][];
|
| 643 |
+
flatChartData: Record<string, number>[];
|
| 644 |
+
ignoredColumns: string[];
|
| 645 |
+
} {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 646 |
// Convert parquet data to chart format
|
| 647 |
let seriesNames: string[] = [];
|
| 648 |
|
|
|
|
| 678 |
const excludedColumns = EXCLUDED_COLUMNS.V3 as readonly string[];
|
| 679 |
|
| 680 |
// Create columns structure similar to V2.1 for proper hierarchical naming
|
| 681 |
+
const columns: ColumnDef[] = Object.entries(info.features)
|
| 682 |
.filter(
|
| 683 |
([key, value]) =>
|
| 684 |
["float32", "int32"].includes(value.dtype) &&
|
|
|
|
| 686 |
!excludedColumns.includes(key),
|
| 687 |
)
|
| 688 |
.map(([key, feature]) => {
|
| 689 |
+
let column_names: unknown = feature.names;
|
| 690 |
+
while (typeof column_names === "object" && column_names !== null) {
|
| 691 |
if (Array.isArray(column_names)) break;
|
| 692 |
+
column_names = Object.values(column_names)[0];
|
| 693 |
}
|
| 694 |
return {
|
| 695 |
key,
|
| 696 |
value: Array.isArray(column_names)
|
| 697 |
? column_names.map(
|
| 698 |
+
(name: string) => `${key}${SERIES_NAME_DELIMITER}${name}`,
|
| 699 |
)
|
| 700 |
: Array.from(
|
| 701 |
{ length: feature.shape[0] || 1 },
|
|
|
|
| 816 |
// Process chart data into organized groups using utility function
|
| 817 |
const chartGroups = processChartDataGroups(seriesNames, chartData);
|
| 818 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 819 |
const chartDataGroups = chartGroups.map((group) =>
|
| 820 |
chartData.map((row) => {
|
| 821 |
const grouped = groupRowBySuffix(pick(row, [...group, "timestamp"]));
|
|
|
|
| 828 |
}),
|
| 829 |
);
|
| 830 |
|
| 831 |
+
return { chartDataGroups, flatChartData: chartData, ignoredColumns };
|
| 832 |
}
|
| 833 |
|
| 834 |
// Video info extraction with segmentation for v3.0
|
|
|
|
| 849 |
key.startsWith(`videos/${videoKey}/`),
|
| 850 |
);
|
| 851 |
|
| 852 |
+
let chunkIndex: number,
|
| 853 |
+
fileIndex: number,
|
| 854 |
+
segmentStart: number,
|
| 855 |
+
segmentEnd: number;
|
| 856 |
+
|
| 857 |
+
const toNum = (v: string | number): number =>
|
| 858 |
+
typeof v === "string" ? parseFloat(v) || 0 : v;
|
| 859 |
|
| 860 |
if (cameraSpecificKeys.length > 0) {
|
| 861 |
+
chunkIndex = toNum(episodeMetadata[`videos/${videoKey}/chunk_index`]);
|
| 862 |
+
fileIndex = toNum(episodeMetadata[`videos/${videoKey}/file_index`]);
|
| 863 |
+
segmentStart =
|
| 864 |
+
toNum(episodeMetadata[`videos/${videoKey}/from_timestamp`]) || 0;
|
| 865 |
+
segmentEnd =
|
| 866 |
+
toNum(episodeMetadata[`videos/${videoKey}/to_timestamp`]) || 30;
|
|
|
|
| 867 |
} else {
|
|
|
|
| 868 |
chunkIndex = episodeMetadata.video_chunk_index || 0;
|
| 869 |
fileIndex = episodeMetadata.video_file_index || 0;
|
| 870 |
segmentStart = episodeMetadata.video_from_timestamp || 0;
|
|
|
|
| 966 |
// Check if this is v3.0 format with named keys
|
| 967 |
if ("episode_index" in row) {
|
| 968 |
// v3.0 format - use named keys
|
| 969 |
+
// Convert BigInt values to numbers
|
| 970 |
+
const toBigIntSafe = (value: unknown): number => {
|
| 971 |
+
if (typeof value === "bigint") return Number(value);
|
| 972 |
+
if (typeof value === "number") return value;
|
| 973 |
+
if (typeof value === "string") return parseInt(value) || 0;
|
| 974 |
+
return 0;
|
| 975 |
+
};
|
| 976 |
+
|
| 977 |
+
const toNumSafe = (value: unknown): number => {
|
| 978 |
+
if (typeof value === "number") return value;
|
| 979 |
+
if (typeof value === "bigint") return Number(value);
|
| 980 |
+
if (typeof value === "string") return parseFloat(value) || 0;
|
| 981 |
+
return 0;
|
| 982 |
};
|
| 983 |
|
| 984 |
// Handle video metadata - look for video-specific keys
|
| 985 |
const videoKeys = Object.keys(row).filter(
|
| 986 |
(key) => key.includes("videos/") && key.includes("/chunk_index"),
|
| 987 |
);
|
| 988 |
+
let videoChunkIndex = 0,
|
| 989 |
+
videoFileIndex = 0,
|
| 990 |
+
videoFromTs = 0,
|
| 991 |
+
videoToTs = 30;
|
| 992 |
if (videoKeys.length > 0) {
|
| 993 |
+
const videoBaseName = videoKeys[0].replace("/chunk_index", "");
|
| 994 |
+
videoChunkIndex = toBigIntSafe(row[`${videoBaseName}/chunk_index`]);
|
| 995 |
+
videoFileIndex = toBigIntSafe(row[`${videoBaseName}/file_index`]);
|
| 996 |
+
videoFromTs = toNumSafe(row[`${videoBaseName}/from_timestamp`]);
|
| 997 |
+
videoToTs = toNumSafe(row[`${videoBaseName}/to_timestamp`]) || 30;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 998 |
}
|
| 999 |
|
| 1000 |
+
const episodeData: EpisodeMetadataV3 = {
|
| 1001 |
+
episode_index: toBigIntSafe(row["episode_index"]),
|
| 1002 |
+
data_chunk_index: toBigIntSafe(row["data/chunk_index"]),
|
| 1003 |
+
data_file_index: toBigIntSafe(row["data/file_index"]),
|
| 1004 |
+
dataset_from_index: toBigIntSafe(row["dataset_from_index"]),
|
| 1005 |
+
dataset_to_index: toBigIntSafe(row["dataset_to_index"]),
|
| 1006 |
+
length: toBigIntSafe(row["length"]),
|
| 1007 |
+
video_chunk_index: videoChunkIndex,
|
| 1008 |
+
video_file_index: videoFileIndex,
|
| 1009 |
+
video_from_timestamp: videoFromTs,
|
| 1010 |
+
video_to_timestamp: videoToTs,
|
| 1011 |
+
};
|
| 1012 |
+
|
| 1013 |
+
// Store per-camera metadata for extractVideoInfoV3WithSegmentation
|
| 1014 |
Object.keys(row).forEach((key) => {
|
| 1015 |
if (key.startsWith("videos/")) {
|
| 1016 |
+
const val = row[key];
|
| 1017 |
+
episodeData[key] =
|
| 1018 |
+
typeof val === "bigint"
|
| 1019 |
+
? Number(val)
|
| 1020 |
+
: typeof val === "number" || typeof val === "string"
|
| 1021 |
+
? val
|
| 1022 |
+
: 0;
|
| 1023 |
}
|
| 1024 |
});
|
| 1025 |
|
| 1026 |
return episodeData as EpisodeMetadataV3;
|
| 1027 |
} else {
|
| 1028 |
// Fallback to numeric keys for compatibility
|
| 1029 |
+
const toNum = (v: unknown, fallback = 0): number =>
|
| 1030 |
+
typeof v === "number"
|
| 1031 |
+
? v
|
| 1032 |
+
: typeof v === "bigint"
|
| 1033 |
+
? Number(v)
|
| 1034 |
+
: fallback;
|
| 1035 |
return {
|
| 1036 |
+
episode_index: toNum(row["0"]),
|
| 1037 |
+
data_chunk_index: toNum(row["1"]),
|
| 1038 |
+
data_file_index: toNum(row["2"]),
|
| 1039 |
+
dataset_from_index: toNum(row["3"]),
|
| 1040 |
+
dataset_to_index: toNum(row["4"]),
|
| 1041 |
+
video_chunk_index: toNum(row["5"]),
|
| 1042 |
+
video_file_index: toNum(row["6"]),
|
| 1043 |
+
video_from_timestamp: toNum(row["7"]),
|
| 1044 |
+
video_to_timestamp: toNum(row["8"], 30),
|
| 1045 |
+
length: toNum(row["9"], 30),
|
| 1046 |
};
|
| 1047 |
}
|
| 1048 |
}
|
|
|
|
| 1064 |
return fallback;
|
| 1065 |
}
|
| 1066 |
|
| 1067 |
+
// ─── Stats computation ───────────────────────────────────────────
|
| 1068 |
+
|
| 1069 |
+
/**
|
| 1070 |
+
* Compute per-column min/max values from the current episode's chart data.
|
| 1071 |
+
*/
|
| 1072 |
+
export function computeColumnMinMax(
|
| 1073 |
+
chartDataGroups: ChartRow[][],
|
| 1074 |
+
): ColumnMinMax[] {
|
| 1075 |
+
const stats: Record<string, { min: number; max: number }> = {};
|
| 1076 |
+
|
| 1077 |
+
for (const group of chartDataGroups) {
|
| 1078 |
+
for (const row of group) {
|
| 1079 |
+
for (const [key, value] of Object.entries(row)) {
|
| 1080 |
+
if (key === "timestamp") continue;
|
| 1081 |
+
if (typeof value === "number" && isFinite(value)) {
|
| 1082 |
+
if (!stats[key]) {
|
| 1083 |
+
stats[key] = { min: value, max: value };
|
| 1084 |
+
} else {
|
| 1085 |
+
if (value < stats[key].min) stats[key].min = value;
|
| 1086 |
+
if (value > stats[key].max) stats[key].max = value;
|
| 1087 |
+
}
|
| 1088 |
+
} else if (typeof value === "object" && value !== null) {
|
| 1089 |
+
// Nested group like { joint_0: 1.2, joint_1: 3.4 }
|
| 1090 |
+
for (const [subKey, subVal] of Object.entries(value)) {
|
| 1091 |
+
const fullKey = `${key} | ${subKey}`;
|
| 1092 |
+
if (typeof subVal === "number" && isFinite(subVal)) {
|
| 1093 |
+
if (!stats[fullKey]) {
|
| 1094 |
+
stats[fullKey] = { min: subVal, max: subVal };
|
| 1095 |
+
} else {
|
| 1096 |
+
if (subVal < stats[fullKey].min) stats[fullKey].min = subVal;
|
| 1097 |
+
if (subVal > stats[fullKey].max) stats[fullKey].max = subVal;
|
| 1098 |
+
}
|
| 1099 |
+
}
|
| 1100 |
+
}
|
| 1101 |
+
}
|
| 1102 |
+
}
|
| 1103 |
+
}
|
| 1104 |
+
}
|
| 1105 |
+
|
| 1106 |
+
return Object.entries(stats).map(([column, { min, max }]) => ({
|
| 1107 |
+
column,
|
| 1108 |
+
min: Math.round(min * 1000) / 1000,
|
| 1109 |
+
max: Math.round(max * 1000) / 1000,
|
| 1110 |
+
}));
|
| 1111 |
+
}
|
| 1112 |
+
|
| 1113 |
+
/**
|
| 1114 |
+
* Load all episode lengths from the episodes metadata parquet files (v3.0).
|
| 1115 |
+
* Returns min/max/mean/median/std and a histogram, or null if unavailable.
|
| 1116 |
+
*/
|
| 1117 |
+
export async function loadAllEpisodeLengthsV3(
|
| 1118 |
+
repoId: string,
|
| 1119 |
+
version: string,
|
| 1120 |
+
fps: number,
|
| 1121 |
+
): Promise<EpisodeLengthStats | null> {
|
| 1122 |
+
try {
|
| 1123 |
+
const allEpisodes: { index: number; length: number }[] = [];
|
| 1124 |
+
let fileIndex = 0;
|
| 1125 |
+
const chunkIndex = 0;
|
| 1126 |
+
|
| 1127 |
+
while (true) {
|
| 1128 |
+
const path = `meta/episodes/chunk-${chunkIndex.toString().padStart(3, "0")}/file-${fileIndex.toString().padStart(3, "0")}.parquet`;
|
| 1129 |
+
const url = buildVersionedUrl(repoId, version, path);
|
| 1130 |
+
try {
|
| 1131 |
+
const buf = await fetchParquetFile(url);
|
| 1132 |
+
const rows = await readParquetAsObjects(buf, []);
|
| 1133 |
+
if (rows.length === 0 && fileIndex > 0) break;
|
| 1134 |
+
for (const row of rows) {
|
| 1135 |
+
const parsed = parseEpisodeRowSimple(row);
|
| 1136 |
+
allEpisodes.push({
|
| 1137 |
+
index: parsed.episode_index,
|
| 1138 |
+
length: parsed.length,
|
| 1139 |
+
});
|
| 1140 |
+
}
|
| 1141 |
+
fileIndex++;
|
| 1142 |
+
} catch {
|
| 1143 |
+
break;
|
| 1144 |
+
}
|
| 1145 |
+
}
|
| 1146 |
+
|
| 1147 |
+
if (allEpisodes.length === 0) return null;
|
| 1148 |
+
|
| 1149 |
+
const withSeconds = allEpisodes.map((ep) => ({
|
| 1150 |
+
episodeIndex: ep.index,
|
| 1151 |
+
frames: ep.length,
|
| 1152 |
+
lengthSeconds: Math.round((ep.length / fps) * 100) / 100,
|
| 1153 |
+
}));
|
| 1154 |
+
|
| 1155 |
+
const sortedByLength = [...withSeconds].sort(
|
| 1156 |
+
(a, b) => a.lengthSeconds - b.lengthSeconds,
|
| 1157 |
+
);
|
| 1158 |
+
const shortestEpisodes = sortedByLength.slice(0, 5);
|
| 1159 |
+
const longestEpisodes = sortedByLength.slice(-5).reverse();
|
| 1160 |
+
|
| 1161 |
+
const lengths = withSeconds.map((e) => e.lengthSeconds);
|
| 1162 |
+
const sum = lengths.reduce((a, b) => a + b, 0);
|
| 1163 |
+
const mean = Math.round((sum / lengths.length) * 100) / 100;
|
| 1164 |
+
|
| 1165 |
+
const sorted = [...lengths].sort((a, b) => a - b);
|
| 1166 |
+
const mid = Math.floor(sorted.length / 2);
|
| 1167 |
+
const median =
|
| 1168 |
+
sorted.length % 2 === 0
|
| 1169 |
+
? Math.round(((sorted[mid - 1] + sorted[mid]) / 2) * 100) / 100
|
| 1170 |
+
: sorted[mid];
|
| 1171 |
+
|
| 1172 |
+
const variance =
|
| 1173 |
+
lengths.reduce((acc, l) => acc + (l - mean) ** 2, 0) / lengths.length;
|
| 1174 |
+
const std = Math.round(Math.sqrt(variance) * 100) / 100;
|
| 1175 |
+
|
| 1176 |
+
// Build histogram
|
| 1177 |
+
const histMin = Math.min(...lengths);
|
| 1178 |
+
const histMax = Math.max(...lengths);
|
| 1179 |
+
|
| 1180 |
+
if (histMax === histMin) {
|
| 1181 |
+
return {
|
| 1182 |
+
shortestEpisodes,
|
| 1183 |
+
longestEpisodes,
|
| 1184 |
+
allEpisodeLengths: withSeconds,
|
| 1185 |
+
meanEpisodeLength: mean,
|
| 1186 |
+
medianEpisodeLength: median,
|
| 1187 |
+
stdEpisodeLength: std,
|
| 1188 |
+
episodeLengthHistogram: [
|
| 1189 |
+
{ binLabel: `${histMin.toFixed(1)}s`, count: lengths.length },
|
| 1190 |
+
],
|
| 1191 |
+
};
|
| 1192 |
+
}
|
| 1193 |
+
|
| 1194 |
+
const p1 = sorted[Math.floor(sorted.length * 0.01)];
|
| 1195 |
+
const p99 = sorted[Math.ceil(sorted.length * 0.99) - 1];
|
| 1196 |
+
const range = p99 - p1 || 1;
|
| 1197 |
+
|
| 1198 |
+
const targetBins = Math.max(
|
| 1199 |
+
10,
|
| 1200 |
+
Math.min(50, Math.ceil(Math.log2(lengths.length) + 1)),
|
| 1201 |
+
);
|
| 1202 |
+
const rawBinWidth = range / targetBins;
|
| 1203 |
+
const magnitude = Math.pow(10, Math.floor(Math.log10(rawBinWidth)));
|
| 1204 |
+
const niceSteps = [1, 2, 2.5, 5, 10];
|
| 1205 |
+
const niceBinWidth =
|
| 1206 |
+
niceSteps.map((s) => s * magnitude).find((w) => w >= rawBinWidth) ??
|
| 1207 |
+
rawBinWidth;
|
| 1208 |
+
|
| 1209 |
+
const niceMin = Math.floor(p1 / niceBinWidth) * niceBinWidth;
|
| 1210 |
+
const niceMax = Math.ceil(p99 / niceBinWidth) * niceBinWidth;
|
| 1211 |
+
const actualBinCount = Math.max(
|
| 1212 |
+
1,
|
| 1213 |
+
Math.round((niceMax - niceMin) / niceBinWidth),
|
| 1214 |
+
);
|
| 1215 |
+
const bins = Array.from({ length: actualBinCount }, () => 0);
|
| 1216 |
+
|
| 1217 |
+
for (const len of lengths) {
|
| 1218 |
+
let binIdx = Math.floor((len - niceMin) / niceBinWidth);
|
| 1219 |
+
if (binIdx < 0) binIdx = 0;
|
| 1220 |
+
if (binIdx >= actualBinCount) binIdx = actualBinCount - 1;
|
| 1221 |
+
bins[binIdx]++;
|
| 1222 |
+
}
|
| 1223 |
+
|
| 1224 |
+
const histogram = bins.map((count, i) => {
|
| 1225 |
+
const lo = niceMin + i * niceBinWidth;
|
| 1226 |
+
const hi = lo + niceBinWidth;
|
| 1227 |
+
return { binLabel: `${lo.toFixed(1)}–${hi.toFixed(1)}s`, count };
|
| 1228 |
+
});
|
| 1229 |
+
|
| 1230 |
+
return {
|
| 1231 |
+
shortestEpisodes,
|
| 1232 |
+
longestEpisodes,
|
| 1233 |
+
allEpisodeLengths: withSeconds,
|
| 1234 |
+
meanEpisodeLength: mean,
|
| 1235 |
+
medianEpisodeLength: median,
|
| 1236 |
+
stdEpisodeLength: std,
|
| 1237 |
+
episodeLengthHistogram: histogram,
|
| 1238 |
+
};
|
| 1239 |
+
} catch {
|
| 1240 |
+
return null;
|
| 1241 |
+
}
|
| 1242 |
+
}
|
| 1243 |
+
|
| 1244 |
+
/**
|
| 1245 |
+
* Load video frame info for all episodes across all cameras.
|
| 1246 |
+
* Returns camera names + a map of camera → EpisodeFrameInfo[].
|
| 1247 |
+
*/
|
| 1248 |
+
export async function loadAllEpisodeFrameInfo(
|
| 1249 |
+
repoId: string,
|
| 1250 |
+
version: string,
|
| 1251 |
+
info: DatasetMetadata,
|
| 1252 |
+
): Promise<EpisodeFramesData> {
|
| 1253 |
+
const videoFeatures = Object.entries(info.features).filter(
|
| 1254 |
+
([, f]) => f.dtype === "video",
|
| 1255 |
+
);
|
| 1256 |
+
if (videoFeatures.length === 0) return { cameras: [], framesByCamera: {} };
|
| 1257 |
+
|
| 1258 |
+
const cameras = videoFeatures.map(([key]) => key);
|
| 1259 |
+
const framesByCamera: Record<string, EpisodeFrameInfo[]> = {};
|
| 1260 |
+
for (const cam of cameras) framesByCamera[cam] = [];
|
| 1261 |
+
|
| 1262 |
+
if (version === "v3.0") {
|
| 1263 |
+
let fileIndex = 0;
|
| 1264 |
+
while (true) {
|
| 1265 |
+
const path = `meta/episodes/chunk-000/file-${fileIndex.toString().padStart(3, "0")}.parquet`;
|
| 1266 |
+
try {
|
| 1267 |
+
const buf = await fetchParquetFile(
|
| 1268 |
+
buildVersionedUrl(repoId, version, path),
|
| 1269 |
+
);
|
| 1270 |
+
const rows = await readParquetAsObjects(buf, []);
|
| 1271 |
+
if (rows.length === 0 && fileIndex > 0) break;
|
| 1272 |
+
for (const row of rows) {
|
| 1273 |
+
const epIdx = Number(row["episode_index"] ?? 0);
|
| 1274 |
+
for (const cam of cameras) {
|
| 1275 |
+
const cIdx = Number(
|
| 1276 |
+
row[`videos/${cam}/chunk_index`] ?? row["video_chunk_index"] ?? 0,
|
| 1277 |
+
);
|
| 1278 |
+
const fIdx = Number(
|
| 1279 |
+
row[`videos/${cam}/file_index`] ?? row["video_file_index"] ?? 0,
|
| 1280 |
+
);
|
| 1281 |
+
const fromTs = Number(
|
| 1282 |
+
row[`videos/${cam}/from_timestamp`] ??
|
| 1283 |
+
row["video_from_timestamp"] ??
|
| 1284 |
+
0,
|
| 1285 |
+
);
|
| 1286 |
+
const toTs = Number(
|
| 1287 |
+
row[`videos/${cam}/to_timestamp`] ??
|
| 1288 |
+
row["video_to_timestamp"] ??
|
| 1289 |
+
30,
|
| 1290 |
+
);
|
| 1291 |
+
const videoPath = `videos/${cam}/chunk-${cIdx.toString().padStart(3, "0")}/file-${fIdx.toString().padStart(3, "0")}.mp4`;
|
| 1292 |
+
framesByCamera[cam].push({
|
| 1293 |
+
episodeIndex: epIdx,
|
| 1294 |
+
videoUrl: buildVersionedUrl(repoId, version, videoPath),
|
| 1295 |
+
firstFrameTime: fromTs,
|
| 1296 |
+
lastFrameTime: Math.max(0, toTs - 0.05),
|
| 1297 |
+
});
|
| 1298 |
+
}
|
| 1299 |
+
}
|
| 1300 |
+
fileIndex++;
|
| 1301 |
+
} catch {
|
| 1302 |
+
break;
|
| 1303 |
+
}
|
| 1304 |
+
}
|
| 1305 |
+
return { cameras, framesByCamera };
|
| 1306 |
+
}
|
| 1307 |
+
|
| 1308 |
+
// v2.x — construct URLs from template
|
| 1309 |
+
for (let i = 0; i < info.total_episodes; i++) {
|
| 1310 |
+
const chunk = Math.floor(i / (info.chunks_size || 1000));
|
| 1311 |
+
for (const cam of cameras) {
|
| 1312 |
+
const videoPath = formatStringWithVars(info.video_path, {
|
| 1313 |
+
video_key: cam,
|
| 1314 |
+
episode_chunk: chunk.toString().padStart(3, "0"),
|
| 1315 |
+
episode_index: i.toString().padStart(6, "0"),
|
| 1316 |
+
});
|
| 1317 |
+
framesByCamera[cam].push({
|
| 1318 |
+
episodeIndex: i,
|
| 1319 |
+
videoUrl: buildVersionedUrl(repoId, version, videoPath),
|
| 1320 |
+
firstFrameTime: 0,
|
| 1321 |
+
lastFrameTime: null,
|
| 1322 |
+
});
|
| 1323 |
+
}
|
| 1324 |
+
}
|
| 1325 |
+
return { cameras, framesByCamera };
|
| 1326 |
+
}
|
| 1327 |
+
|
| 1328 |
+
// ─── Cross-episode action variance ──────────────────────────────
|
| 1329 |
+
|
| 1330 |
+
export type LowMovementEpisode = {
|
| 1331 |
+
episodeIndex: number;
|
| 1332 |
+
totalMovement: number;
|
| 1333 |
+
};
|
| 1334 |
+
|
| 1335 |
+
export type AggVelocityStat = {
|
| 1336 |
+
name: string;
|
| 1337 |
+
std: number; // normalized by motor range
|
| 1338 |
+
maxAbs: number; // normalized by motor range
|
| 1339 |
+
bins: number[];
|
| 1340 |
+
lo: number; // normalized by motor range
|
| 1341 |
+
hi: number; // normalized by motor range
|
| 1342 |
+
motorRange: number;
|
| 1343 |
+
inactive?: boolean; // true if p95(|Δa|) < 1% of motor range
|
| 1344 |
+
discrete?: boolean; // true if motor has very few unique values (e.g. open/close gripper)
|
| 1345 |
+
};
|
| 1346 |
+
|
| 1347 |
+
export type AggAutocorrelation = {
|
| 1348 |
+
chartData: Record<string, number>[];
|
| 1349 |
+
suggestedChunk: number | null;
|
| 1350 |
+
shortKeys: string[];
|
| 1351 |
+
};
|
| 1352 |
+
|
| 1353 |
+
export type SpeedDistEntry = {
|
| 1354 |
+
episodeIndex: number;
|
| 1355 |
+
speed: number;
|
| 1356 |
+
};
|
| 1357 |
+
|
| 1358 |
+
export type AggAlignment = {
|
| 1359 |
+
ccData: { lag: number; max: number; mean: number; min: number }[];
|
| 1360 |
+
meanPeakLag: number;
|
| 1361 |
+
meanPeakCorr: number;
|
| 1362 |
+
maxPeakLag: number;
|
| 1363 |
+
maxPeakCorr: number;
|
| 1364 |
+
minPeakLag: number;
|
| 1365 |
+
minPeakCorr: number;
|
| 1366 |
+
lagRangeMin: number;
|
| 1367 |
+
lagRangeMax: number;
|
| 1368 |
+
numPairs: number;
|
| 1369 |
+
};
|
| 1370 |
+
|
| 1371 |
+
export type JerkyEpisode = {
|
| 1372 |
+
episodeIndex: number;
|
| 1373 |
+
meanAbsDelta: number;
|
| 1374 |
+
};
|
| 1375 |
+
|
| 1376 |
+
export type CrossEpisodeVarianceData = {
|
| 1377 |
+
actionNames: string[];
|
| 1378 |
+
timeBins: number[];
|
| 1379 |
+
variance: number[][];
|
| 1380 |
+
numEpisodes: number;
|
| 1381 |
+
lowMovementEpisodes: LowMovementEpisode[];
|
| 1382 |
+
aggVelocity: AggVelocityStat[];
|
| 1383 |
+
aggAutocorrelation: AggAutocorrelation | null;
|
| 1384 |
+
speedDistribution: SpeedDistEntry[];
|
| 1385 |
+
jerkyEpisodes: JerkyEpisode[];
|
| 1386 |
+
aggAlignment: AggAlignment | null;
|
| 1387 |
+
};
|
| 1388 |
+
|
| 1389 |
+
export async function loadCrossEpisodeActionVariance(
|
| 1390 |
+
repoId: string,
|
| 1391 |
+
version: string,
|
| 1392 |
+
info: DatasetMetadata,
|
| 1393 |
+
fps: number,
|
| 1394 |
+
maxEpisodes = 500,
|
| 1395 |
+
numTimeBins = 50,
|
| 1396 |
+
): Promise<CrossEpisodeVarianceData | null> {
|
| 1397 |
+
const actionEntry = Object.entries(info.features).find(
|
| 1398 |
+
([key, f]) => key === "action" && f.shape.length === 1,
|
| 1399 |
+
);
|
| 1400 |
+
if (!actionEntry) {
|
| 1401 |
+
console.warn(
|
| 1402 |
+
"[cross-ep] No action feature found. Available features:",
|
| 1403 |
+
Object.entries(info.features)
|
| 1404 |
+
.map(([k, f]) => `${k}(${f.dtype}, shape=${JSON.stringify(f.shape)})`)
|
| 1405 |
+
.join(", "),
|
| 1406 |
+
);
|
| 1407 |
+
return null;
|
| 1408 |
+
}
|
| 1409 |
+
|
| 1410 |
+
const [actionKey, actionMeta] = actionEntry;
|
| 1411 |
+
const actionDim = actionMeta.shape[0];
|
| 1412 |
+
|
| 1413 |
+
let names: unknown = actionMeta.names;
|
| 1414 |
+
while (typeof names === "object" && names !== null && !Array.isArray(names)) {
|
| 1415 |
+
names = Object.values(names)[0];
|
| 1416 |
+
}
|
| 1417 |
+
const actionNames = Array.isArray(names)
|
| 1418 |
+
? (names as string[]).map((n) => `${actionKey}${SERIES_NAME_DELIMITER}${n}`)
|
| 1419 |
+
: Array.from(
|
| 1420 |
+
{ length: actionDim },
|
| 1421 |
+
(_, i) => `${actionKey}${SERIES_NAME_DELIMITER}${i}`,
|
| 1422 |
+
);
|
| 1423 |
+
|
| 1424 |
+
// State feature for alignment computation
|
| 1425 |
+
const stateEntry = Object.entries(info.features).find(
|
| 1426 |
+
([key, f]) => key === "observation.state" && f.shape.length === 1,
|
| 1427 |
+
);
|
| 1428 |
+
const stateKey = stateEntry?.[0] ?? null;
|
| 1429 |
+
const stateDim = stateEntry?.[1].shape[0] ?? 0;
|
| 1430 |
+
|
| 1431 |
+
// Collect episode metadata
|
| 1432 |
+
type EpMeta = {
|
| 1433 |
+
index: number;
|
| 1434 |
+
chunkIdx: number;
|
| 1435 |
+
fileIdx: number;
|
| 1436 |
+
from: number;
|
| 1437 |
+
to: number;
|
| 1438 |
+
};
|
| 1439 |
+
const allEps: EpMeta[] = [];
|
| 1440 |
+
|
| 1441 |
+
if (version === "v3.0") {
|
| 1442 |
+
let fileIndex = 0;
|
| 1443 |
+
while (true) {
|
| 1444 |
+
const path = `meta/episodes/chunk-000/file-${fileIndex.toString().padStart(3, "0")}.parquet`;
|
| 1445 |
+
try {
|
| 1446 |
+
const buf = await fetchParquetFile(
|
| 1447 |
+
buildVersionedUrl(repoId, version, path),
|
| 1448 |
+
);
|
| 1449 |
+
const rows = await readParquetAsObjects(buf, []);
|
| 1450 |
+
if (rows.length === 0 && fileIndex > 0) break;
|
| 1451 |
+
for (const row of rows) {
|
| 1452 |
+
const parsed = parseEpisodeRowSimple(row);
|
| 1453 |
+
allEps.push({
|
| 1454 |
+
index: parsed.episode_index,
|
| 1455 |
+
chunkIdx: parsed.data_chunk_index,
|
| 1456 |
+
fileIdx: parsed.data_file_index,
|
| 1457 |
+
from: parsed.dataset_from_index,
|
| 1458 |
+
to: parsed.dataset_to_index,
|
| 1459 |
+
});
|
| 1460 |
+
}
|
| 1461 |
+
fileIndex++;
|
| 1462 |
+
} catch {
|
| 1463 |
+
break;
|
| 1464 |
+
}
|
| 1465 |
+
}
|
| 1466 |
+
} else {
|
| 1467 |
+
for (let i = 0; i < info.total_episodes; i++) {
|
| 1468 |
+
allEps.push({ index: i, chunkIdx: 0, fileIdx: 0, from: 0, to: 0 });
|
| 1469 |
+
}
|
| 1470 |
+
}
|
| 1471 |
+
|
| 1472 |
+
if (allEps.length < 2) {
|
| 1473 |
+
console.warn(
|
| 1474 |
+
`[cross-ep] Only ${allEps.length} episode(s) found in metadata, need ≥2`,
|
| 1475 |
+
);
|
| 1476 |
+
return null;
|
| 1477 |
+
}
|
| 1478 |
+
console.log(
|
| 1479 |
+
`[cross-ep] Found ${allEps.length} episodes in metadata, sampling up to ${maxEpisodes}`,
|
| 1480 |
+
);
|
| 1481 |
+
|
| 1482 |
+
// Sample episodes evenly
|
| 1483 |
+
const sampled =
|
| 1484 |
+
allEps.length <= maxEpisodes
|
| 1485 |
+
? allEps
|
| 1486 |
+
: Array.from(
|
| 1487 |
+
{ length: maxEpisodes },
|
| 1488 |
+
(_, i) =>
|
| 1489 |
+
allEps[Math.round((i * (allEps.length - 1)) / (maxEpisodes - 1))],
|
| 1490 |
+
);
|
| 1491 |
+
|
| 1492 |
+
// Load action (and state) data per episode
|
| 1493 |
+
const episodeActions: { index: number; actions: number[][] }[] = [];
|
| 1494 |
+
const episodeStates: (number[][] | null)[] = [];
|
| 1495 |
+
|
| 1496 |
+
if (version === "v3.0") {
|
| 1497 |
+
const byFile = new Map<string, EpMeta[]>();
|
| 1498 |
+
for (const ep of sampled) {
|
| 1499 |
+
const key = `${ep.chunkIdx}-${ep.fileIdx}`;
|
| 1500 |
+
if (!byFile.has(key)) byFile.set(key, []);
|
| 1501 |
+
byFile.get(key)!.push(ep);
|
| 1502 |
+
}
|
| 1503 |
+
|
| 1504 |
+
for (const [, eps] of byFile) {
|
| 1505 |
+
const ep0 = eps[0];
|
| 1506 |
+
const dataPath = `data/chunk-${ep0.chunkIdx.toString().padStart(3, "0")}/file-${ep0.fileIdx.toString().padStart(3, "0")}.parquet`;
|
| 1507 |
+
try {
|
| 1508 |
+
const buf = await fetchParquetFile(
|
| 1509 |
+
buildVersionedUrl(repoId, version, dataPath),
|
| 1510 |
+
);
|
| 1511 |
+
const rows = await readParquetAsObjects(buf, []);
|
| 1512 |
+
const fileStart =
|
| 1513 |
+
rows.length > 0 && rows[0].index !== undefined
|
| 1514 |
+
? Number(rows[0].index)
|
| 1515 |
+
: 0;
|
| 1516 |
+
|
| 1517 |
+
for (const ep of eps) {
|
| 1518 |
+
const localFrom = Math.max(0, ep.from - fileStart);
|
| 1519 |
+
const localTo = Math.min(rows.length, ep.to - fileStart);
|
| 1520 |
+
const actions: number[][] = [];
|
| 1521 |
+
const states: number[][] = [];
|
| 1522 |
+
for (let r = localFrom; r < localTo; r++) {
|
| 1523 |
+
const raw = rows[r]?.[actionKey];
|
| 1524 |
+
if (Array.isArray(raw)) actions.push(raw.map(Number));
|
| 1525 |
+
if (stateKey) {
|
| 1526 |
+
const sRaw = rows[r]?.[stateKey];
|
| 1527 |
+
if (Array.isArray(sRaw)) states.push(sRaw.map(Number));
|
| 1528 |
+
}
|
| 1529 |
+
}
|
| 1530 |
+
if (actions.length > 0) {
|
| 1531 |
+
episodeActions.push({ index: ep.index, actions });
|
| 1532 |
+
episodeStates.push(
|
| 1533 |
+
stateKey && states.length === actions.length ? states : null,
|
| 1534 |
+
);
|
| 1535 |
+
}
|
| 1536 |
+
}
|
| 1537 |
+
} catch {
|
| 1538 |
+
/* skip file */
|
| 1539 |
+
}
|
| 1540 |
+
}
|
| 1541 |
+
} else {
|
| 1542 |
+
const chunkSize = info.chunks_size || 1000;
|
| 1543 |
+
for (const ep of sampled) {
|
| 1544 |
+
const chunk = Math.floor(ep.index / chunkSize);
|
| 1545 |
+
const dataPath = formatStringWithVars(info.data_path, {
|
| 1546 |
+
episode_chunk: chunk.toString().padStart(3, "0"),
|
| 1547 |
+
episode_index: ep.index.toString().padStart(6, "0"),
|
| 1548 |
+
});
|
| 1549 |
+
try {
|
| 1550 |
+
const buf = await fetchParquetFile(
|
| 1551 |
+
buildVersionedUrl(repoId, version, dataPath),
|
| 1552 |
+
);
|
| 1553 |
+
const rows = await readParquetAsObjects(buf, []);
|
| 1554 |
+
const actions: number[][] = [];
|
| 1555 |
+
const states: number[][] = [];
|
| 1556 |
+
for (const row of rows) {
|
| 1557 |
+
const raw = row[actionKey];
|
| 1558 |
+
if (Array.isArray(raw)) {
|
| 1559 |
+
actions.push(raw.map(Number));
|
| 1560 |
+
} else {
|
| 1561 |
+
const vec: number[] = [];
|
| 1562 |
+
for (let d = 0; d < actionDim; d++) {
|
| 1563 |
+
const v = row[`${actionKey}.${d}`] ?? row[d];
|
| 1564 |
+
vec.push(typeof v === "number" ? v : Number(v) || 0);
|
| 1565 |
+
}
|
| 1566 |
+
actions.push(vec);
|
| 1567 |
+
}
|
| 1568 |
+
if (stateKey) {
|
| 1569 |
+
const sRaw = row[stateKey];
|
| 1570 |
+
if (Array.isArray(sRaw)) states.push(sRaw.map(Number));
|
| 1571 |
+
}
|
| 1572 |
+
}
|
| 1573 |
+
if (actions.length > 0) {
|
| 1574 |
+
episodeActions.push({ index: ep.index, actions });
|
| 1575 |
+
episodeStates.push(
|
| 1576 |
+
stateKey && states.length === actions.length ? states : null,
|
| 1577 |
+
);
|
| 1578 |
+
}
|
| 1579 |
+
} catch {
|
| 1580 |
+
/* skip */
|
| 1581 |
+
}
|
| 1582 |
+
}
|
| 1583 |
+
}
|
| 1584 |
+
|
| 1585 |
+
if (episodeActions.length < 2) {
|
| 1586 |
+
console.warn(
|
| 1587 |
+
`[cross-ep] Only ${episodeActions.length} episode(s) had loadable action data out of ${sampled.length} sampled`,
|
| 1588 |
+
);
|
| 1589 |
+
return null;
|
| 1590 |
+
}
|
| 1591 |
+
console.log(
|
| 1592 |
+
`[cross-ep] Loaded action data for ${episodeActions.length}/${sampled.length} episodes`,
|
| 1593 |
+
);
|
| 1594 |
+
|
| 1595 |
+
// Resample each episode to numTimeBins and compute variance
|
| 1596 |
+
const timeBins = Array.from(
|
| 1597 |
+
{ length: numTimeBins },
|
| 1598 |
+
(_, i) => i / (numTimeBins - 1),
|
| 1599 |
+
);
|
| 1600 |
+
const sums = Array.from(
|
| 1601 |
+
{ length: numTimeBins },
|
| 1602 |
+
() => new Float64Array(actionDim),
|
| 1603 |
+
);
|
| 1604 |
+
const sumsSq = Array.from(
|
| 1605 |
+
{ length: numTimeBins },
|
| 1606 |
+
() => new Float64Array(actionDim),
|
| 1607 |
+
);
|
| 1608 |
+
const counts = new Uint32Array(numTimeBins);
|
| 1609 |
+
|
| 1610 |
+
for (const { actions: epActions } of episodeActions) {
|
| 1611 |
+
const T = epActions.length;
|
| 1612 |
+
for (let b = 0; b < numTimeBins; b++) {
|
| 1613 |
+
const srcIdx = Math.min(Math.round(timeBins[b] * (T - 1)), T - 1);
|
| 1614 |
+
const row = epActions[srcIdx];
|
| 1615 |
+
for (let d = 0; d < actionDim; d++) {
|
| 1616 |
+
const v = row[d] ?? 0;
|
| 1617 |
+
sums[b][d] += v;
|
| 1618 |
+
sumsSq[b][d] += v * v;
|
| 1619 |
+
}
|
| 1620 |
+
counts[b]++;
|
| 1621 |
+
}
|
| 1622 |
+
}
|
| 1623 |
+
|
| 1624 |
+
const variance: number[][] = [];
|
| 1625 |
+
for (let b = 0; b < numTimeBins; b++) {
|
| 1626 |
+
const row: number[] = [];
|
| 1627 |
+
const n = counts[b];
|
| 1628 |
+
for (let d = 0; d < actionDim; d++) {
|
| 1629 |
+
if (n < 2) {
|
| 1630 |
+
row.push(0);
|
| 1631 |
+
continue;
|
| 1632 |
+
}
|
| 1633 |
+
const mean = sums[b][d] / n;
|
| 1634 |
+
row.push(sumsSq[b][d] / n - mean * mean);
|
| 1635 |
+
}
|
| 1636 |
+
variance.push(row);
|
| 1637 |
+
}
|
| 1638 |
+
|
| 1639 |
+
// Per-episode average movement per frame: mean L2 norm of frame-to-frame action deltas
|
| 1640 |
+
const movementScores: LowMovementEpisode[] = episodeActions.map(
|
| 1641 |
+
({ index, actions: ep }) => {
|
| 1642 |
+
if (ep.length < 2) return { episodeIndex: index, totalMovement: 0 };
|
| 1643 |
+
let total = 0;
|
| 1644 |
+
for (let t = 1; t < ep.length; t++) {
|
| 1645 |
+
let sumSq = 0;
|
| 1646 |
+
for (let d = 0; d < actionDim; d++) {
|
| 1647 |
+
const delta = (ep[t][d] ?? 0) - (ep[t - 1][d] ?? 0);
|
| 1648 |
+
sumSq += delta * delta;
|
| 1649 |
+
}
|
| 1650 |
+
total += Math.sqrt(sumSq);
|
| 1651 |
+
}
|
| 1652 |
+
const avgPerFrame = total / (ep.length - 1);
|
| 1653 |
+
return {
|
| 1654 |
+
episodeIndex: index,
|
| 1655 |
+
totalMovement: Math.round(avgPerFrame * 10000) / 10000,
|
| 1656 |
+
};
|
| 1657 |
+
},
|
| 1658 |
+
);
|
| 1659 |
+
|
| 1660 |
+
movementScores.sort((a, b) => a.totalMovement - b.totalMovement);
|
| 1661 |
+
const lowMovementEpisodes = movementScores.slice(0, 10);
|
| 1662 |
+
|
| 1663 |
+
// Precompute per-dimension normalization: motor range (max − min) and unique value count
|
| 1664 |
+
const motorRanges: number[] = new Array(actionDim);
|
| 1665 |
+
const motorUniqueCount: number[] = new Array(actionDim);
|
| 1666 |
+
const DISCRETE_THRESHOLD = 4; // ≤ this many unique values → discrete motor
|
| 1667 |
+
for (let d = 0; d < actionDim; d++) {
|
| 1668 |
+
let lo = Infinity,
|
| 1669 |
+
hi = -Infinity;
|
| 1670 |
+
const uniqueVals = new Set<number>();
|
| 1671 |
+
for (const { actions: ep } of episodeActions) {
|
| 1672 |
+
for (let t = 0; t < ep.length; t++) {
|
| 1673 |
+
const v = ep[t][d] ?? 0;
|
| 1674 |
+
if (v < lo) lo = v;
|
| 1675 |
+
if (v > hi) hi = v;
|
| 1676 |
+
if (uniqueVals.size <= DISCRETE_THRESHOLD) uniqueVals.add(v);
|
| 1677 |
+
}
|
| 1678 |
+
}
|
| 1679 |
+
motorRanges[d] = hi - lo || 1;
|
| 1680 |
+
motorUniqueCount[d] = uniqueVals.size;
|
| 1681 |
+
}
|
| 1682 |
+
|
| 1683 |
+
// Per-episode, per-dimension activity: p95(|Δa|) >= 1% of motor range
|
| 1684 |
+
const ACTIVITY_THRESHOLD = 0.001; // 0.1% of motor range
|
| 1685 |
+
// activeMap[episodeIdx][dimIdx] = true if motor d is active in that episode
|
| 1686 |
+
const activeMap: boolean[][] = episodeActions.map(({ actions: ep }) => {
|
| 1687 |
+
const flags: boolean[] = new Array(actionDim);
|
| 1688 |
+
for (let d = 0; d < actionDim; d++) {
|
| 1689 |
+
if (ep.length < 2) {
|
| 1690 |
+
flags[d] = false;
|
| 1691 |
+
continue;
|
| 1692 |
+
}
|
| 1693 |
+
const absDeltas: number[] = [];
|
| 1694 |
+
for (let t = 1; t < ep.length; t++) {
|
| 1695 |
+
absDeltas.push(Math.abs((ep[t][d] ?? 0) - (ep[t - 1][d] ?? 0)));
|
| 1696 |
+
}
|
| 1697 |
+
absDeltas.sort((a, b) => a - b);
|
| 1698 |
+
const p95 = absDeltas[Math.floor(absDeltas.length * 0.95)];
|
| 1699 |
+
flags[d] = p95 >= motorRanges[d] * ACTIVITY_THRESHOLD;
|
| 1700 |
+
}
|
| 1701 |
+
return flags;
|
| 1702 |
+
});
|
| 1703 |
+
// A motor is globally inactive only if inactive in all episodes
|
| 1704 |
+
const globallyActive: boolean[] = new Array(actionDim);
|
| 1705 |
+
for (let d = 0; d < actionDim; d++) {
|
| 1706 |
+
globallyActive[d] = activeMap.some((flags) => flags[d]);
|
| 1707 |
+
}
|
| 1708 |
+
|
| 1709 |
+
// Aggregated velocity stats: pool deltas from all episodes, normalized by motor range
|
| 1710 |
+
const shortName = (k: string) => {
|
| 1711 |
+
const p = k.split(SERIES_NAME_DELIMITER);
|
| 1712 |
+
return p.length > 1 ? p[p.length - 1] : k;
|
| 1713 |
+
};
|
| 1714 |
+
|
| 1715 |
+
const aggVelocity: AggVelocityStat[] = (() => {
|
| 1716 |
+
const binCount = 30;
|
| 1717 |
+
const results: AggVelocityStat[] = [];
|
| 1718 |
+
for (let d = 0; d < actionDim; d++) {
|
| 1719 |
+
const motorRange = motorRanges[d];
|
| 1720 |
+
const inactive = !globallyActive[d];
|
| 1721 |
+
// Collect all deltas (unfiltered) for histogram display
|
| 1722 |
+
const allDeltas: number[] = [];
|
| 1723 |
+
// Collect only deltas from active episodes for stats
|
| 1724 |
+
const activeDeltas: number[] = [];
|
| 1725 |
+
for (let ei = 0; ei < episodeActions.length; ei++) {
|
| 1726 |
+
const ep = episodeActions[ei].actions;
|
| 1727 |
+
for (let t = 1; t < ep.length; t++) {
|
| 1728 |
+
const delta = (ep[t][d] ?? 0) - (ep[t - 1][d] ?? 0);
|
| 1729 |
+
allDeltas.push(delta);
|
| 1730 |
+
if (activeMap[ei][d]) activeDeltas.push(delta);
|
| 1731 |
+
}
|
| 1732 |
+
}
|
| 1733 |
+
const deltas = activeDeltas.length > 0 ? activeDeltas : allDeltas;
|
| 1734 |
+
const nUnique = motorUniqueCount[d];
|
| 1735 |
+
const discrete = nUnique <= DISCRETE_THRESHOLD;
|
| 1736 |
+
if (deltas.length === 0) {
|
| 1737 |
+
results.push({
|
| 1738 |
+
name: shortName(actionNames[d]),
|
| 1739 |
+
std: 0,
|
| 1740 |
+
maxAbs: 0,
|
| 1741 |
+
bins: new Array(binCount).fill(0),
|
| 1742 |
+
lo: 0,
|
| 1743 |
+
hi: 0,
|
| 1744 |
+
motorRange,
|
| 1745 |
+
inactive,
|
| 1746 |
+
discrete,
|
| 1747 |
+
});
|
| 1748 |
+
continue;
|
| 1749 |
+
}
|
| 1750 |
+
let sum = 0,
|
| 1751 |
+
maxAbsRaw = 0,
|
| 1752 |
+
loRaw = Infinity,
|
| 1753 |
+
hiRaw = -Infinity;
|
| 1754 |
+
for (const v of deltas) {
|
| 1755 |
+
sum += v;
|
| 1756 |
+
const a = Math.abs(v);
|
| 1757 |
+
if (a > maxAbsRaw) maxAbsRaw = a;
|
| 1758 |
+
if (v < loRaw) loRaw = v;
|
| 1759 |
+
if (v > hiRaw) hiRaw = v;
|
| 1760 |
+
}
|
| 1761 |
+
const mean = sum / deltas.length;
|
| 1762 |
+
let varSum = 0;
|
| 1763 |
+
for (const v of deltas) varSum += (v - mean) ** 2;
|
| 1764 |
+
const rawStd = Math.sqrt(varSum / deltas.length);
|
| 1765 |
+
const std = rawStd / motorRange;
|
| 1766 |
+
const maxAbs = maxAbsRaw / motorRange;
|
| 1767 |
+
const lo = loRaw / motorRange;
|
| 1768 |
+
const hi = hiRaw / motorRange;
|
| 1769 |
+
const range = hi - lo || 1;
|
| 1770 |
+
const binW = range / binCount;
|
| 1771 |
+
const bins = new Array(binCount).fill(0);
|
| 1772 |
+
for (const v of deltas) {
|
| 1773 |
+
const normV = v / motorRange;
|
| 1774 |
+
let b = Math.floor((normV - lo) / binW);
|
| 1775 |
+
if (b >= binCount) b = binCount - 1;
|
| 1776 |
+
bins[b]++;
|
| 1777 |
+
}
|
| 1778 |
+
results.push({
|
| 1779 |
+
name: shortName(actionNames[d]),
|
| 1780 |
+
std,
|
| 1781 |
+
maxAbs,
|
| 1782 |
+
bins,
|
| 1783 |
+
lo,
|
| 1784 |
+
hi,
|
| 1785 |
+
motorRange,
|
| 1786 |
+
inactive,
|
| 1787 |
+
discrete,
|
| 1788 |
+
});
|
| 1789 |
+
}
|
| 1790 |
+
return results;
|
| 1791 |
+
})();
|
| 1792 |
+
|
| 1793 |
+
// Aggregated autocorrelation: average per-episode ACFs
|
| 1794 |
+
const aggAutocorrelation: AggAutocorrelation | null = (() => {
|
| 1795 |
+
const maxLag = Math.min(
|
| 1796 |
+
100,
|
| 1797 |
+
Math.floor(
|
| 1798 |
+
episodeActions.reduce(
|
| 1799 |
+
(min, e) => Math.min(min, e.actions.length),
|
| 1800 |
+
Infinity,
|
| 1801 |
+
) / 2,
|
| 1802 |
+
),
|
| 1803 |
+
);
|
| 1804 |
+
if (maxLag < 2) return null;
|
| 1805 |
+
|
| 1806 |
+
const avgAcf: number[][] = Array.from({ length: actionDim }, () =>
|
| 1807 |
+
new Array(maxLag).fill(0),
|
| 1808 |
+
);
|
| 1809 |
+
let epCount = 0;
|
| 1810 |
+
|
| 1811 |
+
for (const { actions: ep } of episodeActions) {
|
| 1812 |
+
if (ep.length < maxLag * 2) continue;
|
| 1813 |
+
epCount++;
|
| 1814 |
+
for (let d = 0; d < actionDim; d++) {
|
| 1815 |
+
const vals = ep.map((row) => row[d] ?? 0);
|
| 1816 |
+
const n = vals.length;
|
| 1817 |
+
const m = vals.reduce((a, b) => a + b, 0) / n;
|
| 1818 |
+
const centered = vals.map((v) => v - m);
|
| 1819 |
+
const vari = centered.reduce((a, v) => a + v * v, 0);
|
| 1820 |
+
if (vari === 0) continue;
|
| 1821 |
+
for (let lag = 1; lag <= maxLag; lag++) {
|
| 1822 |
+
let s = 0;
|
| 1823 |
+
for (let t = 0; t < n - lag; t++)
|
| 1824 |
+
s += centered[t] * centered[t + lag];
|
| 1825 |
+
avgAcf[d][lag - 1] += s / vari;
|
| 1826 |
+
}
|
| 1827 |
+
}
|
| 1828 |
+
}
|
| 1829 |
+
|
| 1830 |
+
if (epCount === 0) return null;
|
| 1831 |
+
for (let d = 0; d < actionDim; d++)
|
| 1832 |
+
for (let l = 0; l < maxLag; l++) avgAcf[d][l] /= epCount;
|
| 1833 |
+
|
| 1834 |
+
const shortKeys = actionNames.map(shortName);
|
| 1835 |
+
const chartData = Array.from({ length: maxLag }, (_, lag) => {
|
| 1836 |
+
const row: Record<string, number> = {
|
| 1837 |
+
lag: lag + 1,
|
| 1838 |
+
time: (lag + 1) / fps,
|
| 1839 |
+
};
|
| 1840 |
+
shortKeys.forEach((k, d) => {
|
| 1841 |
+
row[k] = avgAcf[d][lag];
|
| 1842 |
+
});
|
| 1843 |
+
return row;
|
| 1844 |
+
});
|
| 1845 |
+
|
| 1846 |
+
// Suggested chunk: median lag where ACF drops below 0.5
|
| 1847 |
+
const lags = avgAcf
|
| 1848 |
+
.map((acf) => {
|
| 1849 |
+
const i = acf.findIndex((v) => v < 0.5);
|
| 1850 |
+
return i >= 0 ? i + 1 : null;
|
| 1851 |
+
})
|
| 1852 |
+
.filter(Boolean) as number[];
|
| 1853 |
+
const suggestedChunk =
|
| 1854 |
+
lags.length > 0
|
| 1855 |
+
? lags.sort((a, b) => a - b)[Math.floor(lags.length / 2)]
|
| 1856 |
+
: null;
|
| 1857 |
+
|
| 1858 |
+
return { chartData, suggestedChunk, shortKeys };
|
| 1859 |
+
})();
|
| 1860 |
+
|
| 1861 |
+
// Per-episode jerkiness: mean |Δa| across dimensions active in that episode, normalized by motor range
|
| 1862 |
+
const jerkyEpisodes: JerkyEpisode[] = episodeActions
|
| 1863 |
+
.map(({ index, actions: ep }, ei) => {
|
| 1864 |
+
let sum = 0,
|
| 1865 |
+
count = 0;
|
| 1866 |
+
for (let t = 1; t < ep.length; t++) {
|
| 1867 |
+
for (let d = 0; d < actionDim; d++) {
|
| 1868 |
+
if (!activeMap[ei][d]) continue; // skip motors inactive in this episode
|
| 1869 |
+
sum +=
|
| 1870 |
+
Math.abs((ep[t][d] ?? 0) - (ep[t - 1][d] ?? 0)) / motorRanges[d];
|
| 1871 |
+
count++;
|
| 1872 |
+
}
|
| 1873 |
+
}
|
| 1874 |
+
return { episodeIndex: index, meanAbsDelta: count > 0 ? sum / count : 0 };
|
| 1875 |
+
})
|
| 1876 |
+
.sort((a, b) => b.meanAbsDelta - a.meanAbsDelta);
|
| 1877 |
+
|
| 1878 |
+
// Speed distribution: all episode movement scores (not just lowest 10)
|
| 1879 |
+
const speedDistribution: SpeedDistEntry[] = movementScores.map((s) => ({
|
| 1880 |
+
episodeIndex: s.episodeIndex,
|
| 1881 |
+
speed: s.totalMovement,
|
| 1882 |
+
}));
|
| 1883 |
+
|
| 1884 |
+
// Aggregated state-action alignment across episodes
|
| 1885 |
+
const aggAlignment: AggAlignment | null = (() => {
|
| 1886 |
+
if (!stateKey || stateDim === 0) return null;
|
| 1887 |
+
|
| 1888 |
+
let sNms: unknown = stateEntry![1].names;
|
| 1889 |
+
while (typeof sNms === "object" && sNms !== null && !Array.isArray(sNms))
|
| 1890 |
+
sNms = Object.values(sNms)[0];
|
| 1891 |
+
const stateNames = Array.isArray(sNms)
|
| 1892 |
+
? (sNms as string[])
|
| 1893 |
+
: Array.from({ length: stateDim }, (_, i) => `${i}`);
|
| 1894 |
+
const actionSuffixes = actionNames.map((n) => {
|
| 1895 |
+
const p = n.split(SERIES_NAME_DELIMITER);
|
| 1896 |
+
return p[p.length - 1];
|
| 1897 |
+
});
|
| 1898 |
+
|
| 1899 |
+
// Match pairs by suffix, fall back to index
|
| 1900 |
+
const pairs: [number, number][] = [];
|
| 1901 |
+
for (let ai = 0; ai < actionDim; ai++) {
|
| 1902 |
+
const si = stateNames.findIndex((s) => s === actionSuffixes[ai]);
|
| 1903 |
+
if (si >= 0) pairs.push([ai, si]);
|
| 1904 |
+
}
|
| 1905 |
+
if (pairs.length === 0) {
|
| 1906 |
+
const count = Math.min(actionDim, stateDim);
|
| 1907 |
+
for (let i = 0; i < count; i++) pairs.push([i, i]);
|
| 1908 |
+
}
|
| 1909 |
+
if (pairs.length === 0) return null;
|
| 1910 |
+
|
| 1911 |
+
const maxLag = 30;
|
| 1912 |
+
const numLags = 2 * maxLag + 1;
|
| 1913 |
+
const corrSums = pairs.map(() => new Float64Array(numLags));
|
| 1914 |
+
const corrCounts = pairs.map(() => new Uint32Array(numLags));
|
| 1915 |
+
|
| 1916 |
+
for (let ei = 0; ei < episodeActions.length; ei++) {
|
| 1917 |
+
const states = episodeStates[ei];
|
| 1918 |
+
if (!states) continue;
|
| 1919 |
+
const { actions } = episodeActions[ei];
|
| 1920 |
+
const n = Math.min(actions.length, states.length);
|
| 1921 |
+
if (n < 10) continue;
|
| 1922 |
+
|
| 1923 |
+
for (let pi = 0; pi < pairs.length; pi++) {
|
| 1924 |
+
const [ai, si] = pairs[pi];
|
| 1925 |
+
const aVals = actions.slice(0, n).map((r) => r[ai] ?? 0);
|
| 1926 |
+
const sDeltas = Array.from(
|
| 1927 |
+
{ length: n - 1 },
|
| 1928 |
+
(_, t) => (states[t + 1][si] ?? 0) - (states[t][si] ?? 0),
|
| 1929 |
+
);
|
| 1930 |
+
const effN = Math.min(aVals.length, sDeltas.length);
|
| 1931 |
+
const aM = aVals.slice(0, effN).reduce((a, b) => a + b, 0) / effN;
|
| 1932 |
+
const sM = sDeltas.slice(0, effN).reduce((a, b) => a + b, 0) / effN;
|
| 1933 |
+
|
| 1934 |
+
for (let li = 0; li < numLags; li++) {
|
| 1935 |
+
const lag = -maxLag + li;
|
| 1936 |
+
let sum = 0,
|
| 1937 |
+
aV = 0,
|
| 1938 |
+
sV = 0;
|
| 1939 |
+
for (let t = 0; t < effN; t++) {
|
| 1940 |
+
const sIdx = t + lag;
|
| 1941 |
+
if (sIdx < 0 || sIdx >= sDeltas.length) continue;
|
| 1942 |
+
const a = aVals[t] - aM,
|
| 1943 |
+
s = sDeltas[sIdx] - sM;
|
| 1944 |
+
sum += a * s;
|
| 1945 |
+
aV += a * a;
|
| 1946 |
+
sV += s * s;
|
| 1947 |
+
}
|
| 1948 |
+
const d = Math.sqrt(aV * sV);
|
| 1949 |
+
if (d > 0) {
|
| 1950 |
+
corrSums[pi][li] += sum / d;
|
| 1951 |
+
corrCounts[pi][li]++;
|
| 1952 |
+
}
|
| 1953 |
+
}
|
| 1954 |
+
}
|
| 1955 |
+
}
|
| 1956 |
+
|
| 1957 |
+
const avgCorrs = pairs.map((_, pi) =>
|
| 1958 |
+
Array.from({ length: numLags }, (_, li) =>
|
| 1959 |
+
corrCounts[pi][li] > 0 ? corrSums[pi][li] / corrCounts[pi][li] : 0,
|
| 1960 |
+
),
|
| 1961 |
+
);
|
| 1962 |
+
|
| 1963 |
+
const ccData = Array.from({ length: numLags }, (_, li) => {
|
| 1964 |
+
const lag = -maxLag + li;
|
| 1965 |
+
const vals = avgCorrs.map((pc) => pc[li]);
|
| 1966 |
+
return {
|
| 1967 |
+
lag,
|
| 1968 |
+
max: Math.max(...vals),
|
| 1969 |
+
mean: vals.reduce((a, b) => a + b, 0) / vals.length,
|
| 1970 |
+
min: Math.min(...vals),
|
| 1971 |
+
};
|
| 1972 |
+
});
|
| 1973 |
+
|
| 1974 |
+
let meanPeakLag = 0,
|
| 1975 |
+
meanPeakCorr = -Infinity;
|
| 1976 |
+
let maxPeakLag = 0,
|
| 1977 |
+
maxPeakCorr = -Infinity;
|
| 1978 |
+
let minPeakLag = 0,
|
| 1979 |
+
minPeakCorr = -Infinity;
|
| 1980 |
+
for (const row of ccData) {
|
| 1981 |
+
if (row.max > maxPeakCorr) {
|
| 1982 |
+
maxPeakCorr = row.max;
|
| 1983 |
+
maxPeakLag = row.lag;
|
| 1984 |
+
}
|
| 1985 |
+
if (row.mean > meanPeakCorr) {
|
| 1986 |
+
meanPeakCorr = row.mean;
|
| 1987 |
+
meanPeakLag = row.lag;
|
| 1988 |
+
}
|
| 1989 |
+
if (row.min > minPeakCorr) {
|
| 1990 |
+
minPeakCorr = row.min;
|
| 1991 |
+
minPeakLag = row.lag;
|
| 1992 |
+
}
|
| 1993 |
+
}
|
| 1994 |
+
|
| 1995 |
+
const perPairPeakLags = avgCorrs.map((pc) => {
|
| 1996 |
+
let best = -Infinity,
|
| 1997 |
+
bestLag = 0;
|
| 1998 |
+
for (let li = 0; li < pc.length; li++) {
|
| 1999 |
+
if (pc[li] > best) {
|
| 2000 |
+
best = pc[li];
|
| 2001 |
+
bestLag = -maxLag + li;
|
| 2002 |
+
}
|
| 2003 |
+
}
|
| 2004 |
+
return bestLag;
|
| 2005 |
+
});
|
| 2006 |
+
|
| 2007 |
+
return {
|
| 2008 |
+
ccData,
|
| 2009 |
+
meanPeakLag,
|
| 2010 |
+
meanPeakCorr,
|
| 2011 |
+
maxPeakLag,
|
| 2012 |
+
maxPeakCorr,
|
| 2013 |
+
minPeakLag,
|
| 2014 |
+
minPeakCorr,
|
| 2015 |
+
lagRangeMin: Math.min(...perPairPeakLags),
|
| 2016 |
+
lagRangeMax: Math.max(...perPairPeakLags),
|
| 2017 |
+
numPairs: pairs.length,
|
| 2018 |
+
};
|
| 2019 |
+
})();
|
| 2020 |
+
|
| 2021 |
+
return {
|
| 2022 |
+
actionNames,
|
| 2023 |
+
timeBins,
|
| 2024 |
+
variance,
|
| 2025 |
+
numEpisodes: episodeActions.length,
|
| 2026 |
+
lowMovementEpisodes,
|
| 2027 |
+
aggVelocity,
|
| 2028 |
+
aggAutocorrelation,
|
| 2029 |
+
speedDistribution,
|
| 2030 |
+
jerkyEpisodes,
|
| 2031 |
+
aggAlignment,
|
| 2032 |
+
};
|
| 2033 |
+
}
|
| 2034 |
+
|
| 2035 |
+
// Load only flatChartData for a specific episode (used by URDF viewer episode switching)
|
| 2036 |
+
export async function loadEpisodeFlatChartData(
|
| 2037 |
+
repoId: string,
|
| 2038 |
+
version: string,
|
| 2039 |
+
info: DatasetMetadata,
|
| 2040 |
+
episodeId: number,
|
| 2041 |
+
): Promise<Record<string, number>[]> {
|
| 2042 |
+
const episodeMetadata = await loadEpisodeMetadataV3Simple(
|
| 2043 |
+
repoId,
|
| 2044 |
+
version,
|
| 2045 |
+
episodeId,
|
| 2046 |
+
);
|
| 2047 |
+
const { flatChartData } = await loadEpisodeDataV3(
|
| 2048 |
+
repoId,
|
| 2049 |
+
version,
|
| 2050 |
+
info,
|
| 2051 |
+
episodeMetadata,
|
| 2052 |
+
);
|
| 2053 |
+
return flatChartData;
|
| 2054 |
+
}
|
| 2055 |
+
|
| 2056 |
// Safe wrapper for UI error display
|
| 2057 |
export async function getEpisodeDataSafe(
|
| 2058 |
org: string,
|
|
|
|
| 2062 |
try {
|
| 2063 |
const data = await getEpisodeData(org, dataset, episodeId);
|
| 2064 |
return { data };
|
| 2065 |
+
} catch (err: unknown) {
|
| 2066 |
+
const message = err instanceof Error ? err.message : String(err);
|
| 2067 |
+
return { error: message || "Unknown error" };
|
|
|
|
|
|
|
| 2068 |
}
|
| 2069 |
}
|
src/app/[org]/[dataset]/[episode]/page.tsx
CHANGED
|
@@ -27,7 +27,7 @@ export default async function EpisodePage({
|
|
| 27 |
const { data, error } = await getEpisodeDataSafe(org, dataset, episodeNumber);
|
| 28 |
return (
|
| 29 |
<Suspense fallback={null}>
|
| 30 |
-
<EpisodeViewer data={data} error={error} />
|
| 31 |
</Suspense>
|
| 32 |
);
|
| 33 |
}
|
|
|
|
| 27 |
const { data, error } = await getEpisodeDataSafe(org, dataset, episodeNumber);
|
| 28 |
return (
|
| 29 |
<Suspense fallback={null}>
|
| 30 |
+
<EpisodeViewer data={data} error={error} org={org} dataset={dataset} />
|
| 31 |
</Suspense>
|
| 32 |
);
|
| 33 |
}
|
src/app/explore/page.tsx
CHANGED
|
@@ -2,7 +2,7 @@ import React from "react";
|
|
| 2 |
import ExploreGrid from "./explore-grid";
|
| 3 |
import { fetchJson, formatStringWithVars } from "@/utils/parquetUtils";
|
| 4 |
import { getDatasetVersion, buildVersionedUrl } from "@/utils/versionUtils";
|
| 5 |
-
import type { DatasetMetadata } from "@/
|
| 6 |
|
| 7 |
export default async function ExplorePage({
|
| 8 |
searchParams,
|
|
@@ -10,7 +10,7 @@ export default async function ExplorePage({
|
|
| 10 |
searchParams: Promise<{ p?: string }>;
|
| 11 |
}) {
|
| 12 |
const params = await searchParams;
|
| 13 |
-
let datasets:
|
| 14 |
let currentPage = 1;
|
| 15 |
let totalPages = 1;
|
| 16 |
try {
|
|
@@ -40,7 +40,7 @@ export default async function ExplorePage({
|
|
| 40 |
// Fetch episode 0 data for each dataset
|
| 41 |
const datasetWithVideos = (
|
| 42 |
await Promise.all(
|
| 43 |
-
datasets.map(async (ds
|
| 44 |
try {
|
| 45 |
const [org, dataset] = ds.id.split("/");
|
| 46 |
const repoId = `${org}/${dataset}`;
|
|
|
|
| 2 |
import ExploreGrid from "./explore-grid";
|
| 3 |
import { fetchJson, formatStringWithVars } from "@/utils/parquetUtils";
|
| 4 |
import { getDatasetVersion, buildVersionedUrl } from "@/utils/versionUtils";
|
| 5 |
+
import type { DatasetMetadata } from "@/utils/parquetUtils";
|
| 6 |
|
| 7 |
export default async function ExplorePage({
|
| 8 |
searchParams,
|
|
|
|
| 10 |
searchParams: Promise<{ p?: string }>;
|
| 11 |
}) {
|
| 12 |
const params = await searchParams;
|
| 13 |
+
let datasets: { id: string }[] = [];
|
| 14 |
let currentPage = 1;
|
| 15 |
let totalPages = 1;
|
| 16 |
try {
|
|
|
|
| 40 |
// Fetch episode 0 data for each dataset
|
| 41 |
const datasetWithVideos = (
|
| 42 |
await Promise.all(
|
| 43 |
+
datasets.map(async (ds) => {
|
| 44 |
try {
|
| 45 |
const [org, dataset] = ds.id.split("/");
|
| 46 |
const repoId = `${org}/${dataset}`;
|