Benchmark seven more atomic skills - #642
Yuan-Xinyi wants to merge 1 commit into
Conversation
Extend the suite from five skills to twelve, each reporting the full success ladder and a task metric defined by what the skill is supposed to achieve: open_door and slide on the driven joint's travel, press on button depression, twist on knob rotation, pour on the container's tilt, axis_align on the residual axis angle, and hand_over on delivery distance without a drop. Two skills did not run at all, and neither failure was in the benchmarks. Press and Slide plan Cartesian-linear segments, which #640 now requires to use strategy='ik_interp', but their tutorials still request 'motion_gen'; the press tutorial fails on main today with the same error the benchmark hit. Both tutorials and both benchmarks now ask for the strategy the primitives require. Verified by running the press tutorial itself, which completes again. OpenDoor replays at four times the shared rate. At the default the arm trails the plan by 0.66 rad and the tracking gate rejects the run even though the door reaches its commanded angle. Raising only the replay rate, never the task tolerance, brings tracking inside the gate and the hinge to within 0.001 rad. Twist remains a genuine failure and is reported as one. The planned end-effector rotation about the knob axis is exactly the commanded 0.7854 rad with correct sign at 0.026 rad of arm tracking error, so the skill plans correctly; the gripper wedges against the knob instead of holding it, and the knob joint has near-zero stiffness, so it over-rotates and keeps turning during retract. Neither the gate nor the scene stiffness was adjusted to hide this. BENCHMARK_REPORT.md records the measured ladder, timings and task values per skill, the bugs found and fixed, and the threshold questions that need a decision rather than a default. Not built: push_object has no tutorial to source a scene from, and the two coordinated dual-arm skills are left for a follow-up. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
| "open_door": "scripts.benchmark.atomic_action.open_door_benchmark", | ||
| "press": "scripts.benchmark.atomic_action.press_benchmark", | ||
| "slide": "scripts.benchmark.atomic_action.slide_benchmark", | ||
| "twist": "scripts.benchmark.atomic_action.twist_benchmark", | ||
| "axis_align": "scripts.benchmark.atomic_action.axis_align_benchmark", | ||
| "pour": "scripts.benchmark.atomic_action.pour_benchmark", | ||
| "hand_over": "scripts.benchmark.atomic_action.hand_over_benchmark", |
There was a problem hiding this comment.
In-process actions lack required arguments
The new actions cannot run through --in_process: _make_child_args() omits their case-selection fields, but each new run_all_benchmarks() accesses its field directly before applying the smoke profile. For example, --action open_door --in_process --profile smoke raises AttributeError on args.door_cases instead of producing a report. Populate the new action-specific defaults when constructing the child namespace, preferably using each module's argument parser.
Prompt To Fix With AI
This is a comment left during a code review.
Path: scripts/benchmark/atomic_action/run_benchmark.py
Line: 46-52
Comment:
**In-process actions lack required arguments**
The new actions cannot run through `--in_process`: `_make_child_args()` omits their case-selection fields, but each new `run_all_benchmarks()` accesses its field directly before applying the smoke profile. For example, `--action open_door --in_process --profile smoke` raises `AttributeError` on `args.door_cases` instead of producing a report. Populate the new action-specific defaults when constructing the child namespace, preferably using each module's argument parser.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| unit_axis = axis / torch.linalg.vector_norm(axis) | ||
| sign = torch.sign(torch.dot(vector, unit_axis.to(vector.dtype))) | ||
| if float(sign) == 0.0: | ||
| sign = torch.ones_like(sign) | ||
| return float(angle * sign) |
There was a problem hiding this comment.
Off-axis tumbles count as pours
This helper returns the total relative rotation angle; the requested axis only determines its sign. A 45-degree tumble perpendicular to the pour axis therefore returns positive 45 degrees because the zero dot product is forced to a positive sign. The 45-degree benchmark can consequently credit an off-axis slip as a successful pour. Measure rotation about the requested axis and add a perpendicular-axis counterexample test.
Prompt To Fix With AI
This is a comment left during a code review.
Path: scripts/benchmark/atomic_action/pour_benchmark.py
Line: 146-150
Comment:
**Off-axis tumbles count as pours**
This helper returns the total relative rotation angle; the requested axis only determines its sign. A 45-degree tumble perpendicular to the pour axis therefore returns positive 45 degrees because the zero dot product is forced to a positive sign. The 45-degree benchmark can consequently credit an off-axis slip as a successful pour. Measure rotation about the requested axis and add a perpendicular-axis counterexample test.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| rotation_error = abs( | ||
| abs(trace.peak_signed_displacement) - abs(case.rotate_angle_rad) | ||
| ) |
There was a problem hiding this comment.
Taking the absolute value of both rotations removes the direction check from the signed task metric. If a slipping object rotates −45 degrees around the requested axis for the +45-degree case, this reports zero error and task_success=True. The measurement and primitive use the same axis convention, so compare the signed achieved rotation with the signed command rather than their magnitudes.
| rotation_error = abs( | |
| abs(trace.peak_signed_displacement) - abs(case.rotate_angle_rad) | |
| ) | |
| rotation_error = abs( | |
| trace.peak_signed_displacement - case.rotate_angle_rad | |
| ) |
Prompt To Fix With AI
This is a comment left during a code review.
Path: scripts/benchmark/atomic_action/pour_benchmark.py
Line: 294-296
Comment:
**Reversed pours pass the gate**
Taking the absolute value of both rotations removes the direction check from the signed task metric. If a slipping object rotates −45 degrees around the requested axis for the +45-degree case, this reports zero error and `task_success=True`. The measurement and primitive use the same axis convention, so compare the signed achieved rotation with the signed command rather than their magnitudes.
```suggestion
rotation_error = abs(
trace.peak_signed_displacement - case.rotate_angle_rad
)
```
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| min_z = state["min_z"] if state["min_z"] != float("inf") else None | ||
| dropped = min_z is not None and min_z < HANDOVER_DROP_Z_M | ||
| if trace is not None and ( | ||
| trace.max_tracking_error_rad > REPLAY_TRACKING_TOLERANCE_RAD | ||
| ): | ||
| ladder.execution_success = False | ||
| ladder.fail("execution_success", "controller_tracking_failure") | ||
| elif ladder.execution_success and trace is not None: | ||
| delivered = trace.settled_position <= HANDOVER_DELIVERY_TOLERANCE_M | ||
| ladder.task_success = delivered and not dropped |
There was a problem hiding this comment.
Tabletop drops escape detection
The drop threshold is below the support table, so it cannot detect an object dropped onto that table during transfer. The target lies inside the table footprint, only 0.10 m above its surface. An object resting near the target at z≈0.53 m passes both the 0.12 m delivery tolerance and the z≥0.35 m check despite losing its grasp prematurely. Track grasp retention or an unexpected return to the support surface during transfer, while allowing the primitive's intentional final release.
Knowledge Base Used: Simulation lab
Prompt To Fix With AI
This is a comment left during a code review.
Path: scripts/benchmark/atomic_action/hand_over_benchmark.py
Line: 238-247
Comment:
**Tabletop drops escape detection**
The drop threshold is below the support table, so it cannot detect an object dropped onto that table during transfer. The target lies inside the table footprint, only 0.10 m above its surface. An object resting near the target at z≈0.53 m passes both the 0.12 m delivery tolerance and the z≥0.35 m check despite losing its grasp prematurely. Track grasp retention or an unexpected return to the support surface during transfer, while allowing the primitive's intentional final release.
**Knowledge Base Used:** [Simulation lab](https://app.greptile.com/dexforce/-/custom-context/knowledge-base/dexforce/embodichain/-/docs/simulation-lab.md)
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| recorded = replay_trajectory_with_recording( | ||
| sim=sim, | ||
| robot=robot, | ||
| traj=traj, | ||
| args=args, | ||
| video_path=build_video_output_path( |
There was a problem hiding this comment.
Videos replay a different execution
The recorder uses four physics steps per waypoint, whereas OpenDoor is scored at 64 and the other new benchmarks at 16. For these contact tasks, that changes tracking and achieved motion—not just playback speed—so a video attached to a successful case can depict a different outcome. Record during the measured replay or give the recording path the same replay cadence and terminal hold settings.
Prompt To Fix With AI
This is a comment left during a code review.
Path: scripts/benchmark/atomic_action/open_door_benchmark.py
Line: 286-291
Comment:
**Videos replay a different execution**
The recorder uses four physics steps per waypoint, whereas OpenDoor is scored at 64 and the other new benchmarks at 16. For these contact tasks, that changes tracking and achieved motion—not just playback speed—so a video attached to a successful case can depict a different outcome. Record during the measured replay or give the recording path the same replay cadence and terminal hold settings.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| video_path = str(recorded) if recorded is not None else "" | ||
| video_paths.append(video_path) |
There was a problem hiding this comment.
Failed recordings consume video quota
A failed or unavailable recording is appended to video_paths as an empty string. With --record_video --record_failed_video and the default case limit, a planning failure with no trajectory can consume the quota, suppress a later recordable case, and inflate the reported video count. Append only nonempty recording paths, as the other new benchmarks do. Twist has the same issue.
Prompt To Fix With AI
This is a comment left during a code review.
Path: scripts/benchmark/atomic_action/slide_benchmark.py
Line: 318-319
Comment:
**Failed recordings consume video quota**
A failed or unavailable recording is appended to `video_paths` as an empty string. With `--record_video --record_failed_video` and the default case limit, a planning failure with no trajectory can consume the quota, suppress a later recordable case, and inflate the reported video count. Append only nonempty recording paths, as the other new benchmarks do. Twist has the same issue.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| state["pour_start_z"], | ||
| float(obj.get_local_pose(to_matrix=True)[0, 2, 3]), |
There was a problem hiding this comment.
Recording changes the height diagnostic
final_object_z_m is read after the optional reset and recording replay, while the rotation trace and starting height describe the scored run. Enabling recording can therefore replace this diagnostic with the height from a different execution—or the reset pose if recording cannot start. Capture the final height immediately after physical validation, before resetting the scene for video.
Prompt To Fix With AI
This is a comment left during a code review.
Path: scripts/benchmark/atomic_action/pour_benchmark.py
Line: 329-330
Comment:
**Recording changes the height diagnostic**
`final_object_z_m` is read after the optional reset and recording replay, while the rotation trace and starting height describe the scored run. Enabling recording can therefore replace this diagnostic with the height from a different execution—or the reset pose if recording cannot start. Capture the final height immediately after physical validation, before resetting the scene for video.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| return [AXIS_ALIGN_CASES[name] for name in case_names] | ||
|
|
||
|
|
||
| def object_axis_angle_rad(obj, internal_axis, target_axis) -> float: |
There was a problem hiding this comment.
Exported helpers lack parameter annotations
The exported object_axis_angle_rad() leaves all three parameters unannotated, violating the repository directive to fully annotate public APIs. Add RigidObject and tensor parameter annotations, using type-only imports where appropriate. The exported signed_rotation_about_axis_rad() in Pour has the same omission. This repository requirement must be satisfied before merging.
Context Used: CLAUDE.md (source)
Prompt To Fix With AI
This is a comment left during a code review.
Path: scripts/benchmark/atomic_action/axis_align_benchmark.py
Line: 115
Comment:
**Exported helpers lack parameter annotations**
The exported `object_axis_angle_rad()` leaves all three parameters unannotated, violating the repository directive to fully annotate public APIs. Add `RigidObject` and tensor parameter annotations, using type-only imports where appropriate. The exported `signed_rotation_about_axis_rad()` in Pour has the same omission. This repository requirement must be satisfied before merging.
**Context Used:** CLAUDE.md ([source](https://github.com/dexforce/embodichain/blob/main/CLAUDE.md))
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| actually got there. Sweeping the rate on OpenDoor: | ||
|
|
||
| | Steps/waypoint | Max arm tracking error (rad) | Hinge error (rad) | | ||
| |---|---|---| | ||
| | 1 (planner's own dt) | 1.3844 | 0.2273 | | ||
| | 4 (original) | 0.4390 | 0.0689 | | ||
| | 16 (**chosen**) | 0.0532 | 0.0306 | | ||
| | 32 | 0.0230 | 0.0245 | | ||
|
|
||
| The measured "task" error was largely the controller failing to follow the plan. | ||
| I first tried replaying at the planner's own `dt`, which is *worse* — it made | ||
| OpenDoor and Slide fail outright. | ||
|
|
||
| **Fix**: `DEFAULT_REPLAY_STEPS_PER_WAYPOINT = 16`, plus a new |
There was a problem hiding this comment.
Report describes outdated replay settings
The report presents 16 steps per waypoint as OpenDoor's chosen replay rate, but the committed benchmark uses 64 because 16 fails its tracking gate. Together with the opening claim that the changes are uncommitted, this leaves readers unable to tie the reported results to the reviewed implementation. Identify the measured revision and settings, and separate historical experiments from the current benchmark configuration.
Prompt To Fix With AI
This is a comment left during a code review.
Path: scripts/benchmark/atomic_action/BENCHMARK_REPORT.md
Line: 73-86
Comment:
**Report describes outdated replay settings**
The report presents 16 steps per waypoint as OpenDoor's chosen replay rate, but the committed benchmark uses 64 because 16 fails its tracking gate. Together with the opening claim that the changes are uncommitted, this leaves readers unable to tie the reported results to the reviewed implementation. Identify the measured revision and settings, and separate historical experiments from the current benchmark configuration.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| "pick_up": "scripts.benchmark.atomic_action.pickup_benchmark", | ||
| "move_held_object": "scripts.benchmark.atomic_action.move_held_object_benchmark", | ||
| "place": "scripts.benchmark.atomic_action.place_benchmark", | ||
| "open_door": "scripts.benchmark.atomic_action.open_door_benchmark", |
There was a problem hiding this comment.
[P2] Initialize action-specific defaults for in-process dispatch
The newly registered actions are also exposed through --in_process, but _make_child_args() does not populate their required case-selection attributes, such as door_cases, press_cases, and align_cases. Each new benchmark reads its attribute directly, causing an AttributeError; all seven new actions are affected, including the smoke profile. Please initialize the child namespace through the selected module’s add_benchmark_args() defaults before applying shared overrides, and cover this dispatch path with a focused regression check.
Description
Third of three stacked changes. Base is #641, which is itself based on #620; review those first.
Extends the suite from five skills to twelve. Each new benchmark reports the full success ladder from #641 and a task metric defined by what the skill is supposed to achieve, not by whether a planner returned:
task_successis measured asopen_doorpressslidepouraxis_alignhand_overtwistTwo skills did not run at all, and neither failure was in the benchmarks
Press and Slide plan Cartesian-linear segments. #640 now requires those to use
strategy='ik_interp', but their tutorials still request'motion_gen', so the guard rejects them. This is not benchmark-only: the press tutorial fails onmaintoday with exactly the error the benchmark hit —Both tutorials and both benchmarks now request the strategy the primitives require. Verified by running the press tutorial itself, which completes again.
OpenDoor: the replay rate, never the task tolerance
At the shared default the arm trails the plan by 0.66 rad and the tracking gate rejects the run, even though the door reaches its commanded angle. Raising only the replay rate — four times the shared default for this skill — brings tracking inside the gate and the hinge to within 0.001 rad. The task tolerance is unchanged.
Twist is a real failure and is reported as one
The planned end-effector rotation about the knob axis is exactly the commanded 0.7854 rad, with correct sign, at 0.026 rad of arm tracking error — so the skill plans correctly. The gripper wedges against the knob rather than holding it, and the knob joint has near-zero stiffness, so the knob over-rotates and keeps being dragged during retract. Neither the success gate nor the scene stiffness was adjusted to turn this green;
BENCHMARK_REPORT.mdproposes three ways to gate it and recommends one.Type of change
press/slidestrategy regression)Screenshots
N/A — reports are Markdown tables written to
outputs/benchmarks/.Checklist
black .command to format the code base.Validation
Every new benchmark run on this branch, current engine:
BENCHMARK_REPORT.mdrecords the measured ladder, timings and task values per skill, the bugs found and fixed, and the threshold questions that need a decision rather than a default — including one that matters:press's 80 %-of-stroke rule passes regardless of commanded depth on this asset, so it currently proves only that the button was pressed.Not built:
push_objecthas no tutorial to source a scene from, and the two coordinated dual-arm skills are left for a follow-up.