Files
Robot_Arm/webapp/js/harness.js
T

369 lines
14 KiB
JavaScript
Raw Normal View History

/* 线束与走线指示系统(通道管、扎带、T 接、缓冲环、桥接曲线) */
/* ===================== 线束与走线指示系统 (Wire Harness System) ===================== */
const harnessGroup = new THREE.Group();
harnessGroup.name = 'HARNESS_ROOT';
scene.add(harnessGroup);
const matTrunk = new THREE.MeshStandardMaterial({
color: 0xf59e0b, roughness: 0.35, metalness: 0.25
});
const matDrop = new THREE.MeshStandardMaterial({
color: 0x10b981, roughness: 0.3, metalness: 0.2
});
const matTap = new THREE.MeshStandardMaterial({
color: 0x8b5cf6, roughness: 0.4, metalness: 0.3
});
const matTerm = new THREE.MeshStandardMaterial({
color: 0xef4444, roughness: 0.2, metalness: 0.5
});
const matClip = new THREE.MeshStandardMaterial({
color: 0x1e293b, roughness: 0.5, metalness: 0.2
});
const harnessMeshes = [];
function makeHarnTube(ptsCAD, r, mat, name, cn, parentGrp, segs = 18, radSegs = 8) {
const pts = ptsCAD.map(p => new THREE.Vector3(...VC(p)));
const curve = new THREE.CatmullRomCurve3(pts);
const geo = new THREE.TubeGeometry(curve, segs, r, radSegs, false);
const mesh = new THREE.Mesh(geo, mat);
mesh.userData = {
idx: 'H-CABLE', name: name || '4芯高柔拖链总线 (CAN+24V)',
cn: cn || '主干总线', vol: 8000, printed: false, harness: true
};
(parentGrp || harnessGroup).add(mesh);
harnessMeshes.push(mesh);
return mesh;
}
function makeHarnTap(pCAD, label, parentGrp) {
const geo = new THREE.BoxGeometry(10, 8, 14);
const mesh = new THREE.Mesh(geo, matTap);
mesh.position.set(...VC(pCAD));
mesh.userData = {
idx: 'H-TAP', name: label || 'T接分线小板',
cn: '微型T型总线分线节点', vol: 1100, printed: false, harness: true
};
const ledGeo = new THREE.SphereGeometry(1.2, 8, 8);
const ledMat = new THREE.MeshBasicMaterial({ color: 0x34d399 });
const led = new THREE.Mesh(ledGeo, ledMat);
led.position.set(0, 4.5, 0);
mesh.add(led);
(parentGrp || harnessGroup).add(mesh);
harnessMeshes.push(mesh);
return mesh;
}
function makeHarnClip(pCAD, parentGrp) {
const geo = new THREE.CylinderGeometry(3.0, 3.0, 4.0, 12, 1, true);
const mesh = new THREE.Mesh(geo, matClip);
mesh.position.set(...VC(pCAD));
(parentGrp || harnessGroup).add(mesh);
harnessMeshes.push(mesh);
return mesh;
}
function makeHarnTerm(pCAD, parentGrp) {
const geo = new THREE.CylinderGeometry(2.5, 2.5, 8.0, 12);
const mesh = new THREE.Mesh(geo, matTerm);
mesh.position.set(...VC(pCAD));
mesh.rotation.z = Math.PI / 2;
mesh.userData = {
idx: 'H-TERM', name: '120Ω 终端电阻 (总线末端阻抗匹配)',
cn: 'CAN总线终端匹配电阻', vol: 500, printed: false, harness: true
};
(parentGrp || harnessGroup).add(mesh);
harnessMeshes.push(mesh);
return mesh;
}
// 1. Root group harness (P01 立柱、P03 吊臂、M1 电机)
const hRoot = new THREE.Group();
hRoot.name = 'HARN_ROOT_SUB';
JG['root'].add(hRoot);
makeHarnTube([
[-115.0, 128.0, 35.0], [-115.0, 128.0, 250.0], [-115.0, 128.0, 470.0],
[-115.0, 128.0, 500.0]
], 2.0, matTrunk, 'P01立柱内穿线管主干 (24V+CAN,穿44x44内腔与P03减重窗)', '基座立柱主干', hRoot);
makeHarnTube([
[-115.0, 128.0, 500.0], [-115.0, 101.5, 520.0], [-105.0, 90.0, 545.0],
[-85.0, 89.8, 570.0], [-70.0, 87.4, 592.0], [-56.0, 85.6, 610.0],
[-20.0, 85.8, 610.0], [5.5, 86.1, 610.0]
], 1.8, matTrunk, 'P03吊臂侧向骨架主干 (扎带拱桥锁附,线心贴桥外露面)', 'P03吊臂实体主干', hRoot, 24, 8);
makeHarnTap([-115.0, 128.0, 505.0], 'T接小板① (M1供电分线节点)', hRoot);
// M1 外部大空间绕行通道:线束自基座 +Y 侧向外绕过 P03 法兰外廓(免打孔),在外部宽阔自由空间大半径弧形引入 M1 电机侧向缺口
makeHarnTube([
[-115.0, 128.0, 505.0], [-115.0, 155.0, 515.0], [-115.0, 172.0, 530.0],
[-95.0, 172.0, 565.0], [-75.0, 172.0, 595.0], [-55.0, 172.0, 610.15],
[-46.0, 166.0, 610.15], [-48.5, 158.0, 610.15], [-49.5, 154.5, 610.15]
], 1.5, matDrop, 'P03吊臂M1供电支干 (外侧大空间绕行引入)', 'M1 供电支干', hRoot, 16, 8);
makeHarnTap([-46.0, 166.0, 610.15], 'M1大空间侧向接插件 (20x20mm容纳位)', hRoot);
makeHarnClip([-115.0, 128.0, 300.0], hRoot);
makeHarnClip([-85.0, 89.8, 570.0], hRoot);
makeHarnClip([-56.0, 85.6, 610.0], hRoot);
makeHarnClip([5.5, 86.1, 610.0], hRoot);
const anchorA0 = new THREE.Object3D();
anchorA0.position.set(...VC([5.5, 86.1, 610.0]));
hRoot.add(anchorA0);
// 2. J1 group harness (P04 肩叉、M2 电机)
const hJ1 = new THREE.Group();
hJ1.name = 'HARN_J1';
JG['J1'].add(hJ1);
const anchorB1 = new THREE.Object3D();
anchorB1.position.set(...VC([20.0, 87.0, 615.0]));
hJ1.add(anchorB1);
makeHarnTube([
[20.0, 87.0, 615.0], [20.0, 87.0, 585.0], [20.0, 87.0, 560.0],
[20.0, 87.0, 535.0], [20.0, 87.0, 515.0], [5.0, 87.0, 500.0],
[-10.0, 87.0, 492.0], [-20.0, 88.0, 490.0], [-22.0, 96.0, 490.0],
[-20.0, 102.5, 490.0], [-17.5, 102.5, 485.0]
], 1.8, matTrunk, 'P04肩叉侧向外廓线卡走线 (扎带拱桥锁附)', 'P04 侧向走线', hJ1, 24, 8);
makeHarnTap([-17.5, 102.5, 490.0], 'T接小板② (J2/M2 分线节点)', hJ1);
// M2 侧向缺口出线 (CAD Notch: [-8.04, 102.50, 487.28],电机后端盖圆形PCBA侧向出线槽)
makeHarnTube([
[-8.04, 102.50, 487.28], [-13.0, 102.50, 488.5], [-17.5, 102.50, 490.0]
], 1.3, matDrop, 'M2 电机侧向缺口引出线 (2xCAN + 2x电源)', 'M2 出线分支', hJ1, 10, 6);
makeHarnClip([20.0, 87.0, 615.0], hJ1);
makeHarnClip([20.0, 87.0, 560.0], hJ1);
makeHarnClip([20.0, 87.0, 515.0], hJ1);
const anchorA1 = new THREE.Object3D();
anchorA1.position.set(...VC([-17.5, 102.5, 485.0]));
hJ1.add(anchorA1);
// 3. J2 group harness (P05 大臂旋转座、M3 电机)
const hJ2 = new THREE.Group();
hJ2.name = 'HARN_J2';
JG['J2'].add(hJ2);
const anchorB2 = new THREE.Object3D();
anchorB2.position.set(...VC([-17.5, 102.5, 445.0]));
hJ2.add(anchorB2);
makeHarnTube([
[-17.5, 102.5, 445.0], [-17.5, 102.5, 440.0]
], 1.8, matTrunk, 'P05大臂旋转座侧向外廓走线 (外侧平滑过渡,不穿电机)', 'P05 侧向走线', hJ2, 6, 6);
makeHarnTap([-17.5, 102.5, 440.0], 'T接小板③ (J3/M3 分线节点)', hJ2);
// M3 侧向缺口出线 (CAD Notch: [22.09, 100.30, 442.50],前向敞开侧圆形PCBA径向缺口)
makeHarnTube([
[22.09, 100.30, 442.50], [22.09, 93.0, 443.0], [5.0, 94.0, 444.0],
[-10.0, 98.0, 445.0], [-17.5, 102.50, 440.0]
], 1.3, matDrop, 'M3 电机侧向缺口引出线 (2xCAN + 2x电源)', 'M3 出线分支', hJ2, 12, 6);
makeHarnClip([-17.5, 102.5, 425.0], hJ2);
const anchorA2 = new THREE.Object3D();
anchorA2.position.set(...VC([-17.5, 102.5, 400.0]));
hJ2.add(anchorA2);
makeHarnTube([
[-17.5, 102.5, 440.0], [-17.5, 102.5, 425.0], [-17.5, 102.5, 400.0]
], 1.8, matTrunk, 'P05大臂旋转座侧向外壁引出段', 'P05引出', hJ2, 8, 6);
// 4. J3 group harness (P06 大臂骨架、M4 电机)
const hJ3 = new THREE.Group();
hJ3.name = 'HARN_J3';
JG['J3'].add(hJ3);
const anchorB3 = new THREE.Object3D();
anchorB3.position.set(...VC([-17.5, 102.5, 360.0]));
hJ3.add(anchorB3);
makeHarnTube([
[-17.5, 102.5, 360.0], [-20.7, 102.5, 345.0]
], 1.8, matTrunk, 'P06大臂外侧线槽走线', 'P06 侧向走线', hJ3, 8, 6);
makeHarnTap([-17.5, 102.5, 332.0], 'T接小板④ (J4/M4 肘部分线节点)', hJ3);
// M4 肘电机侧向缺口出线 (CAD Notch: [-8.04, 102.50, 329.28],电机后端盖圆形PCBA侧向出线槽)
makeHarnTube([
[-8.04, 102.50, 329.28], [-13.0, 102.50, 330.5], [-17.5, 102.50, 332.0]
], 1.3, matDrop, 'M4 肘电机侧向引出线 (2xCAN + 2x电源)', 'M4 出线分支', hJ3, 10, 6);
makeHarnClip([-20.7, 102.5, 345.0], hJ3);
makeHarnTube([
[-20.7, 102.5, 345.0], [-17.5, 102.5, 320.0], [-17.5, 102.5, 305.0]
], 1.8, matTrunk, 'P06大臂侧向外廊引出段', 'P06引出', hJ3, 8, 6);
const anchorA3 = new THREE.Object3D();
anchorA3.position.set(...VC([-17.5, 102.5, 305.0]));
hJ3.add(anchorA3);
// 5. J4 group harness (P07 小臂骨架、M5 电机)
const hJ4 = new THREE.Group();
hJ4.name = 'HARN_J4';
JG['J4'].add(hJ4);
const anchorB4 = new THREE.Object3D();
anchorB4.position.set(...VC([-17.5, 102.5, 255.0]));
hJ4.add(anchorB4);
makeHarnTube([
[-17.5, 102.5, 255.0], [-20.7, 102.5, 235.0]
], 1.8, matTrunk, 'P07小臂侧向外廓线槽走线 (外侧平滑过渡,不强行内穿)', 'P07 侧向走线', hJ4, 10, 6);
makeHarnTap([-17.5, 102.5, 222.0], 'T接小板⑤ (J5/M5 分线节点)', hJ4);
// M5 腕仰俯电机侧向缺口出线 (CAD Notch: [-8.04, 102.50, 219.28],电机后端盖圆形PCBA侧向出线槽)
makeHarnTube([
[-8.04, 102.50, 219.28], [-13.0, 102.50, 220.5], [-17.5, 102.50, 222.0]
], 1.3, matDrop, 'M5 电机侧向引出线 (2xCAN + 2x电源)', 'M5 出线分支', hJ4, 10, 6);
makeHarnClip([-20.7, 102.5, 235.0], hJ4);
makeHarnTube([
[-20.7, 102.5, 235.0], [-17.5, 102.5, 215.0], [-17.5, 102.5, 205.0]
], 1.8, matTrunk, 'P07小臂侧向外壁引出段', 'P07引出', hJ4, 8, 6);
const anchorA4 = new THREE.Object3D();
anchorA4.position.set(...VC([-17.5, 102.5, 205.0]));
hJ4.add(anchorA4);
// 6. J5 group harness (P08 腕偏转座、M6 电机)
const hJ5 = new THREE.Group();
hJ5.name = 'HARN_J5';
JG['J5'].add(hJ5);
const anchorB5 = new THREE.Object3D();
anchorB5.position.set(...VC([-17.5, 102.5, 175.0]));
hJ5.add(anchorB5);
makeHarnTube([
[-17.5, 102.5, 175.0], [-17.5, 102.5, 172.0]
], 1.8, matTrunk, 'P08腕偏转座侧向走线', 'P08 腕部外侧走线', hJ5, 6, 6);
makeHarnTap([-17.5, 102.5, 172.0], 'T接小板⑥ (J6/M6 终端分线节点)', hJ5);
// M6 腕自转电机侧向缺口出线 (CAD Notch: [22.09, 100.30, 173.50],前向敞开侧圆形PCBA径向缺口)
makeHarnTube([
[22.09, 100.30, 173.50], [22.09, 93.0, 174.0], [5.0, 94.0, 174.5],
[-10.0, 98.0, 175.0], [-17.5, 102.50, 172.0]
], 1.3, matDrop, 'M6 腕电机侧向引出线 (2xCAN + 2x电源)', 'M6 出线分支', hJ5, 12, 6);
makeHarnTerm([-17.5, 102.5, 168.0], hJ5);
// 7. Dynamic Flexible Bridges across J1~J5 (Service Loop / 弓形弹性缓冲回环)
const bridgeMeshes = [];
for (let i = 0; i < 5; i++) {
const geo = new THREE.BufferGeometry();
const mesh = new THREE.Mesh(geo, matTrunk);
mesh.userData = {
idx: 'H-BRIDGE-' + (i + 1),
name: 'J' + (i + 1) + ' 跨关节柔性服务回环 (Service Loop / 弓形缓冲弯曲段)',
cn: 'J' + (i + 1) + ' 柔性缓冲回环', vol: 2500, printed: false, harness: true
};
harnessGroup.add(mesh);
bridgeMeshes.push(mesh);
harnessMeshes.push(mesh);
}
const allHarnessContainers = [hRoot, hJ1, hJ2, hJ3, hJ4, hJ5, harnessGroup];
let harnessVisibleState = true;
function setHarnessVisible(v) {
harnessVisibleState = v;
allHarnessContainers.forEach(g => g.visible = v);
if (v) updateHarnessBridges();
requestRender();
}
window.setHarnessVisible = setHarnessVisible;
window.isHarnessVisible = () => harnessVisibleState;
const _pA = new THREE.Vector3(), _pB = new THREE.Vector3();
function updateHarnessBridges() {
if (!harnessVisibleState) return;
scene.updateMatrixWorld();
const pairs = [
{ a: anchorA0, b: anchorB1, type: 'roll1', slackL: 25, minH: 5, dir: new THREE.Vector3(0, 0.1, 0.99) }, // J1 roll
{ a: anchorA1, b: anchorB2, type: 'pitch2', slackL: 48, minH: 8, dir: new THREE.Vector3(-1, 0, 0) }, // J2 pitch
{ a: anchorA2, b: anchorB3, type: 'roll3', slackL: 46, minH: 7, dir: new THREE.Vector3(-0.7, 0, 0.7) }, // J3 roll
{ a: anchorA3, b: anchorB4, type: 'pitch4', slackL: 56, minH: 8, dir: new THREE.Vector3(-1, 0, 0) }, // J4 elbow pitch
{ a: anchorA4, b: anchorB5, type: 'pitch5', slackL: 36, minH: 6, dir: new THREE.Vector3(-1, 0, 0) }, // J5 wrist pitch
];
for (let i = 0; i < 5; i++) {
const pr = pairs[i];
pr.a.getWorldPosition(_pA);
pr.b.getWorldPosition(_pB);
const m = bridgeMeshes[i];
const dist = _pA.distanceTo(_pB);
const halfL = Math.max(dist * 0.5 + 2, pr.slackL * 0.5);
const halfD = dist * 0.5;
const h = Math.sqrt(Math.max(pr.minH * pr.minH, halfL * halfL - halfD * halfD));
const dirNorm = pr.dir.clone().normalize();
// 13 点正弦弹性拱形样条插值:端点切向连续,中间大半径圆滑过渡,彻底杜绝折角与多边形棱角
const pts = [];
const N = 12;
for (let k = 0; k <= N; k++) {
const u = k / N;
const base = new THREE.Vector3().lerpVectors(_pA, _pB, u);
const bulge = Math.sin(Math.PI * u) * h;
base.addScaledVector(dirNorm, bulge);
pts.push(base);
}
const curve = new THREE.CatmullRomCurve3(pts, false, 'centripetal', 0.5);
if (m.geometry) m.geometry.dispose();
m.geometry = new THREE.TubeGeometry(curve, 32, 1.8, 10, false);
}
}
window.updateHarnessBridges = updateHarnessBridges;
window.harnessGroup = harnessGroup;
window.harnessMeshes = harnessMeshes;
updateHarnessBridges();
const J6j = JO.find(j => j.id === 'J6');
// the jaws slide along the gripper motor's own local X, so that axis must come from J6
const JAWD = J6j ? new V3(...VC(J6j.xdir)).normalize() : new V3(0, 0, 1);
const PIN_R = 14.0;
const JMAX = TL.J6 ? PIN_R * (1 - Math.cos(TL.J6.hi * Math.PI / 180)) : 9.0;
const JAW_MAX = Math.min(9.0, Math.max(2.0, JMAX));
let jawMM = 0;
function updXform() {
for (const m of meshes) {
m.position.copy(m.base).multiplyScalar(expl);
if (m.userData.jaw) m.position.addScaledVector(JAWD, m.userData.jaw * jawMM);
}
if (window.setHarnessVisible) {
const bH = document.getElementById('bHarn');
const on = bH ? bH.classList.contains('on') : true;
setHarnessVisible(on && (expl < 0.05));
}
requestRender();
}
function setJaw(t) {
jawMM = Math.max(0, Math.min(JAW_MAX, t));
// the crank pins sit at radius 14 along the jaw axis as authored, so a rotation of phi
// puts the pin at x = 14*cos(phi) and each jaw rides out by exactly that much
const phi = Math.acos(Math.max(-1, Math.min(1, (PIN_R - jawMM) / PIN_R)));
ang.J6 = -phi * 180 / Math.PI;
applyJoint('J6');
updXform();
const e = document.getElementById('vJ6'); if (e) e.textContent = (2 * jawMM).toFixed(1) + ' mm';
}
function setExpl(k) {
expl = k;
updXform();
if (typeof updStat === 'function') updStat();
requestRender();
}
setExpl(0); // start assembled, not exploded