diff --git a/web/arm-ik-solver.js b/web/arm-ik-solver.js index 0384bf7..38a346c 100644 --- a/web/arm-ik-solver.js +++ b/web/arm-ik-solver.js @@ -226,10 +226,10 @@ function make(cfg) { return out; } - const inLimit = q => { + const inLimit = q => { // 取否定式:NaN 候选必须落选(原写法 NaN 比较全 false 会放行) for (let i = 0; i < 5; i++) { const t = LIM[ORDER[i]]; - if (q[i] < t.lo - 1e-9 || q[i] > t.hi + 1e-9) return false; + if (!(q[i] >= t.lo - 1e-9 && q[i] <= t.hi + 1e-9)) return false; } return true; }; @@ -309,7 +309,7 @@ function make(cfg) { const p = fk(q, tool); const dx = p[0] - target[0], dy = p[1] - target[1], dz = p[2] - target[2]; const e2 = dx * dx + dy * dy + dz * dz; - if (e2 > tol2) continue; + if (!(e2 <= tol2)) continue; // 否定式:e2 为 NaN(目标含 NaN/Inf)时继续走拒绝分支 const c = scoreBranch(q, Math.sqrt(e2)); if (c && (!best || c.rcost < best.rcost - 1e-12)) best = c; } diff --git a/web/ik-glue.js b/web/ik-glue.js index a2835f0..396da3e 100644 --- a/web/ik-glue.js +++ b/web/ik-glue.js @@ -210,6 +210,7 @@ function refreshTarget() { let anim = null; function setJoints(q, doAnim) { if (doAnim) { animTo(q, 620); return; } + anim = null; // 直接写入必须取消在飞动画,否则入场动画会覆写实时解 for (let i = 0; i < 6; i++) { const id = ORDER[i]; ang[id] = q[i]; const s = $('s' + id), v = $('v' + id); @@ -445,7 +446,7 @@ function rollAngleAt(e) { const pl = new THREE.Plane().setFromNormalAndCoplanarPoint(n, gTgt.position); const hit = new THREE.Vector3(); ray2.setFromCamera(ndc2(e), cam); // 该版本 setFromCamera 不返回 this,勿链式 - if (!ray2.ray.intersectPlane(pl, hit)) return 0; + if (!ray2.ray.intersectPlane(pl, hit)) return null; // 错过平面:该帧不参与滚转(返回 0 会按 -a0 突跳) const v = hit.sub(gTgt.position); const up = Math.abs(n.y) > 0.9 ? new THREE.Vector3(1, 0, 0) : new THREE.Vector3(0, 1, 0); const u = new THREE.Vector3().crossVectors(n, up).normalize(); @@ -497,6 +498,7 @@ cv.addEventListener('pointermove', e => { if (grab.pose === 'roll') { // 拖圆环:绕轴滚转 dragged = true; const a1 = rollAngleAt(e); + if (a1 == null) return; // 射线错过平面:跳过该帧,角度不变 POSE.roll = S.wrap180(POSE.roll + S.wrap180(a1 - grab.a0)); grab.a0 = a1; syncPoseInputs(); refreshTarget(); diff --git a/web/index.html b/web/index.html index f798325..9ff8d79 100644 --- a/web/index.html +++ b/web/index.html @@ -1442,10 +1442,10 @@ function make(cfg) { return out; } - const inLimit = q => { + const inLimit = q => { // 取否定式:NaN 候选必须落选(原写法 NaN 比较全 false 会放行) for (let i = 0; i < 5; i++) { const t = LIM[ORDER[i]]; - if (q[i] < t.lo - 1e-9 || q[i] > t.hi + 1e-9) return false; + if (!(q[i] >= t.lo - 1e-9 && q[i] <= t.hi + 1e-9)) return false; } return true; }; @@ -1525,7 +1525,7 @@ function make(cfg) { const p = fk(q, tool); const dx = p[0] - target[0], dy = p[1] - target[1], dz = p[2] - target[2]; const e2 = dx * dx + dy * dy + dz * dz; - if (e2 > tol2) continue; + if (!(e2 <= tol2)) continue; // 否定式:e2 为 NaN(目标含 NaN/Inf)时继续走拒绝分支 const c = scoreBranch(q, Math.sqrt(e2)); if (c && (!best || c.rcost < best.rcost - 1e-12)) best = c; } @@ -2237,6 +2237,7 @@ function refreshTarget() { let anim = null; function setJoints(q, doAnim) { if (doAnim) { animTo(q, 620); return; } + anim = null; // 直接写入必须取消在飞动画,否则入场动画会覆写实时解 for (let i = 0; i < 6; i++) { const id = ORDER[i]; ang[id] = q[i]; const s = $('s' + id), v = $('v' + id); @@ -2472,7 +2473,7 @@ function rollAngleAt(e) { const pl = new THREE.Plane().setFromNormalAndCoplanarPoint(n, gTgt.position); const hit = new THREE.Vector3(); ray2.setFromCamera(ndc2(e), cam); // 该版本 setFromCamera 不返回 this,勿链式 - if (!ray2.ray.intersectPlane(pl, hit)) return 0; + if (!ray2.ray.intersectPlane(pl, hit)) return null; // 错过平面:该帧不参与滚转(返回 0 会按 -a0 突跳) const v = hit.sub(gTgt.position); const up = Math.abs(n.y) > 0.9 ? new THREE.Vector3(1, 0, 0) : new THREE.Vector3(0, 1, 0); const u = new THREE.Vector3().crossVectors(n, up).normalize(); @@ -2524,6 +2525,7 @@ cv.addEventListener('pointermove', e => { if (grab.pose === 'roll') { // 拖圆环:绕轴滚转 dragged = true; const a1 = rollAngleAt(e); + if (a1 == null) return; // 射线错过平面:跳过该帧,角度不变 POSE.roll = S.wrap180(POSE.roll + S.wrap180(a1 - grab.a0)); grab.a0 = a1; syncPoseInputs(); refreshTarget(); diff --git a/web/机器人手臂3D查看器.html b/web/机器人手臂3D查看器.html index f798325..9ff8d79 100644 --- a/web/机器人手臂3D查看器.html +++ b/web/机器人手臂3D查看器.html @@ -1442,10 +1442,10 @@ function make(cfg) { return out; } - const inLimit = q => { + const inLimit = q => { // 取否定式:NaN 候选必须落选(原写法 NaN 比较全 false 会放行) for (let i = 0; i < 5; i++) { const t = LIM[ORDER[i]]; - if (q[i] < t.lo - 1e-9 || q[i] > t.hi + 1e-9) return false; + if (!(q[i] >= t.lo - 1e-9 && q[i] <= t.hi + 1e-9)) return false; } return true; }; @@ -1525,7 +1525,7 @@ function make(cfg) { const p = fk(q, tool); const dx = p[0] - target[0], dy = p[1] - target[1], dz = p[2] - target[2]; const e2 = dx * dx + dy * dy + dz * dz; - if (e2 > tol2) continue; + if (!(e2 <= tol2)) continue; // 否定式:e2 为 NaN(目标含 NaN/Inf)时继续走拒绝分支 const c = scoreBranch(q, Math.sqrt(e2)); if (c && (!best || c.rcost < best.rcost - 1e-12)) best = c; } @@ -2237,6 +2237,7 @@ function refreshTarget() { let anim = null; function setJoints(q, doAnim) { if (doAnim) { animTo(q, 620); return; } + anim = null; // 直接写入必须取消在飞动画,否则入场动画会覆写实时解 for (let i = 0; i < 6; i++) { const id = ORDER[i]; ang[id] = q[i]; const s = $('s' + id), v = $('v' + id); @@ -2472,7 +2473,7 @@ function rollAngleAt(e) { const pl = new THREE.Plane().setFromNormalAndCoplanarPoint(n, gTgt.position); const hit = new THREE.Vector3(); ray2.setFromCamera(ndc2(e), cam); // 该版本 setFromCamera 不返回 this,勿链式 - if (!ray2.ray.intersectPlane(pl, hit)) return 0; + if (!ray2.ray.intersectPlane(pl, hit)) return null; // 错过平面:该帧不参与滚转(返回 0 会按 -a0 突跳) const v = hit.sub(gTgt.position); const up = Math.abs(n.y) > 0.9 ? new THREE.Vector3(1, 0, 0) : new THREE.Vector3(0, 1, 0); const u = new THREE.Vector3().crossVectors(n, up).normalize(); @@ -2524,6 +2525,7 @@ cv.addEventListener('pointermove', e => { if (grab.pose === 'roll') { // 拖圆环:绕轴滚转 dragged = true; const a1 = rollAngleAt(e); + if (a1 == null) return; // 射线错过平面:跳过该帧,角度不变 POSE.roll = S.wrap180(POSE.roll + S.wrap180(a1 - grab.a0)); grab.a0 = a1; syncPoseInputs(); refreshTarget(); diff --git a/机器人手臂3D查看器.html b/机器人手臂3D查看器.html index f798325..9ff8d79 100644 --- a/机器人手臂3D查看器.html +++ b/机器人手臂3D查看器.html @@ -1442,10 +1442,10 @@ function make(cfg) { return out; } - const inLimit = q => { + const inLimit = q => { // 取否定式:NaN 候选必须落选(原写法 NaN 比较全 false 会放行) for (let i = 0; i < 5; i++) { const t = LIM[ORDER[i]]; - if (q[i] < t.lo - 1e-9 || q[i] > t.hi + 1e-9) return false; + if (!(q[i] >= t.lo - 1e-9 && q[i] <= t.hi + 1e-9)) return false; } return true; }; @@ -1525,7 +1525,7 @@ function make(cfg) { const p = fk(q, tool); const dx = p[0] - target[0], dy = p[1] - target[1], dz = p[2] - target[2]; const e2 = dx * dx + dy * dy + dz * dz; - if (e2 > tol2) continue; + if (!(e2 <= tol2)) continue; // 否定式:e2 为 NaN(目标含 NaN/Inf)时继续走拒绝分支 const c = scoreBranch(q, Math.sqrt(e2)); if (c && (!best || c.rcost < best.rcost - 1e-12)) best = c; } @@ -2237,6 +2237,7 @@ function refreshTarget() { let anim = null; function setJoints(q, doAnim) { if (doAnim) { animTo(q, 620); return; } + anim = null; // 直接写入必须取消在飞动画,否则入场动画会覆写实时解 for (let i = 0; i < 6; i++) { const id = ORDER[i]; ang[id] = q[i]; const s = $('s' + id), v = $('v' + id); @@ -2472,7 +2473,7 @@ function rollAngleAt(e) { const pl = new THREE.Plane().setFromNormalAndCoplanarPoint(n, gTgt.position); const hit = new THREE.Vector3(); ray2.setFromCamera(ndc2(e), cam); // 该版本 setFromCamera 不返回 this,勿链式 - if (!ray2.ray.intersectPlane(pl, hit)) return 0; + if (!ray2.ray.intersectPlane(pl, hit)) return null; // 错过平面:该帧不参与滚转(返回 0 会按 -a0 突跳) const v = hit.sub(gTgt.position); const up = Math.abs(n.y) > 0.9 ? new THREE.Vector3(1, 0, 0) : new THREE.Vector3(0, 1, 0); const u = new THREE.Vector3().crossVectors(n, up).normalize(); @@ -2524,6 +2525,7 @@ cv.addEventListener('pointermove', e => { if (grab.pose === 'roll') { // 拖圆环:绕轴滚转 dragged = true; const a1 = rollAngleAt(e); + if (a1 == null) return; // 射线错过平面:跳过该帧,角度不变 POSE.roll = S.wrap180(POSE.roll + S.wrap180(a1 - grab.a0)); grab.a0 = a1; syncPoseInputs(); refreshTarget();