Skip to content

fix(models,ik): correct YuMi gripper parents and IK failure-path q compaction - #649

Merged
petercorke merged 2 commits into
mainfrom
fix/yumi-gripper-parent-swap
Aug 26, 2026
Merged

fix(models,ik): correct YuMi gripper parents and IK failure-path q compaction#649
petercorke merged 2 commits into
mainfrom
fix/yumi-gripper-parent-swap

Conversation

@petercorke

Copy link
Copy Markdown
Owner

Summary

  • YuMi.py's r_gripper/l_gripper links were wired to the opposite arm's gripper base link, so end='r_gripper' silently solved for the left arm and vice versa (root-cause piece of Dual Arm robot (YuMi robot) gives different solutions, when check using forward kinematics, gives different trajectories each time... #379's "inconsistent results" report).
  • While verifying the fix against the #379 repro, found a second, independent bug: IKSolver._solve() (shared by IK_LM/IK_NR/IK_GN/IK_QP) compacts the returned q via ets.jindices on success but not on failure. Invisible for a sub-chain whose jindex happens to start at 0, but for one that doesn't (e.g. YuMi's l_gripper, jindex 7-13, only reachable once the parent-swap above is fixed) the failed solution came back at the wrong length — this is what actually crashed the #379 repro once the swap was corrected.
  • The C++ IK solvers (IK_LM_c/IK_NR_c/IK_GN_c) were checked and don't share this bug — they never operate in padded/global space to begin with, so there's nothing to mirror there.

Part of a larger IK-solver investigation; this is Item 1 of a 4-item plan (see claude-notes/ik-solver-cpp-python-divergence.md). Items 2-4 (broken ikine_LMS examples, IKSolution return-type consistency, fkine() compact-q support) are separate follow-up PRs.

Test plan

  • New regression test: r_gripper/l_gripper resolve to the correct arm's gripper_r_base/gripper_l_base (fails on old code)
  • New regression test: a failed IK_LM.solve() on a sub-chain with non-zero-based jindex returns q of length ets.n (fails on old code)
  • Full test suite green (733 passed, 18 skipped) in a clean isolated venv
  • Manually re-ran the #379 repro script end-to-end; the length-mismatch crash it hit is gone (a separate, already-known fkine() compact-q limitation remains, tracked as Item 4)

🤖 Generated with Claude Code

petercorke and others added 2 commits August 26, 2026 16:39
r_gripper and l_gripper were wired to the opposite arm's gripper base
link, so ikine_LM(end='r_gripper') silently solved for the left arm
and vice versa.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
IKSolver._solve() compacted q via ets.jindices on success but returned
the raw zero-padded global-length vector on failure. Invisible for a
sub-chain whose jindex happens to start at 0, but for one that doesn't
(e.g. YuMi's l_gripper, jindex 7-13) the failed solution came back at
the wrong length, breaking any caller expecting length == ets.n.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@codecov

codecov Bot commented Aug 26, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 0.00%. Comparing base (5b5a0fe) to head (b4eec80).

Files with missing lines Patch % Lines
src/roboticstoolbox/models/URDF/YuMi.py 0.00% 2 Missing ⚠️
Additional details and impacted files
@@          Coverage Diff          @@
##            main    #649   +/-   ##
=====================================
  Coverage   0.00%   0.00%           
=====================================
  Files        143     143           
  Lines      14035   14035           
=====================================
  Misses     14035   14035           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@petercorke
petercorke merged commit 7c40242 into main Aug 26, 2026
23 checks passed
@petercorke
petercorke deleted the fix/yumi-gripper-parent-swap branch August 26, 2026 10:59
@github-actions github-actions Bot mentioned this pull request Aug 26, 2026
petercorke added a commit that referenced this pull request Aug 26, 2026
* feat(ets): fkine/jacob0/jacobe/hessian0/hessiane accept compact q

ikine_LM/ik_LM and friends return a solution sized to just the joints
on the requested sub-chain (e.g. YuMi's l_gripper, 7 elements) -- but
fkine/jacob0/etc. only understood a full, global jindex-addressed q
(14 elements for YuMi), silently misindexing when handed the shorter
compact solution directly. This is the root cause of #379's remaining
"fkine gives garbage" symptom, on top of the gripper-labelling bug
already fixed in #649.

Add BaseETS._resolve_q(), the single place that disambiguates the two
shapes: q of length ets.n is compact and gets scattered into a global-
length array via ets.jindices; q of length >= max(jindices)+1 is
already global and passes through unchanged (this also preserves two
pre-existing behaviours: accepting a q longer than strictly needed,
and never reordering an already-global q even when jindices aren't in
increasing order, e.g. after .inv()). Anything else raises ValueError
naming both accepted lengths.

Wired into eval/jacob0/jacobe/hessian0/hessiane. No C++ changes: the
resolution happens once, in Python, before the facade decides between
the C++ extension and the pure-Python fallback -- both keep receiving
exactly the global-length q they always have. Verified numerically
identical between the two paths (~1e-16) on a real branched robot.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* feat(ets2): extend compact-q support to the 2D/planar variant

Same fix as the 3D ETS, for consistency -- ETS2's eval/jacob0/jacobe
don't go through the C++ facade at all (pure Python, indexing q by
jindex inline), but share the same BaseETS._resolve_q(). Guarded
against ETS2's lazy jindex auto-assignment inside jacob0() (jindices
can legitimately be unassigned until that runs): _resolve_q() no-ops
when it can't cleanly determine jindices, preserving prior behaviour
exactly in that case.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* docs(kinematics): explain fkine()'s compact-vs-global q on branched robots

Adds a worked YuMi example alongside the existing single-chain one,
covering the new dual-mode q accepted by RobotKinematics.fkine (see
the ETS.eval()/compact-q commit).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* test(robot): add end-to-end compact-q regression test on YuMi

Solves ikine_LM for l_gripper and feeds the 7-element solution
straight into fkine/jacob0 without any manual full-vector workaround
-- this is the actual #379 usage pattern the compact-q fix targets.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
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.

1 participant