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
322 changes: 322 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,322 @@
# EmbodiChain — Developer Reference

EmbodiChain is an **end-to-end, GPU-accelerated, modular platform** for building generalized Embodied Intelligence, developed by DexForce Technology Co., Ltd. It provides a simulation lab, gym environments, RL training infrastructure, and agentic frameworks for robot learning research.

- **License**: Apache 2.0
- **Repository**: https://github.com/DexForce/EmbodiChain

---

## Project Structure

```
EmbodiChain/
├── embodichain/ # Main Python package
│ ├── agents/ # AI agents
│ │ ├── hierarchy/ # LLM-based hierarchical agents (task, code, validation)
│ │ ├── mllm/ # Multimodal LLM prompt scaffolding
│ │ └── rl/ # RL agents: PPO algo, rollout buffer, actor-critic models
│ ├── data/ # Assets, datasets, constants, enums
│ ├── lab/ # Simulation lab
│ │ ├── gym/ # OpenAI Gym-compatible environments
│ │ │ ├── envs/ # BaseEnv, EmbodiedEnv, RLEnv
│ │ │ │ ├── managers/ # Observation, event, reward, record, dataset managers
│ │ │ │ │ └── randomization/ # Physics, geometry, spatial, visual randomizers
│ │ │ │ ├── tasks/ # Task implementations (tableware, RL, special)
│ │ │ │ ├── action_bank/ # Configurable action primitives
│ │ │ │ └── wrapper/ # Env wrappers (e.g. no_fail)
│ │ │ └── utils/ # Gym registration, misc helpers
│ │ ├── sim/ # Simulation core
│ │ │ ├── objects/ # Robot, RigidObject, Articulation, Light, Gizmo, SoftObject
│ │ │ ├── sensors/ # Camera, StereoCamera, BaseSensor
│ │ │ ├── robots/ # Robot-specific configs and params (dexforce_w1, cobotmagic)
│ │ │ ├── planners/ # Motion planners (TOPPRA, motion generator)
│ │ │ └── solvers/ # IK solvers (SRS, OPW, pink, pinocchio, pytorch)
│ │ ├── devices/ # Real-device controllers
│ │ └── scripts/ # Entry-point scripts (run_env, run_agent)
│ ├── toolkits/ # Standalone tools
│ │ ├── graspkit/pg_grasp/ # Parallel-gripper grasp sampling
│ │ └── urdf_assembly/ # URDF builder utilities
│ └── utils/ # Shared utilities
│ ├── configclass.py # @configclass decorator
│ ├── logger.py # Project logger
│ ├── math/ # Tensor math helpers
│ └── warp/kinematics/ # GPU kinematics via Warp
├── configs/ # Agent configs and task prompts (text/YAML)
├── docs/ # Sphinx documentation source + build
│ └── source/ # .md doc pages (overview, quick_start, features, resources)
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

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

The project-structure comment says docs/source/ contains “.md doc pages”, but the docs tree is a mix of .rst and .md (with MyST enabled). Adjusting this wording would better reflect the actual documentation setup.

Suggested change
│ └── source/ # .md doc pages (overview, quick_start, features, resources)
│ └── source/ # Sphinx doc sources (.rst and .md via MyST; overview, quick_start, features, resources)

Copilot uses AI. Check for mistakes.
├── tests/ # Test suite
├── .github/ # CI workflows and issue/PR templates
├── setup.py # Package setup
└── VERSION # Package version file
```

---

## Code Style

### Formatting

- **Formatter**: `black==24.3.0` — run before every commit.
```bash
black .
```

### File Headers

Every source file begins with the Apache 2.0 copyright header:

```python
# ----------------------------------------------------------------------------
# Copyright (c) 2021-2026 DexForce Technology Co., Ltd.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# ...
Comment on lines +67 to +74
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

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

“Every source file begins with the Apache 2.0 copyright header” is not currently true across the repo (for example docs/source/conf.py does not include this header). Consider scoping this to the directories that actually follow it (e.g. embodichain/ and tests/) or updating the other files to match.

Suggested change
Every source file begins with the Apache 2.0 copyright header:
```python
# ----------------------------------------------------------------------------
# Copyright (c) 2021-2026 DexForce Technology Co., Ltd.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# ...
Python source files in the `embodichain/` and `tests/` directories must begin with the Apache 2.0 copyright header:
```python
# ----------------------------------------------------------------------------
# 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.

Copilot uses AI. Check for mistakes.
# ----------------------------------------------------------------------------
```

### Type Annotations

- Use full type hints on all public APIs.
- Use `from __future__ import annotations` at the top of every file.
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

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

This claims from __future__ import annotations should be at the top of every file, but several core modules (e.g. embodichain/lab/gym/envs/base_env.py, embodied_env.py, rl_env.py) do not include it. To avoid establishing an inaccurate convention, either update these files to match or soften the guidance (e.g. "prefer for new files" / "used in many modules").

Suggested change
- Use `from __future__ import annotations` at the top of every file.
- Prefer `from __future__ import annotations` at the top of new Python files and when updating existing modules, where feasible.

Copilot uses AI. Check for mistakes.
- Use `TYPE_CHECKING` guards for circular-import-safe imports.
- Prefer `Union[A, B]` or `A | B` (Python 3.10+ union syntax is acceptable).

### Configuration Pattern (`@configclass`)

All configuration objects use the `@configclass` decorator (similar to Isaac Lab's pattern):

```python
from embodichain.utils import configclass
from dataclasses import MISSING

@configclass
class MyManagerCfg:
param_a: float = 1.0
param_b: str = MISSING # required — must be set by caller
```

### Functor / Manager Pattern

Managers (observation, event, reward, randomization) use a `Functor`/`FunctorCfg` pattern:

- **Function-style**: a plain function with signature `(env, env_ids, ...) -> None`.
- **Class-style**: a class inheriting `Functor`, with `__init__(cfg, env)` and `__call__(env, env_ids, ...)`.
- Registered in a manager config via `FunctorCfg(func=..., params={...})`.

```python
from embodichain.lab.gym.envs.managers import Functor, FunctorCfg

class my_randomizer(Functor):
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

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

In the Functor example, class my_randomizer(Functor) uses a lowercase class name, which conflicts with standard Python class naming and can be confused with the function-style functor described above. Consider using a MyRandomizer-style class name (or make this example a plain function) to keep the pattern clear.

Suggested change
class my_randomizer(Functor):
class MyRandomizer(Functor):

Copilot uses AI. Check for mistakes.
def __init__(self, cfg: FunctorCfg, env):
super().__init__(cfg, env)

def __call__(self, env, env_ids, my_param: float = 0.5):
...
```

### Docstrings

Use Google-style docstrings with Sphinx directives:

```python
def my_function(env, env_ids, scale: float = 1.0) -> None:
"""Short one-line summary.

Longer description if needed.

.. attention::
Note a non-obvious behavior here.

.. tip::
Helpful usage hint.

Args:
env: The environment instance.
env_ids: Target environment IDs.
scale: Scaling factor applied to the result.

Returns:
Description if not None.

Raises:
ValueError: If the entity type is unsupported.
"""
```

### Module Exports

Define `__all__` in every public module to declare the exported API:

```python
__all__ = ["MyClass", "my_function"]
```

### Documentation

- Docs are built with **Sphinx** using **Markdown** source files (`docs/source/`).
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

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

This documentation note says the docs are built with Sphinx using Markdown source files, but the repo uses both .rst and .md in docs/source/ (with MyST). Consider updating the wording to “Markdown and reStructuredText” to avoid confusing contributors.

Suggested change
- Docs are built with **Sphinx** using **Markdown** source files (`docs/source/`).
- Docs are built with **Sphinx** using **Markdown and reStructuredText** source files (`docs/source/`).

Copilot uses AI. Check for mistakes.
- Build locally:
```bash
pip install -r docs/requirements.txt
cd docs && make html
# Preview at docs/build/html/index.html
```
- If you encounter locale errors: `export LC_ALL=C.UTF-8; export LANG=C.UTF-8`

---

## Contributing Guide

### Bug Reports

Use the **Bug Report** issue template (`.github/ISSUE_TEMPLATE/bug.md`). Title format: `[Bug Report] Short description`.

Include:
- Clear description of the bug
- Minimal reproduction steps and stack trace
- System info: commit hash, OS, GPU model, CUDA version, GPU driver version
- Confirm you checked for duplicate issues

### Feature Requests / Proposals

Use the **Proposal** issue template (`.github/ISSUE_TEMPLATE/proposal.md`). Title format: `[Proposal] Short description`.

Include:
- Description of the feature and its core capabilities
- Motivation and problem it solves
- Any related existing issues

### Pull Requests

1. **Fork** the repository and create a focused branch.
2. **Keep PRs small** — one logical change per PR.
3. **Format** the code with `black==24.3.0` before submitting.
4. **Update documentation** for any public API changes.
5. **Add tests** that prove your fix or feature works.
6. **Submit** using the PR template (`.github/PULL_REQUEST_TEMPLATE.md`):
- Summarize changes and link the related issue (`Fixes #123`).
- Specify the type of change (bug fix / enhancement / new feature / breaking change / docs).
- Attach before/after screenshots for visual changes.
- Complete the checklist:
- [ ] `black .` has been run
- [ ] Documentation updated
- [ ] Tests added
- [ ] Dependencies updated (if applicable)

> It is recommended to open an issue and discuss the design before opening a large PR.

### Adding a New Robot

Refer to `embodichain/lab/sim/robots/` for existing examples (`dexforce_w1`, `cobotmagic`). Each robot needs: a config (`cfg.py`), parameters (`params.py`), types (`types.py`), and utilities (`utils.py`).

### Adding a New Task Environment

Refer to `embodichain/lab/gym/envs/tasks/` for existing examples. Tasks subclass `EmbodiedEnv` or `BaseAgentEnv` and implement `_setup_scene`, `_reset_idx`, and evaluation logic.

---

## Unit Tests

### Structure

Tests live in `tests/` and mirror the source tree:

```text
tests/
├── toolkits/
│ └── test_pg_grasp.py
├── gym/
│ └── action_bank/
│ └── test_configurable_action.py
└── sim/
├── objects/
│ ├── test_light.py
│ └── test_rigid_object_group.py
├── sensors/
│ ├── test_camera.py
│ └── test_stereo.py
└── planners/
└── test_motion_generator.py
```

Place new test files at `tests/<subpackage>/test_<module>.py`, matching the layout of `embodichain/`.

### Two accepted styles

**pytest style** — for pure-Python logic with no test ordering dependency:

```python
# ----------------------------------------------------------------------------
# Copyright (c) 2021-2026 DexForce Technology Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# ...
# ----------------------------------------------------------------------------

from embodichain.my_module import my_function


def test_expected_output():
result = my_function(input_value)
assert result == expected_value


def test_edge_case():
result = my_function(edge_input)
assert result is not None
```

**`unittest.TestCase` style** — when tests must run in a specific order or share `setUp`/`tearDown` state:

```python
# ----------------------------------------------------------------------------
# Copyright (c) 2021-2026 DexForce Technology Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# ...
# ----------------------------------------------------------------------------

import unittest
from embodichain.my_module import MyClass


class TestMyClass(unittest.TestCase):
def setUp(self):
self.obj = MyClass(param=1.0)

def tearDown(self):
pass

def test_basic_behavior(self):
result = self.obj.run()
self.assertEqual(result, expected)

def test_raises_on_bad_input(self):
self.assertRaises(ValueError, self.obj.run, bad_input)


if __name__ == "__main__":
unittest.main()
```

### Conventions

- **File header**: include the standard Apache 2.0 copyright block (same as all source files).
- **Naming**: test files are `test_<module>.py`; test functions/methods are `test_<scenario>`.
- **Simulation-dependent tests**: tests that require a running `SimulationManager` (GPU, sensors, robots) must initialize and teardown the sim inside `setUp`/`tearDown` or a pytest fixture. Keep them isolated from pure-logic tests.
- **No magic numbers**: define expected values as named constants or comments explaining their origin.
- **`if __name__ == "__main__"`**: include this block for tests that support optional visual/interactive output (pass `is_visual=True` manually when debugging).

### Running tests

```bash
# Run all tests
pytest tests/

# Run a specific file
pytest tests/toolkits/test_pg_grasp.py

# Run a specific test function
pytest tests/toolkits/test_pg_grasp.py::test_antipodal_score_selector

# Run with verbose output
pytest -v tests/
```
61 changes: 61 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,67 @@ We welcome pull requests for bug fixes, new features, and documentation improvem
* Include a summary of the changes and link to any relevant issues (e.g., `Fixes #123`).
* Ensure all checks pass.

## Using Claude Code for Contributions

[Claude Code](https://docs.anthropic.com/en/docs/claude-code/overview) is an AI-powered CLI that can assist you throughout the contribution workflow — from understanding the codebase to writing, reviewing, and debugging code.

### Setup

Install Claude Code and authenticate:

```bash
npm install -g @anthropic-ai/claude-code
claude
```

A `CLAUDE.md` file is present at the root of this repository. Claude Code reads it automatically at startup to load project conventions, structure, and style rules, so it is context-aware from the first prompt.

### Suggested workflows

**Explore the codebase before making changes**

```
> Explain how the Functor/Manager pattern works in embodichain/lab/gym/envs/managers/
> What is the difference between EmbodiedEnv and RLEnv?
> Show me an example of how a randomization functor is registered in a task config.
```

**Implement a new feature**

```
> I want to add a new observation functor that returns the end-effector velocity.
Which existing functor should I model it after?
> Generate the functor following the project style, with a proper docstring and type hints.
```

**Validate style and formatting before submitting**

```
> Review my changes in embodichain/lab/gym/envs/managers/randomization/visual.py
for style issues, missing type hints, and docstring completeness.
```

**Write or update tests**

```
> Write a pytest test for the randomize_emission_light function in
embodichain/lab/gym/envs/managers/randomization/visual.py.
```

**Understand a bug**

```
> I'm getting a KeyError in observation_manager.py at line 42 when env_ids is None.
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

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

The example prompt hard-codes a line number in observation_manager.py ("at line 42"). That line number is already not where a KeyError would occur and will drift over time, making the guidance misleading. Consider removing the line number and referencing a function name or just the file + traceback instead.

Suggested change
> I'm getting a KeyError in observation_manager.py at line 42 when env_ids is None.
> I'm getting a KeyError in observation_manager.py when env_ids is None. The traceback points into the observation lookup logic.

Copilot uses AI. Check for mistakes.
What could cause this and how should it be fixed?
```

### Tips

* Always run `black .` after Claude Code generates or edits Python files — Claude Code can do this for you if you ask.
* Claude Code respects the `CLAUDE.md` conventions. If you notice it deviating (wrong docstring style, missing `__all__`, etc.), point it out and it will correct the output.
* For large features, break the work into small, focused tasks and handle them one at a time.
* Claude Code can help draft your PR description and populate the PR checklist once your changes are ready.

## Contribute specific robots

TODO: Add instructions for contributing new robot models and its configurations.
Expand Down