personalrobotics / personalrobotics/pycbirrt

Adopt SSIK as the analytical IK backend and deprecate EAIK

Open
#63 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
5
Forks
1
Avg merge
12m
Merged PRs (30d)
22

Description

Summary

Replace EAIK with SSIK as pycbirrt's preferred enumerative IK backend.

This is a backend migration, not a planner redesign. The generic planner continues to depend only on an IKSolver capability. SSIK remains optional, MuJoCo differential IK remains available as the numerical fallback, and collision checking remains the planner validator's responsibility.

This work depends on personalrobotics/ssik#562 and should target the SSIK release that includes both seed-nearest rewrapping and default-on enumeration of finite-limit windings, expected in SSIK 6.

Why switch

The current EAIK integration has three limitations:

  1. It is the recommended analytical backend in the examples but supports a narrower set of manipulator geometries than SSIK.
  2. EAIKSolver mixes IK generation with joint-limit and collision filtering through solve_valid, even though pycbirrt 1.2 already assigns these responsibilities to JointSpace and the planner's validator.
  3. It returns principal IK representatives and therefore does not resolve #36 for bounded joints whose ranges exceed $2\pi$.

SSIK provides enumerative 6R and 7R IK, accepts a seed, exposes prebuilt robot artifacts and Manipulator, and uses its native backend by default where available. After SSIK #562, it also provides the finite-limit winding semantics pycbirrt needs for complete TSR-induced configuration sets.

Design decision

Keep the planner interface generic

The required IK protocol should be only:

class IKSolver(Protocol):
    def solve(
        self,
        pose: np.ndarray,
        q_init: np.ndarray | None = None,
    ) -> list[np.ndarray]: ...

Remove solve_valid from the required IKSolver protocol and from the interface documentation. It is not called by the planner. Existing backends may retain it temporarily as a convenience method, but implementing it must not be required to satisfy IKSolver.

Keep the existing q_init spelling in this issue to avoid an unrelated public-interface rename. The SSIK adapter maps it to SSIK's q_seed argument.

Add a thin SSIK adapter

Add pycbirrt.backends.ssik.SSIKSolver. It should wrap either an ssik.Manipulator or a prebuilt SSIK artifact, rather than duplicating SSIK's robot-construction API:

from ssik.prebuilt.universal_robots import ur5e_ik
from pycbirrt.backends.ssik import SSIKSolver

ik = SSIKSolver(ur5e_ik)

and, for a user-supplied robot:

import ssik
from pycbirrt.backends.ssik import SSIKSolver

arm = ssik.Manipulator.from_urdf(path, base=base_link, ee=ee_link)
ik = SSIKSolver(arm)

SSIKSolver.solve(pose, q_init) must:

  1. Call the wrapped solver with q_seed=q_init when a seed is supplied.
  2. Request limit-respecting solutions and default-on finite-limit winding enumeration from SSIK 6.
  3. Not impose an internal max_solutions cap. TSRConfigurationSet.sample needs every candidate so the planner can determine which branches and windings are admissible.
  4. Convert every SSIK Solution.q to an independent one-dimensional numpy.ndarray and return list[np.ndarray].
  5. Return [] unchanged when SSIK finds no solution.
  6. Perform no collision checking and accept no collision checker.

Do not reproduce SSIK diagnostics, refinement controls, batch APIs, or robot constructors in the first adapter. Users who need those controls can configure the wrapped SSIK object before passing it to pycbirrt, and the adapter can grow only when a planner requirement is demonstrated.

Preserve the model-boundary contract

The wrapped SSIK model and pycbirrt's RobotModel must use:

  • the same joint order and sign conventions;
  • the same base frame;
  • the same end-effector frame;
  • compatible finite versus continuous joint semantics.

The adapter must document this contract. It should not silently guess joint mappings or insert frame transforms. For the UR5e MuJoCo example, add an explicit conformance test comparing SSIK FK and MuJoCoRobotModel.forward_kinematics at several configurations. If they differ, configure SSIK with the matching base/end-effector frames or apply an explicit, documented fixed transform at construction rather than hiding the mismatch inside solve.

Planner interaction

No planner branch should depend on the concrete SSIKSolver type.

  • TSRConfigurationSet.sample calls solve(pose) and receives all geometric branches and finite-limit winding representatives. It continues to enforce its own JointSpace boundary before returning candidates.
  • TSRConfigurationSet.project calls solve(target, q_init=q) and chooses under the problem's JointSpace metric. Passing the seed to SSIK ensures candidates are represented and ordered sensibly, but pycbirrt retains responsibility for its projection policy.
  • Collision filtering remains in the planner validator so alternative IK branches are not discarded inside the IK backend.

Once the SSIK #562 regression is covered here, close #36 as resolved by the IK backend rather than adding winding expansion to TSRConfigurationSet or planner core.

Dependency and deprecation plan

  1. Add an optional dependency such as:

    ssik = ["ssik>=6,<7"]
    

    SSIK must not become a mandatory dependency of pycbirrt's generic core.

  2. Include the SSIK extra in all and change the README and examples to recommend SSIK.

  3. Keep MuJoCo differential IK as the fallback for unsupported models or users who do not install SSIK.

  4. Retain EAIKSolver for one pycbirrt minor release with a deprecation warning on construction. Remove it, the eaik extra, and EAIK-specific documentation in pycbirrt 2.0.

  5. Do not make pycbirrt depend on SSIK's URDF extra merely to use a prebuilt artifact. Document that users constructing ssik.Manipulator.from_urdf must install SSIK's corresponding URDF support.

Required code and documentation changes

  • Add src/pycbirrt/backends/ssik.py.
  • Reduce src/pycbirrt/interfaces/ik_solver.py to the solve capability.
  • Add the optional SSIK dependency and update the all extra.
  • Migrate examples/ur5e_mujoco.py and examples/tsr_union_demo.py to prefer SSIK, with MuJoCo IK as the fallback.
  • Migrate tests/test_ur5e_integration.py from EAIK to SSIK.
  • Replace the README EAIK section with SSIK construction examples and explain the frame/joint-order contract.
  • Mark EAIKSolver deprecated and document its planned 2.0 removal.
  • Update the changelog and package metadata for the next minor release.

Tests

Adapter unit tests
  • A fake SSIK-like solver verifies that q_init is forwarded as q_seed.
  • Solution.q values are converted to independent float arrays with the expected shape.
  • Empty results remain empty.
  • All returned solutions are preserved; the adapter does not cap or collision-filter them.
  • Malformed returned configurations fail with a clear error rather than entering the planner.
  • Importing pycbirrt without the optional SSIK dependency still works; importing or constructing the SSIK backend fails with an actionable installation message.
SSIK integration tests
  • A prebuilt 6R artifact completes an FK-to-IK round trip and returns FK-certified solutions.
  • A seeded solve exposes the seed-nearest finite-limit representative provided by SSIK #562.
  • An unseeded solve exposes distinct in-limit winding representatives on a finite joint wider than $2\pi$.
  • Continuous joints do not produce an unbounded family of windings.
  • pycbirrt's JointSpace retains valid SSIK windings as distinct bounded configurations.
MuJoCo planning regression
  • The existing UR5e constrained-planning test runs with SSIK and MuJoCo collision checking.
  • SSIK and MuJoCo FK agree for the configured joint order and end-effector frame before planning.
  • The test still demonstrates that the planner can retain a collision-free IK candidate when other returned branches collide.
  • Every path waypoint is within the MuJoCo joint limits and every motion segment respects the planner's validation contract.
  • Add the short-route regression from #36: when the current configuration is near one winding, sampling or projection must not induce an unnecessary nearly-$2\pi$ motion to the principal representative.

Acceptance criteria

  • pycbirrt[ssik] installs against the SSIK release containing #562.
  • SSIKSolver satisfies the generic IKSolver protocol without collision dependencies.
  • Core pycbirrt installs and tests without SSIK, EAIK, or MuJoCo.
  • The UR5e examples prefer SSIK and fall back to MuJoCo differential IK.
  • The real-arm integration test uses SSIK and passes with MuJoCo validation.
  • Seed-nearest and default-on winding behavior are covered end to end.
  • #36 can be closed without adding alias logic to planner core.
  • EAIK is deprecated with an explicit pycbirrt 2.0 removal path.

Non-goals

  • Rewriting CBiRRT or SSIK in C++.
  • Adding a general planner plugin API.
  • Integrating RoboPlan or OMPL.
  • Moving collision checking into SSIK.
  • Removing MuJoCo numerical IK.
  • Adding planner-specific behavior to SSIK.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with src/pycbirrt/interfaces/ik_solver.py and the existing EAIK backend, then inspect tests/test_ur5e_integration.py and the example files named in the issue. Implement and test the optional SSIK adapter, protocol and dependency changes, while preserving MuJoCo fallback and planner validation. Done means the listed adapter, integration, winding, FK-conformance, deprecation, documentation, and installation checks pass.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
backend, documentation, testing-qa
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.