Skip to content
Closed
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
50 changes: 50 additions & 0 deletions .agents/skills/review-pr/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,56 @@ impact are established.
file or subsystem coverage ledger and review dependency foundations before
their consumers.

## Explicitly requested PR publication

Keep review read-only unless the user explicitly asks to publish the findings
to a remote PR. A request to review, audit, or approve a change alone does not
authorize remote comments or a review event.

When publication is explicitly requested:

1. Verify the repository, PR number, base branch, head commit, and changed
files before posting. If the target is not unambiguous, ask for the PR
identifier rather than guessing.
2. Write every comment and review summary posted to the remote PR in English.
The conversational review may use the user's language, but translate the
finding faithfully before publication. Preserve code identifiers, paths,
and short log excerpts when they are needed as evidence.
3. Publish each actionable finding as an inline comment on the smallest
relevant changed line when the hosting integration supports it. Put
findings that cannot be anchored to a changed line in a top-level review
summary. Preserve the finding priority and evidence in the posted text.
4. Use a normal comment review by default. Submit `request changes` or
`approve` only when the user explicitly requests that exact review event;
never infer approval authority from a review request.
5. Use an available GitHub connector or authenticated `gh` workflow. Do not
claim that comments were posted if the integration is unavailable or a
request fails. If publication is unavailable, return the complete,
copy-ready Markdown review instead.
6. Report the published comment or review URLs, any findings that were not
posted, and any partial failures. Avoid duplicate comments when the same
finding has already been posted for the same head commit.

Do not publish speculative open questions or residual risks as defects. They
may be included in the top-level summary only when clearly labeled as such.

### Trigger phrases

The skill may be selected for review requests containing terms such as
`review`, `audit`, `inspect`, `assess`, or `approve`. These terms authorize
analysis only; they do not authorize remote PR changes.

Publication requires an explicit remote-posting request, for example:

- “publish/post/submit the review findings to the PR”
- “add inline comments to the PR”
- “leave the review comments on GitHub”
- “把评审意见提交到 PR” or “在 PR 上添加评审评论”

Phrases such as “review this PR”, “approve this PR”, or “给我评审建议” do not
by themselves trigger publication. In particular, “approve this PR” selects
the review task but does not authorize an `approve` review event.

## 1. Resolve the review target

Read the applicable `AGENTS.md` instructions first. Determine the exact delta
Expand Down
10 changes: 10 additions & 0 deletions docs/source/api_reference/public_api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,7 @@ embodichain.lab.gym.envs.managers.actions
QposTerm
QposDenormalizedTerm
QposNormalizedTerm
EefPoseGripperTerm
EefPoseTerm
QvelTerm
QfTerm
Expand Down Expand Up @@ -2190,6 +2191,15 @@ embodichain_tasks.configs

get_config_path

embodichain_tasks.manipulation.repeated_pick_place
------------------------------------------------

.. currentmodule:: embodichain_tasks.manipulation.repeated_pick_place

.. autosummary::

RepeatedPickPlaceRlinfEnv

embodichain_tasks.manipulation.push_cube
----------------------------------------

Expand Down
66 changes: 58 additions & 8 deletions embodichain/lab/gym/envs/managers/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@

from embodichain.lab.sim.types import EnvAction
from embodichain.utils.math import matrix_from_euler, matrix_from_quat

from .action_manager import ActionTerm
from .cfg import ActionTermCfg

Expand All @@ -54,12 +55,13 @@
__all__ = [
"DefaultJointPositionTerm",
"DeltaQposTerm",
"QposTerm",
"EefPoseGripperTerm",
"EefPoseTerm",
"QfTerm",
"QposDenormalizedTerm",
"QposNormalizedTerm",
"EefPoseTerm",
"QposTerm",
"QvelTerm",
"QfTerm",
]


Expand Down Expand Up @@ -259,6 +261,7 @@ def __init__(self, cfg: ActionTermCfg, env: EmbodiedEnv):
super().__init__(cfg, env)
self._scale = cfg.params.get("scale", 1.0)
self._pose_dim = cfg.params.get("pose_dim", 7) # 6 for euler, 7 for quat
self._part_name = cfg.params.get("part_name")

@property
def input_key(self) -> str:
Expand Down Expand Up @@ -286,21 +289,68 @@ def process_action(self, action: torch.Tensor) -> EnvAction:
f"EEF pose action must be 6D or 7D, got {scaled.shape[-1]}D"
)
# Batch IK: robot.compute_ik supports (num_envs, 4, 4) pose and (num_envs, dof) seed
joint_seed = current_qpos
part_joint_ids = None
if self._part_name is not None:
part_joint_ids = self._env.robot.get_joint_ids(
self._part_name, remove_mimic=True
)
joint_seed = current_qpos[:, part_joint_ids]
ret, qpos_ik = self._env.robot.compute_ik(
pose=target_pose,
joint_seed=current_qpos,
)
# Fallback to current_qpos where IK failed
result_qpos = torch.where(
ret.unsqueeze(-1).expand_as(qpos_ik), qpos_ik, current_qpos
joint_seed=joint_seed,
name=self._part_name,
)
if part_joint_ids is None or qpos_ik.shape[-1] == current_qpos.shape[-1]:
result_qpos = torch.where(
ret.unsqueeze(-1).expand_as(qpos_ik), qpos_ik, current_qpos
)
else:
result_qpos = current_qpos.clone()
selected_qpos = torch.where(
ret.unsqueeze(-1).expand_as(qpos_ik),
qpos_ik,
current_qpos[:, part_joint_ids],
)
result_qpos[:, part_joint_ids] = selected_qpos
return TensorDict(
{"qpos": result_qpos, "ik_success": ret},
batch_size=[batch_size],
device=self.device,
)


class EefPoseGripperTerm(EefPoseTerm):
"""Convert a 7D Cartesian action into arm IK targets and gripper joints.

The action is ``[x, y, z, roll, pitch, yaw, gripper]``. The first six
values use the same absolute-pose convention as :class:`EefPoseTerm`; the
final value is normalized from ``[-1, 1]`` to the active hand joint limits.
"""

@property
def action_dim(self) -> int:
return 7

def process_action(self, action: torch.Tensor) -> EnvAction:
if action.shape[-1] != 7:
raise ValueError(
f"EefPoseGripperTerm expects 7D actions, got {action.shape[-1]}D."
)

arm_action = action[..., :6]
result = super().process_action(arm_action)
hand_joint_ids = self._env.robot.get_joint_ids("hand", remove_mimic=True)
if not hand_joint_ids:
raise ValueError("EefPoseGripperTerm requires a robot hand control part.")

limits = self._env.robot.body_data.qpos_limits[0, hand_joint_ids]
gripper = action[..., 6:7].clamp(-1.0, 1.0)
hand_qpos = limits[:, 0] + (gripper + 1.0) * 0.5 * (limits[:, 1] - limits[:, 0])
result["qpos"][:, hand_joint_ids] = hand_qpos.expand(-1, len(hand_joint_ids))
return result


class QvelTerm(ActionTerm):
"""Joint velocity action: scale * action -> qvel.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
embodiment_id: franka_panda_vla

simulation:
class_type: FrankaPanda
robot_type: panda
uid: FrankaPanda
solver_cfg:
arm:
class_type: PytorchSolver
end_link_name: fr3_hand_tcp
root_link_name: base
tcp:
- [1.0, 0.0, 0.0, 0.0]
- [0.0, 1.0, 0.0, 0.0]
- [0.0, 0.0, 1.0, 0.0]
- [0.0, 0.0, 0.0, 1.0]
num_samples: 30
link_attrs:
newton_gripper_contacts:
link_names_expr: ["fr3_leftfinger|fr3_rightfinger"]
attrs:
collision_props:
backend: newton
condim: 4
material_props:
backend: newton
ke: 40000.0
kd: 400.0
torsional_friction: 0.1
rolling_friction: 0.01

sensor:
- sensor_type: Camera
uid: cam_high
width: 640
height: 480
intrinsics: [488.1665, 488.1665, 320.0, 240.0]
extrinsics:
eye: [-1.2, -1.2, 1.25]
target: [-0.1, 0.15, 0.20]
up: [0.0, 0.0, 1.0]

skill_profile:
contract_id: single_arm_parallel_gripper
profile_id: franka_panda
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
environment_id: repeated_pick_place_rlinf
physics: default
max_episodes: 1
max_episode_steps: 240
num_envs: 1
arena_space: 2.5

simulation:
light:
direct:
- uid: main_light
light_type: sun
color: [0.6, 0.6, 0.6]
intensity: 5.0
direction: [0.0, 0.0, -1.0]
background:
- uid: target
shape:
shape_type: Cube
size: [0.11, 0.11, 0.01]
body_type: kinematic
init_pos: [-0.40, 0.48, 0.005]
attrs:
material_props: {static_friction: 0.0, dynamic_friction: 0.0}
rigid_object:
- uid: cube
shape:
shape_type: Cube
size: [0.05, 0.05, 0.05]
body_type: dynamic
init_pos: [-0.42, -0.08, 0.025]
attrs:
mass_props: {mass: 0.05}
rigid_props: {linear_damping: 0.2, angular_damping: 0.2}
material_props: {dynamic_friction: 0.97, static_friction: 0.99}

env:
sim_steps_per_control: 4
observations:
eef_pose:
func: get_robot_eef_pose
mode: add
name: robot/eef_pose
params: {part_name: arm, position_only: false}
actions:
eef_pose_gripper:
func: EefPoseGripperTerm
params: {scale: 1.0, pose_dim: 6, part_name: arm}
rewards:
success_bonus:
func: success_reward
mode: add
weight: 10.0
params: {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
id: RepeatedPickPlaceRlinf-Franka-v1

environment:
component: env.rlinf.yaml

embodiment:
component: ../../../components/embodiments/franka_panda_vla.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
id: TaskProgramRepeatedPickPlaceRlinfExpert-Franka-v1

environment:
component: env.yaml

task_program:
program: task_program/program.yaml
integration: task_program/integration.yaml
execution_policy: ../../../components/execution_policies/trajectory_open_loop.yaml

embodiment:
component: ../../../components/embodiments/franka_panda_vla.yaml
overrides:
init_rot: [0.0, 0.0, 180.0]
Loading
Loading