FA-88061 / Inverse kinematics solvers / Open access
Inverted wrist singularity reuses the upright roll formula · case 01
At b=180 the returned g has the wrong sign relation to the rotation.
ROOT CAUSE
The b=180 branch computes g from atan2(R10,R00), which is only valid for b=0.
VERIFIED REPAIR
For b=180 with a=0, use g = atan2(R01, -R00).
Unsuccessful approach: Negating R01 instead of R00 mirrors g about the x axis.
Case contract
Input [R, flip]: R is a 3x3 wrist rotation R = Rz(a) Ry(b) Rz(g). Return [a,b,g] in degrees rounded to 4. With flip false b is in [0,180]; with flip true b is negative and a,g are shifted by 180 degrees. When hypot(R02,R12) < 1e-9 the wrist is singular: a=0 and b=0 (R22>0, g=atan2(R10,R00)) or b=180 (g=atan2(R01,-R00)).
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):
R,flip=x
sb=math.hypot(R[0][2],R[1][2])
if sb<1e-9:
if R[2][2]>0: return [0.0,0.0,round(math.degrees(math.atan2(R[1][0],R[0][0])),4)]
return [0.0,180.0,round(math.degrees(math.atan2(R[1][0],R[0][0])),4)]
if flip:
b=math.atan2(-sb,R[2][2])
a=math.atan2(-R[1][2],-R[0][2])
g=math.atan2(-R[2][1],R[2][0])
else:
b=math.atan2(sb,R[2][2])
a=math.atan2(R[1][2],R[0][2])
g=math.atan2(R[2][1],-R[2][0])
return [round(math.degrees(v),4) for v in (a,b,g)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['generic no flip', [[[[0.04341204441673269, -0.8295983733257066, 0.5566703992264194], [0.9096158864219903, 0.26325835480968696, 0.32139380484326957], [-0.4131759111665348, 0.49240387650610395, 0.766044443118978]], False]], [30.0, 40.0, 50.0]], ['generic flip', [[[[0.04341204441673269, -0.8295983733257066, 0.5566703992264194], [0.9096158864219903, 0.26325835480968696, 0.32139380484326957], [-0.4131759111665348, 0.49240387650610395, 0.766044443118978]], True]], [-150.0, -40.0, -130.0]], ['large tilt no flip', [[[[-0.005813254051503125, 0.9237208365458508, 0.3830222215594891], [0.6941091380258464, 0.2794538206643772, -0.6634139481689384], [-0.7198463103929542, 0.2620026302293849, -0.6427876096865394]], False]], [-60.0, 130.0, 20.0]], ['large tilt flip', [[[[-0.005813254051503125, 0.9237208365458508, 0.3830222215594891], [0.6941091380258464, 0.2794538206643772, -0.6634139481689384], [-0.7198463103929542, 0.2620026302293849, -0.6427876096865394]], True]], [120.0, -130.0, -160.0]], ['near upright singular', [[[[0.5, -0.8660254037844386, 1.5818054920253123e-10], [0.8660254037844386, 0.5, 7.376080146430387e-11], [-1.429690024728532e-10, 1.0010797326181444e-10, 1.0]], False]], [0.0, 0.0, 60.0]], ['near inverted singular', [[[[-0.5, 0.8660254037844385, 1.718814073341628e-10], [0.8660254037844385, 0.5, 3.030732959515473e-11], [-5.969378631680947e-11, 1.6400732998718326e-10, -1.0]], False]], [0.0, 180.0, 60.0]], ['exact upright', [[[[0.766044443118978, -0.6427876096865393, 0.0], [0.6427876096865393, 0.766044443118978, 0.0], [-0.0, 0.0, 1.0]], False]], [0.0, 0.0, 40.0]]], [['large tilt flip', [[[[-0.005813254051503125, 0.9237208365458508, 0.3830222215594891], [0.6941091380258464, 0.2794538206643772, -0.6634139481689384], [-0.7198463103929542, 0.2620026302293849, -0.6427876096865394]], True]], [120.0, -130.0, -160.0]], ['near upright singular', [[[[0.5, -0.8660254037844386, 1.5818054920253123e-10], [0.8660254037844386, 0.5, 7.376080146430387e-11], [-1.429690024728532e-10, 1.0010797326181444e-10, 1.0]], False]], [0.0, 0.0, 60.0]], ['near inverted singular', [[[[-0.5, 0.8660254037844385, 1.718814073341628e-10], [0.8660254037844385, 0.5, 3.030732959515473e-11], [-5.969378631680947e-11, 1.6400732998718326e-10, -1.0]], False]], [0.0, 180.0, 60.0]], ['exact upright', [[[[0.766044443118978, -0.6427876096865393, 0.0], [0.6427876096865393, 0.766044443118978, 0.0], [-0.0, 0.0, 1.0]], False]], [0.0, 0.0, 40.0]], ['exact inverted', [[[[-0.766044443118978, 0.6427876096865393, 1.2246467991473532e-16], [0.6427876096865393, 0.766044443118978, 0.0], [-9.381338752702731e-17, 7.871877887341985e-17, -1.0]], True]], [0.0, 180.0, 40.0]], ['negative angles', [[[[-0.3209407678712127, -0.8147047612756303, -0.482962913144534], [0.44411428382689044, 0.3209407678712125, -0.8365163037378079], [0.8365163037378079, -0.4829629131445341, 0.25881904510252074]], False]], [-120.0, 75.0, -150.0]], ['negative angles flip', [[[[-0.3209407678712127, -0.8147047612756303, -0.482962913144534], [0.44411428382689044, 0.3209407678712125, -0.8365163037378079], [0.8365163037378079, -0.4829629131445341, 0.25881904510252074]], True]], [60.0, -75.0, 30.0]]], [['exact upright', [[[[0.766044443118978, -0.6427876096865393, 0.0], [0.6427876096865393, 0.766044443118978, 0.0], [-0.0, 0.0, 1.0]], False]], [0.0, 0.0, 40.0]], ['exact inverted', [[[[-0.766044443118978, 0.6427876096865393, 1.2246467991473532e-16], [0.6427876096865393, 0.766044443118978, 0.0], [-9.381338752702731e-17, 7.871877887341985e-17, -1.0]], True]], [0.0, 180.0, 40.0]], ['negative angles', [[[[-0.3209407678712127, -0.8147047612756303, -0.482962913144534], [0.44411428382689044, 0.3209407678712125, -0.8365163037378079], [0.8365163037378079, -0.4829629131445341, 0.25881904510252074]], False]], [-120.0, 75.0, -150.0]], ['negative angles flip', [[[[-0.3209407678712127, -0.8147047612756303, -0.482962913144534], [0.44411428382689044, 0.3209407678712125, -0.8365163037378079], [0.8365163037378079, -0.4829629131445341, 0.25881904510252074]], True]], [60.0, -75.0, 30.0]], ['tilt 90', [[[[0.49999999999999994, -0.49999999999999994, 0.7071067811865476], [-0.49999999999999994, 0.5000000000000001, 0.7071067811865475], [-0.7071067811865476, -0.7071067811865475, 6.123233995736766e-17]], False]], [45.0, 90.0, -45.0]], ['back quadrant', [[[[-0.08550503583141712, 0.515076844803523, -0.8528685319524432], [-0.984923155196477, 0.08550503583141716, 0.15038373318043521], [0.15038373318043524, 0.8528685319524432, 0.5000000000000001]], False]], [170.0, 60.0, 100.0]], ['inverted with roll', [[[[0.49999999999999983, -0.8660254037844387, 1.2246467991473532e-16], [-0.8660254037844387, -0.49999999999999983, 0.0], [6.123233995736764e-17, -1.0605752387249069e-16, -1.0]], False]], [0.0, 180.0, -120.0]]], [['generic no flip', [[[[0.04341204441673269, -0.8295983733257066, 0.5566703992264194], [0.9096158864219903, 0.26325835480968696, 0.32139380484326957], [-0.4131759111665348, 0.49240387650610395, 0.766044443118978]], False]], [30.0, 40.0, 50.0]], ['generic flip', [[[[0.04341204441673269, -0.8295983733257066, 0.5566703992264194], [0.9096158864219903, 0.26325835480968696, 0.32139380484326957], [-0.4131759111665348, 0.49240387650610395, 0.766044443118978]], True]], [-150.0, -40.0, -130.0]], ['near inverted singular', [[[[-0.5, 0.8660254037844385, 1.718814073341628e-10], [0.8660254037844385, 0.5, 3.030732959515473e-11], [-5.969378631680947e-11, 1.6400732998718326e-10, -1.0]], False]], [0.0, 180.0, 60.0]], ['tilt 90', [[[[0.49999999999999994, -0.49999999999999994, 0.7071067811865476], [-0.49999999999999994, 0.5000000000000001, 0.7071067811865475], [-0.7071067811865476, -0.7071067811865475, 6.123233995736766e-17]], False]], [45.0, 90.0, -45.0]], ['back quadrant', [[[[-0.08550503583141712, 0.515076844803523, -0.8528685319524432], [-0.984923155196477, 0.08550503583141716, 0.15038373318043521], [0.15038373318043524, 0.8528685319524432, 0.5000000000000001]], False]], [170.0, 60.0, 100.0]], ['inverted with roll', [[[[0.49999999999999983, -0.8660254037844387, 1.2246467991473532e-16], [-0.8660254037844387, -0.49999999999999983, 0.0], [6.123233995736764e-17, -1.0605752387249069e-16, -1.0]], False]], [0.0, 180.0, -120.0]], ['small tilt flip', [[[[0.34136939837363944, -0.9398073646785109, 0.015134435901338627], [0.9360020627702564, 0.34136939837363944, 0.08583165117743129], [-0.08583165117743129, -0.01513443590133862, 0.9961946980917455]], True]], [-100.0, -5.0, 170.0]]], [['generic no flip', [[[[0.04341204441673269, -0.8295983733257066, 0.5566703992264194], [0.9096158864219903, 0.26325835480968696, 0.32139380484326957], [-0.4131759111665348, 0.49240387650610395, 0.766044443118978]], False]], [30.0, 40.0, 50.0]], ['generic flip', [[[[0.04341204441673269, -0.8295983733257066, 0.5566703992264194], [0.9096158864219903, 0.26325835480968696, 0.32139380484326957], [-0.4131759111665348, 0.49240387650610395, 0.766044443118978]], True]], [-150.0, -40.0, -130.0]], ['large tilt no flip', [[[[-0.005813254051503125, 0.9237208365458508, 0.3830222215594891], [0.6941091380258464, 0.2794538206643772, -0.6634139481689384], [-0.7198463103929542, 0.2620026302293849, -0.6427876096865394]], False]], [-60.0, 130.0, 20.0]], ['large tilt flip', [[[[-0.005813254051503125, 0.9237208365458508, 0.3830222215594891], [0.6941091380258464, 0.2794538206643772, -0.6634139481689384], [-0.7198463103929542, 0.2620026302293849, -0.6427876096865394]], True]], [120.0, -130.0, -160.0]], ['near upright singular', [[[[0.5, -0.8660254037844386, 1.5818054920253123e-10], [0.8660254037844386, 0.5, 7.376080146430387e-11], [-1.429690024728532e-10, 1.0010797326181444e-10, 1.0]], False]], [0.0, 0.0, 60.0]], ['exact inverted', [[[[-0.766044443118978, 0.6427876096865393, 1.2246467991473532e-16], [0.6427876096865393, 0.766044443118978, 0.0], [-9.381338752702731e-17, 7.871877887341985e-17, -1.0]], True]], [0.0, 180.0, 40.0]], ['small tilt flip', [[[[0.34136939837363944, -0.9398073646785109, 0.015134435901338627], [0.9360020627702564, 0.34136939837363944, 0.08583165117743129], [-0.08583165117743129, -0.01513443590133862, 0.9961946980917455]], True]], [-100.0, -5.0, 170.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 |
|---|---|---|---|
| generic no flip | [30.0, 40.0, 50.0] | [30.0, 40.0, 50.0] | Passed |
| generic flip | [-150.0, -40.0, -130.0] | [-150.0, -40.0, -130.0] | Passed |
| large tilt no flip | [-60.0, 130.0, 20.0] | [-60.0, 130.0, 20.0] | Passed |
| large tilt flip | [120.0, -130.0, -160.0] | [120.0, -130.0, -160.0] | Passed |
| near upright singular | [0.0, 0.0, 60.0] | [0.0, 0.0, 60.0] | Passed |
| near inverted singular | [0.0, 180.0, 120.0] | [0.0, 180.0, 60.0] | Failed |
| exact upright | [0.0, 0.0, 40.0] | [0.0, 0.0, 40.0] | Passed |
SHA-256 / 97388db3d7b588f0120a1442fff12f621fee38b372f8e8cac17a2cec5b7ebc7d
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x):
R,flip=x
sb=math.hypot(R[0][2],R[1][2])
if sb<1e-9:
if R[2][2]>0: return [0.0,0.0,round(math.degrees(math.atan2(R[1][0],R[0][0])),4)]
return [0.0,180.0,round(math.degrees(math.atan2(-R[0][1],-R[0][0])),4)]
if flip:
b=math.atan2(-sb,R[2][2])
a=math.atan2(-R[1][2],-R[0][2])
g=math.atan2(-R[2][1],R[2][0])
else:
b=math.atan2(sb,R[2][2])
a=math.atan2(R[1][2],R[0][2])
g=math.atan2(R[2][1],-R[2][0])
return [round(math.degrees(v),4) for v in (a,b,g)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['generic no flip', [[[[0.04341204441673269, -0.8295983733257066, 0.5566703992264194], [0.9096158864219903, 0.26325835480968696, 0.32139380484326957], [-0.4131759111665348, 0.49240387650610395, 0.766044443118978]], False]], [30.0, 40.0, 50.0]], ['generic flip', [[[[0.04341204441673269, -0.8295983733257066, 0.5566703992264194], [0.9096158864219903, 0.26325835480968696, 0.32139380484326957], [-0.4131759111665348, 0.49240387650610395, 0.766044443118978]], True]], [-150.0, -40.0, -130.0]], ['large tilt no flip', [[[[-0.005813254051503125, 0.9237208365458508, 0.3830222215594891], [0.6941091380258464, 0.2794538206643772, -0.6634139481689384], [-0.7198463103929542, 0.2620026302293849, -0.6427876096865394]], False]], [-60.0, 130.0, 20.0]], ['large tilt flip', [[[[-0.005813254051503125, 0.9237208365458508, 0.3830222215594891], [0.6941091380258464, 0.2794538206643772, -0.6634139481689384], [-0.7198463103929542, 0.2620026302293849, -0.6427876096865394]], True]], [120.0, -130.0, -160.0]], ['near upright singular', [[[[0.5, -0.8660254037844386, 1.5818054920253123e-10], [0.8660254037844386, 0.5, 7.376080146430387e-11], [-1.429690024728532e-10, 1.0010797326181444e-10, 1.0]], False]], [0.0, 0.0, 60.0]], ['near inverted singular', [[[[-0.5, 0.8660254037844385, 1.718814073341628e-10], [0.8660254037844385, 0.5, 3.030732959515473e-11], [-5.969378631680947e-11, 1.6400732998718326e-10, -1.0]], False]], [0.0, 180.0, 60.0]], ['exact upright', [[[[0.766044443118978, -0.6427876096865393, 0.0], [0.6427876096865393, 0.766044443118978, 0.0], [-0.0, 0.0, 1.0]], False]], [0.0, 0.0, 40.0]]], [['large tilt flip', [[[[-0.005813254051503125, 0.9237208365458508, 0.3830222215594891], [0.6941091380258464, 0.2794538206643772, -0.6634139481689384], [-0.7198463103929542, 0.2620026302293849, -0.6427876096865394]], True]], [120.0, -130.0, -160.0]], ['near upright singular', [[[[0.5, -0.8660254037844386, 1.5818054920253123e-10], [0.8660254037844386, 0.5, 7.376080146430387e-11], [-1.429690024728532e-10, 1.0010797326181444e-10, 1.0]], False]], [0.0, 0.0, 60.0]], ['near inverted singular', [[[[-0.5, 0.8660254037844385, 1.718814073341628e-10], [0.8660254037844385, 0.5, 3.030732959515473e-11], [-5.969378631680947e-11, 1.6400732998718326e-10, -1.0]], False]], [0.0, 180.0, 60.0]], ['exact upright', [[[[0.766044443118978, -0.6427876096865393, 0.0], [0.6427876096865393, 0.766044443118978, 0.0], [-0.0, 0.0, 1.0]], False]], [0.0, 0.0, 40.0]], ['exact inverted', [[[[-0.766044443118978, 0.6427876096865393, 1.2246467991473532e-16], [0.6427876096865393, 0.766044443118978, 0.0], [-9.381338752702731e-17, 7.871877887341985e-17, -1.0]], True]], [0.0, 180.0, 40.0]], ['negative angles', [[[[-0.3209407678712127, -0.8147047612756303, -0.482962913144534], [0.44411428382689044, 0.3209407678712125, -0.8365163037378079], [0.8365163037378079, -0.4829629131445341, 0.25881904510252074]], False]], [-120.0, 75.0, -150.0]], ['negative angles flip', [[[[-0.3209407678712127, -0.8147047612756303, -0.482962913144534], [0.44411428382689044, 0.3209407678712125, -0.8365163037378079], [0.8365163037378079, -0.4829629131445341, 0.25881904510252074]], True]], [60.0, -75.0, 30.0]]], [['exact upright', [[[[0.766044443118978, -0.6427876096865393, 0.0], [0.6427876096865393, 0.766044443118978, 0.0], [-0.0, 0.0, 1.0]], False]], [0.0, 0.0, 40.0]], ['exact inverted', [[[[-0.766044443118978, 0.6427876096865393, 1.2246467991473532e-16], [0.6427876096865393, 0.766044443118978, 0.0], [-9.381338752702731e-17, 7.871877887341985e-17, -1.0]], True]], [0.0, 180.0, 40.0]], ['negative angles', [[[[-0.3209407678712127, -0.8147047612756303, -0.482962913144534], [0.44411428382689044, 0.3209407678712125, -0.8365163037378079], [0.8365163037378079, -0.4829629131445341, 0.25881904510252074]], False]], [-120.0, 75.0, -150.0]], ['negative angles flip', [[[[-0.3209407678712127, -0.8147047612756303, -0.482962913144534], [0.44411428382689044, 0.3209407678712125, -0.8365163037378079], [0.8365163037378079, -0.4829629131445341, 0.25881904510252074]], True]], [60.0, -75.0, 30.0]], ['tilt 90', [[[[0.49999999999999994, -0.49999999999999994, 0.7071067811865476], [-0.49999999999999994, 0.5000000000000001, 0.7071067811865475], [-0.7071067811865476, -0.7071067811865475, 6.123233995736766e-17]], False]], [45.0, 90.0, -45.0]], ['back quadrant', [[[[-0.08550503583141712, 0.515076844803523, -0.8528685319524432], [-0.984923155196477, 0.08550503583141716, 0.15038373318043521], [0.15038373318043524, 0.8528685319524432, 0.5000000000000001]], False]], [170.0, 60.0, 100.0]], ['inverted with roll', [[[[0.49999999999999983, -0.8660254037844387, 1.2246467991473532e-16], [-0.8660254037844387, -0.49999999999999983, 0.0], [6.123233995736764e-17, -1.0605752387249069e-16, -1.0]], False]], [0.0, 180.0, -120.0]]], [['generic no flip', [[[[0.04341204441673269, -0.8295983733257066, 0.5566703992264194], [0.9096158864219903, 0.26325835480968696, 0.32139380484326957], [-0.4131759111665348, 0.49240387650610395, 0.766044443118978]], False]], [30.0, 40.0, 50.0]], ['generic flip', [[[[0.04341204441673269, -0.8295983733257066, 0.5566703992264194], [0.9096158864219903, 0.26325835480968696, 0.32139380484326957], [-0.4131759111665348, 0.49240387650610395, 0.766044443118978]], True]], [-150.0, -40.0, -130.0]], ['near inverted singular', [[[[-0.5, 0.8660254037844385, 1.718814073341628e-10], [0.8660254037844385, 0.5, 3.030732959515473e-11], [-5.969378631680947e-11, 1.6400732998718326e-10, -1.0]], False]], [0.0, 180.0, 60.0]], ['tilt 90', [[[[0.49999999999999994, -0.49999999999999994, 0.7071067811865476], [-0.49999999999999994, 0.5000000000000001, 0.7071067811865475], [-0.7071067811865476, -0.7071067811865475, 6.123233995736766e-17]], False]], [45.0, 90.0, -45.0]], ['back quadrant', [[[[-0.08550503583141712, 0.515076844803523, -0.8528685319524432], [-0.984923155196477, 0.08550503583141716, 0.15038373318043521], [0.15038373318043524, 0.8528685319524432, 0.5000000000000001]], False]], [170.0, 60.0, 100.0]], ['inverted with roll', [[[[0.49999999999999983, -0.8660254037844387, 1.2246467991473532e-16], [-0.8660254037844387, -0.49999999999999983, 0.0], [6.123233995736764e-17, -1.0605752387249069e-16, -1.0]], False]], [0.0, 180.0, -120.0]], ['small tilt flip', [[[[0.34136939837363944, -0.9398073646785109, 0.015134435901338627], [0.9360020627702564, 0.34136939837363944, 0.08583165117743129], [-0.08583165117743129, -0.01513443590133862, 0.9961946980917455]], True]], [-100.0, -5.0, 170.0]]], [['generic no flip', [[[[0.04341204441673269, -0.8295983733257066, 0.5566703992264194], [0.9096158864219903, 0.26325835480968696, 0.32139380484326957], [-0.4131759111665348, 0.49240387650610395, 0.766044443118978]], False]], [30.0, 40.0, 50.0]], ['generic flip', [[[[0.04341204441673269, -0.8295983733257066, 0.5566703992264194], [0.9096158864219903, 0.26325835480968696, 0.32139380484326957], [-0.4131759111665348, 0.49240387650610395, 0.766044443118978]], True]], [-150.0, -40.0, -130.0]], ['large tilt no flip', [[[[-0.005813254051503125, 0.9237208365458508, 0.3830222215594891], [0.6941091380258464, 0.2794538206643772, -0.6634139481689384], [-0.7198463103929542, 0.2620026302293849, -0.6427876096865394]], False]], [-60.0, 130.0, 20.0]], ['large tilt flip', [[[[-0.005813254051503125, 0.9237208365458508, 0.3830222215594891], [0.6941091380258464, 0.2794538206643772, -0.6634139481689384], [-0.7198463103929542, 0.2620026302293849, -0.6427876096865394]], True]], [120.0, -130.0, -160.0]], ['near upright singular', [[[[0.5, -0.8660254037844386, 1.5818054920253123e-10], [0.8660254037844386, 0.5, 7.376080146430387e-11], [-1.429690024728532e-10, 1.0010797326181444e-10, 1.0]], False]], [0.0, 0.0, 60.0]], ['exact inverted', [[[[-0.766044443118978, 0.6427876096865393, 1.2246467991473532e-16], [0.6427876096865393, 0.766044443118978, 0.0], [-9.381338752702731e-17, 7.871877887341985e-17, -1.0]], True]], [0.0, 180.0, 40.0]], ['small tilt flip', [[[[0.34136939837363944, -0.9398073646785109, 0.015134435901338627], [0.9360020627702564, 0.34136939837363944, 0.08583165117743129], [-0.08583165117743129, -0.01513443590133862, 0.9961946980917455]], True]], [-100.0, -5.0, 170.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 |
|---|---|---|---|
| generic no flip | [30.0, 40.0, 50.0] | [30.0, 40.0, 50.0] | Passed |
| generic flip | [-150.0, -40.0, -130.0] | [-150.0, -40.0, -130.0] | Passed |
| large tilt no flip | [-60.0, 130.0, 20.0] | [-60.0, 130.0, 20.0] | Passed |
| large tilt flip | [120.0, -130.0, -160.0] | [120.0, -130.0, -160.0] | Passed |
| near upright singular | [0.0, 0.0, 60.0] | [0.0, 0.0, 60.0] | Passed |
| near inverted singular | [0.0, 180.0, -60.0] | [0.0, 180.0, 60.0] | Failed |
| exact upright | [0.0, 0.0, 40.0] | [0.0, 0.0, 40.0] | Passed |
SHA-256 / 450d62f363703e26767f12ed020de7c8e71e375cc5469b27b10939b9108f9911
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x):
R,flip=x
sb=math.hypot(R[0][2],R[1][2])
if sb<1e-9:
if R[2][2]>0: return [0.0,0.0,round(math.degrees(math.atan2(R[1][0],R[0][0])),4)]
return [0.0,180.0,round(math.degrees(math.atan2(R[0][1],-R[0][0])),4)]
if flip:
b=math.atan2(-sb,R[2][2])
a=math.atan2(-R[1][2],-R[0][2])
g=math.atan2(-R[2][1],R[2][0])
else:
b=math.atan2(sb,R[2][2])
a=math.atan2(R[1][2],R[0][2])
g=math.atan2(R[2][1],-R[2][0])
return [round(math.degrees(v),4) for v in (a,b,g)]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['generic no flip', [[[[0.04341204441673269, -0.8295983733257066, 0.5566703992264194], [0.9096158864219903, 0.26325835480968696, 0.32139380484326957], [-0.4131759111665348, 0.49240387650610395, 0.766044443118978]], False]], [30.0, 40.0, 50.0]], ['generic flip', [[[[0.04341204441673269, -0.8295983733257066, 0.5566703992264194], [0.9096158864219903, 0.26325835480968696, 0.32139380484326957], [-0.4131759111665348, 0.49240387650610395, 0.766044443118978]], True]], [-150.0, -40.0, -130.0]], ['large tilt no flip', [[[[-0.005813254051503125, 0.9237208365458508, 0.3830222215594891], [0.6941091380258464, 0.2794538206643772, -0.6634139481689384], [-0.7198463103929542, 0.2620026302293849, -0.6427876096865394]], False]], [-60.0, 130.0, 20.0]], ['large tilt flip', [[[[-0.005813254051503125, 0.9237208365458508, 0.3830222215594891], [0.6941091380258464, 0.2794538206643772, -0.6634139481689384], [-0.7198463103929542, 0.2620026302293849, -0.6427876096865394]], True]], [120.0, -130.0, -160.0]], ['near upright singular', [[[[0.5, -0.8660254037844386, 1.5818054920253123e-10], [0.8660254037844386, 0.5, 7.376080146430387e-11], [-1.429690024728532e-10, 1.0010797326181444e-10, 1.0]], False]], [0.0, 0.0, 60.0]], ['near inverted singular', [[[[-0.5, 0.8660254037844385, 1.718814073341628e-10], [0.8660254037844385, 0.5, 3.030732959515473e-11], [-5.969378631680947e-11, 1.6400732998718326e-10, -1.0]], False]], [0.0, 180.0, 60.0]], ['exact upright', [[[[0.766044443118978, -0.6427876096865393, 0.0], [0.6427876096865393, 0.766044443118978, 0.0], [-0.0, 0.0, 1.0]], False]], [0.0, 0.0, 40.0]]], [['large tilt flip', [[[[-0.005813254051503125, 0.9237208365458508, 0.3830222215594891], [0.6941091380258464, 0.2794538206643772, -0.6634139481689384], [-0.7198463103929542, 0.2620026302293849, -0.6427876096865394]], True]], [120.0, -130.0, -160.0]], ['near upright singular', [[[[0.5, -0.8660254037844386, 1.5818054920253123e-10], [0.8660254037844386, 0.5, 7.376080146430387e-11], [-1.429690024728532e-10, 1.0010797326181444e-10, 1.0]], False]], [0.0, 0.0, 60.0]], ['near inverted singular', [[[[-0.5, 0.8660254037844385, 1.718814073341628e-10], [0.8660254037844385, 0.5, 3.030732959515473e-11], [-5.969378631680947e-11, 1.6400732998718326e-10, -1.0]], False]], [0.0, 180.0, 60.0]], ['exact upright', [[[[0.766044443118978, -0.6427876096865393, 0.0], [0.6427876096865393, 0.766044443118978, 0.0], [-0.0, 0.0, 1.0]], False]], [0.0, 0.0, 40.0]], ['exact inverted', [[[[-0.766044443118978, 0.6427876096865393, 1.2246467991473532e-16], [0.6427876096865393, 0.766044443118978, 0.0], [-9.381338752702731e-17, 7.871877887341985e-17, -1.0]], True]], [0.0, 180.0, 40.0]], ['negative angles', [[[[-0.3209407678712127, -0.8147047612756303, -0.482962913144534], [0.44411428382689044, 0.3209407678712125, -0.8365163037378079], [0.8365163037378079, -0.4829629131445341, 0.25881904510252074]], False]], [-120.0, 75.0, -150.0]], ['negative angles flip', [[[[-0.3209407678712127, -0.8147047612756303, -0.482962913144534], [0.44411428382689044, 0.3209407678712125, -0.8365163037378079], [0.8365163037378079, -0.4829629131445341, 0.25881904510252074]], True]], [60.0, -75.0, 30.0]]], [['exact upright', [[[[0.766044443118978, -0.6427876096865393, 0.0], [0.6427876096865393, 0.766044443118978, 0.0], [-0.0, 0.0, 1.0]], False]], [0.0, 0.0, 40.0]], ['exact inverted', [[[[-0.766044443118978, 0.6427876096865393, 1.2246467991473532e-16], [0.6427876096865393, 0.766044443118978, 0.0], [-9.381338752702731e-17, 7.871877887341985e-17, -1.0]], True]], [0.0, 180.0, 40.0]], ['negative angles', [[[[-0.3209407678712127, -0.8147047612756303, -0.482962913144534], [0.44411428382689044, 0.3209407678712125, -0.8365163037378079], [0.8365163037378079, -0.4829629131445341, 0.25881904510252074]], False]], [-120.0, 75.0, -150.0]], ['negative angles flip', [[[[-0.3209407678712127, -0.8147047612756303, -0.482962913144534], [0.44411428382689044, 0.3209407678712125, -0.8365163037378079], [0.8365163037378079, -0.4829629131445341, 0.25881904510252074]], True]], [60.0, -75.0, 30.0]], ['tilt 90', [[[[0.49999999999999994, -0.49999999999999994, 0.7071067811865476], [-0.49999999999999994, 0.5000000000000001, 0.7071067811865475], [-0.7071067811865476, -0.7071067811865475, 6.123233995736766e-17]], False]], [45.0, 90.0, -45.0]], ['back quadrant', [[[[-0.08550503583141712, 0.515076844803523, -0.8528685319524432], [-0.984923155196477, 0.08550503583141716, 0.15038373318043521], [0.15038373318043524, 0.8528685319524432, 0.5000000000000001]], False]], [170.0, 60.0, 100.0]], ['inverted with roll', [[[[0.49999999999999983, -0.8660254037844387, 1.2246467991473532e-16], [-0.8660254037844387, -0.49999999999999983, 0.0], [6.123233995736764e-17, -1.0605752387249069e-16, -1.0]], False]], [0.0, 180.0, -120.0]]], [['generic no flip', [[[[0.04341204441673269, -0.8295983733257066, 0.5566703992264194], [0.9096158864219903, 0.26325835480968696, 0.32139380484326957], [-0.4131759111665348, 0.49240387650610395, 0.766044443118978]], False]], [30.0, 40.0, 50.0]], ['generic flip', [[[[0.04341204441673269, -0.8295983733257066, 0.5566703992264194], [0.9096158864219903, 0.26325835480968696, 0.32139380484326957], [-0.4131759111665348, 0.49240387650610395, 0.766044443118978]], True]], [-150.0, -40.0, -130.0]], ['near inverted singular', [[[[-0.5, 0.8660254037844385, 1.718814073341628e-10], [0.8660254037844385, 0.5, 3.030732959515473e-11], [-5.969378631680947e-11, 1.6400732998718326e-10, -1.0]], False]], [0.0, 180.0, 60.0]], ['tilt 90', [[[[0.49999999999999994, -0.49999999999999994, 0.7071067811865476], [-0.49999999999999994, 0.5000000000000001, 0.7071067811865475], [-0.7071067811865476, -0.7071067811865475, 6.123233995736766e-17]], False]], [45.0, 90.0, -45.0]], ['back quadrant', [[[[-0.08550503583141712, 0.515076844803523, -0.8528685319524432], [-0.984923155196477, 0.08550503583141716, 0.15038373318043521], [0.15038373318043524, 0.8528685319524432, 0.5000000000000001]], False]], [170.0, 60.0, 100.0]], ['inverted with roll', [[[[0.49999999999999983, -0.8660254037844387, 1.2246467991473532e-16], [-0.8660254037844387, -0.49999999999999983, 0.0], [6.123233995736764e-17, -1.0605752387249069e-16, -1.0]], False]], [0.0, 180.0, -120.0]], ['small tilt flip', [[[[0.34136939837363944, -0.9398073646785109, 0.015134435901338627], [0.9360020627702564, 0.34136939837363944, 0.08583165117743129], [-0.08583165117743129, -0.01513443590133862, 0.9961946980917455]], True]], [-100.0, -5.0, 170.0]]], [['generic no flip', [[[[0.04341204441673269, -0.8295983733257066, 0.5566703992264194], [0.9096158864219903, 0.26325835480968696, 0.32139380484326957], [-0.4131759111665348, 0.49240387650610395, 0.766044443118978]], False]], [30.0, 40.0, 50.0]], ['generic flip', [[[[0.04341204441673269, -0.8295983733257066, 0.5566703992264194], [0.9096158864219903, 0.26325835480968696, 0.32139380484326957], [-0.4131759111665348, 0.49240387650610395, 0.766044443118978]], True]], [-150.0, -40.0, -130.0]], ['large tilt no flip', [[[[-0.005813254051503125, 0.9237208365458508, 0.3830222215594891], [0.6941091380258464, 0.2794538206643772, -0.6634139481689384], [-0.7198463103929542, 0.2620026302293849, -0.6427876096865394]], False]], [-60.0, 130.0, 20.0]], ['large tilt flip', [[[[-0.005813254051503125, 0.9237208365458508, 0.3830222215594891], [0.6941091380258464, 0.2794538206643772, -0.6634139481689384], [-0.7198463103929542, 0.2620026302293849, -0.6427876096865394]], True]], [120.0, -130.0, -160.0]], ['near upright singular', [[[[0.5, -0.8660254037844386, 1.5818054920253123e-10], [0.8660254037844386, 0.5, 7.376080146430387e-11], [-1.429690024728532e-10, 1.0010797326181444e-10, 1.0]], False]], [0.0, 0.0, 60.0]], ['exact inverted', [[[[-0.766044443118978, 0.6427876096865393, 1.2246467991473532e-16], [0.6427876096865393, 0.766044443118978, 0.0], [-9.381338752702731e-17, 7.871877887341985e-17, -1.0]], True]], [0.0, 180.0, 40.0]], ['small tilt flip', [[[[0.34136939837363944, -0.9398073646785109, 0.015134435901338627], [0.9360020627702564, 0.34136939837363944, 0.08583165117743129], [-0.08583165117743129, -0.01513443590133862, 0.9961946980917455]], True]], [-100.0, -5.0, 170.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 |
|---|---|---|---|
| generic no flip | [30.0, 40.0, 50.0] | [30.0, 40.0, 50.0] | Passed |
| generic flip | [-150.0, -40.0, -130.0] | [-150.0, -40.0, -130.0] | Passed |
| large tilt no flip | [-60.0, 130.0, 20.0] | [-60.0, 130.0, 20.0] | Passed |
| large tilt flip | [120.0, -130.0, -160.0] | [120.0, -130.0, -160.0] | Passed |
| near upright singular | [0.0, 0.0, 60.0] | [0.0, 0.0, 60.0] | Passed |
| near inverted singular | [0.0, 180.0, 60.0] | [0.0, 180.0, 60.0] | Passed |
| exact upright | [0.0, 0.0, 40.0] | [0.0, 0.0, 40.0] | Passed |
SHA-256 / 429780cd5f6afad9aca9d348ea1357eb02bb3579c6193680fe0ebdf597cdadb0
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:04.560547+00:00.
Case digest / 564a7d1bd40f623b07425ebd005dfc5a022f3b6766809fc334a4d27ba0f70977