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
2 changes: 1 addition & 1 deletion agent_context/MAP.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ topics:
title: IK Solver System
aliases: [ik solver, inverse kinematics, IK 求解器, 逆运动学]
keywords: [BaseSolver, SolverCfg, SRSSolver, OPWSolver, URSolver, PinkSolver, PytorchSolver, PinocchioSolver,
DifferentialSolver, NeuralIKSolver, QposSeedSampler, QposSeedSelSampler, NullSpacePostureTask, compute kinematics, Warp
DifferentialSolver, NeuralIKSolver, FEPSolver, FEPSolverCfg, FEP, QposSeedSampler, QposSeedSelSampler, NullSpacePostureTask, compute kinematics, Warp
kinematics]
paths: [topics/ik-solvers/ik-solvers.md]
source_of_truth:
Expand Down
17 changes: 15 additions & 2 deletions agent_context/topics/ik-solvers/ik-solvers.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ current options and dependencies. The main change sites are:

| Need | Solver module under `motion/solvers/` |
|---|---|
| Analytical structure for SRS, OPW or UR arms | `srs_solver.py`, `opw_solver.py`, `ur_solver.py` |
| Analytical structure for SRS, OPW, UR or Franka (FEP) arms | `srs_solver.py`, `opw_solver.py`, `ur_solver.py`, `fep_solver.py` |
| General iterative PK solve and multi-start seeds | `pytorch_solver.py` |
| Reduced Pinocchio model or task-based QP | `pinocchio_solver.py`, `pink_solver.py` |
| Jacobian differential commands | `differential_solver.py` |
Expand Down Expand Up @@ -75,14 +75,27 @@ Candidate count `K` belongs to the solver; OPW implements this capability.
## Computation and state ownership

Stateful configuration, device buffers, seed policy, TCP and limit updates stay
in `lab/sim/motion/solvers`. Pure OPW/SRS/UR kernels belong to
in `lab/sim/motion/solvers`. Pure OPW/SRS/UR/FEP kernels belong to
`compute/kinematics/_warp/` and cannot import simulation modules.
`utils/warp/kinematics/*_solver.py` retains compatibility aliases only.

Analytical scratch reuse must not let later calls mutate earlier public results.
See [buffer lifecycle](solver-details.md#analytical-buffers-and-selection) before
changing allocation, stream behavior or nearest/all-solutions dispatch.

FEP validates Franka-compatible URDF geometry and shares one CPU/CUDA kernel.
The seed fixes q7 unless `redundancy_search` is enabled; finite search failure
does not prove global unreachability. Arm-angle preferences are soft, while
FK acceptance, joint limits and `max_joint_step` remain hard constraints.
Per-call `arm_angle` belongs to `FEPSolver.get_ik`; Robot's generic IK facade
uses the solver configuration. `ik_solution_selection="manipulability"` ranks
valid fixed-q7 branches, or the eight candidates retained by search, using the
shared Yoshikawa helper. It does not rank every sampled q7 candidate. Near-equal
scores use weighted seed distance to avoid mirror-branch jumps from roundoff.
`num_samples` is rejected; enable adaptive `redundancy_search` explicitly.
Franka's default remains Pytorch; the opt-in FEP example lives in
`examples/sim/motion/solvers/fep_solver.py`.

## Focused validation

| Changed boundary | Existing coverage |
Expand Down
30 changes: 24 additions & 6 deletions agent_context/topics/simulation-system/rendering.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,30 @@ and readiness reporting. Return to the [simulation overview](simulation-system.m

## Rendering does not advance physics

`NewtonPhysicsCfg.sync_to_renderer=None` preserves DexSim's consumer-aware
per-step policy. Camera rendering and reset observations explicitly synchronize
on demand; correctness must not depend on continuous render sync in headless
training. `SimulationManager.sync_render_state()` and backend render hooks
publish state without advancing physics time. Marker publication while paused
uses `capture_visualization(force=True)`.
`SimulationManager.render_frame()` owns a read-only consumption phase after
physics, interval events or reset writes. An open native window, nonempty camera
groups, due simulation-time recording and due Viser captures share one state
publication in that phase. Empty camera groups alone are not consumers; an open
window remains a consumer even with `NewtonPhysicsCfg.sync_to_renderer=False`.
Viser decides whether a frame is due before invoking its publication callback.

`update()` renders after each substep by default. Gym uses
`update(render_final_step=False)` and enters `render_frame()` around observations
after interval events, so the final camera, recording and Viser reads share the
post-event state. Standalone callers combining those consumers can use the same
sequence. Reset completes all writes before a new frame with forced Viser capture.
An independent camera/capture call still publishes fresh state. No publication
cache survives a frame, so paused edits and direct writes between frames do not
need mutation counters. State edits inside a read-only frame require an explicit
`sync_render_state()` before further reads; that API always publishes.

The Newton backend step hook suppresses automatic DexSim publication during
manager-owned updates to avoid publishing twice. The manager honors explicit
`sync_to_renderer=True` even without consumers; `None`/`False` publish only for
consumers. Raw DexSim `World.update()` retains its configured automatic policy.
Render-thread recording only consumes already-published state and must never
call the blocking physics-to-render bridge. Marker publication while paused uses
`capture_visualization(force=True)` without advancing physics.

Viser forces headless mode and is mutually exclusive with the native window.
An empty server may start before assets are declared; it must not finalize
Expand Down
4 changes: 2 additions & 2 deletions docs/architecture/curated.json
Original file line number Diff line number Diff line change
Expand Up @@ -672,7 +672,7 @@
"symbol": "<module>",
"start_line": 17,
"end_line": 20,
"excerpt": "\"\"\"Inverse-kinematics solvers sharing the ``BaseSolver`` interface.\n\nProvides analytic (SRS, OPW, UR), numerical (Pinocchio, Pink, Differential), and learning-based (PyTorch, NeuralIK) solvers, one per robot control part.\n\"\"\""
"excerpt": "\"\"\"Inverse-kinematics solvers sharing the ``BaseSolver`` interface.\n\nProvides analytic (SRS, OPW, UR, FEP), numerical (Pinocchio, Pink, Differential), and learning-based (PyTorch, NeuralIK) solvers, one per robot control part.\n\"\"\""
}
],
"documentation": [
Expand Down Expand Up @@ -1479,7 +1479,7 @@
"symbol": "<module>",
"start_line": 17,
"end_line": 22,
"excerpt": "\"\"\"Kinematics computations and scalar motion profiles.\n\nPrivate Warp kernels implement OPW, SRS, UR, and trapezoidal/Double-S profiles.\nStateful solver interfaces and robot configuration remain in\n``embodichain.lab.sim.motion.solvers``.\nPure batched manipulability helpers live in :mod:`.manipulability`.\"\"\""
"excerpt": "\"\"\"Kinematics computations and scalar motion profiles.\n\nPrivate Warp kernels implement FEP, OPW, SRS, UR, and trapezoidal/Double-S profiles.\nStateful solver interfaces and robot configuration remain in\n``embodichain.lab.sim.motion.solvers``.\nPure batched manipulability helpers live in :mod:`.manipulability`.\"\"\""
}
],
"documentation": [
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 24 additions & 0 deletions docs/source/api_reference/embodichain/embodichain.lab.sim.cfg.rst
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,27 @@ Constructors and ``from_dict()`` accept only the current fields. Surface-element
properties are grouped under ``attrs.surface_props``. Volume elasticity uses
``youngs`` and ``poissons``. Configuration dictionaries serialize this same
schema; no legacy aliases or field migration are provided.

World-level configuration module
--------------------------------

The same world-level configuration types and backend selectors are available
from ``embodichain.lab.sim.cfg.simulation``. For manager-owned Newton steps,
``sync_to_renderer=None`` or ``False`` publishes only for visual consumers;
``True`` additionally requests publication on steps without consumers. An open
window remains a consumer regardless of this automatic policy.

.. currentmodule:: embodichain.lab.sim.cfg.simulation

.. autosummary::

DLSSCfg
RenderCfg
GPUMemoryCfg
PhysicsBackendCfg
DefaultPhysicsCfg
NewtonCollisionPipelineCfg
NewtonPhysicsCfg
physics_cfg_for_backend
physics_backend_from_cfg
validate_physics_cfg
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ limits) and is constructed from a :class:`SolverCfg` subclass whose
control part. All solvers share a ``pytorch_kinematics`` serial chain for FK and
Jacobian computation, with ``torch.compile`` applied to the FK path.

Available implementations: analytic/closed-form (``SRS``, ``OPW``, ``UR``),
Available implementations: analytic/closed-form (``SRS``, ``OPW``, ``UR``, ``FEP``),
numerical (``Pinocchio``, ``Pink`` with null-space posture tasks,
``Differential``), learning-based (``PytorchSolver``, ``NeuralIKSolver``).

Expand All @@ -31,6 +31,8 @@ numerical (``Pinocchio``, ``Pink`` with null-space posture tasks,
URSolver
PytorchSolverCfg
PytorchSolver
FEPSolverCfg
FEPSolver
PinocchioSolverCfg
PinocchioSolver
PinkSolverCfg
Expand Down Expand Up @@ -76,6 +78,69 @@ PyTorch Solver
:inherited-members:
:show-inheritance:

FEP Solver
----------

Geometric IK for Franka-compatible screw-axis layouts, following the fixed-q7
construction in HolisticMotion/`GeoFIK <https://arxiv.org/abs/2503.03992>`_.
Dimensions, axis signs and fixed transforms are extracted from the URDF;
incompatible seven-axis chains raise an error. CPU and CUDA share one Warp
kernel.

The seed fixes q7 by default and selects the nearest valid branch. Without a
seed, the joint-limit midpoint is used. ``return_all_solutions=True`` returns
up to eight branches with validity masks; invalid slots contain the clamped
seed. Every accepted solution satisfies the actual URDF FK, TCP and joint limits.

``ik_solution_selection="nearest"`` is the default. Set it to
``"manipulability"`` to select the valid candidate with the highest shared
Yoshikawa score. At fixed q7 this ranks the geometric branches. During search
it ranks only the eight candidates retained by the continuity, arm-angle and
limit-margin scores, not every sampled candidate. All-solutions ordering is
unchanged. Scores within a small relative tolerance use weighted seed distance
as a deterministic continuity tie-break. Nearest-result shapes are ``(N,)`` and
``(N, 7)``; Robot's IK
interface and the batch adapter also return joints without a candidate axis.

Enable ``redundancy_search`` to sample and refine q7 using seed distance,
arm-angle preference and joint-limit margin. ``max_joint_step`` remains a hard
per-joint displacement bound in radians. Pass the previous solution as the next
seed for sequential motion. Search returns the best sampled solutions, not an
exhaustive continuous family; failure does not prove global unreachability.
``num_samples`` is rejected in both configuration and IK calls: numerical
multi-start sampling is not equivalent to q7 search. Franka retains its
existing Pytorch default; FEP is selected explicitly through ``solver_cfg``.

``get_arm_angle(qpos)`` measures the TCP-independent GeoFIK swivel angle in
radians, returning NaN at undefined planes. It differs from q7. During search,
``get_ik(..., arm_angle=...)`` overrides ``cfg.arm_angle``. With neither supplied,
the seed's angle is preferred when defined. This is a soft posture preference, not a
complete humanlike or collision model. See the API below for shapes and options.

Run ``python -m examples.sim.motion.solvers.fep_solver --device cuda --radius 0.15``
from the repository root (or use ``--device cpu``). Add ``--redundancy-search``
and optionally ``--arm-angle 0.3`` for posture optimization, or
``--headless --max-steps 301`` for a finite run. The circle example draws target
and actual TCP paths and reports IK and physical tracking errors separately.

.. currentmodule:: embodichain.lab.sim.motion.solvers.fep_solver

.. autosummary::

FEPSolverCfg
FEPSolver

.. currentmodule:: embodichain.lab.sim.motion.solvers

.. autoclass:: FEPSolverCfg
:members:
:exclude-members: __init__, copy, replace, to_dict, validate

.. autoclass:: FEPSolver
:members:
:inherited-members:
:show-inheritance:

Pinocchio Solver
----------------

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,31 @@ not write drive targets. Set this field to ``None`` to opt out or select
prevents automatic recreation. ``GizmoCfg(ik_start_enabled=True)`` activates
native IK on the first update with an open window, as used by the robot tutorial.

Physics and visual consumption
------------------------------

State-only headless stepping does not publish Newton state to the renderer.
An open native window, camera groups, due recording and due Viser captures
request publication when they consume state. ``render_frame()`` shares that
publication within one read-only consumption phase. Gym arranges this phase
after physics and interval events automatically. Standalone callers combining
camera observations with recording or Viser can use:

.. code-block:: python

sim.update(physics_dt, step=1, render_final_step=False)
# Complete direct state edits before entering the read-only frame.
with sim.render_frame():
sim.render_camera_group(camera_group_ids)
# Due recording and Viser captures run on context exit.

The phase never advances physics and never caches publication across frames.
Independent camera/capture calls request fresh publication. If state changes
inside a frame, call ``sync_render_state()`` explicitly before further reads.
Opening a window and rendering while paused also request publication. Native
render-thread recording consumes already-published state without invoking the
blocking physics-to-render bridge from that thread.

.. rubric:: Classes

.. autosummary::
Expand Down
25 changes: 19 additions & 6 deletions docs/source/overview/sim/atomic_actions/builtin_actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -722,7 +722,12 @@ 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`
**Example:** `scripts/tutorials/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
simulation updates, with two physics steps per update. `--rigid_object`
selects the standalone rigid button with the same sampling settings.

(builtin-slide)=

Expand Down Expand Up @@ -842,10 +847,13 @@ policy therefore needs
`sample_count >= 2 * hand_interp_steps + door_waypoint_count + 7`.

**Example:** `scripts/tutorials/atomic_action/open_door.py` configures only the
microwave's `door_handle` link. Automatic traversal resolves `door_hinge`
through the intermediate fixed joint. Its absolute `--open_angle` value is
normalized against the resolved hinge limits and passed as the goal's
`open_fraction`.
`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
`open_fraction`. The tutorial requests `MotionPolicy.sample_count=300`,
`OpenDoorOptions.hand_interp_steps=30`, and `door_waypoint_count=50` by
default, then holds the final pose for 240 simulation updates, with two physics
steps per update.

(builtin-twist)=

Expand Down Expand Up @@ -901,7 +909,12 @@ 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`
**Example:** `scripts/tutorials/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
simulation updates, with two physics steps per update. `--rigid_object`
selects the standalone rigid knob with the same sampling settings.

(builtin-coordinated-pickment)=

Expand Down
2 changes: 1 addition & 1 deletion embodichain/compute/kinematics/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

"""Kinematics computations and scalar motion profiles.

Private Warp kernels implement OPW, SRS, UR, and trapezoidal/Double-S profiles.
Private Warp kernels implement FEP, OPW, SRS, UR, and trapezoidal/Double-S profiles.
Stateful solver interfaces and robot configuration remain in
``embodichain.lab.sim.motion.solvers``.
Pure batched manipulability helpers live in :mod:`.manipulability`."""
Expand Down
Loading
Loading