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
123 changes: 123 additions & 0 deletions docs/source/guides/cli.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# CLI Reference

EmbodiChain provides a unified CLI via ``python -m embodichain <subcommand>``.

---

## Asset Download

List and download simulation assets (robots, objects, scenes, etc.).

```bash
# List all available assets
python -m embodichain.data list

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

# Download a specific asset
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
```

---

## Preview Asset

Preview a USD or mesh asset in the simulation without writing code.

```bash
# Preview a rigid object
python -m embodichain preview-asset \
--asset_path /path/to/sugar_box.usda \
--asset_type rigid \
--preview

# Preview an articulation
python -m embodichain preview-asset \
--asset_path /path/to/robot.usd \
--asset_type articulation \
--preview

# Headless check (no render window)
python -m embodichain preview-asset \
--asset_path /path/to/asset.usda \
--headless
```

### Arguments

| Argument | Default | Description |
|---|---|---|
| ``--asset_path`` | *(required)* | Path to the asset file (``.usd``/``.usda``/``.usdc``/``.obj``/``.stl``/``.glb``) |
| ``--asset_type`` | ``rigid`` | Asset type: ``rigid`` or ``articulation``. URDF files are auto-detected as articulation. |
| ``--uid`` | *(from filename)* | Unique identifier for the asset in the scene |
| ``--init_pos X Y Z`` | ``0 0 0.5`` | Initial position |
| ``--init_rot RX RY RZ`` | ``0 0 0`` | Initial rotation in degrees |
| ``--body_type`` | ``kinematic`` | Body type for rigid objects: ``dynamic``, ``kinematic``, or ``static`` |
| ``--use_usd_properties`` | ``False`` | Use physical properties from the USD file |
| ``--fix_base`` | ``True`` | Fix the base of articulations |
| ``--sim_device`` | ``cpu`` | Simulation device |
| ``--headless`` | ``False`` | Run without rendering window |
| ``--enable_rt`` | ``False`` | Enable ray tracing |
| ``--preview`` | ``False`` | Enter interactive embed mode after loading |

### Preview Mode

When ``--preview`` is enabled, an interactive REPL is available:

- **``p``** — enter an IPython embed session with ``sim`` and ``asset`` in scope
- **``s <N>``** — step the simulation *N* times (default 10)
- **``q``** — quit

---

## Run Environment

Launch a Gymnasium environment for data generation or interactive preview.

```bash
# Run an environment with a gym config file
python -m embodichain run-env --gym_config path/to/config.json

# Run with multiple environments on GPU
python -m embodichain run-env \
--gym_config config.json \
--num_envs 4 \
--device cuda \
--gpu_id 0

# Preview mode for interactive development
python -m embodichain run-env --gym_config config.json --preview

# Headless execution
python -m embodichain run-env --gym_config config.json --headless
```

### Arguments

| Argument | Default | Description |
|---|---|---|
| ``--gym_config`` | *(required)* | Path to gym config file |
| ``--action_config`` | ``None`` | Path to action config file |
| ``--num_envs`` | ``1`` | Number of parallel environments |
| ``--device`` | ``cpu`` | Device (``cpu`` or ``cuda``) |
| ``--headless`` | ``False`` | Run in headless mode |
| ``--enable_rt`` | ``False`` | Use RTX rendering backend |
| ``--arena_space`` | ``5.0`` | Arena space size |
| ``--gpu_id`` | ``0`` | GPU ID to use |
| ``--preview`` | ``False`` | Enter interactive preview mode |
| ``--filter_visual_rand`` | ``False`` | Filter out visual randomization |
| ``--filter_dataset_saving`` | ``False`` | Filter out dataset saving |

### Preview Mode

When ``--preview`` is enabled, an interactive REPL is available:

- **``p``** — enter an IPython embed session with ``env`` in scope
- **``q``** — quit
3 changes: 2 additions & 1 deletion docs/source/guides/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ How-to Guides
:hidden:

add_robot

cli

91 changes: 91 additions & 0 deletions embodichain/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# ----------------------------------------------------------------------------
# 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.
# ----------------------------------------------------------------------------

"""Unified CLI entry point for ``python -m embodichain``.

Usage examples::

python -m embodichain preview-asset --asset_path /path/to/asset.usda --preview
python -m embodichain run-env --env_name my_env
"""

from __future__ import annotations

import argparse
import sys


def main() -> None:
"""Dispatch to the appropriate sub-command CLI."""
parser = argparse.ArgumentParser(
prog="embodichain",
description="EmbodiChain command-line interface.",
)
subparsers = parser.add_subparsers(dest="command")

# -- preview-asset -------------------------------------------------------
preview_asset_parser = subparsers.add_parser(
"preview-asset",
help="Preview a USD or mesh asset in the simulation.",
)
# Import and wire up the existing CLI so argparse is handled by the
# sub-command module itself. We pass ``parse_known_args`` style by
# letting the sub-command parser own its own arguments.
from embodichain.lab.scripts.preview_asset import cli as preview_asset_cli

preview_asset_parser.set_defaults(func=preview_asset_cli)

# Re-add the preview-asset arguments here so ``--help`` works on the
# sub-command. We delegate to the existing ``cli()`` which calls
# ``parse_args()`` internally, so we pass through the raw argv.
# Instead of duplicating argument definitions, we let the sub-command
# module handle its own argument parsing by slicing sys.argv.

# -- run-env -------------------------------------------------------------
run_env_parser = subparsers.add_parser(
"run-env",
help="Run an environment for data generation or preview.",
)
from embodichain.lab.scripts.run_env import cli as run_env_cli

run_env_parser.set_defaults(func=run_env_cli)

# -- Parse ---------------------------------------------------------------
# If no sub-command is given, print help and exit.
if len(sys.argv) < 2 or sys.argv[1] in ("-h", "--help"):
parser.print_help()
sys.exit(0)

# Determine which sub-command was selected, then reconstruct argv so
# that each sub-command's ``cli()`` can call ``parse_args()`` normally.
known, _ = parser.parse_known_args()

if hasattr(known, "func"):
# Rewrite sys.argv so the sub-command's argparse sees only its own args.
subcommand_argv = [f"embodichain {sys.argv[1]}"] + sys.argv[2:]
original_argv = sys.argv
sys.argv = subcommand_argv
try:
known.func()
finally:
sys.argv = original_argv
else:
parser.print_help()
sys.exit(1)


if __name__ == "__main__":
main()
10 changes: 9 additions & 1 deletion embodichain/lab/scripts/preview_asset.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,11 @@ def main(args: argparse.Namespace) -> None:
sim.destroy()


if __name__ == "__main__":
def cli():
"""Command-line interface for asset preview.

Parses CLI arguments and launches the preview workflow.
"""
parser = argparse.ArgumentParser(
description="Preview a USD or mesh asset in the EmbodiChain simulation."
)
Expand Down Expand Up @@ -298,3 +302,7 @@ def main(args: argparse.Namespace) -> None:
args.uid = os.path.splitext(os.path.basename(args.asset_path))[0]

main(args)


if __name__ == "__main__":
cli()
11 changes: 10 additions & 1 deletion embodichain/lab/scripts/run_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,12 @@ def preview(env: gymnasium.Env) -> None:
exit(0)


if __name__ == "__main__":
def cli():
"""Command-line interface for environment runner.

Parses CLI arguments, builds the environment config, and launches
the data generation or preview workflow.
"""
np.set_printoptions(5, suppress=True)
torch.set_printoptions(precision=5, sci_mode=False)

Expand All @@ -187,3 +192,7 @@ def preview(env: gymnasium.Env) -> None:
env = gymnasium.make(id=gym_config["id"], cfg=env_cfg, **action_config)

main(args, env, gym_config)


if __name__ == "__main__":
cli()
2 changes: 1 addition & 1 deletion embodichain/lab/sim/sim_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ def reset(cls, instance_id: int = 0) -> None:
This allows creating a new instance with different configuration.
"""
if instance_id in cls._instances:
logger.log_info(f"Resetting SimulationManager instance {instance_id}.")
logger.log_debug(f"Resetting SimulationManager instance {instance_id}.")
del cls._instances[instance_id]

@classmethod
Expand Down
Loading