Skip to content

add articulation affordance - #509

Open
matafela wants to merge 4 commits into
mainfrom
cj/add-articulation-affordance
Open

add articulation affordance#509
matafela wants to merge 4 commits into
mainfrom
cj/add-articulation-affordance

Conversation

@matafela

Copy link
Copy Markdown
Collaborator

Description

  • Add articulation affordance
  • Add turn knob action

Type of change

  • Enhancement (non-breaking change which improves an existing functionality)

Checklist

  • I have run the black . command to format the code base.
  • I have made corresponding changes to the documentation
  • I have added tests that prove my fix is effective or that my feature works
  • Dependencies have been updated, if applicable.

Copilot AI lite review requested due to automatic review settings August 12, 2026 11:36
@matafela
matafela requested a review from skywhite1024 August 12, 2026 11:38
@greptile-apps

greptile-apps Bot commented Aug 12, 2026

Copy link
Copy Markdown

Greptile Summary

The PR adds articulation-backed affordances and a built-in action for planning knob-turn trajectories, together with a microwave tutorial, documentation, and tests. The vertical-axis grasp-frame limitation previously reported remains on the current head.

  • Adds TurnAffordance, TurnKnobGoal, options, planning logic, and built-in registration.
  • Adds articulation-link geometry support to AntipodalAffordance.
  • Adds a microwave asset wrapper, runnable tutorial, documentation, and action tests.

Confidence Score: 4/5

The PR does not appear safe to merge because vertical turn axes can still abort trajectory compilation, including the configuration exercised by the new microwave workflow.

The grasp-frame implementation explicitly raises when the transformed turn axis is parallel to world up, so valid articulation configurations can fail before motion planning.

Files Needing Attention: embodichain/lab/sim/atomic_actions/affordance.py and scripts/tutorials/atomic_action/turn_knob.py

Important Files Changed

Filename Overview
embodichain/lab/sim/atomic_actions/affordance.py Adds articulation-backed antipodal and turn affordances, but TurnAffordance still rejects vertical world-space turn axes and blocks affected plans.
embodichain/lab/sim/atomic_actions/primitives/turn_knob.py Adds the six-segment knob-turn planner and registration contract; its planning path remains dependent on the unresolved vertical-axis grasp-frame construction.
scripts/tutorials/atomic_action/turn_knob.py Adds the microwave demonstration, whose configured articulation axis can still reach the outstanding grasp-frame failure.
tests/sim/atomic_actions/test_affordance.py Adds affordance coverage but codifies rejection of vertical axes rather than covering a valid fallback frame.

Reviews (4): Last reviewed commit: "update" | Re-trigger Greptile

Comment thread embodichain/lab/sim/atomic_actions/affordance.py
return parser.parse_args()


def create_microwave(sim) -> Articulation:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Public API annotations are incomplete

The new create_microwave helper leaves sim untyped, while MicrowaveOven.__init__ declares data_root as str despite accepting None, giving type checkers and API consumers incomplete or inaccurate signatures.

Context Used: AGENTS.md (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: scripts/tutorials/atomic_action/turn_knob.py
Line: 78

Comment:
**Public API annotations are incomplete**

The new `create_microwave` helper leaves `sim` untyped, while `MicrowaveOven.__init__` declares `data_root` as `str` despite accepting `None`, giving type checkers and API consumers incomplete or inaccurate signatures.

**Context Used:** AGENTS.md ([source](https://github.com/dexforce/embodichain/blob/main/AGENTS.md))

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Codex Fix in Claude Code

Copilot AI left a comment

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.

Pull request overview

This PR extends the atomic-actions subsystem with articulation-backed affordances and a new TurnKnob primitive, enabling deterministic knob-turn planning from an articulation link’s live pose/geometry and providing an end-to-end tutorial + docs/tests.

Changes:

  • Added TurnAffordance (articulation-link knob semantics) and TurnKnob atomic action (approach → reach → close → turn → open → retract).
  • Extended AntipodalAffordance to optionally resolve mesh/pose from an articulation link.
  • Added tests, docs, and a new tutorial script demonstrating knob turning on a microwave articulation.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tests/sim/atomic_actions/test_affordance.py Adds coverage for articulation-backed antipodal affordances and TurnAffordance grasp-pose behavior.
tests/sim/atomic_actions/test_actions.py Adds coverage for TurnKnob planning/segments and validates affordance-type requirements.
scripts/tutorials/atomic_action/tutorial_utils.py Adds optional init_qpos support for tutorial robot setup.
scripts/tutorials/atomic_action/turn_knob.py New tutorial demonstrating TurnKnob on a microwave articulation.
embodichain/lab/sim/atomic_actions/primitives/turn_knob.py New TurnKnob primitive, goal, and options implementation.
embodichain/lab/sim/atomic_actions/primitives/init.py Registers TurnKnob as a built-in primitive and exports symbols.
embodichain/lab/sim/atomic_actions/affordance.py Adds TurnAffordance and extends AntipodalAffordance to support articulation-link geometry/pose resolution.
embodichain/lab/sim/atomic_actions/init.py Re-exports TurnAffordance and TurnKnob* public API.
embodichain/data/assets/obj_assets.py Adds a MicrowaveOven dataset helper entry for the tutorial asset.
docs/source/overview/sim/atomic_actions/index.md Updates built-in action count reference (now 10).
docs/source/overview/sim/atomic_actions/builtin_actions.md Documents TurnKnob contract and adds it to the built-in actions table.
docs/source/api_reference/embodichain/embodichain.lab.sim.atomic_actions.primitives.rst Adds API reference entries for TurnKnob, TurnKnobGoal, TurnKnobOptions.
Suppressed comments (2)

embodichain/lab/sim/atomic_actions/primitives/turn_knob.py:206

  • TurnKnob allocates the output trajectory with self.n_envs rows, but fills it from context.last_qpos and hand_* tensors built with context.batch_size. This will error if those batch sizes ever diverge. Allocate using context.batch_size (or link_pose.shape[0]) so the tensor shapes are consistent within _plan.
        full = torch.empty(
            (self.n_envs, sum(lengths), self.robot_dof),
            dtype=context.robot.qpos.dtype,
            device=self.device,
        )
        full[:] = context.last_qpos.unsqueeze(1)

scripts/tutorials/atomic_action/tutorial_utils.py:759

  • create_ur5_gripper_robot_cfg adds an init_qpos parameter but the docstring Args section doesn't document it, which makes the function contract unclear in the tutorial utilities.
    init_qpos: Sequence[float] | None = None,
) -> RobotCfg:
    """Build a UR5 arm + DH_PGI_140_80 gripper robot configuration.

    The arm is taken from :class:`~embodichain.lab.sim.robots.ur_robot.URRobotCfg`

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +328 to +336
y_axis = torch.tensor(
[0.0, 0.0, 1.0], dtype=torch.float32, device=device
).expand_as(z_axis)
x_axis = torch.linalg.cross(y_axis, z_axis, dim=1)
if torch.any(torch.linalg.vector_norm(x_axis, dim=1) <= 1.0e-6):
raise ValueError(
"TurnAffordance turn axis must not be parallel to world (0, 0, 1)."
)
x_axis = torch.nn.functional.normalize(x_axis, dim=1)
Comment on lines +123 to +130
link_pose = affordance.get_link_pose().to(
device=self.device, dtype=torch.float32
)
if link_pose.shape != (self.n_envs, 4, 4):
raise ValueError(
"Articulation link pose must have shape "
f"({self.n_envs}, 4, 4), got {tuple(link_pose.shape)}."
)
Comment on lines 224 to 228
def add_ur5_gripper_robot(
sim: SimulationManager,
init_pos: Sequence[float] = (0.0, 0.0, 0.0),
init_qpos: Sequence[float] | None = None,
) -> Robot:
Copilot AI review requested due to automatic review settings August 14, 2026 03:39

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated no new comments.

Suppressed comments (3)

scripts/tutorials/atomic_action/tutorial_utils.py:784

  • init_qpos is accepted as an arbitrary sequence, but there is no validation that it matches the expected number of joints for this tutorial robot (arm + gripper). A length mismatch will fail later during robot initialization/reset with a harder-to-debug error.
    qpos = (
        [0.0, -1.57, 1.57, -1.57, -1.57, 0.0, 0.0, 0.0]
        if init_qpos is None
        else list(init_qpos)
    )

scripts/tutorials/atomic_action/tutorial_utils.py:230

  • The new init_qpos parameter is not mentioned in the function docstring, so it’s easy to miss that callers can override the default tutorial joint pose.
    """Add the standard UR5 plus PGI gripper tutorial robot.

scripts/tutorials/atomic_action/tutorial_utils.py:758

  • init_qpos was added to the signature, but the docstring description doesn’t mention what it does. Adding a short note here makes the new capability discoverable without scanning the whole function.

This issue also appears on line 780 of the same file.

    """Build a UR5 arm + DH_PGI_140_80 gripper robot configuration.

Copilot AI review requested due to automatic review settings August 17, 2026 01:48

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated no new comments.

Suppressed comments (2)

embodichain/lab/sim/atomic_actions/primitives/turn_knob.py:115

  • TurnKnob mixes batch dimensions: hand joint targets are built with context.batch_size, but the rest of the planner (link_pose shape check, trajectory allocation) assumes self.n_envs from the bound robot. If PlanningContext.batch_size differs (env subset planning), this can trigger shape mismatches or incorrect broadcasting.
        hand_open_qpos = end_effector.joint_positions(
            OPEN_COMMAND,
            n_envs=context.batch_size,
            device=self.device,
            dtype=context.robot.qpos.dtype,
        )

embodichain/data/assets/obj_assets.py:257

  • The new MicrowaveOven dataset block has formatting that deviates from the surrounding DataDescriptor pattern (closing paren on the same line, extra whitespace) and is likely to fail black/linters. It also removes the blank line separation between dataset classes.
        data_descriptor = o3d.data.DataDescriptor(
            os.path.join(EMBODICHAIN_DOWNLOAD_PREFIX, obj_assets, "MicrowaveOven.zip"),
            "5c90aa6911b445811fc81d704d461057",        )
        prefix = type(self).__name__

Copilot AI review requested due to automatic review settings August 17, 2026 03:31

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated no new comments.

Suppressed comments (2)

embodichain/lab/sim/atomic_actions/affordance.py:332

  • TurnAffordance.get_grasp_pose constructs a rotation matrix with y_axis fixed to world up without re-orthogonalizing it against z_axis. If the provided turn_axis has any world-up component, y_axis will not be perpendicular to z_axis and the resulting pose will not be a valid orthonormal transform (can break downstream FK/IK / relative-rotation math). Project world-up onto the plane orthogonal to z_axis and normalize before computing x_axis.
        y_axis = torch.tensor(
            [0.0, 0.0, 1.0], dtype=torch.float32, device=device
        ).expand_as(z_axis)
        x_axis = torch.linalg.cross(y_axis, z_axis, dim=1)
        if torch.any(torch.linalg.vector_norm(x_axis, dim=1) <= 1.0e-6):

embodichain/data/assets/obj_assets.py:246

  • MicrowaveOven dataset docstring points to "MicrowaveOven/microwave_oven.urdf", but this PR’s new TurnKnob tutorial uses "MicrowaveOven/microwave_oven_with_inertials.urdf". This mismatch is confusing for users trying to locate the correct asset path via get_data_path(). Consider updating the docstring to match the tutorial asset (or mention both URDFs if both are shipped).
class MicrowaveOven(EmbodiChainDataset):
    """get_data_path("MicrowaveOven/microwave_oven.urdf")"""

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants