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
5 changes: 2 additions & 3 deletions .ci/test-impact.toml
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ risk = "medium"
[[rules]]
id = "atomic-actions"
paths = ["embodichain/lab/sim/atomic_actions/**"]
tests = ["tests/sim/atomic_actions/**"]
tests = ["tests/sim/atomic_actions/**", "tests/examples/atomic_action/test_support.py"]
resource_hints = ["sim"]
risk = "medium"

Expand All @@ -361,8 +361,7 @@ risk = "medium"
[[rules]]
id = "atomic-action-tutorials"
paths = ["scripts/tutorials/atomic_action/*.py"]
tests = ["tests/sim/atomic_actions/**"]
resource_hints = ["sim"]
tests = ["tests/visualization/test_example_tutorial_coverage.py"]
risk = "medium"

[[rules]]
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ outputs
/output/
test_configs/*
embodichain_data/
scripts/tutorials/atomic_action/*.glb
examples/sim/atomic_action/*.glb

wandb/
*.mp4
Expand Down
14 changes: 7 additions & 7 deletions agent_context/topics/atomic-actions/affordance-sampling.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,23 @@ Action/Goal inputs rather than Affordance variation.
## Current host boundary

The only host integration is direct simulation. The PickUp, AxisAlign,
HandOver, OpenDoor, Press, Slide, and Twist tutorials map
HandOver, OpenDoor, Press, Slide, and Twist examples map
`--affordance_branches` one-to-one to simulation rows and inject the context
through the shared host helpers in
`scripts/tutorials/atomic_action/tutorial_utils.py`. HandOver samples pickup
`examples/sim/atomic_action/support.py`. HandOver samples pickup
and receiving grasps in separate named streams while preserving opposite object
ends and per-row arm assignment; its diagnostics retain the selected assignment's
candidate metadata and control parts. Press samples local contact-frame roll;
the Twist tutorial explicitly declares `grasp_roll_range=(-pi, pi)` for parallel
branches. Press, Slide, and OpenDoor tutorials use `ik_interp` for their exact
the Twist example explicitly declares `grasp_roll_range=(-pi, pi)` for parallel
branches. Press, Slide, and OpenDoor examples use `ik_interp` for their exact
Cartesian contact/path samples.

The seed and attempt identity control Affordance selection, not upstream grasp
generation. Antipodal approach-direction perturbations still use PyTorch's
global RNG and each row has its own candidate pool. Candidate IDs and reuse
flags are row-local; they do not certify cross-row uniqueness. One branch keeps
sampling disabled. See the human-facing
[tutorial matrix](../../../docs/source/overview/sim/atomic_actions/affordance_sampling.md)
[example matrix](../../../docs/source/overview/sim/atomic_actions/affordance_sampling.md)
for supported geometric freedom and CLI examples.

Do not route this feature through Task Program, the Gym bridge, environment
Expand All @@ -54,7 +54,7 @@ define an explicit expansion policy.
- Sampling contracts and selection: `atomic_actions/affordance_sampling.py`.
- Geometric samplers: `atomic_actions/affordance.py`.
- Feasibility and diagnostics: matching file under `atomic_actions/primitives/`.
- Direct host examples: `scripts/tutorials/atomic_action/` and its shared
`tutorial_utils.py` sampling helpers.
- Direct host examples: `examples/sim/atomic_action/` and its shared
`support.py` sampling helpers.
- Tests: `tests/sim/atomic_actions/test_affordance_sampling.py`,
`test_affordance.py`, `test_actions.py`, `test_core.py`, and `test_engine.py`.
6 changes: 5 additions & 1 deletion agent_context/topics/atomic-actions/atomic-actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,11 @@ The example lives in `examples/sim/atomic_action/` and is shipped as ordinary
distribution data with the rest of that tree. Its `__main__.py` owns
typed configuration and validated selection; `host.py` owns compilation,
expansion and output. Scene modules own physical setup and local defaults.
Tutorial files forwarding there are compatibility entries. Read the
All runnable demonstrations and helpers live there. Specialized examples such
as coordinated manipulation and observed recovery run by file path through
`embodichain run-example`. The tutorial directory contains only `single_arm.py`
(PickUp→Place) and `dual_arm.py` (HandOver), which reuse example setup while
explaining typed application composition. Read the
[example README](../../../examples/sim/atomic_action/README.md)
for supported combinations, fixture calibration, playback and output artifacts.

Expand Down
7 changes: 3 additions & 4 deletions agent_context/topics/motion-planning/motion-planning.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,10 +149,9 @@ explicitly enabled factor that cannot produce a qpos variant raises rather than
disappearing: `ik` without Jacobians, and `approach`, which owns Cartesian
standoff poses the caller must replan. The resolved config is reported on the result.

`scripts/tutorials/atomic_action/place.py` is the demonstration host, hooked
the way the Affordance sampling tutorials are: shared helpers live in
`examples/sim/atomic_action/support.py` through the `tutorial_utils.py` adapter;
the tutorial declares only which named segments may move,
`examples/sim/atomic_action/place.py` is the demonstration host. Shared
helpers live directly in `examples/sim/atomic_action/support.py`; the example
declares which named segments may move,
and one variant replays per simulation row. Phases come from the action's own
`TrajectorySegment` ranges; only motion at or after the lift is retimed so the
shared `clear_dynamics()` step stays aligned. `--variant_plot_dir` writes a
Expand Down
7 changes: 5 additions & 2 deletions agent_context/topics/motion-planning/planner-details.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,11 @@ Preserve these coupled result rules in `motion_generator.py`:
- Resampling is time-based, preserves each row's duration, recomputes velocities
and invalidates accelerations. Unchanged samples retain native derivatives.
- `uses_sparse_joint_waypoints` lets a backend receive `start_qpos` plus goals
without generic pre-interpolation. `preserve_plan_samples` keeps its native
grid and derivatives through normalization. TrapezoidalPlanner declares both.
without generic pre-interpolation. Free Cartesian goals become sparse IK
waypoints; only an explicit `is_linear` request adds Cartesian path samples.
This avoids treating interpolated samples as mandatory rest points.
`preserve_plan_samples` keeps its native grid and derivatives through
normalization. TrapezoidalPlanner declares both.
- Failed rows hold the supplied start pose unless the backend declares
`preserve_failed_plan_positions`; NeuralPlanner uses that capability to keep
partial rollout evidence while reporting failure.
Expand Down
16 changes: 8 additions & 8 deletions docs/source/overview/sim/atomic_actions/affordance_sampling.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,17 +55,17 @@ Explicit caller-provided grasp poses remain exact. Slide travel distance, Twist
angle, and OpenDoor opening fraction are action or goal parameters and are not
sampled by the Affordance layer.

## Parallel simulation tutorials
## Parallel simulation examples

The following tutorials map one Affordance branch to one simulation environment.
The following examples map one Affordance branch to one simulation environment.
They share argument validation, sampling-context construction, and per-row
diagnostic logging in `scripts/tutorials/atomic_action/tutorial_utils.py`.
diagnostic logging in `examples/sim/atomic_action/support.py`.

| Entry point under `scripts/tutorials/atomic_action/` | Geometric augmentation | Sampling metadata | Preserved task inputs |
| Entry point under `examples/sim/atomic_action/` | Geometric augmentation | Sampling metadata | Preserved task inputs |
|---|---|---|---|
| `pickup.py` | Select antipodal candidates returned by `AntipodalGraspPoseGenerator.get_valid_grasp_poses()`, after pickup IK and symmetric-roll screening | `grasp`: candidate ID, candidate counts, reuse | Approach direction, pre-grasp distance, lift height |
| `axis_align.py` | Select generator candidates within the action's preferred alignment-grasp set, then apply the existing symmetric/upright pose adjustments | `grasp`: candidate ID, candidate counts, reuse | Object internal axis and requested target axis |
| `hand_over.py` | Independently select generator candidates for pickup and receiving grasps; the first hand uses its near projected object end and the receiver uses the opposite end at the predicted middle pose | `pickup_grasp` and `receive_grasp`: candidate ID, candidate counts, reuse, executing arm | Nearest-arm assignment, opposite-end constraint, lift height, final target |
| `handover.py` | Independently select generator candidates for pickup and receiving grasps; the first hand uses its near projected object end and the receiver uses the opposite end at the predicted middle pose | `pickup_grasp` and `receive_grasp`: candidate ID, candidate counts, reuse, executing arm | Nearest-arm assignment, opposite-end constraint, lift height, final target |
| `open_door.py` | Select generator candidates on the door handle and replan the hinge-following motion | `grasp`: candidate ID, candidate counts, reuse | Hinge geometry and requested opening fraction |
| `press.py` | Sample a contact-frame roll in `[-pi, pi]` radians around the press pose's local z-axis | `roll`: per-row contact roll in radians | Contact position, local z-axis, approach distance, press distance |
| `slide.py` | Select generator candidates on the handle for both pull and push; push plans from the handle pose observed after pull replay | `grasp`: candidate ID, candidate counts, reuse | Pull/push direction and translation distance |
Expand Down Expand Up @@ -96,7 +96,7 @@ Every listed entry point accepts the same branch controls:
For example:

```bash
python scripts/tutorials/atomic_action/pickup.py \
embodichain run-example sim/atomic_action/pickup \
--affordance_branches 4 \
--sampling_seed 7 \
--sampling_attempt 0 \
Expand All @@ -110,9 +110,9 @@ also support the same roll augmentation. Press, Slide, and OpenDoor use
`strategy="ik_interp"` to preserve their required Cartesian contact/path
samples; their exact-path segments do not use the `--planner` backend.

Each tutorial logs the selected candidate and reuse status, or the sampled roll,
Each example logs the selected candidate and reuse status, or the sampled roll,
for every row before replay. HandOver also logs the arm for each grasp stage.
The tutorials replay only when every row has a successful plan. Planning
The examples replay only when every row has a successful plan. Planning
success is not a physical task-success measurement.

The shared parser accepts `--num_envs=1` as the default sentinel; otherwise
Expand Down
49 changes: 26 additions & 23 deletions docs/source/overview/sim/atomic_actions/builtin_actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
```{currentmodule} embodichain.lab.sim.atomic_actions
```

EmbodiChain ships thirteen built-in action implementations with stable skill IDs;
EmbodiChain ships built-in action implementations with stable skill IDs;
`AtomicActionEngine` creates and registers a fresh instance of every built-in by
default. Applications select them by stable skill ID rather than registering
routine instances themselves.
Expand Down Expand Up @@ -36,8 +36,9 @@ multi-stage in-hand manipulation require additional command types and segments.

## Visual catalog

The animations below are the focused simulator demos under
`scripts/tutorials/atomic_action/`.
The animations below are the runnable simulator demonstrations under
`examples/sim/atomic_action/`. The two teaching walkthroughs are described in
{doc}`/tutorial/atomic_actions`.

::::{grid} 1 2 2 2
:gutter: 2
Expand Down Expand Up @@ -366,7 +367,7 @@ Use an explicit pose for a fixed target, `SceneEntityPose` for a tracked target,
or `(B, N, 4, 4)` for intermediate waypoints. The action does not command an end
effector/hand resource.

**Example:** `scripts/tutorials/atomic_action/move_end_effector.py`
**Example:** `examples/sim/atomic_action/move_end_effector.py`

(builtin-move-joints)=

Expand Down Expand Up @@ -403,7 +404,7 @@ explicit_goal = JointPositionGoal(target=home_qpos)
named_goal = JointPositionGoal(target="home")
```

**Example:** `scripts/tutorials/atomic_action/move_joints.py`
**Example:** `examples/sim/atomic_action/move_joint.py`

(builtin-pick-up)=

Expand Down Expand Up @@ -473,10 +474,10 @@ generation or trajectory determinism. Explicit goal grasps and
`ObjectSemantics.entity` without an ID is a deprecated compatibility path. Its
live pose does not create an automatic scene dependency.

**Example:** `scripts/tutorials/atomic_action/pickup.py` currently exercises the
**Example:** `examples/sim/atomic_action/pickup.py` currently exercises the
deprecated entity-only fallback. For canonical snapshot grounding and moving
target recovery, see
`scripts/tutorials/atomic_action/moving_target_recovery.py`.
`examples/sim/atomic_action/moving_target_recovery.py`.

### Picking articulated targets

Expand Down Expand Up @@ -531,7 +532,7 @@ Set `root_props=ArticulationRootPropertiesCfg(fixed_base=False)`. Left at the
default, `plan_success` still reports success and the whole trajectory replays
while the object never moves, so verify the target pose rather than the plan.

`scripts/tutorials/atomic_action/pickup_rubiks_cube.py` demonstrates both on a
`examples/sim/atomic_action/pickup_rubiks_cube.py` demonstrates both on a
Rubik's cube whose `top_turn` joint is locked with a stiff position drive so the
two layers grasp as one body. Grasps are sampled from the `lower_two_layers`
link, the joint's parent.
Expand Down Expand Up @@ -579,7 +580,7 @@ and `(B, 3)` respectively. Zero or non-finite axes are
rejected, and exactly opposite axes use a deterministic 180-degree rotation
rather than an unstable cross-product direction.

**Example:** `scripts/tutorials/atomic_action/axis_align.py` provides
**Example:** `examples/sim/atomic_action/axis_align.py` provides
`--alignment upright` (align object-local X to world Z) and
`--alignment horizontal_align` (align object-local X to world Y).

Expand Down Expand Up @@ -618,7 +619,9 @@ sampling remains in `MotionPolicy`. In a vectorized batch, rows
where another manipulator holds the same semantic object or live entity are
marked unsuccessful and held in place.

**Example:** `scripts/tutorials/atomic_action/move_held_object.py`
**Examples:** `examples/sim/atomic_action/move_held_object.py` provides the fixed
cube scene (`--skill move_held_object`);
`examples/sim/atomic_action/move_held_object_cup.py` preserves the paper-cup sequence.

(builtin-pour)=

Expand Down Expand Up @@ -649,7 +652,7 @@ planner can chain the outbound and return legs.
| Motion | rotate by `rotate_angle`, then reverse by the same angle, with the hand held at `grasp` |
| Effect | none; the existing attachment is preserved |

**Example:** `scripts/tutorials/atomic_action/pour.py` compiles a horizontal
**Example:** `examples/sim/atomic_action/pour.py` compiles a horizontal
`PickUp` followed by `Pour`.

(builtin-push-object)=
Expand Down Expand Up @@ -724,7 +727,7 @@ The bound `primary.grasp` endpoint must provide `open` and `grasp`. Important
| `cartesian_waypoint_count` | Fixed-orientation translation keyframes per segment |
| `preserve_current_object_orientation` | Keep the observed object orientation while using the target translation |

**Example:** `scripts/tutorials/atomic_action/place.py`
**Example:** `examples/sim/atomic_action/place.py`

(builtin-assemble)=

Expand All @@ -748,7 +751,7 @@ temporarily falls back to the affordance's `base_object_entity` with a
deprecation warning; that fallback is not a scene dependency. Planning declares
the same detach effect as a normal place.

**Example:** `scripts/tutorials/atomic_action/assemble.py` currently exercises
**Example:** `examples/sim/atomic_action/assemble.py` currently exercises
the legacy `base_object_entity` fallback and is not the canonical `base_pose`
form. It remains a compatibility example until the registry-backed tutorial
migration.
Expand Down Expand Up @@ -794,7 +797,7 @@ generic `ActionBinding`, and the action keeps the gripper closed for all arm
motion segments. Applications that require force/contact confirmation must
verify it externally.

**Example:** `scripts/tutorials/atomic_action/press.py` loads
**Example:** `examples/sim/atomic_action/press.py` loads
`Microwave/microwave.urdf` and targets `button_link`, whose parent prismatic
joint is `button_joint`. It requests `MotionPolicy.sample_count=256` with
`PressOptions.hand_interp_steps=12`, then holds the final pose for 256
Expand Down Expand Up @@ -862,7 +865,7 @@ translation axis belongs to `SlideAffordance`; the bound `primary.grasp`
endpoint must provide `open` and `grasp`. Reach, pull/push, and push-return use
axis-aligned Cartesian samples rather than sparse joint-space endpoints.

**Example:** `scripts/tutorials/atomic_action/slide.py`
**Example:** `examples/sim/atomic_action/slide.py`
plans and replays a pull first, then replans a push from the drawer's measured
post-pull link pose.

Expand Down Expand Up @@ -918,7 +921,7 @@ segment start plus one for every configured door-arc keyframe; the full motion
policy therefore needs
`sample_count >= 2 * hand_interp_steps + door_waypoint_count + 7`.

**Example:** `scripts/tutorials/atomic_action/open_door.py` configures only the
**Example:** `examples/sim/atomic_action/open_door.py` configures only the
`handle_link` target in `Microwave/microwave.urdf`. Automatic traversal resolves
`door_hinge` through `door_handle_fixed_joint`. Its absolute `--open_angle`
value is normalized against the resolved hinge limits and passed as the goal's
Expand Down Expand Up @@ -981,7 +984,7 @@ completion therefore means commanded motion completion only. Applications that
need semantic success must observe button/contact or articulation state and
verify it outside the side-effect-free planner.

**Example:** `scripts/tutorials/atomic_action/twist.py` loads
**Example:** `examples/sim/atomic_action/twist.py` loads
`Microwave/microwave.urdf` and targets `knob_link`, whose parent revolute joint
is `knob_joint`. It requests `MotionPolicy.sample_count=256` with
`TwistOptions.hand_interp_steps=20`, then holds the final pose for 256
Expand Down Expand Up @@ -1040,7 +1043,7 @@ corresponding participant slots. Coordinated dual-arm planning with
supported by the cuRobo backend; use the supported IK/interpolation path for
this primitive.

**Example:** `scripts/tutorials/atomic_action/coordinated_pickment.py`
**Example:** `examples/sim/atomic_action/coordinated_pickment.py`

(builtin-coordinated-placement)=

Expand Down Expand Up @@ -1074,7 +1077,7 @@ The placing/support motion and grasp endpoints come exclusively from the
corresponding participant slots. The same cuRobo restriction as coordinated
pickment applies to dual-arm `strategy="motion_gen"` planning.

**Example:** `scripts/tutorials/atomic_action/coordinated_placement.py`
**Example:** `examples/sim/atomic_action/coordinated_placement.py`

(builtin-hand-over)=

Expand Down Expand Up @@ -1137,7 +1140,7 @@ interval between them as a likely path- or collision-planning failure.
As with the other coordinated primitive, cuRobo does not currently support its
dual-arm `strategy="motion_gen"` path.

**Example:** `scripts/tutorials/atomic_action/hand_over.py`
**Example:** `examples/sim/atomic_action/handover.py`

## Running the demos

Expand All @@ -1146,9 +1149,9 @@ keyboard prompts and combine it with `--headless --device cpu` for a headless
run that records video under `outputs/videos`:

```bash
python scripts/tutorials/atomic_action/move_end_effector.py --headless --auto_play --device cpu
python scripts/tutorials/atomic_action/pickup.py --headless --auto_play --device cpu
python scripts/tutorials/atomic_action/hand_over.py --headless --auto_play --device cpu
embodichain run-example sim/atomic_action/move_end_effector --headless --auto_play --device cpu
embodichain run-example sim/atomic_action/pickup --headless --auto_play --device cpu
embodichain run-example sim/atomic_action/handover --headless --auto_play --device cpu
```

The scripts share a `--planner` selector for the non-neural backends
Expand Down
Loading
Loading