mit einem Klick
add-atomic-action
// Use when adding a new observation, event, reward, action, dataset, or randomization functor to an EmbodiChain environment
// Use when adding a new observation, event, reward, action, dataset, or randomization functor to an EmbodiChain environment
| name | add-atomic-action |
| description | Use when adding a new observation, event, reward, action, dataset, or randomization functor to an EmbodiChain environment |
Scaffold a new atomic action following EmbodiChain's ActionCfg / AtomicAction pattern.
AtomicActionEngine with a behaviour not covered by the built-ins| Purpose | Path |
|---|---|
Base classes (ActionCfg, AtomicAction, ObjectSemantics) | embodichain/lab/sim/atomic_actions/core.py |
| Built-in actions (reference implementations) | embodichain/lab/sim/atomic_actions/actions.py |
Engine + global registry (register_action) | embodichain/lab/sim/atomic_actions/engine.py |
| Public API exports | embodichain/lab/sim/atomic_actions/__init__.py |
| Reference docs | docs/source/overview/sim/atomic_actions.md |
Add a @configclass-decorated class that extends ActionCfg (or MoveActionCfg /
GraspActionCfg if the new action reuses arm/gripper fields).
Place it in embodichain/lab/sim/atomic_actions/actions.py alongside the existing configs,
or in a new file if the action is large.
from embodichain.utils import configclass
from embodichain.lab.sim.atomic_actions.core import ActionCfg # or MoveActionCfg
@configclass
class PushActionCfg(ActionCfg):
name: str = "push" # must match the registry key
push_distance: float = 0.05 # metres to push forward
push_speed: int = 30 # waypoints for the push phase
control_part: str = "arm" # robot segment to control
Rules:
name must be unique and match the string passed to register_action.GraspActionCfg when the action needs hand open/close fields.Subclass AtomicAction and implement the two abstract methods.
import torch
from typing import Optional, Union
from embodichain.lab.sim.atomic_actions.core import AtomicAction, ObjectSemantics
class PushAction(AtomicAction):
"""Push an object forward by a fixed distance."""
def __init__(self, motion_generator, cfg: PushActionCfg | None = None):
super().__init__(motion_generator, cfg=cfg or PushActionCfg())
self.arm_joint_ids = self.robot.get_joint_ids(name=self.cfg.control_part)
# ------------------------------------------------------------------
def execute(
self,
target: Union[torch.Tensor, ObjectSemantics],
start_qpos: Optional[torch.Tensor] = None,
**kwargs,
) -> tuple[bool, torch.Tensor, list]:
"""Plan the push motion and return a joint trajectory.
Args:
target: EEF pose tensor (n_envs, 4, 4) or ObjectSemantics.
start_qpos: Starting joint positions (n_envs, dof). Uses current
robot state when None.
Returns:
Tuple of (is_success, trajectory, joint_ids) where
trajectory has shape (n_envs, n_waypoints, len(joint_ids)).
"""
# 1. Resolve target pose
# 2. Plan trajectory with self.motion_generator
# 3. Return result
return is_success, trajectory, self.arm_joint_ids
# ------------------------------------------------------------------
def validate(
self,
target: Union[torch.Tensor, ObjectSemantics],
start_qpos: Optional[torch.Tensor] = None,
**kwargs,
) -> bool:
"""Fast feasibility check — no trajectory generated.
Returns:
True if the action can be attempted.
"""
return True # add IK reachability check here if needed
Rules:
execute() must always return (is_success: bool, trajectory: Tensor, joint_ids: list).trajectory shape: (n_envs, n_waypoints, len(joint_ids)).joint_ids tells the engine which DOF columns the trajectory covers.validate() must be cheap — no motion planning allowed.super().__init__() — it sets self.robot, self.motion_generator, and self.cfg.Register the new class so AtomicActionEngine can discover it by name.
Option A — register at module load (built-ins style)
In embodichain/lab/sim/atomic_actions/engine.py, add to the _builtin_action_map dict:
_builtin_action_map: dict[str, type[AtomicAction]] = {
"move": MoveAction,
"pickup": PickUpAction,
"place": PlaceAction,
"push": PushAction, # ← add here
}
Option B — register at runtime (custom / plugin style)
from embodichain.lab.sim.atomic_actions import register_action
register_action("push", PushAction)
Add config and action class to embodichain/lab/sim/atomic_actions/__init__.py:
from .actions import PushAction, PushActionCfg
__all__ = [
...,
"PushAction",
"PushActionCfg",
]
Add a row to the table in docs/source/overview/sim/atomic_actions.md under
"Supported Actions":
| `PushAction` | `PushActionCfg` | `Tensor (4,4)` — contact pose | Approach → push forward |
Add a test in tests/sim/atomic_actions/ (append to an existing file or create a new one):
def test_push_action_cfg_defaults():
cfg = PushActionCfg()
assert cfg.name == "push"
assert cfg.push_distance == 0.05
def test_push_action_validate(mock_motion_generator):
action = PushAction(mock_motion_generator)
assert action.validate(target=torch.eye(4)) is True
| Mistake | Fix |
|---|---|
name in config doesn't match registry key | Keep cfg.name identical to the string in register_action("push", ...) |
Returning trajectory without joint_ids | Always return the 3-tuple (bool, Tensor, list) |
trajectory shape (n_envs, dof, n_waypoints) | Correct shape is (n_envs, n_waypoints, dof) |
Doing motion planning inside validate() | validate() must be fast — IK check only |
Not calling super().__init__() | Required to set self.robot, self.motion_generator, self.cfg |
Inheriting MoveActionCfg instead of ActionCfg | Use MoveActionCfg only when the action reuses arm-control fields; otherwise use ActionCfg |
Forgetting to export from __init__.py | Users import from the public API — missing exports cause ImportError |
| Step | Action |
|---|---|
| 1 | Define @configclass config extending ActionCfg with name field |
| 2 | Subclass AtomicAction, implement execute() and validate() |
| 3 | Register: add to _builtin_action_map or call register_action() |
| 4 | Export from __init__.py |
| 5 | Add row to supported-actions table in overview docs |
| 6 | Write tests for config defaults and validate() |
Use when adding a new observation, event, reward, action, dataset, or randomization functor to an EmbodiChain environment
Use when creating a new task environment for EmbodiChain, including expert demonstration tasks, RL tasks or any EmbodiedEnv subclass
Use when writing tests for EmbodiChain modules, including observation functors, reward functors, solvers, sensors, environments, or any Python module
Write benchmark scripts for EmbodiChain modules following project conventions
Create a pull request for EmbodiChain following the project's PR template and conventions, including selecting proper GitHub repository labels
Use before committing or creating a PR for EmbodiChain to verify code style, headers, annotations, exports, and docstrings pass CI checks