Skip to content

Add ProxQP differential inverse kinematics in Python#385

Draft
mickaelbegon wants to merge 6 commits into
pyomeca:masterfrom
mickaelbegon:codex/proxqp-dik
Draft

Add ProxQP differential inverse kinematics in Python#385
mickaelbegon wants to merge 6 commits into
pyomeca:masterfrom
mickaelbegon:codex/proxqp-dik

Conversation

@mickaelbegon

@mickaelbegon mickaelbegon commented Jun 9, 2026

Copy link
Copy Markdown

Summary

  • add a Python DifferentialInverseKinematics solver using ProxQP for repeated linearized marker-tracking QPs
  • expose InverseKinematicsProxQP as an alias and keep proxsuite as an optional dependency
  • add optional marker loop-closure constraints through MarkerLoopClosureConstraint
  • add targeted Python tests and a loop-closure benchmark example

Scope

This is a first Python implementation for marker-tracking differential inverse kinematics with generalized-coordinate bounds. Loop closure is supported for marker-marker constraints by linearizing the closure residual into equality constraints in the QP. Point-on-ellipsoid constraints are not included yet.

References and acknowledgements

  • This implementation is motivated by Puchaud, Millan, and Joseph (2026), Inverse Kinematics of the Closed-Loop Shoulder: Quadratic Programming is Faster than Interior-Point Methods with Equivalent Accuracy, Multidisciplinary Biomechanics Journal, 3, 73-75. https://doi.org/10.46298/mbj.17921
  • The approach also acknowledges Pierre Puchaud's Pinocchio prototype branch as the initial implementation reference for the ProxQP differential IK direction: https://github.com/Ipuch/pinocchio/tree/topic/pointOnEllipsoid

Validation

  • PYTHONPYCACHEPREFIX=/private/tmp/biorbd_pycache python3 -m py_compile binding/python3/inverse_kinematics.py binding/python3/rigid_body.py examples/python3/inverse_kinematics_loop_constraint.py test/binding/python3/test_inverse_kinematics.py
  • targeted pytest on test_inverse_kinematics.py: 6 passed, 1 skipped, 2 warnings
  • git diff --check

Exploratory benchmark

Small synthetic benchmark on 20 frames from pyomecaman.bioMod, with markers generated exactly by the model:

method s/frame mean marker residual max marker residual q RMSE
only_lm 0.005856 3.99e-17 3.38e-16 2.36e-16
lm 0.008093 1.62e-4 1.30e-2 1.13e-2
trf 0.009666 3.38e-3 1.44e-2 5.31e-2
proxqp_dik_cold_10 0.007343 3.38e-3 1.44e-2 5.31e-2
proxqp_dik_warm_2 0.002341 3.22e-3 1.44e-2 5.19e-2
proxqp_dik_warm_5 0.006259 3.22e-3 1.44e-2 5.19e-2

Loop-closure example benchmark

examples/python3/inverse_kinematics_loop_constraint.py creates a temporary loop-constrained model with tracking markers, hides the closure markers from the measurements, and compares unconstrained least-squares/DIK with constrained TRF and constrained DIK. trf_loop solves marker tracking plus loop closure as an augmented scipy.optimize.least_squares(method="trf") residual, while proxqp_dik_loop solves the linearized closure as a QP equality constraint:

method s/frame mean marker residual (m) mean loop gap (m) max loop gap (m) mean iterations
only_lm 0.00475997 4.52e-12 2.64e-08 2.16e-07 20.6
lm 0.00439708 1.22e-12 2.64e-08 2.16e-07 19.6
trf 0.00236178 4.76e-11 2.65e-08 2.16e-07 4.9
trf_loop 0.05992007 5.59e-09 1.30e-12 8.42e-12 32.5
proxqp_dik 0.00107789 3.31e-11 2.64e-08 2.16e-07 3.9
proxqp_dik_loop 0.00244623 5.59e-09 2.57e-12 1.52e-11 3.95

The benchmarks are intentionally small and should be interpreted as sanity checks rather than final performance claims.


This change is Reviewable

@Ipuch

Ipuch commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator

😎😎

@pariterre pariterre left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@pariterre reviewed 3 files and all commit messages, and made 4 comments.
Reviewable status: all files reviewed, 4 unresolved discussions (waiting on mickaelbegon).


binding/python3/rigid_body.py line 21 at r1 (raw file):

class DifferentialInverseKinematicsResult:
    fun: np.ndarray
    nfev: int

Can these names be more specific and/or described in a docstring?


binding/python3/rigid_body.py line 464 at r1 (raw file):

class DifferentialInverseKinematics(InverseKinematics):

(I assume this is AI coded, so here is a prompt that should be tested)

Can you move this class and the base class in a dedicated file (and folder?)

If the base class can/should be abstracted, this should be done and the current backend should be given a proper distinctive name.

I feel an API that gives something like this:

ik = InverseKinematics(solver=INVERSE_KINEMATICS_SOLVER.IPOPT, ...)
ik.solve()

could be nice

If it increases the complexity too much, do not do it and keep the two current classes


binding/python3/rigid_body.py line 471 at r1 (raw file):

    handles marker tracking and generalized-coordinate bounds, but no holonomic
    loop constraints.
    """

Can this docstring explains the pros and cons against other approaches?


binding/python3/rigid_body.py line 584 at r1 (raw file):

            success = False
            iterations = 0
            for iterations in range(1, max_iterations + 1):

Isn't doing the main loop in Python slow?
Should this logic be moved in a dedicated C++ class and fully compiled?

@pariterre pariterre left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@pariterre reviewed 3 files and all commit messages, and resolved 4 discussions.
Reviewable status: :shipit: complete! all files reviewed, all discussions resolved (waiting on mickaelbegon).

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.

3 participants