Skip to content

Fix duplicate variable in null-space condition of _calc_qnull#510

Open
Lidang-Jiang wants to merge 1 commit intopetercorke:futurefrom
Lidang-Jiang:fix/nullspace-typo-future
Open

Fix duplicate variable in null-space condition of _calc_qnull#510
Lidang-Jiang wants to merge 1 commit intopetercorke:futurefrom
Lidang-Jiang:fix/nullspace-typo-future

Conversation

@Lidang-Jiang
Copy link
Copy Markdown

Summary

  • Fix a typo in _calc_qnull() where if λΣ > 0 or λΣ > 0: should be if λΣ > 0 or λm > 0:.
  • This bug caused null-space motion to be skipped entirely when only km (manipulability maximisation) was enabled without kq (joint limit avoidance).
  • Added 5 regression tests for _calc_qnull covering all gain combinations.

Fixes #499

Root Cause

In roboticstoolbox/robot/IK.py, the function _calc_qnull has a condition that gates null-space motion calculation:

# Before (buggy):
if λΣ > 0 or λΣ > 0:  # λΣ checked twice, λm never checked

# After (fixed):
if λΣ > 0 or λm > 0:  # correctly checks both gains

When kq=0 (so λΣ=0) and km>0 (so λm>0), the buggy condition evaluates to False, causing the null-space projection to be skipped and returning a zero vector instead of the manipulability gradient projected into the null space.

Before (with bug): qnull is all zeros when only km > 0
============================================================
Issue #499: Null Space Calculation Bug in _calc_qnull
============================================================

--- Test 1: Both gains zero (kq=0, km=0) ---
qnull = [0. 0. 0. 0. 0. 0. 0.]
All zeros: True

--- Test 2: Only km > 0 (kq=0, km=1) [THE BUG CASE] ---
qnull = [0. 0. 0. 0. 0. 0. 0.]
All zeros (BUG if True): True

--- Test 3: Only kq > 0 (kq=1, km=0) ---
qnull = [0. 0. 0. 0. 0. 0. 0.]
All zeros: True

--- Test 4: Both gains positive (kq=1, km=1) ---
qnull = [-0.00356543 -0.00089796 -0.00055962  0.00013665  0.00314415 -0.00055438
 -0.00219734]
All zeros: False

============================================================
Expected behavior:
  Test 2 should return NON-ZERO qnull (manipulability gradient)
  With the bug, Test 2 returns ALL ZEROS
============================================================
After (fixed): qnull is non-zero when km > 0
============================================================
Issue #499: Null Space Calculation Bug in _calc_qnull
============================================================

--- Test 1: Both gains zero (kq=0, km=0) ---
qnull = [0. 0. 0. 0. 0. 0. 0.]
All zeros: True

--- Test 2: Only km > 0 (kq=0, km=1) [THE BUG CASE] ---
qnull = [-0.00356543 -0.00089796 -0.00055962  0.00013665  0.00314415 -0.00055438
 -0.00219734]
All zeros (BUG if True): False

--- Test 3: Only kq > 0 (kq=1, km=0) ---
qnull = [0. 0. 0. 0. 0. 0. 0.]
All zeros: True

--- Test 4: Both gains positive (kq=1, km=1) ---
qnull = [-0.00356543 -0.00089796 -0.00055962  0.00013665  0.00314415 -0.00055438
 -0.00219734]
All zeros: False

============================================================
Expected behavior:
  Test 2 should return NON-ZERO qnull (manipulability gradient)
  With the bug, Test 2 returns ALL ZEROS
============================================================
Unit test results (5 new tests, all passing)
============================= test session starts ==============================
platform linux -- Python 3.11.14, pytest-9.0.2, pluggy-1.6.0 -- /home/devuser/workspace/public-oss/embodied-robotics/myenv-rtb-499/bin/python
cachedir: .pytest_cache
rootdir: /home/devuser/workspace/public-oss/embodied-robotics/robotics-toolbox-python
configfile: pyproject.toml
collecting ... collected 41 items / 36 deselected / 5 selected

tests/test_IK.py::TestCalcQnull::test_qnull_both_gains_positive PASSED   [ 20%]
tests/test_IK.py::TestCalcQnull::test_qnull_both_gains_zero PASSED       [ 40%]
tests/test_IK.py::TestCalcQnull::test_qnull_km_only PASSED               [ 60%]
tests/test_IK.py::TestCalcQnull::test_qnull_km_only_matches_both_when_kq_inactive PASSED [ 80%]
tests/test_IK.py::TestCalcQnull::test_qnull_kq_only PASSED               [100%]

======================= 5 passed, 36 deselected in 1.07s =======================
Full IK test suite (no regressions; 5 QP failures are pre-existing due to missing qpsolvers dependency)
============================= test session starts ==============================
platform linux -- Python 3.11.14, pytest-9.0.2, pluggy-1.6.0 -- /home/devuser/workspace/public-oss/embodied-robotics/myenv-rtb-499/bin/python
cachedir: .pytest_cache
rootdir: /home/devuser/workspace/public-oss/embodied-robotics/robotics-toolbox-python
configfile: pyproject.toml
collecting ... collected 41 items

tests/test_IK.py::TestIK::test_IK_GN1 PASSED                             [  2%]
tests/test_IK.py::TestIK::test_IK_GN2 PASSED                             [  4%]
tests/test_IK.py::TestIK::test_IK_GN3 PASSED                             [  7%]
tests/test_IK.py::TestIK::test_IK_LM1 PASSED                             [  9%]
tests/test_IK.py::TestIK::test_IK_LM2 PASSED                             [ 12%]
tests/test_IK.py::TestIK::test_IK_LM3 PASSED                             [ 14%]
tests/test_IK.py::TestIK::test_IK_NR1 PASSED                             [ 17%]
tests/test_IK.py::TestIK::test_IK_NR2 PASSED                             [ 19%]
tests/test_IK.py::TestIK::test_IK_NR3 PASSED                             [ 21%]
tests/test_IK.py::TestIK::test_IK_NR4 PASSED                             [ 24%]
tests/test_IK.py::TestIK::test_IK_NR5 PASSED                             [ 26%]
tests/test_IK.py::TestIK::test_IK_NR6 PASSED                             [ 29%]
tests/test_IK.py::TestIK::test_IK_NR7 PASSED                             [ 31%]
tests/test_IK.py::TestIK::test_IK_NR8 PASSED                             [ 34%]
tests/test_IK.py::TestIK::test_IK_QP1 FAILED                             [ 36%]
tests/test_IK.py::TestIK::test_IK_QP2 FAILED                             [ 39%]
tests/test_IK.py::TestIK::test_IK_QP3 FAILED                             [ 41%]
tests/test_IK.py::TestIK::test_ets_ikine_GN1 PASSED                      [ 43%]
tests/test_IK.py::TestIK::test_ets_ikine_GN2 PASSED                      [ 46%]
tests/test_IK.py::TestIK::test_ets_ikine_LM1 PASSED                      [ 48%]
tests/test_IK.py::TestIK::test_ets_ikine_LM2 PASSED                      [ 51%]
tests/test_IK.py::TestIK::test_ets_ikine_NR1 PASSED                      [ 53%]
tests/test_IK.py::TestIK::test_ets_ikine_NR2 PASSED                      [ 56%]
tests/test_IK.py::TestIK::test_ets_ikine_QP1 FAILED                      [ 58%]
tests/test_IK.py::TestIK::test_ets_ikine_QP2 FAILED                      [ 60%]
tests/test_IK.py::TestIK::test_ik_gn PASSED                              [ 63%]
tests/test_IK.py::TestIK::test_ik_lm_chan PASSED                         [ 65%]
tests/test_IK.py::TestIK::test_ik_lm_sugihara PASSED                     [ 68%]
tests/test_IK.py::TestIK::test_ik_lm_wampler PASSED                      [ 70%]
tests/test_IK.py::TestIK::test_ik_nr PASSED                              [ 73%]
tests/test_IK.py::TestIK::test_iter_iksol PASSED                         [ 75%]
tests/test_IK.py::TestIK::test_sol_print1 PASSED                         [ 78%]
tests/test_IK.py::TestIK::test_sol_print2 PASSED                         [ 80%]
tests/test_IK.py::TestIK::test_sol_print3 PASSED                         [ 82%]
tests/test_IK.py::TestIK::test_sol_print4 PASSED                         [ 85%]
tests/test_IK.py::TestIK::test_sol_print5 PASSED                         [ 87%]
tests/test_IK.py::TestCalcQnull::test_qnull_both_gains_positive PASSED   [ 90%]
tests/test_IK.py::TestCalcQnull::test_qnull_both_gains_zero PASSED       [ 92%]
tests/test_IK.py::TestCalcQnull::test_qnull_km_only PASSED               [ 95%]
tests/test_IK.py::TestCalcQnull::test_qnull_km_only_matches_both_when_kq_inactive PASSED [ 97%]
tests/test_IK.py::TestCalcQnull::test_qnull_kq_only PASSED               [100%]

=========================== short test summary info ============================
FAILED tests/test_IK.py::TestIK::test_IK_QP1 - ImportError: the package qpsolvers is required
FAILED tests/test_IK.py::TestIK::test_IK_QP2 - ImportError: the package qpsolvers is required
FAILED tests/test_IK.py::TestIK::test_IK_QP3 - ImportError: the package qpsolvers is required
FAILED tests/test_IK.py::TestIK::test_ets_ikine_QP1 - ImportError: the package qpsolvers is required
FAILED tests/test_IK.py::TestIK::test_ets_ikine_QP2 - ImportError: the package qpsolvers is required
========================= 5 failed, 36 passed in 2.70s =========================

Test Plan

  • Verify _calc_qnull returns zero vector when both kq=0 and km=0
  • Verify _calc_qnull returns non-zero vector when km>0 and kq=0 (the Null Space Calculation Bug in _calc_qnull method #499 regression case)
  • Verify _calc_qnull works correctly when kq>0 and km=0
  • Verify _calc_qnull works correctly when both kq>0 and km>0
  • Verify km-only result matches both-gains result when joints are far from limits
  • Full IK test suite passes with no regressions (5 QP failures are pre-existing)

The condition `if λΣ > 0 or λΣ > 0:` duplicates the λΣ check,
which should be `if λΣ > 0 or λm > 0:`. This caused null-space
motion to be skipped entirely when only km (manipulability
maximisation) was enabled without kq (joint limit avoidance).

Fixes petercorke#499

Signed-off-by: Lidang-Jiang <lidangjiang@gmail.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