FA-88331 / Inverse kinematics solvers / Open access
Adaptive gain numerator uses the squared error norm · case 01
The step gain ignores how well the predicted motion aligns with the error.
ROOT CAUSE
alpha is computed as |e|^2/|J J^T e|^2 rather than <e, J J^T e>/|J J^T e|^2.
THE FAILURE
alpha is computed as |e|^2/|J J^T e|^2 rather than <e, J J^T e>/|J J^T e|^2.
Unsuccessful approach: Using |J J^T e| in the numerator gives a scale-dependent gain.
Case contract
Input [l1,l2,q1,q2,tx,ty]: one Jacobian-transpose step for a planar 2R arm with the error-optimal gain alpha = <e, J J^T e> / <J J^T e, J J^T e>, dq = alpha J^T e. When <J J^T e, J J^T e> < 1e-15 return ["stationary", q1, q2]. Otherwise return ["step", alpha (6 decimals), q1 and q2 updated in degrees (4 decimals)].
Why this case matters
Inverse kinematics code turns task-space goals into joint commands; a wrong branch, sign, limit or update order sends a real arm to the wrong pose.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x):
l1,l2,q1,q2,tx,ty=x
a=math.radians(q1)
b=math.radians(q1+q2)
ex=tx-(l1*math.cos(a)+l2*math.cos(b))
ey=ty-(l1*math.sin(a)+l2*math.sin(b))
j11=-l1*math.sin(a)-l2*math.sin(b)
j12=-l2*math.sin(b)
j21=l1*math.cos(a)+l2*math.cos(b)
j22=l2*math.cos(b)
g1=j11*ex+j21*ey
g2=j12*ex+j22*ey
h1=j11*g1+j12*g2
h2=j21*g1+j22*g2
den=h1*h1+h2*h2
if den<1e-15: return ['stationary',q1,q2]
alpha=(ex*ex+ey*ey)/den
return ['step',round(alpha,6),round(q1+math.degrees(alpha*g1),4),round(q2+math.degrees(alpha*g2),4)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['moderate error', [[1, 1, 30, 60, 0.5, 1.6]], ['step', 0.263207, 39.5859, 65.5199]], ['at target', [[1, 1, 0, 90, 1, 1]], ['stationary', 0, 90]], ['large error', [[1, 1, 0, 45, -1.5, 0.2]], ['step', 0.287793, 23.1194, 76.4812]], ['unequal links', [[2, 1, 20, -40, 1.5, 2.0]], ['step', 0.11371, 53.3906, -32.7888]], ['negative angles', [[1.5, 1, -30, -60, 0.8, -2]], ['step', 0.183895, -42.6234, -65.2581]], ['behind base', [[1, 1, 120, 30, -1, -1]], ['step', 0.217338, 154.0209, 53.2367]], ['small error', [[1, 1, 10, 10, 1.9, 0.6]], ['step', 0.201469, 12.0193, 11.0115]]], [['moderate error', [[1, 1, 30, 60, 0.5, 1.6]], ['step', 0.263207, 39.5859, 65.5199]], ['large error', [[1, 1, 0, 45, -1.5, 0.2]], ['step', 0.287793, 23.1194, 76.4812]], ['negative angles', [[1.5, 1, -30, -60, 0.8, -2]], ['step', 0.183895, -42.6234, -65.2581]], ['behind base', [[1, 1, 120, 30, -1, -1]], ['step', 0.217338, 154.0209, 53.2367]], ['small error', [[1, 1, 10, 10, 1.9, 0.6]], ['step', 0.201469, 12.0193, 11.0115]], ['long forearm', [[0.5, 2, 70, -20, -0.5, 2.2]], ['step', 0.098879, 93.8254, -1.5747]], ['straight arm radial error', [[1, 1, 0, 0, 1.5, 0]], ['stationary', 0, 0]]], [['moderate error', [[1, 1, 30, 60, 0.5, 1.6]], ['step', 0.263207, 39.5859, 65.5199]], ['at target', [[1, 1, 0, 90, 1, 1]], ['stationary', 0, 90]], ['large error', [[1, 1, 0, 45, -1.5, 0.2]], ['step', 0.287793, 23.1194, 76.4812]], ['unequal links', [[2, 1, 20, -40, 1.5, 2.0]], ['step', 0.11371, 53.3906, -32.7888]], ['negative angles', [[1.5, 1, -30, -60, 0.8, -2]], ['step', 0.183895, -42.6234, -65.2581]], ['long forearm', [[0.5, 2, 70, -20, -0.5, 2.2]], ['step', 0.098879, 93.8254, -1.5747]], ['straight arm radial error', [[1, 1, 0, 0, 1.5, 0]], ['stationary', 0, 0]]], [['at target', [[1, 1, 0, 90, 1, 1]], ['stationary', 0, 90]], ['large error', [[1, 1, 0, 45, -1.5, 0.2]], ['step', 0.287793, 23.1194, 76.4812]], ['unequal links', [[2, 1, 20, -40, 1.5, 2.0]], ['step', 0.11371, 53.3906, -32.7888]], ['negative angles', [[1.5, 1, -30, -60, 0.8, -2]], ['step', 0.183895, -42.6234, -65.2581]], ['behind base', [[1, 1, 120, 30, -1, -1]], ['step', 0.217338, 154.0209, 53.2367]], ['small error', [[1, 1, 10, 10, 1.9, 0.6]], ['step', 0.201469, 12.0193, 11.0115]], ['long forearm', [[0.5, 2, 70, -20, -0.5, 2.2]], ['step', 0.098879, 93.8254, -1.5747]]], [['moderate error', [[1, 1, 30, 60, 0.5, 1.6]], ['step', 0.263207, 39.5859, 65.5199]], ['at target', [[1, 1, 0, 90, 1, 1]], ['stationary', 0, 90]], ['negative angles', [[1.5, 1, -30, -60, 0.8, -2]], ['step', 0.183895, -42.6234, -65.2581]], ['behind base', [[1, 1, 120, 30, -1, -1]], ['step', 0.217338, 154.0209, 53.2367]], ['small error', [[1, 1, 10, 10, 1.9, 0.6]], ['step', 0.201469, 12.0193, 11.0115]], ['long forearm', [[0.5, 2, 70, -20, -0.5, 2.2]], ['step', 0.098879, 93.8254, -1.5747]], ['straight arm radial error', [[1, 1, 0, 0, 1.5, 0]], ['stationary', 0, 0]]]]
for label, args, expected in fixtures[N-1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| moderate error | ['step', 0.070435, 32.5652, 61.4771] | ['step', 0.263207, 39.5859, 65.5199] | Failed |
| at target | ['stationary', 0, 90] | ['stationary', 0, 90] | Passed |
| large error | ['step', 0.54076, 43.4411, 104.1529] | ['step', 0.287793, 23.1194, 76.4812] | Failed |
| unequal links | ['step', 0.018566, 25.452, -38.8226] | ['step', 0.11371, 53.3906, -32.7888] | Failed |
| negative angles | ['step', 0.034012, -32.3347, -60.9725] | ['step', 0.183895, -42.6234, -65.2581] | Failed |
| behind base | ['step', 0.113811, 137.8154, 42.1681] | ['step', 0.217338, 154.0209, 53.2367] | Failed |
| small error | ['step', 0.04059, 10.4068, 10.2038] | ['step', 0.201469, 12.0193, 11.0115] | Failed |
SHA-256 / 0910ffbd6ce7f9164c3d2bea1f9d1d6df68d6933b2c5f5e7c05e02fd8808c98d
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x):
l1,l2,q1,q2,tx,ty=x
a=math.radians(q1)
b=math.radians(q1+q2)
ex=tx-(l1*math.cos(a)+l2*math.cos(b))
ey=ty-(l1*math.sin(a)+l2*math.sin(b))
j11=-l1*math.sin(a)-l2*math.sin(b)
j12=-l2*math.sin(b)
j21=l1*math.cos(a)+l2*math.cos(b)
j22=l2*math.cos(b)
g1=j11*ex+j21*ey
g2=j12*ex+j22*ey
h1=j11*g1+j12*g2
h2=j21*g1+j22*g2
den=h1*h1+h2*h2
if den<1e-15: return ['stationary',q1,q2]
alpha=math.sqrt(den)/den
return ['step',round(alpha,6),round(q1+math.degrees(alpha*g1),4),round(q2+math.degrees(alpha*g2),4)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['moderate error', [[1, 1, 30, 60, 0.5, 1.6]], ['step', 0.263207, 39.5859, 65.5199]], ['at target', [[1, 1, 0, 90, 1, 1]], ['stationary', 0, 90]], ['large error', [[1, 1, 0, 45, -1.5, 0.2]], ['step', 0.287793, 23.1194, 76.4812]], ['unequal links', [[2, 1, 20, -40, 1.5, 2.0]], ['step', 0.11371, 53.3906, -32.7888]], ['negative angles', [[1.5, 1, -30, -60, 0.8, -2]], ['step', 0.183895, -42.6234, -65.2581]], ['behind base', [[1, 1, 120, 30, -1, -1]], ['step', 0.217338, 154.0209, 53.2367]], ['small error', [[1, 1, 10, 10, 1.9, 0.6]], ['step', 0.201469, 12.0193, 11.0115]]], [['moderate error', [[1, 1, 30, 60, 0.5, 1.6]], ['step', 0.263207, 39.5859, 65.5199]], ['large error', [[1, 1, 0, 45, -1.5, 0.2]], ['step', 0.287793, 23.1194, 76.4812]], ['negative angles', [[1.5, 1, -30, -60, 0.8, -2]], ['step', 0.183895, -42.6234, -65.2581]], ['behind base', [[1, 1, 120, 30, -1, -1]], ['step', 0.217338, 154.0209, 53.2367]], ['small error', [[1, 1, 10, 10, 1.9, 0.6]], ['step', 0.201469, 12.0193, 11.0115]], ['long forearm', [[0.5, 2, 70, -20, -0.5, 2.2]], ['step', 0.098879, 93.8254, -1.5747]], ['straight arm radial error', [[1, 1, 0, 0, 1.5, 0]], ['stationary', 0, 0]]], [['moderate error', [[1, 1, 30, 60, 0.5, 1.6]], ['step', 0.263207, 39.5859, 65.5199]], ['at target', [[1, 1, 0, 90, 1, 1]], ['stationary', 0, 90]], ['large error', [[1, 1, 0, 45, -1.5, 0.2]], ['step', 0.287793, 23.1194, 76.4812]], ['unequal links', [[2, 1, 20, -40, 1.5, 2.0]], ['step', 0.11371, 53.3906, -32.7888]], ['negative angles', [[1.5, 1, -30, -60, 0.8, -2]], ['step', 0.183895, -42.6234, -65.2581]], ['long forearm', [[0.5, 2, 70, -20, -0.5, 2.2]], ['step', 0.098879, 93.8254, -1.5747]], ['straight arm radial error', [[1, 1, 0, 0, 1.5, 0]], ['stationary', 0, 0]]], [['at target', [[1, 1, 0, 90, 1, 1]], ['stationary', 0, 90]], ['large error', [[1, 1, 0, 45, -1.5, 0.2]], ['step', 0.287793, 23.1194, 76.4812]], ['unequal links', [[2, 1, 20, -40, 1.5, 2.0]], ['step', 0.11371, 53.3906, -32.7888]], ['negative angles', [[1.5, 1, -30, -60, 0.8, -2]], ['step', 0.183895, -42.6234, -65.2581]], ['behind base', [[1, 1, 120, 30, -1, -1]], ['step', 0.217338, 154.0209, 53.2367]], ['small error', [[1, 1, 10, 10, 1.9, 0.6]], ['step', 0.201469, 12.0193, 11.0115]], ['long forearm', [[0.5, 2, 70, -20, -0.5, 2.2]], ['step', 0.098879, 93.8254, -1.5747]]], [['moderate error', [[1, 1, 30, 60, 0.5, 1.6]], ['step', 0.263207, 39.5859, 65.5199]], ['at target', [[1, 1, 0, 90, 1, 1]], ['stationary', 0, 90]], ['negative angles', [[1.5, 1, -30, -60, 0.8, -2]], ['step', 0.183895, -42.6234, -65.2581]], ['behind base', [[1, 1, 120, 30, -1, -1]], ['step', 0.217338, 154.0209, 53.2367]], ['small error', [[1, 1, 10, 10, 1.9, 0.6]], ['step', 0.201469, 12.0193, 11.0115]], ['long forearm', [[0.5, 2, 70, -20, -0.5, 2.2]], ['step', 0.098879, 93.8254, -1.5747]], ['straight arm radial error', [[1, 1, 0, 0, 1.5, 0]], ['stationary', 0, 0]]]]
for label, args, expected in fixtures[N-1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| moderate error | ['step', 0.699442, 55.4733, 74.6685] | ['step', 0.263207, 39.5859, 65.5199] | Failed |
| at target | ['stationary', 0, 90] | ['stationary', 0, 90] | Passed |
| large error | ['step', 0.226478, 18.1938, 69.7741] | ['step', 0.287793, 23.1194, 76.4812] | Failed |
| unequal links | ['step', 0.064313, 38.8852, -35.9215] | ['step', 0.11371, 53.3906, -32.7888] | Failed |
| negative angles | ['step', 0.330414, -52.6812, -69.4475] | ['step', 0.183895, -42.6234, -65.2581] | Failed |
| behind base | ['step', 0.140908, 142.0571, 45.0653] | ['step', 0.217338, 154.0209, 53.2367] | Failed |
| small error | ['step', 2.294165, 32.9939, 21.518] | ['step', 0.201469, 12.0193, 11.0115] | Failed |
SHA-256 / ab171225c237a9bc3f0c905b938f8f873c035b586faf610d7dc5fdeb2bb596d7
HELD IN THE MEMBER ARCHIVE
The verified repair and its recorded checks are member-only.
This mechanism has 7 recorded checks per implementation. The open-access tier publishes the failure and the unsuccessful fix; the repaired source that passes every check, and the observations that prove it, are available to members.
Every case sharing this mechanism uses the same contract and the same repair, so this one record is held back for all of them.
Member access is invitation-based. Sign in with your invited account to inspect the repair.
Sign in to the archive ↗Verification & scope
Deterministic planar or low-dimensional teaching model with a stipulated convention; not a general robotics library. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.
Observations recorded using Python 3.12.14 at 2026-09-29T14:51:07.144645+00:00.
Case digest / 1cffdb921ebd04a3bf5e567ed2ecf959cc95e0bdbed179ed632593ea3f85f19c