Skip to content
Open
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
24 changes: 19 additions & 5 deletions agent_context/topics/motion-planning/motion-planning.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
| Planner registry | `embodichain/lab/sim/planners/__init__.py` |
| Base planner class & config | `embodichain/lab/sim/planners/base_planner.py` → `BasePlanner`, `BasePlannerCfg`, `PlanOptions` |
| TOPPRA planner | `embodichain/lab/sim/planners/toppra_planner.py` → `ToppraPlanner`, `ToppraPlannerCfg`, `ToppraPlanOptions` |
| Neural planner | `embodichain/lab/sim/planners/neural_planner.py` → `NeuralPlanner`, `NeuralPlannerCfg`, `NeuralPlanOptions` |
| Planner assets | `embodichain/data/assets/planner_assets.py` → `download_neural_planner_checkpoint()` |
| Motion generator | `embodichain/lab/sim/planners/motion_generator.py` → `MotionGenerator`, `MotionGenCfg`, `MotionGenOptions` |
| Planner utilities & data types | `embodichain/lab/sim/planners/utils.py` → `PlanState`, `PlanResult`, `MoveType`, `MovePart`, `TrajectorySampleMethod` |

Expand All @@ -22,20 +24,23 @@ All planners resolve their robot at init via `SimulationManager.get_instance().g

```
BasePlanner (ABC)
└─ ToppraPlanner Time-optimal path parameterization
├─ ToppraPlanner Time-optimal path parameterization
└─ NeuralPlanner (experimental) APG waypoint rollout

MotionGenerator Wraps any BasePlanner; adds interpolation and multi-part support
```

Config hierarchy:
```
BasePlannerCfg robot_uid (MISSING), planner_type
└─ ToppraPlannerCfg planner_type = "toppra"
├─ ToppraPlannerCfg planner_type = "toppra"
└─ NeuralPlannerCfg planner_type = "neural", checkpoint_path (MISSING)

MotionGenCfg planner_cfg (MISSING — must be a BasePlannerCfg subclass)

PlanOptions (empty base)
└─ ToppraPlanOptions constraints, sample_method, sample_interval
├─ ToppraPlanOptions constraints, sample_method, sample_interval
└─ NeuralPlanOptions control_part, start_qpos, max_steps

MotionGenOptions start_qpos, control_part, plan_opts, is_interpolate,
interpolate_nums, is_linear, interpolate_position_step,
Expand All @@ -60,12 +65,21 @@ Time-optimal path parameterization using the [toppra](https://github.com/hungpha
| `sample_method` | `TrajectorySampleMethod` | `QUANTITY` | `TIME`, `QUANTITY`, or `DISTANCE` |
| `sample_interval` | `float \| int` | `0.01` | Time interval (seconds) or sample count depending on method |

### NeuralPlanner (experimental)

Learning-based EEF waypoint planner. Franka Panda only.

- Checkpoint: `download_neural_planner_checkpoint()` from HuggingFace (gated, needs `HF_TOKEN`)
- Use via `MotionGenerator` with `planner_type="neural"` and `plan_opts=NeuralPlanOptions(...)`
- Input: `EEF_MOVE` `PlanState` list with 4×4 `xpos`
- Key cfg: `checkpoint_path` (from download), `control_part`

### MotionGenerator

Unified interface for trajectory planning with optional pre-interpolation.

- Wraps a `BasePlanner` instance (resolved from `planner_cfg.planner_type`).
- Supported planner types: `{"toppra": (ToppraPlanner, ToppraPlannerCfg)}`.
- Supported planner types: `{"toppra": (ToppraPlanner, ToppraPlannerCfg), "neural": (NeuralPlanner, NeuralPlannerCfg)}`.
- `MotionGenCfg.planner_cfg` is **MISSING** — must be provided.

`MotionGenOptions` fields:
Expand Down Expand Up @@ -140,7 +154,7 @@ Describes one waypoint or action:
```python
_support_planner_dict = {
"toppra": (ToppraPlanner, ToppraPlannerCfg),
"my_planner": (MyPlanner, MyPlannerCfg),
"neural": (NeuralPlanner, NeuralPlannerCfg),
}
```
5. Export from `embodichain/lab/sim/planners/__init__.py`.
Expand Down
5 changes: 5 additions & 0 deletions docs/source/overview/sim/planners/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,21 @@ The `embodichain` project provides a unified interface for robot trajectory plan

- **MotionGenerator**: A unified trajectory planning interface that supports joint/Cartesian interpolation, automatic constraint handling, flexible planner selection, and is easily extensible for collision checking and additional planners.
- **ToppraPlanner**: A time-optimal trajectory planner based on the TOPPRA library, supporting joint trajectory generation under velocity and acceleration constraints.
- **NeuralPlanner** (experimental): A learning-based EEF waypoint planner for Franka Panda.
- **TrajectorySampleMethod**: An enumeration for trajectory sampling strategies, supporting sampling by time, quantity, or distance.

These tools can be used to generate smooth and dynamically feasible robot trajectories, and are extensible for future collision checking and various sampling requirements.

Use NeuralPlanner (experimental) when you have a trained APG checkpoint and need
learned EEF waypoint rollout on Franka Panda.

See also
--------

.. toctree::
:maxdepth: 1

toppra_planner.md
neural_planner.md
trajectory_sample_method.md
motion_generator.md
2 changes: 1 addition & 1 deletion docs/source/overview/sim/planners/motion_generator.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
## Features

* **Unified planning interface**: Supports trajectory planning with or without collision checking (collision checking is reserved for future implementation).
* **Flexible planner selection**: Allows selection of different planners (currently supports TOPPRA for time-optimal planning).
* **Flexible planner selection**: Supports TOPPRA and NeuralPlanner (experimental).
* **Automatic constraint handling**: Retrieves velocity and acceleration limits from the robot or uses user-specified/default values.
* **Supports both joint and Cartesian interpolation**: Generates discrete trajectories using either joint space or Cartesian space interpolation.
* **Convenient sampling**: Supports various sampling strategies via `TrajectorySampleMethod`.
Expand Down
63 changes: 63 additions & 0 deletions docs/source/overview/sim/planners/neural_planner.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# NeuralPlanner

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Mention that the policy of Neural Planner is specified for a robot.


````{admonition} Experimental
:class: warning

`NeuralPlanner` is an **experimental** feature. The API, checkpoint format,
and default parameters may change without a deprecation cycle. It is currently
only validated on the **Franka Panda** robot.
````

`NeuralPlanner` is a learning-based EEF waypoint planner. It rolls out a
trained APG checkpoint through `MotionGenerator` to reach Cartesian targets.

## Configuration

Pre-trained checkpoints are hosted on HuggingFace and can be downloaded with
`download_neural_planner_checkpoint()` (requires `HF_TOKEN` environment variable).

```python
from embodichain.data.assets.planner_assets import download_neural_planner_checkpoint
from embodichain.lab.sim.planners import (
MotionGenCfg,
MotionGenOptions,
MotionGenerator,
MoveType,
NeuralPlannerCfg,
PlanState,
)
from embodichain.lab.sim.planners.neural_planner import NeuralPlanOptions

checkpoint_path = download_neural_planner_checkpoint()

motion_generator = MotionGenerator(
cfg=MotionGenCfg(
planner_cfg=NeuralPlannerCfg(
robot_uid=robot.uid,
checkpoint_path=checkpoint_path,
control_part="main_arm",
)
)
)

result = motion_generator.generate(
target_states=[
PlanState(move_type=MoveType.EEF_MOVE, xpos=waypoint)
for waypoint in waypoints
],
options=MotionGenOptions(
plan_opts=NeuralPlanOptions(
control_part="main_arm",
start_qpos=start_qpos,
),
),
)
```

## Example

```bash
python examples/sim/planners/neural_planner.py --headless --device cuda
```

The example downloads the checkpoint automatically on first run.
2 changes: 2 additions & 0 deletions embodichain/data/assets/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@
from .eef_assets import *
from .robot_assets import *
from .scene_assets import *
from .solver_assets import *

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Move solver as well

from .planner_assets import *
91 changes: 91 additions & 0 deletions embodichain/data/assets/planner_assets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# ----------------------------------------------------------------------------

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Rename to nmg_weights would be better. and place to a weights folder

# 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.
# ----------------------------------------------------------------------------
from __future__ import annotations

import os

from huggingface_hub import hf_hub_download

# HuggingFace endpoint. Mirrors (e.g. hf-mirror.com) often redirect to the
# real hub without forwarding the required commit-hash response headers, so we
# default to the canonical endpoint and rely on the system proxy when needed.
_HF_ENDPOINT = "https://huggingface.co"

__all__ = ["download_neural_planner_checkpoint"]


def download_neural_planner_checkpoint(
repo_id: str = "dexforce/neural_motion_generator",
filename: str = "franka/franka.pt",
token: str | None = None,
endpoint: str = _HF_ENDPOINT,
) -> str:
"""Download a neural planner checkpoint from HuggingFace.

The repository is gated. Either set the ``HF_TOKEN`` environment variable or
run ``huggingface-cli login`` before calling this function.

If your network requires an HTTP proxy, set ``HTTPS_PROXY`` or
``https_proxy`` in the environment before launching Python.

Args:
repo_id: HuggingFace repository ID.
filename: Checkpoint path in the repo, e.g. ``franka/franka.pt``.
token: HuggingFace API token. Falls back to the ``HF_TOKEN``
environment variable or the cached token from
``huggingface-cli login``.
endpoint: HuggingFace-compatible endpoint URL. Defaults to
``https://huggingface.co``. Mirrors that merely redirect to the
real hub are not supported.

Returns:
str: Local path to the downloaded checkpoint file.

Raises:
RuntimeError: If the download fails, with authentication instructions.
"""
# Normalize proxy env vars: the ``requests`` library on Linux requires the
# lowercase form (``https_proxy``), but users typically export the uppercase
# form (``HTTPS_PROXY``).
https_proxy = os.environ.get("HTTPS_PROXY") or os.environ.get("https_proxy")
if https_proxy:
os.environ.setdefault("https_proxy", https_proxy)
os.environ.setdefault("HTTPS_PROXY", https_proxy)

# Allow callers to pass the token explicitly; otherwise fall back to
# HF_TOKEN (huggingface_hub also reads this automatically, but being
# explicit makes the fallback order transparent).
if token is None:
token = os.environ.get("HF_TOKEN") or None

try:
return hf_hub_download(
repo_id=repo_id,
filename=filename,
token=token,
endpoint=endpoint,
)
except Exception as exc:
raise RuntimeError(
f"Failed to download '{filename}' from '{repo_id}'.\n"
"The repository is gated and requires an authenticated HuggingFace account.\n"
"To fix this:\n"
" 1. Accept the model license at https://huggingface.co/dexforce/neural_motion_generator\n"
" 2. Create an access token at https://huggingface.co/settings/tokens\n"
" 3. Export the token: export HF_TOKEN=<your_token>\n"
" or run: huggingface-cli login\n"
f"Original error: {exc}"
) from exc
1 change: 1 addition & 0 deletions embodichain/lab/sim/planners/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@
from .utils import *
from .base_planner import *
from .toppra_planner import *
from .neural_planner import *
from .motion_generator import *
11 changes: 10 additions & 1 deletion embodichain/lab/sim/planners/motion_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
BasePlanner,
ToppraPlanner,
ToppraPlannerCfg,
NeuralPlanner,
NeuralPlannerCfg,
)
from embodichain.lab.sim.utility.action_utils import interpolate_with_nums
from embodichain.utils import logger, configclass
Expand Down Expand Up @@ -93,6 +95,7 @@ class MotionGenerator:

_support_planner_dict = {
"toppra": (ToppraPlanner, ToppraPlannerCfg),
"neural": (NeuralPlanner, NeuralPlannerCfg),
}

def __init__(self, cfg: MotionGenCfg) -> None:
Expand Down Expand Up @@ -217,7 +220,13 @@ def generate(
else:
target_plan_states = target_states

options.plan_opts.control_part = options.control_part
if options.plan_opts is None:
if hasattr(self.planner, "default_plan_options"):
options.plan_opts = self.planner.default_plan_options()
else:
options.plan_opts = PlanOptions()
# Planner-specific options (e.g. NeuralPlanOptions) must be set on
# plan_opts explicitly, same as ToppraPlanOptions.
result = self.planner.plan(
target_states=target_plan_states, options=options.plan_opts
)
Expand Down
Loading
Loading