Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ Table of Contents
:caption: Resources
:glob:

resources/assets.md
resources/robot/index*
resources/task/index*
resources/roadmap.md
Expand Down
118 changes: 118 additions & 0 deletions docs/source/resources/assets.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@

# Data Assets

EmbodiChain provides a comprehensive set of pre-built data assets hosted on HuggingFace, covering robots, end-effectors, objects, scenes, materials, and more. Assets are automatically downloaded on first use, but you can also pre-download them using the built-in CLI tool.

## Data Root Directory

By default, assets are stored in `~/.cache/embodichain_data`. You can override this by setting the `EMBODICHAIN_DATA_ROOT` environment variable:

```bash
export EMBODICHAIN_DATA_ROOT=/mnt/shared/embodichain_data
```

Similarly, the dataset recording root (used by `LeRobotRecorder`) defaults to `~/.cache/embodichain_datasets` and can be overridden with:

```bash
export EMBODICHAIN_DATASET_ROOT=/mnt/shared/embodichain_datasets
```

## Download CLI

The `embodichain.data` module provides a command-line interface for managing assets.

### List Available Assets

```bash
# List all assets across every category
python -m embodichain.data list

# List assets in a specific category
python -m embodichain.data list --category robot
```

The output shows each asset name and whether it has already been downloaded (`✓`):

```text
[robot] (18 assets)
[✓] ABB
[ ] ARX5
[ ] Agile
[✓] Aubo
...

Data root: /home/user/.cache/embodichain_data
```

### Download Assets

```bash
# Download a single asset by name
python -m embodichain.data download --name CobotMagicArm

# Download all assets in a category
python -m embodichain.data download --category robot

# Download everything
python -m embodichain.data download --all
```

Downloaded files are saved to `<data_root>/download/` and automatically extracted to `<data_root>/extract/`. Non-zip assets (e.g. `.glb` files) are copied into the extract directory.

## Asset Categories

| Category | Description | Examples |
|-------------|------------------------------------------------|-------------------------------------------------|
| `robot` | Robot URDF models | CobotMagicArm, Franka, UniversalRobots, UnitreeH1 |
| `eef` | End-effector / gripper models | DH_PGC_140_50, Robotiq2F85, InspireHand |
| `obj` | Manipulable objects and furniture | ShopTableSimple, CoffeeCup, TableWare |
| `scene` | Full scene environments | SceneData, EmptyRoom |
| `materials` | Rendering materials, IBL, and backgrounds | SimResources, CocoBackground |
| `w1` | DexForce W1 humanoid robot and components | DexforceW1V021, DexforceW1ChassisV021 |
| `demo` | Demo scene data | ScoopIceNewEnv, MultiW1Data |

Comment on lines +64 to +73
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The markdown table under "Asset Categories" is written with double pipes (|| ...) which will render as an extra empty column in most markdown renderers. Use single-pipe table syntax (| Category | Description | Examples | etc.) so the table renders correctly in the docs site.

Copilot uses AI. Check for mistakes.
## Using Assets in Code

Use `get_data_path` to resolve asset paths in your configuration. It accepts a relative path in the format `<AssetClassName>/<subpath>`:

```python
from embodichain.data import get_data_path

# Resolves to the URDF file, downloading if necessary
urdf_path = get_data_path("CobotMagicArm/CobotMagicWithGripperV100.urdf")
```

`get_data_path` resolves paths in the following order:

1. **Absolute path** — returned as-is.
2. **Local data root** — if the file exists under `EMBODICHAIN_DATA_ROOT`, it is returned immediately without triggering a download.
3. **Data-class download** — falls back to the registered asset class, which downloads and extracts the asset from HuggingFace.

You can also instantiate asset classes directly:

```python
from embodichain.data.assets import CobotMagicArm

dataset = CobotMagicArm()
print(dataset.extract_dir) # Path to extracted files
```

## Adding Custom Assets

To add a new asset:

1. **Create a class** in the appropriate file under `embodichain/data/assets/` (e.g., `robot_assets.py` for a robot):

```python
class MyRobot(EmbodiChainDataset):
def __init__(self, data_root: str = None):
data_descriptor = o3d.data.DataDescriptor(
os.path.join(EMBODICHAIN_DOWNLOAD_PREFIX, robot_assets, "MyRobot.zip"),
"<md5_checksum>",
)
prefix = type(self).__name__
path = EMBODICHAIN_DEFAULT_DATA_ROOT if data_root is None else data_root
super().__init__(prefix, data_descriptor, path)
```

2. The class is automatically discovered by the download CLI and `get_data_path` — no additional registration is needed.
21 changes: 21 additions & 0 deletions embodichain/data/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# ----------------------------------------------------------------------------
# Copyright (c) 2021-2026 DexForce Technology Co., Ltd.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ----------------------------------------------------------------------------

"""Allow ``python -m embodichain.data`` to invoke the download CLI."""

from embodichain.data.download import main

main()
4 changes: 2 additions & 2 deletions embodichain/data/assets/demo_assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def __init__(self, data_root: str = None):
),
"e92734a9de0f64be33a11fbda0fbd3b6",
)
prefix = "ScoopIceNewEnv"
prefix = type(self).__name__
path = EMBODICHAIN_DEFAULT_DATA_ROOT if data_root is None else data_root

super().__init__(prefix, data_descriptor, path)
Expand All @@ -46,6 +46,6 @@ def __init__(self, data_root: str = None):
os.path.join(EMBODICHAIN_DOWNLOAD_PREFIX, demo_assets, "multi_w1_demo.zip"),
"984e8fa3aa05cb36a1fd973a475183ed",
)
prefix = "MultiW1Data"
prefix = type(self).__name__
path = EMBODICHAIN_DEFAULT_DATA_ROOT if data_root is None else data_root
super().__init__(prefix, data_descriptor, path)
20 changes: 10 additions & 10 deletions embodichain/data/assets/eef_assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def __init__(self, data_root: str = None):
os.path.join(EMBODICHAIN_DOWNLOAD_PREFIX, eef_assets, "DH_PGC_140_50.zip"),
"c2a642308a76e99b1b8b7cb3a11c5df3",
)
prefix = "DH_PGC_140_50"
prefix = type(self).__name__
path = EMBODICHAIN_DEFAULT_DATA_ROOT if data_root is None else data_root

super().__init__(prefix, data_descriptor, path)
Expand Down Expand Up @@ -79,7 +79,7 @@ def __init__(self, data_root: str = None):
os.path.join(EMBODICHAIN_DOWNLOAD_PREFIX, eef_assets, "DH_PGI_140_80.zip"),
"05a1a08b13c6250cc12affeeda3a08ba",
)
prefix = "DH_PGI_140_80"
prefix = type(self).__name__
path = EMBODICHAIN_DEFAULT_DATA_ROOT if data_root is None else data_root

super().__init__(prefix, data_descriptor, path)
Expand Down Expand Up @@ -111,7 +111,7 @@ def __init__(self, data_root: str = None):
),
"3a9ab5f32639e03afb38dc033b44bb62",
)
prefix = "DH_PGC_140_50_M"
prefix = type(self).__name__
path = EMBODICHAIN_DEFAULT_DATA_ROOT if data_root is None else data_root

super().__init__(prefix, data_descriptor, path)
Expand Down Expand Up @@ -141,7 +141,7 @@ def __init__(self, data_root: str = None):
os.path.join(EMBODICHAIN_DOWNLOAD_PREFIX, eef_assets, "DH_AG95.zip"),
"34b6f3c2f649697ea7f12814b6a50529",
)
prefix = "DH_AG95"
prefix = type(self).__name__
path = EMBODICHAIN_DEFAULT_DATA_ROOT if data_root is None else data_root

super().__init__(prefix, data_descriptor, path)
Expand Down Expand Up @@ -170,7 +170,7 @@ def __init__(self, data_root: str = None):
os.path.join(EMBODICHAIN_DOWNLOAD_PREFIX, eef_assets, "ZH_CTM2F110.zip"),
"0e7c3310425609797fe010b2a76fe465",
)
prefix = "ZH_CTM2F110"
prefix = type(self).__name__
path = EMBODICHAIN_DEFAULT_DATA_ROOT if data_root is None else data_root

super().__init__(prefix, data_descriptor, path)
Expand Down Expand Up @@ -203,7 +203,7 @@ def __init__(self, data_root: str = None):
),
"ff9ac77e7e1493fd32d40c87fecbee6c",
)
prefix = "BrainCoHandRevo1"
prefix = type(self).__name__
path = EMBODICHAIN_DEFAULT_DATA_ROOT if data_root is None else data_root

super().__init__(prefix, data_descriptor, path)
Expand Down Expand Up @@ -238,7 +238,7 @@ def __init__(self, data_root: str = None):
os.path.join(EMBODICHAIN_DOWNLOAD_PREFIX, eef_assets, "InspireHand.zip"),
"c60132a6f03866fb021cca5b6d72845e",
)
prefix = "InspireHand"
prefix = type(self).__name__
path = EMBODICHAIN_DEFAULT_DATA_ROOT if data_root is None else data_root

super().__init__(prefix, data_descriptor, path)
Expand Down Expand Up @@ -271,7 +271,7 @@ def __init__(self, data_root: str = None):
os.path.join(EMBODICHAIN_DOWNLOAD_PREFIX, eef_assets, "Robotiq.zip"),
"9cc84f3a2bfc3a80f428b8ed6864fbeb",
)
prefix = "Robotiq"
prefix = type(self).__name__
path = EMBODICHAIN_DEFAULT_DATA_ROOT if data_root is None else data_root

super().__init__(prefix, data_descriptor, path)
Expand Down Expand Up @@ -300,7 +300,7 @@ def __init__(self, data_root: str = None):
os.path.join(EMBODICHAIN_DOWNLOAD_PREFIX, eef_assets, "Robotiq2F85.zip"),
"53ecbf2c953f43f1134aa7223e592292",
)
prefix = "Robotiq2F85"
prefix = type(self).__name__
path = EMBODICHAIN_DEFAULT_DATA_ROOT if data_root is None else data_root

super().__init__(prefix, data_descriptor, path)
Expand Down Expand Up @@ -329,7 +329,7 @@ def __init__(self, data_root: str = None):
os.path.join(EMBODICHAIN_DOWNLOAD_PREFIX, eef_assets, "WheelTecFA2F.zip"),
"feaf13f25b1c6ce58d011b1f2fa72f58",
)
prefix = "WheelTecFA2F"
prefix = type(self).__name__
path = EMBODICHAIN_DEFAULT_DATA_ROOT if data_root is None else data_root

super().__init__(prefix, data_descriptor, path)
4 changes: 2 additions & 2 deletions embodichain/data/assets/materials.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def __init__(self, data_root: str = None):
),
"53c054b3ae0857416dc52632eb562c12",
)
prefix = "SimResources"
prefix = type(self).__name__
path = EMBODICHAIN_DEFAULT_DATA_ROOT if data_root is None else data_root

super().__init__(prefix, data_descriptor, path)
Expand Down Expand Up @@ -109,7 +109,7 @@ def __init__(self, data_root: str = None):
),
"fda82404a317281263bd5849e9eb31a1",
)
prefix = "CocoBackground"
prefix = type(self).__name__
path = EMBODICHAIN_DEFAULT_DATA_ROOT if data_root is None else data_root

super().__init__(prefix, data_descriptor, path)
Loading
Loading