# 机器人手臂:运动学模拟与碰撞算法详解 > 本文基于 2026-10-03 的代码现状逐文件核对写成,所有数字(关节轴坐标、行程、胶囊数、自检误差)均取自当前仓库里的真实代码与数据文件,不是设计目标值。 > 配图:`docs/algo_svg/*.svg` 为算法示意图,`docs/shots/*.png` 为查看器实机渲染截图。 --- ## 目录 - [第一篇 系统总览:一个事实源、两个碰撞模型](#第一篇-系统总览一个事实源两个碰撞模型) - [第二篇 运动学建模与正运动学(FK)](#第二篇-运动学建模与正运动学fk) - [第三篇 逆运动学:闭式支 + 二维冗余扫描 + 牛顿收口](#第三篇-逆运动学闭式支--二维冗余扫描--牛顿收口) - [第四篇 交互层:把求解器接进 30fps](#第四篇-交互层把求解器接进-30fps) - [第五篇 碰撞检测 I:离线权威扫描(sweep_v2.py)](#第五篇-碰撞检测-i离线权威扫描sweep_v2py) - [第六篇 碰撞检测 II:实时胶囊模型与动态限位](#第六篇-碰撞检测-ii实时胶囊模型与动态限位) - [第七篇 力矩与强度校核(简述)](#第七篇-力矩与强度校核简述) - [第八篇 自检体系与复现命令](#第八篇-自检体系与复现命令) --- ## 第一篇 系统总览:一个事实源、两个碰撞模型 整台手臂的"运动学模拟"分布在两个运行环境里,它们共享同一份关节几何,靠数据文件(而不是人工抄写)保持一致: | 环境 | 载体 | 算法 | |---|---|---| | 离线(Python/CadQuery) | `src/human_layout.py` → `arm_model.py` → `sweep_v2.py` | 参数化 CAD 建模、OCCT 精确布尔碰撞、行程扫描 | | 在线(浏览器) | `web/arm-ik-solver.js` + `ik-glue.js` + three.js 场景图 | 闭式 IK、实时胶囊碰撞、动态限位 | ![系统总览数据流](algo_svg/01_overview_dataflow.svg) 关键纪律是**单一事实源**:6 根关节轴的位置 `rear` 与方向 `d` 只在 `human_layout.py` 里定义一次(`human_layout.py:110-115`),CAD 装配、离线碰撞扫描、网页求解器三处都从这里取值——网页端由 `build_viewer.py` 打包时把同一组常量注入 HTML,因此"CAD 里看到的"和"网页里算的"逐位一致,不存在第二套会漂移的关节表。 两个碰撞模型各司其职: - **离线精确模型**(`sweep_v2.py`):对真实 CAD 实体做布尔求交,慢(每姿态秒级)但精确,负责产出权威行程数据; - **在线近似模型**(`collide.json` 胶囊体):把每件零件拟合成一组胶囊,快(每次 ~5ms)但近似,负责求解过程中的实时避碰。 几何变更时的同步链(缺一步网页就是旧的、行程就是错的): ``` arm_model.py 重建 → AB.export 写 mesh_arm.json → build_viewer.py 重打包 → 渲染出图确认 → sweep_v2.py 重扫(travel.json)→ fit_capsules.py 重拟合(collide.json) ``` 实机全貌(查看器渲染,零位姿态): ![实机等轴测渲染](shots/01_iso.png) --- ## 第二篇 运动学建模与正运动学(FK) ### 2.1 关节拓扑 对标智元灵犀 X1 的人形构型:肩部两轴(抬举+俯仰)→ 大臂自转 → 肘俯仰 → 腕俯仰 → 腕自转。所有关节轴都是坐标轴方向的(±X/±Y/±Z),这是后面一切降阶解法的前提: | 关节 | 名称 | 轴方向 | 轴位置(CAD 系) | 驱动 | |---|---|---|---|---| | J1 | 肩抬举 | +X(滚转) | x=−51, z=610 | M1 → P04 肩叉 | | J2 | 肩俯仰 | +Y(俯仰) | x=19.5, z=490 | M2 → P05 大臂旋转座 | | J3 | 大臂自转 | −Z(滚转) | x=19.5, z=445 | M3 → P06 大臂骨架 | | J4 | 肘部俯仰 | +Y(俯仰) | x=19.5, z=332 | M4 → P07 小臂骨架 | | J5 | 腕部俯仰 | +Y(俯仰) | x=19.5, z=222 | M5 → P08 腕偏转座 | | J6 | 腕自转 | −Z(滚转) | x=19.5, z=176 | M6 → P09 工具法兰 | ![运动链与关节轴](algo_svg/02_kinematic_chain.svg) ![实机侧视](shots/03_side.png) 求解器启动时会对五条**结构前提**做断言(`arm-ik-solver.js` 的 `WARN` 列表):J1 轴是 X;J2/J4/J5 是平行同向的 Y 轴且落在同一 x 平面;J3/J6 是 Z 轴;TCP 落在 J6 轴线上。任何一条被将来的改型破坏,页面会显式报警并回落到通用(慢)路径,而不是悄悄算错。 ### 2.2 正运动学:枢轴链 每个关节是"绕一根不经过原点的轴"的旋转,写成枢轴变换: ``` Ai(θi) = T(Pi) · R(di, θi) · T(−Pi) 对任意点:x' = R·(x−Pi) + Pi ``` 整条链就是六次左乘: ``` World = A1(θ1)·A2(θ2)·A3(θ3)·A4(θ4)·A5(θ5)·A6(θ6)·TCP ``` ![枢轴变换与链式累积](algo_svg/03_fk_transform.svg) 代码里有两套实现互为对账: - `fkChain()`:严格 4×4 同质矩阵链,直白但慢,作为对照基准; - `fk()`:把结构常量(`K.C0=Z2−Z1`、`K.A32=Z3−Z2`、`K.A43=Z4−Z3`、`K.A54=Z5−Z4`、`K.L5=Z5−TCP_z` 等,即各关节轴的竖直间距链)代入后展开三角函数的闭式快路径,是求解器热循环的专用实现。 预置自检:5000 个随机位形下两实现的 TCP 差 ≤ **5.7e-13 mm**(数值见 `ik-glue.js` 的 `PRECOMPUTED_CHK`,绑定代码指纹,模型一变即失效提示重算)。 三个对算法影响深远的结构性质: 1. **J4 ∥ J5 同向** ⇒ `R4·R5 = R_y(θ4+θ5)`,腕部两个俯仰在"工具轴指向"这件事上可以合并成一个未知数 `s = θ4+θ5`; 2. **TCP 在 J6 轴线上** ⇒ θ6 只提供绕工具轴的滚转,**完全不改变 TCP 位置**——位置任务与姿态任务由此解耦; 3. **工具轴(法兰 +Z)= (R1…R5)·ẑ**,与 θ6 无关——姿态任务只剩 2 个方程。 --- ## 第三篇 逆运动学:闭式支 + 二维冗余扫描 + 牛顿收口 ### 3.1 问题形式与降阶 6 自由度机械臂,任务是给定 TCP 目标点(位置任务,3 个方程)或"目标点 + 工具姿态 {方位角 az, 仰角 el, 滚转 roll}"(位姿任务,再加 3 个方程)。臂本身有 6 个未知数,位置任务下存在 **2 维冗余**。 核心思路(`branch()`,arm-ik-solver.js:528):**把 (θ4, θ5) 当作冗余参数**。一旦给定这两个角,腕心相对量 `V` 完全确定(u=−L·sinθ5, w=A54−L·cosθ5, dx=u·c4+w·s4, dz=A43−u·s4+w·c4),剩下的 θ1..θ3 可以用平面三角学**一步闭式解出**(每格至多 4 支:θ3 的 ±σ 与 ±(π−σ)),位置精确到浮点误差,不需要任何迭代。 ![闭式支几何](algo_svg/06_branch_geometry.svg) 于是"求 IK"变成了"在 (θ4,θ5) 二维平面上挑最好的格点"——**扫描代替数值搜索**。每个格点的解天然精确,扫描的全部意义在于挑出代价(关节行程)最小的那支构型。 ### 3.2 姿态参数化:方位角 / 仰角 / 滚转 姿态任务用三个角描述工具姿态(`poseMat()` 构造工具系旋转矩阵,`poseAng()` 逆分解):**az** 绕 CAD Z 的方位角、**el** 自 +Z 的仰角(0=朝上,180=朝下)、**roll** 绕工具轴自身的滚转。三个关键性质撑起了姿态算法: - θ6 只提供绕工具轴的滚转,**工具轴 = (R1…R5)·ẑ 与 θ6 无关**——姿态任务因此只剩 2 个方程; - 滚转由 `rollClose()` 闭式补出:θ6 = −atan2(N[1,0], N[0,0]),其中 N = R5ᵀ·R_target; - 推论:**全姿态偏差 oerr ≥ 工具轴夹角**(20 万随机样本验证零违反)——粗扫用一次点积预筛(1−cos2°)就挡掉所有必被拒的候选,免掉矩阵链 + acos。 ![工具姿态参数化](algo_svg/10_pose_parameterization.svg) ### 3.3 求解流水线 ![IK 流水线](algo_svg/04_ik_pipeline.svg) 1. **逐行可行段探测**:位置闭式支只在平面三角有解时存在,限位矩形里只有 7~27% 的格点能过判据。先用探测步 `hp` 找每行的连续可行段(位置任务 2°、姿态任务 0.5°——姿态任务的可行带实测最窄 **0.98°**,2° 探测会整条漏掉造成"误报不可达"),段端外扩 PAD=2°,段内仍按原步长 h(默认 0.25°)精扫:密度不降、成本大降。 2. **全域粗扫**:每格 `branch()` 出 ≤4 支 → 限位筛(NaN 候选用否定式写法保证落选)→ 打分,候选有界保留 4000。 3. **孤立根枚举**(姿态任务):不再靠网格密度去撞窄带——逐行对姿态残差两个分量找**变号括号**,从括号中点沿该支跑阻尼高斯-牛顿收根;切向相切的根不变号,按每支 ‖e‖ 最小的节点补种。 4. **局部加密 + 模式搜索**:32 个种子各开 ±2h 窗口(步长 h/8,小步长档 h/4),再对最优的 24 个候选做 8 邻域模式搜索(步长逐轮减半至 2e-4°)。 5. **姿态收口**:姿态达标带是 (θ4,θ5) 平面上的斜窄谷(局部灵敏度可达 ~10°/°),模式搜索只能停在谷壁(实测最好 0.106°)。直接对"工具轴在目标轴法平面上的切向偏差"做 **(θ4,θ5) 二维牛顿迭代**(每步重解闭式位置支、按 θ1..θ3 最近匹配保持同支),轴残差压到 **1e-12** 量级、位置全程保持精确;尚无 oerr≤0.05° 的解时姿态权重 ×100 重扫(最多 3 轮)。 6. **选解**:`cost = Σ w·|wrap180(θ−ref)| + 1e-6·Σ|θ|`。ref 是当前姿态——判据是用户定的「**所有电机需要转过的角度之和最小**」(这一步行程最短),连续轴取等价角防绕远。姿态任务先取 oerr≤0.05° 的达标集、集内再比代价,"省几度关节角、丢 0.2° 姿态"的交易被显式禁止。 7. **solveDyn 碰撞后处理**(第六篇)。 ### 3.4 兜底:最近可达位姿 目标不可达时不报错了事,而是返回最近可达位姿,并且按任务语义分三种: - `closest()`:位置 DLS(阻尼最小二乘)——160 迭代 × 160 次 **LCG 确定性重启**(固定种子,同一目标逐位可复现,消灭"随机倒摆")+ anchor 回拉 0.03(压准退化构型下 J3 的无用漂移,实测 82°)+ **零空间抛光**(到目标距离不增的前提下把 Σ|θ| 滑到最小); - `closest6()`:位置 + 姿态 6 维联合 DLS(姿态旋转矢量按 20 mm/rad 折算进误差向量); - `closestOri()`:姿态优先——利用 J4∥J5 的降阶结构(工具轴 = Rx(θ1)·Ry(θ2)·Rz(−θ3)·Ry(s)·ẑ),(θ3, s, θ4) 三维粗扫 + 坐标下降,**姿态精确、位置取最近**。 ### 3.5 混合优先(位置/姿态滑杆) 位置与姿态在远目标上不可兼得(实测同一目标:位置 0mm 时姿态差 44°,姿态 0° 时位置差 116mm)。`mix` 模式用两端锚点(位置优先解 / 姿态优先解)的误差各自归一,按 ``` J(q) = w·(e/e_ref)² + (1−w)·(o/o_ref)² ``` 加权折中,种子取两锚点 + 关节空间插值 + ref,6 维阻尼高斯-牛顿 + 单轴抛光;J 同量级带内取行程更小者防横跳。滑杆 0%/100% 直接走两条专用算法,结果逐位一致。 ### 3.6 实时续解(warm 路径) 拖拽时目标每帧只挪几毫米,全域粗扫(0.25° 下 6~30 万点,40~430ms)是 100ms 级延迟的唯一来源。`solveWarm` 只在上一帧解附近的 (θ4,θ5) 递进窗口(±4°→14°→40°)里走**同一闭式支**:位置残差 ≤1e-6、关节跳 ≤25°(防换支/倒摆)、姿态牛顿收口——任一校验不过就返回 null 交回全量。快路只负责"能快的时候快",松手后的全量求解才是最终结果。 ### 3.7 工作空间剖面 工作空间由纯 FK 采样刻画(`scratch/gen_workspace_svg.cjs`,与查看器「可达空间」同口径:travel.json + adj 机械区间内随机位形、J6=0、未计自碰撞)。J1 整周连续,可达集对 J1 轴旋转对称,所以 (x, r) 二维剖面(r = 离 J1 轴的距离)就是完整工作空间:rmax = 517mm 恰好是**零位臂竖直下垂的 TCP**,前伸边界 x≈+416mm。 ![工作空间剖面](algo_svg/11_workspace_section.svg) 自检成绩(预置值,指纹校验): | 项目 | 样本 | 结果 | |---|---|---| | FK 双实现对账 | 5000 | 最大差 5.7e-13 mm | | 位置 IK 重构 | 200/200 命中 | 最大残差 9.9e-13 mm | | 姿态 IK 重构 | 400 组 | 398 命中,最大残差 1.79°,平均 77.8 ms/次 | | 运动学可达空间 | 400 000 位形 | x −378…415 / y −388…642 / z 94…1125 mm | (姿态 IK 的 2 组未达 0.05° 达标线,以近似解如实上报 `poseTier=approx` 并给出真实残差,不虚报精确。) ![IK 求解姿态](shots/08_pose.png) --- ## 第四篇 交互层:把求解器接进 30fps `ik-glue.js` 负责三件事: **调度**。全量求解(40~430ms)放进 Blob Worker 后台跑,主线程只收结果渲染。每个请求按 id 记账,"是否最新一帧"用第二参交给回调——被插队作废的请求也会被放行,保证实时追踪的门闩(liveBusy/livePending)不因一次竞态永久停摆。Worker 不可用时自动回落主线程同步求解,行为逐位一致。 **拖拽语义**。黄球 = 移动目标(Shift 只上下);姿态控制开启后出现工具箭头:拖箭头头部 = 弧球式瞄准(指针射线与"以目标为球心、箭长为半径"的球求交,方向即工具轴指向),拖箭杆/捏环/粉色侧球 = 绕轴滚转(指针在垂直于工具轴的平面上的转角)。"实时追踪"模式下拖动即算即动,松手全量精修。 **动画与对账**。解算结果经 `animTo` 动画写入:连续轴(J1/J3/J6)按 `wrap180` 走**短弧**(+151.9°→−167° 若按数值插值会反向扫 318.9°,走短弧只有 41.1°),末帧落回求解器给的代表角——与插值终点恰差 360° 整数倍,同一物理角、视觉零跳变。每帧渲染后还用**场景图真实矩阵链**复算一次 TCP 位置与姿态偏差(`sceneOrientErr`),和求解器的闭式残差双通道对账——任何"求解器以为对了、场景画错了"的不同步都会立刻暴露在界面上。 --- ## 第五篇 碰撞检测 I:离线权威扫描(sweep_v2.py) 离线扫描回答的问题是:"**每个关节到底能转到多少度?**"答案直接写进 `docs/travel.json` 成为查看器滑块的硬限位,因此判定必须精确——它直接对真实 CAD 实体做 OCCT 布尔求交,一切预筛都设计成**保守证书**:只能"确证分离"才允许跳过精确布尔。 ![碰撞过滤漏斗](algo_svg/07_collision_funnel.svg) 逐层拆解(每姿态的判定路径 `test_posture()`): 1. **全对全**:19 件(9 打印件 + 6 电机 + 台架 P01/P02)≈ 171 对。台架默认**参与**碰撞——臂撞立柱就是臂撞躯干,是真约束(`--no-stand` 仅用于诊断某个限位是臂自身折叠还是躯干挡的)。 2. **跳过刚性安装对**:12 对电机↔被驱动件由螺钉锁死(`MOUNT_PAIRS`),无相对运动。 3. **组跨度规则**:某姿态下没有活动关节跨越的零件对相对静止,跳过;但**零位(无活动关节)时必须全查**——相对位姿不变不代表不干涉,零位单实体检查就是这么兜住的。 4. **轴区间预剪枝**(单关节扫描专用):按关节轴算每对零件的轴向/径向区间,永不相交的对在该关节任何角度都不可能碰撞——对单关节扫描是**精确安全**的。 5. **AABB**:8 角点变换后取包围盒,0.1mm 余量。 6. **包围球**:球心随组刚体变换、半径旋转不变,仍精确保守。 7. **GJK 凸包预筛**:零件按 0.5mm 容差网格化取顶点云 → scipy 凸包精简(电机 15 万顶点 → 千余点),GJK 带 0.8mm 安全外扩(> 网格内缩 0.5mm,保证"确证分离"判据保守)。单纯形用 Muratori 形式手写,"可能干涉/数值退化/迭代超限"一律回落精确布尔——**结果与纯布尔完全一致**,`--no-gjk` 可关闭做对比校验。 8. **顶点侵入实锤**(反方向捷径):一方的网格顶点(≤240 采样点)落入对方实体内部 >1mm(`BRepClass3d_SolidClassifier`,容差与 `VOL_MIN=2mm³` 语义对齐)即实锤干涉,免掉 4~12s 的 NURBS 布尔。注意方向性:它能证"撞",不能证"自由"——无命中则照旧走布尔。 9. **OCCT 精确布尔** `BRepAlgoAPI_Common`:交体积 > 2mm³ 记干涉并附干涉质心;布尔失败记入 errors 不装作没看见。 **扫描策略**: - **单关节硬限位**:2° 粗扫(小批量并行、首个卡死角即停,少算深度卡死姿态)+ 二分逼近到 0.2°,正反两向。这是 travel.json 的来源。 - **组合姿态收敛**:一个关节保持在极限、扫其余关节(6° 步长),两轮迭代收敛出"组合折叠约束"。这些约束**只写 travel_detail.json、不压缩滑块**——否则手臂会被静态限位锁死(真实机器人由控制器避碰);查看器改用动态模型按当前姿态实时处理(第六篇)。 - **并行**:mp.Pool 每个工作进程独立建模(杜绝 STEP/网格过期),`imap_unordered` 保序回传,workers = min(12, CPU−2, 可用内存/2.5GB)。 当前权威数据(`travel.json` / `travel_detail.json`,2026-10-03 扫描;`collide.json` 胶囊拟合 2026-09-29): | 轴 | 单关节硬限位 | 滑块范围 | 备注 | |---|---|---|---| | J1 | −180…+180 | ±180 连续 | 整周无干涉 | | J2 | −106.25…+10.38 | **−105…+9** | 含 1° 安全余量 | | J3 | −180…+180 | ±180 连续 | 整周无干涉 | | J4 | −112.62…+17.88 | **−111…+16** | | | J5 | −94.75…+35.75 | **−93…+34** | | | J6 | −180…+180 | ±180 连续 | 电机行程 ±360° | 组合折叠明细共 99 条(典型:工具法兰 P09 撞立柱 P01_A 限制了 J3/J4/J5 的组合极限)。 ![零件全览(爆炸图)](shots/05_exploded.png) --- ## 第六篇 碰撞检测 II:实时胶囊模型与动态限位 ### 6.1 模型:CAD → 胶囊 离线布尔每姿态秒级,放进 30fps 的交互里不可能。`scratch/fit_capsules.py` 把每件零件的 CAD 网格拟合为一组**胶囊体**(线段端点 a,b + 半径 r),写进 `docs/collide.json`:共 **1995 枚胶囊**,归属 7 个运动组(g0=台架固定 … g6=J6 组),端点是零位 CAD 世界坐标,组 g 的胶囊随 A1…Ag 嵌套旋转——与 FK 链同一累积变换,与场景图逐位一致。 ![实时胶囊模型与两级宽相位](algo_svg/08_capsule_model.svg) 胶囊是外凸近似,所以它判"干涉"略偏保守(含 margin=0.3mm 外扩),判"自由"需离线布尔背书;换来的是每次 `collideFree()` 只需 **~5ms**(零位最坏情形)。 ### 6.2 宽相位与热路径 ![两级宽相位](algo_svg/08_capsule_model.svg) - **只检 |Δg|≥2 的零件对**:同组相对静止(零位单实体检查已保证不干涉);相邻组的对(P04×P05 / P06×P07 / P07×P08)同心跨在同一关节轴上,胶囊轴距≈0,塞进模型会在零位误报(实测 P04×P03 超报 15mm)——它们改用 **adj 一维自由区间表**(CAD 实测、与姿态无关,由 `scratch/probe_adj.py` 扫描生成;collide.json 缺 adj 时查看器会显式告警并退回静态限位):J2 [−107°,+128°]、J4 [−112°,+112°]、J5 [−96°,+96°]。 - **两级宽相位**:零件包围球(19 球)→ 簇球(每零件的胶囊按主轴投影分桶、每簇 ≤4 枚,主轴由幂迭代 5 轮定向)→ 只有邻近簇才逐对变换胶囊端点做**线段-线段距离**(Ericson《Real-Time Collision Detection》标准式,手写标量直传版,免建数组——碰撞热循环每次调用上万次,分配开销实测占一半)。宽相位阈值带 `rmax+margin` 是有推导的:早期版本漏了 rmax,把 P01_C×P06 在 J1=20°/J2=49° 的 1618mm³ 深撞整对筛掉。 - **性能工程**:预分配缓冲 + 代际戳缓存(一次判定 ~2500 个小数组的 GC 开销占 13% → 归零)、轴对齐特化 `rotAxis`(六个关节轴都是 ±X/±Y/±Z,Rodrigues 通用式退化为每轴 4 个非零元素)、簇球再细分实测得不偿失(62 万次球对测试 27ms/check)。 ### 6.3 动态限位:静态值只是零位快照 travel.json 的单关节限位是"其它轴全零"时的一次性扫描,对"撞固定立柱"这类跨 ≥2 组的约束**系统性过紧**:实测 J2 正向静态 +9.75°(垂臂零位),J1=+90° 时真边界放到 +108.75°。查看器的滑块边界因此分三层: ![动态限位二分](algo_svg/09_dyn_limit.svg) 1. **机械区间** = adj 表(相邻件实测,必然无自碰撞); 2. **动态边界** = `dynLimit()` 从当前角向限位方向**二分**"collideFree"翻转点(tol 0.25°),上限受电机行程 ±360° 与 adj 封顶;"硬边"(lo 侧相邻件自碰撞,配置无关)直接取静态值不放宽; 3. **滑块夹取** `clampAxis()` 带边界缓存:`[轴, 其余轴 1° 量化姿态] → {lo,hi}`,拖同一轴直接取缓存,拖过边界才重算一次二分(~40ms);当前姿已干涉时退回机械区间——否则 dynLimit 会把界外值当边界,滑块再也回不来。 求解侧对应的是 **solveDyn**:静态解先过全姿态碰撞校验(不安全→按代价升序在候选表里找 ≤1.5×cost+20° 的安全解);解贴在限位边界(0.5° 内)时对非连续、非硬边的轴二分动态边界,放宽 ≥5° 才值得重解(粗档步长 ×2,省 3~4 倍),新解 cost 不升才采用——最坏情况就是静态行为,无回退风险。这就是"动态限位放宽重解"徽章的来源。 ![腕部特写](shots/06_wrist.png) --- ## 第七篇 力矩与强度校核(简述) 运动学之外,同一张关节表还驱动两份物理校核(`torque_balance.py` / `strength_check.py`),保证"能转到位"之外还"转得动、扛得住": - **力矩**(`docs/torque_balance.json` + `torque_report.md`):零件质量与质心全部取自 CAD(`bom.json`),在可达姿态网格上搜索每个关节的**最大重力力矩**与 Σm·d² 惯量——不做任何硬编码常数。电机参数为工程假定(连续 2.5 / 峰值 8.0 / 热衰减许用 2.0 N·m,可用环境变量覆盖)。当前结论:J1 重力力矩 5.97 N·m、峰值需求 7.36 N·m(峰值裕度 1.09×),连续负载率超许用(J1 238.8%/J2 171.6%),即**持臂静态工况超出热衰减后的连续额定**——这正是后续减速比/选型迭代的输入。 - **强度**(`docs/strength.json`):对关键截面(吊臂根座、吊臂颊板、肩叉腹板等)按梁理论算弯曲应力与挠度,输出安全系数 SF(当前最小截面 SF≈9.1)。 --- ## 第八篇 自检体系与复现命令 这套系统最重要的工程习惯是**每个算法都带数值自检**,且自检值绑定代码指纹(FNV-1a over 限位表+运动学常量+求解器源码),改模型或改求解器后指纹失配、页面自动标红要求重算: | 自检 | 工具 | 通过线 | |---|---|---| | FK 双实现对账 | `ArmIK.selfTest()` | ≤1e-9 mm(实测 5.7e-13) | | 位置 IK 重构 | 同上 | 逐支精确命中(实测 9.9e-13) | | 姿态 IK 重构 | `poseSelfTest()` | oerr<0.05° 且位置<1e-3 | | 线段距离解析式 | 暴力密集采样对比(scratch 自检脚本) | 逐位一致 | | 螺钉孔对位 | `scratch/fit_check.py` 布尔干涉 | 0 干涉(轴线取样法会误报,弃用) | | 全行程零干涉 | `sweep_v2.py` 全对全扫描 | 单关节推满 0 碰撞 | 复现命令: ```bash python src/arm_model.py # 重建模型 + mesh_arm.json + STEP/STL + bom python src/build_viewer.py # 重打包单文件网页查看器 python src/sweep_v2.py # 离线行程/碰撞权威扫描(几何变更后必跑) node scratch/compute_precomputed.cjs # 重算预置自检值与指纹 python src/torque_balance.py # 力矩校核报告 python src/strength_check.py # 强度校核 ``` ### 文件索引 | 文件 | 角色 | |---|---| | `src/human_layout.py` | 关节轴/拓扑单一事实源 | | `src/arm_model.py` · `arm_build.py` | 参数化 CAD 与导出 | | `src/sweep_v2.py` | 离线碰撞与行程权威工具 | | `src/shot.py` | 渲染出图(docs/shots/) | | `web/arm-ik-solver.js` | 六轴 IK 求解器(纯 JS,浏览器/Node 双端) | | `web/ik-glue.js` | 交互层:拖拽/Worker/动态限位/动画 | | `src/finalize_travel.py` | 从 sweep 明细重建 travel.json 的维护工具 | | `scratch/fit_capsules.py` | CAD 网格 → 胶囊模型拟合(collide.json) | | `scratch/probe_adj.py` | 相邻打印件 1D 自由区间实测(adj 表) | | `scratch/compute_precomputed.cjs` | 重算预置自检值与代码指纹 | | `scratch/gen_workspace_svg.cjs` | 工作空间剖面图生成(本文图 11) | | `docs/travel.json` · `travel_detail.json` | 滑块硬限位 / 组合折叠明细 | | `docs/collide.json` | 1995 胶囊 + adj 机械区间 + 硬边表 | | `docs/algo_svg/` | 本文全部算法示意图源文件 |