feat(arm): 电机双向夹紧与2mm厚度公差让位,立柱减重槽及文档同步更新

This commit is contained in:
cbb
2026-10-04 13:16:32 +08:00
parent b593e47c76
commit e3b25452e1
70 changed files with 14420 additions and 496 deletions
+101
View File
@@ -0,0 +1,101 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 980 700" font-family="'Segoe UI','PingFang SC','Microsoft YaHei',sans-serif">
<rect x="0" y="0" width="980" height="700" fill="#ffffff"/>
<defs>
<marker id="ar" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M0,0 L10,5 L0,10 z" fill="#5a7d9a"/>
</marker>
<marker id="arb" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M0,0 L10,5 L0,10 z" fill="#7c5cd6"/>
</marker>
</defs>
<text x="490" y="34" text-anchor="middle" font-size="21" font-weight="bold" fill="#16324f">系统总览:一个事实源、两个碰撞模型、一条数据链</text>
<!-- 列标题 -->
<text x="175" y="70" text-anchor="middle" font-size="15" font-weight="bold" fill="#1f6f43">① 建模侧(Python · CadQuery)</text>
<text x="490" y="70" text-anchor="middle" font-size="15" font-weight="bold" fill="#8a5a00">② 数据契约(docs/*.json)</text>
<text x="805" y="70" text-anchor="middle" font-size="15" font-weight="bold" fill="#1f4f8f">③ 网页查看器(单文件 HTML)</text>
<!-- ① 建模侧 -->
<rect x="40" y="88" width="270" height="86" rx="8" fill="#eef8f1" stroke="#1f6f43" stroke-width="1.6"/>
<text x="175" y="112" text-anchor="middle" font-size="15" font-weight="bold" fill="#14472c">human_layout.py</text>
<text x="175" y="133" text-anchor="middle" font-size="12.5" fill="#2c5c44">唯一事实源:6 根关节轴 rear/d、</text>
<text x="175" y="151" text-anchor="middle" font-size="12.5" fill="#2c5c44">连杆高度链、台架/机身界面</text>
<rect x="40" y="206" width="270" height="86" rx="8" fill="#eef8f1" stroke="#1f6f43" stroke-width="1.6"/>
<text x="175" y="230" text-anchor="middle" font-size="15" font-weight="bold" fill="#14472c">arm_model.py · arm_build.py</text>
<text x="175" y="251" text-anchor="middle" font-size="12.5" fill="#2c5c44">参数化 CAD:9 打印件 + 6 电机模组</text>
<text x="175" y="269" text-anchor="middle" font-size="12.5" fill="#2c5c44">装配体 / STEP / STL / 质量属性</text>
<rect x="40" y="324" width="270" height="70" rx="8" fill="#f6f8fa" stroke="#8aa2b5" stroke-width="1.2"/>
<text x="175" y="348" text-anchor="middle" font-size="13" font-weight="bold" fill="#3d5164">strength_check.py · torque_balance.py</text>
<text x="175" y="368" text-anchor="middle" font-size="12" fill="#5c7185">同一关节表:截面刚度 / 重力力矩网格搜索</text>
<rect x="40" y="426" width="270" height="70" rx="8" fill="#fdf2ee" stroke="#b0562f" stroke-width="1.4"/>
<text x="175" y="450" text-anchor="middle" font-size="13.5" font-weight="bold" fill="#7c3a1c">sweep_v2.py(离线权威碰撞)</text>
<text x="175" y="470" text-anchor="middle" font-size="12" fill="#8a5433">多进程 + GJK 预筛 + OCCT 精确布尔</text>
<rect x="40" y="528" width="270" height="62" rx="8" fill="#fdf2ee" stroke="#b0562f" stroke-width="1.2" stroke-dasharray="5 3"/>
<text x="175" y="552" text-anchor="middle" font-size="13" font-weight="bold" fill="#7c3a1c">scratch/fit_capsules.py</text>
<text x="175" y="571" text-anchor="middle" font-size="12" fill="#8a5433">CAD 网格 → 实时胶囊模型拟合</text>
<line x1="175" y1="174" x2="175" y2="204" stroke="#5a7d9a" stroke-width="1.6" marker-end="url(#ar)"/>
<line x1="175" y1="292" x2="175" y2="322" stroke="#5a7d9a" stroke-width="1.6" marker-end="url(#ar)"/>
<line x1="175" y1="394" x2="175" y2="424" stroke="#5a7d9a" stroke-width="1.6" marker-end="url(#ar)"/>
<!-- ② 数据契约 -->
<g>
<rect x="365" y="426" width="250" height="56" rx="8" fill="#fff8ea" stroke="#c99418" stroke-width="1.4"/>
<text x="490" y="449" text-anchor="middle" font-size="13.5" font-weight="bold" fill="#7a5a08">travel.json / travel_detail.json</text>
<text x="490" y="468" text-anchor="middle" font-size="11.5" fill="#8f6d1c">单关节硬限位(滑块)+ 组合折叠明细</text>
<rect x="365" y="528" width="250" height="56" rx="8" fill="#fff8ea" stroke="#c99418" stroke-width="1.4"/>
<text x="490" y="551" text-anchor="middle" font-size="13.5" font-weight="bold" fill="#7a5a08">collide.json</text>
<text x="490" y="570" text-anchor="middle" font-size="11.5" fill="#8f6d1c">1995 胶囊 + adj 机械区间 + 硬边标记</text>
<rect x="365" y="324" width="250" height="56" rx="8" fill="#fff8ea" stroke="#c99418" stroke-width="1.4"/>
<text x="490" y="347" text-anchor="middle" font-size="13.5" font-weight="bold" fill="#7a5a08">torque_balance.json · strength.json</text>
<text x="490" y="366" text-anchor="middle" font-size="11.5" fill="#8f6d1c">逐关节力矩负载率 / 截面安全系数</text>
<rect x="365" y="222" width="250" height="56" rx="8" fill="#fff8ea" stroke="#c99418" stroke-width="1.4"/>
<text x="490" y="245" text-anchor="middle" font-size="13.5" font-weight="bold" fill="#7a5a08">mesh_arm.json(网格)</text>
<text x="490" y="264" text-anchor="middle" font-size="11.5" fill="#8f6d1c">AB.export → build_viewer.py 打包</text>
</g>
<line x1="310" y1="455" x2="363" y2="455" stroke="#5a7d9a" stroke-width="1.6" marker-end="url(#ar)"/>
<line x1="310" y1="559" x2="363" y2="559" stroke="#5a7d9a" stroke-width="1.6" marker-end="url(#ar)"/>
<line x1="310" y1="355" x2="363" y2="355" stroke="#5a7d9a" stroke-width="1.6" marker-end="url(#ar)"/>
<line x1="310" y1="255" x2="363" y2="252" stroke="#5a7d9a" stroke-width="1.6" marker-end="url(#ar)"/>
<text x="336" y="446" text-anchor="middle" font-size="11" fill="#5c7185">写出</text>
<text x="336" y="550" text-anchor="middle" font-size="11" fill="#5c7185">写出</text>
<!-- ③ 查看器 -->
<rect x="670" y="120" width="270" height="96" rx="8" fill="#edf3fc" stroke="#1f4f8f" stroke-width="1.6"/>
<text x="805" y="145" text-anchor="middle" font-size="15" font-weight="bold" fill="#153a6e">arm-ik-solver.js(ArmIK)</text>
<text x="805" y="166" text-anchor="middle" font-size="12.5" fill="#3a5a82">闭式支 + (θ4,θ5) 扫描 + 牛顿收口</text>
<text x="805" y="184" text-anchor="middle" font-size="12.5" fill="#3a5a82">DLS 兜底 · 实时胶囊碰撞 · 动态限位</text>
<text x="805" y="202" text-anchor="middle" font-size="12.5" fill="#3a5a82">纯 JS 无依赖 · 主线程 / Worker 同源</text>
<rect x="670" y="248" width="270" height="76" rx="8" fill="#edf3fc" stroke="#1f4f8f" stroke-width="1.6"/>
<text x="805" y="273" text-anchor="middle" font-size="15" font-weight="bold" fill="#153a6e">ik-glue.js(交互层)</text>
<text x="805" y="294" text-anchor="middle" font-size="12.5" fill="#3a5a82">拖拽 / 箭头姿态 / 实时追踪 / Worker</text>
<text x="805" y="312" text-anchor="middle" font-size="12.5" fill="#3a5a82">滑块动态限位夹取(边界缓存)</text>
<rect x="670" y="356" width="270" height="70" rx="8" fill="#edf3fc" stroke="#1f4f8f" stroke-width="1.6"/>
<text x="805" y="381" text-anchor="middle" font-size="15" font-weight="bold" fill="#153a6e">three.js 场景图</text>
<text x="805" y="402" text-anchor="middle" font-size="12.5" fill="#3a5a82">6 级嵌套 pivot(与 FK 链逐位一致)</text>
<text x="805" y="420" text-anchor="middle" font-size="12.5" fill="#3a5a82">FK 渲染 + 残差复核(双通道对账)</text>
<line x1="615" y1="460" x2="668" y2="168" stroke="#7c5cd6" stroke-width="1.8" marker-end="url(#arb)"/>
<line x1="615" y1="556" x2="668" y2="300" stroke="#7c5cd6" stroke-width="1.8" marker-end="url(#arb)"/>
<line x1="615" y1="250" x2="668" y2="392" stroke="#5a7d9a" stroke-width="1.6" marker-end="url(#ar)"/>
<line x1="805" y1="216" x2="805" y2="246" stroke="#5a7d9a" stroke-width="1.6" marker-end="url(#ar)"/>
<line x1="805" y1="324" x2="805" y2="354" stroke="#5a7d9a" stroke-width="1.6" marker-end="url(#ar)"/>
<text x="636" y="508" font-size="11.5" fill="#7c5cd6">限位 / 碰撞注入求解器与 UI</text>
<text x="621" y="262" font-size="11" fill="#5c7185" stroke="#ffffff" stroke-width="3" paint-order="stroke">几何网格</text>
<!-- human_layout → solver 直接注入 -->
<path d="M 310 130 C 480 96, 560 100, 668 138" fill="none" stroke="#1f6f43" stroke-width="1.8" stroke-dasharray="6 4" marker-end="url(#ar)"/>
<text x="470" y="84" text-anchor="middle" font-size="11.5" fill="#1f6f43">关节 rear/d 常量经 build_viewer 注入两处(CAD 与网页同源)</text>
<!-- 底部工作流 -->
<rect x="40" y="620" width="900" height="56" rx="8" fill="#f4f6f8" stroke="#9db0c0" stroke-width="1.2"/>
<text x="490" y="643" text-anchor="middle" font-size="12.5" fill="#3d5164">改几何的同步纪律:arm_model.py 重建 → AB.export 写 mesh_arm.json → build_viewer.py 重打包 → 渲染出图确认 →</text>
<text x="490" y="663" text-anchor="middle" font-size="12.5" fill="#3d5164">几何变更必须重跑 sweep_v2.py(行程/碰撞权威);collide.json 由 fit_capsules.py 重新拟合。</text>
</svg>

After

Width:  |  Height:  |  Size: 8.8 KiB

+125
View File
@@ -0,0 +1,125 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 980 760" font-family="'Segoe UI','PingFang SC','Microsoft YaHei',sans-serif">
<rect x="0" y="0" width="980" height="760" fill="#ffffff"/>
<defs>
<marker id="axr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse">
<path d="M0,0 L10,5 L0,10 z" fill="#d64545"/>
</marker>
<marker id="axb" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse">
<path d="M0,0 L10,5 L0,10 z" fill="#3572c9"/>
</marker>
<marker id="dim" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M0,0 L10,5 L0,10 z" fill="#8a6d3b"/>
</marker>
</defs>
<text x="490" y="34" text-anchor="middle" font-size="21" font-weight="bold" fill="#16324f">运动链与关节轴(正视简化示意)</text>
<text x="490" y="58" text-anchor="middle" font-size="13" fill="#5c7185">沿 −Y 看:X 向右、Z 向上、Y 指向纸内(⊗)。零位 θ=0,臂竖直下垂。</text>
<!-- 地面与台架 -->
<line x1="70" y1="700" x2="910" y2="700" stroke="#8a9aa8" stroke-width="3"/>
<rect x="150" y="676" width="290" height="24" fill="#dfe8f0" stroke="#7ba3c7" stroke-width="1.2"/>
<rect x="228" y="80" width="52" height="598" fill="#dfe8f0" stroke="#7ba3c7" stroke-width="1.2"/>
<rect x="276" y="78" width="288" height="26" fill="#dfe8f0" stroke="#7ba3c7" stroke-width="1.2"/>
<text x="254" y="666" text-anchor="middle" font-size="11.5" fill="#5c7185">立柱 P01(台架)</text>
<text x="295" y="694" text-anchor="middle" font-size="11.5" fill="#5c7185">底座 P02</text>
<!-- 臂主干 -->
<rect x="592" y="86" width="16" height="522" rx="8" fill="#c3d2e0" stroke="#7ba3c7" stroke-width="1"/>
<rect x="560" y="72" width="80" height="36" rx="8" fill="#dbe7f3" stroke="#7ba3c7" stroke-width="1.4"/>
<!-- J1: X 轴 -->
<line x1="508" y1="90" x2="692" y2="90" stroke="#d64545" stroke-width="2.4" marker-start="url(#axr)" marker-end="url(#axr)"/>
<circle cx="600" cy="90" r="7" fill="#7c5cd6"/>
<text x="702" y="82" font-size="13" font-weight="bold" fill="#16324f">J1 肩抬举</text>
<text x="702" y="100" font-size="12" fill="#5c7185">轴 +X(滚转,前后方向)</text>
<!-- J2: Y ⊗ -->
<circle cx="600" cy="210" r="10" fill="#ffffff" stroke="#1f9d63" stroke-width="2.2"/>
<line x1="593" y1="203" x2="607" y2="217" stroke="#1f9d63" stroke-width="2.2"/>
<line x1="607" y1="203" x2="593" y2="217" stroke="#1f9d63" stroke-width="2.2"/>
<text x="620" y="202" font-size="13" font-weight="bold" fill="#16324f">J2 肩俯仰</text>
<text x="620" y="220" font-size="12" fill="#5c7185">轴 +Y(⊗,与 J4/J5 平行同向)</text>
<!-- J3: Z 自转 -->
<line x1="600" y1="225" x2="600" y2="352" stroke="#3572c9" stroke-width="1.6" stroke-dasharray="7 5" marker-start="url(#axb)" marker-end="url(#axb)"/>
<ellipse cx="600" cy="255" rx="26" ry="9" fill="none" stroke="#3572c9" stroke-width="1.6"/>
<circle cx="600" cy="255" r="5.5" fill="#7c5cd6"/>
<text x="640" y="250" font-size="13" font-weight="bold" fill="#16324f">J3 大臂自转</text>
<text x="640" y="268" font-size="12" fill="#5c7185">轴 −Z(臂自身轴线)</text>
<!-- J4 -->
<circle cx="600" cy="368" r="10" fill="#ffffff" stroke="#1f9d63" stroke-width="2.2"/>
<line x1="593" y1="361" x2="607" y2="375" stroke="#1f9d63" stroke-width="2.2"/>
<line x1="607" y1="361" x2="593" y2="375" stroke="#1f9d63" stroke-width="2.2"/>
<text x="620" y="360" font-size="13" font-weight="bold" fill="#16324f">J4 肘部俯仰</text>
<text x="620" y="378" font-size="12" fill="#5c7185">轴 +Y(⊗)</text>
<!-- J5 -->
<circle cx="600" cy="478" r="10" fill="#ffffff" stroke="#1f9d63" stroke-width="2.2"/>
<line x1="593" y1="471" x2="607" y2="485" stroke="#1f9d63" stroke-width="2.2"/>
<line x1="607" y1="471" x2="593" y2="485" stroke="#1f9d63" stroke-width="2.2"/>
<text x="620" y="470" font-size="13" font-weight="bold" fill="#16324f">J5 腕部俯仰</text>
<text x="620" y="488" font-size="12" fill="#5c7185">轴 +Y(⊗,与 J4 同向 ⇒ R4·R5 可合并)</text>
<!-- J6 -->
<line x1="600" y1="492" x2="600" y2="596" stroke="#3572c9" stroke-width="1.6" stroke-dasharray="7 5" marker-start="url(#axb)" marker-end="url(#axb)"/>
<ellipse cx="600" cy="524" rx="24" ry="8" fill="none" stroke="#3572c9" stroke-width="1.6"/>
<circle cx="600" cy="524" r="5.5" fill="#7c5cd6"/>
<text x="640" y="520" font-size="13" font-weight="bold" fill="#16324f">J6 腕自转</text>
<text x="640" y="538" font-size="12" fill="#5c7185">轴 −Z(滚转,TCP 在轴线上 ⇒ 不动位置)</text>
<!-- TCP -->
<circle cx="600" cy="607" r="6" fill="#e08a1e"/>
<text x="620" y="612" font-size="13" font-weight="bold" fill="#b06a10">TCP</text>
<text x="620" y="630" font-size="11.5" fill="#5c7185">工具法兰安装面中心(J6 组打印件最低面)</text>
<!-- 左侧尺寸链 -->
<g stroke="#8a6d3b" stroke-width="1.3">
<line x1="480" y1="90" x2="480" y2="210" marker-start="url(#dim)" marker-end="url(#dim)"/>
<line x1="480" y1="210" x2="480" y2="255" marker-start="url(#dim)" marker-end="url(#dim)"/>
<line x1="480" y1="255" x2="480" y2="368" marker-start="url(#dim)" marker-end="url(#dim)"/>
<line x1="480" y1="368" x2="480" y2="478" marker-start="url(#dim)" marker-end="url(#dim)"/>
<line x1="480" y1="478" x2="480" y2="524" marker-start="url(#dim)" marker-end="url(#dim)"/>
<line x1="470" y1="90" x2="490" y2="90"/><line x1="470" y1="210" x2="490" y2="210"/>
<line x1="470" y1="255" x2="490" y2="255"/><line x1="470" y1="368" x2="490" y2="368"/>
<line x1="470" y1="478" x2="490" y2="478"/><line x1="470" y1="524" x2="490" y2="524"/>
</g>
<g font-size="11.5" fill="#8a6d3b" text-anchor="end">
<text x="472" y="154">C0 = 120</text>
<text x="472" y="236">45</text>
<text x="472" y="315">113</text>
<text x="472" y="427">110(前臂)</text>
<text x="472" y="505">46</text>
</g>
<g font-size="11" fill="#8a9aa8">
<text x="498" y="122">z=610</text><text x="498" y="226">z=490</text><text x="498" y="271">z=445</text>
<text x="498" y="384">z=332</text><text x="498" y="494">z=222</text><text x="498" y="540">z=176</text>
</g>
<!-- 右侧段名 -->
<g font-size="12" fill="#3d5164">
<text x="800" y="154" text-anchor="middle">肩关节组</text>
<line x1="770" y1="92" x2="770" y2="212" stroke="#b9c8d6" stroke-width="1.2"/>
<text x="800" y="295" text-anchor="middle">大臂 158</text>
<line x1="770" y1="212" x2="770" y2="370" stroke="#b9c8d6" stroke-width="1.2"/>
<text x="800" y="428" text-anchor="middle">前臂 110</text>
<line x1="770" y1="370" x2="770" y2="480" stroke="#b9c8d6" stroke-width="1.2"/>
<text x="800" y="560" text-anchor="middle">腕组 46 + 法兰</text>
<line x1="770" y1="480" x2="770" y2="606" stroke="#b9c8d6" stroke-width="1.2"/>
</g>
<!-- 图例 -->
<rect x="70" y="560" width="300" height="120" rx="8" fill="#f4f6f8" stroke="#9db0c0" stroke-width="1.2"/>
<text x="86" y="586" font-size="13" font-weight="bold" fill="#16324f">图例</text>
<line x1="86" y1="606" x2="126" y2="606" stroke="#d64545" stroke-width="2.4" marker-start="url(#axr)" marker-end="url(#axr)"/>
<text x="136" y="610" font-size="12" fill="#3d5164">X 轴(水平,前后)</text>
<circle cx="106" cy="630" r="8" fill="#ffffff" stroke="#1f9d63" stroke-width="2"/>
<line x1="101" y1="625" x2="111" y2="635" stroke="#1f9d63" stroke-width="2"/>
<line x1="111" y1="625" x2="101" y2="635" stroke="#1f9d63" stroke-width="2"/>
<text x="136" y="634" font-size="12" fill="#3d5164">Y 轴(指向纸内)</text>
<line x1="106" y1="648" x2="106" y2="668" stroke="#3572c9" stroke-width="2" marker-start="url(#axb)" marker-end="url(#axb)"/>
<text x="136" y="662" font-size="12" fill="#3d5164">Z 轴(竖直,J3/J6 实为 −Z)</text>
<rect x="70" y="700" width="0" height="0" fill="none"/>
<text x="490" y="730" text-anchor="middle" font-size="12.5" fill="#3d5164">结构前提(求解器启动即断言):J2/J4/J5 平行同向且同一 x 平面(X_ARM≈19.5)、J1 ∥ X、J3/J6 ∥ Z、TCP 落在 J6 轴线上。</text>
<text x="490" y="750" text-anchor="middle" font-size="12.5" fill="#3d5164">这五条性质让六轴问题可以降阶为「二维冗余扫描 + 闭式支」,任何一条不成立时求解器会打出 WARN 并回落通用路径。</text>
</svg>

After

Width:  |  Height:  |  Size: 8.4 KiB

+69
View File
@@ -0,0 +1,69 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 980 440" font-family="'Segoe UI','PingFang SC','Microsoft YaHei',sans-serif">
<rect x="0" y="0" width="980" height="440" fill="#ffffff"/>
<defs>
<marker id="fb" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse">
<path d="M0,0 L10,5 L0,10 z" fill="#3572c9"/>
</marker>
</defs>
<text x="490" y="34" text-anchor="middle" font-size="21" font-weight="bold" fill="#16324f">正运动学:枢轴变换与链式累积</text>
<!-- 左:单关节枢轴 -->
<text x="230" y="66" text-anchor="middle" font-size="14.5" font-weight="bold" fill="#1f4f8f">单关节:绕“不在原点的轴”旋转</text>
<line x1="230" y1="110" x2="230" y2="300" stroke="#3572c9" stroke-width="1.6" stroke-dasharray="7 5"/>
<rect x="212" y="160" width="36" height="120" rx="10" fill="#e7edf3" stroke="#9db0c0" stroke-width="1.4" stroke-dasharray="5 4"/>
<rect x="212" y="160" width="36" height="120" rx="10" fill="#dbe9fb" stroke="#3572c9" stroke-width="1.6" transform="rotate(32 230 160)"/>
<ellipse cx="230" cy="160" rx="46" ry="14" fill="none" stroke="#3572c9" stroke-width="1.8" marker-end="url(#fb)"/>
<circle cx="230" cy="160" r="7" fill="#7c5cd6"/>
<text x="296" y="148" font-size="12.5" fill="#3d5164">枢轴点 P + 轴方向 d</text>
<text x="296" y="166" font-size="12.5" fill="#3d5164">θ=0 位形(虚线)→ θ 位形(蓝)</text>
<text x="296" y="184" font-size="12.5" fill="#3d5164">关节角 θi = 绕轴 d 的有符号转角</text>
<rect x="60" y="318" width="400" height="104" rx="8" fill="#f4f6f8" stroke="#9db0c0" stroke-width="1.2"/>
<text x="80" y="344" font-size="13" fill="#16324f" font-family="Consolas,monospace">A(θ) = T(P) · R(d,θ) · T(−P)</text>
<text x="80" y="368" font-size="13" fill="#16324f" font-family="Consolas,monospace">x' = R·(x − P) + P</text>
<text x="80" y="396" font-size="12" fill="#5c7185">先平移枢轴点到原点 → 绕轴转 θ → 平移回去。关节表只存</text>
<text x="80" y="412" font-size="12" fill="#5c7185">P(rear)与 d(单位轴向量),六个关节共 12 个常量。</text>
<!-- 右:链 -->
<text x="735" y="66" text-anchor="middle" font-size="14.5" font-weight="bold" fill="#1f4f8f">六链累积:World = A₁·A₂·A₃·A₄·A₅·A₆·TCP</text>
<g font-size="14" font-weight="bold" text-anchor="middle">
<rect x="510" y="130" width="52" height="44" rx="6" fill="#dbe9fb" stroke="#3572c9" stroke-width="1.5"/>
<text x="536" y="157" fill="#153a6e">A1</text>
<rect x="578" y="130" width="52" height="44" rx="6" fill="#dbe9fb" stroke="#3572c9" stroke-width="1.5"/>
<text x="604" y="157" fill="#153a6e">A2</text>
<rect x="646" y="130" width="52" height="44" rx="6" fill="#dbe9fb" stroke="#3572c9" stroke-width="1.5"/>
<text x="672" y="157" fill="#153a6e">A3</text>
<rect x="714" y="130" width="52" height="44" rx="6" fill="#dbe9fb" stroke="#3572c9" stroke-width="1.5"/>
<text x="740" y="157" fill="#153a6e">A4</text>
<rect x="782" y="130" width="52" height="44" rx="6" fill="#dbe9fb" stroke="#3572c9" stroke-width="1.5"/>
<text x="808" y="157" fill="#153a6e">A5</text>
<rect x="850" y="130" width="52" height="44" rx="6" fill="#dbe9fb" stroke="#3572c9" stroke-width="1.5"/>
<text x="876" y="157" fill="#153a6e">A6</text>
</g>
<g font-size="11" fill="#5c7185" text-anchor="middle">
<text x="536" y="190">θ1</text><text x="604" y="190">θ2</text><text x="672" y="190">θ3</text>
<text x="740" y="190">θ4</text><text x="808" y="190">θ5</text><text x="876" y="190">θ6</text>
</g>
<g stroke="#5a7d9a" stroke-width="1.5">
<line x1="472" y1="152" x2="508" y2="152"/>
<line x1="562" y1="152" x2="576" y2="152"/>
<line x1="630" y1="152" x2="644" y2="152"/>
<line x1="698" y1="152" x2="712" y2="152"/>
<line x1="766" y1="152" x2="780" y2="152"/>
<line x1="834" y1="152" x2="848" y2="152"/>
<line x1="902" y1="152" x2="918" y2="152"/>
</g>
<text x="462" y="157" text-anchor="end" font-size="13" font-weight="bold" fill="#16324f">World</text>
<text x="960" y="157" text-anchor="end" font-size="13" font-weight="bold" fill="#b06a10">TCP</text>
<text x="735" y="216" text-anchor="middle" font-size="12" fill="#5c7185">每级只读父级位姿与自己 —— 与 three.js 场景图 6 级嵌套 pivot 逐位一致</text>
<rect x="510" y="240" width="420" height="192" rx="8" fill="#f4f6f8" stroke="#9db0c0" stroke-width="1.2"/>
<text x="530" y="266" font-size="13" font-weight="bold" fill="#16324f">两套实现,互为对账:</text>
<text x="530" y="290" font-size="12.5" fill="#3d5164">· fkChain():严格 4×4 同质矩阵链 —— 对照基准,慢而直白</text>
<text x="530" y="312" font-size="12.5" fill="#3d5164">· fk():按结构常量展开三角函数的闭式快路径 —— 求解器</text>
<text x="530" y="332" font-size="12.5" fill="#3d5164"> 热路径专用,每姿态仅 ~20 次乘加</text>
<text x="530" y="360" font-size="12.5" fill="#3d5164">· 自检:5000 个随机位形,两实现的 TCP 差 ≤ 5.7e-13 mm</text>
<text x="530" y="384" font-size="12.5" fill="#3d5164">· 工具长度 tool 沿 J6 轴外伸:TCP_z 随 tool 平移(ax=J6 轴)</text>
<text x="530" y="406" font-size="11.5" fill="#8a9aa8">指纹校验:预置自检值绑定(限位表+常量+源码)的 FNV-1a 指纹,</text>
<text x="530" y="422" font-size="11.5" fill="#8a9aa8">模型或求解器一变即提示重算。</text>
</svg>

After

Width:  |  Height:  |  Size: 5.5 KiB

+103
View File
@@ -0,0 +1,103 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 980 920" font-family="'Segoe UI','PingFang SC','Microsoft YaHei',sans-serif">
<rect x="0" y="0" width="980" height="920" fill="#ffffff"/>
<defs>
<marker id="pa" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse">
<path d="M0,0 L10,5 L0,10 z" fill="#5a7d9a"/>
</marker>
<marker id="pao" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse">
<path d="M0,0 L10,5 L0,10 z" fill="#b06a10"/>
</marker>
</defs>
<text x="490" y="34" text-anchor="middle" font-size="21" font-weight="bold" fill="#16324f">逆运动学求解流水线(solve → solveDyn)</text>
<!-- 主流程 -->
<g>
<rect x="265" y="60" width="450" height="64" rx="8" fill="#dbe9fb" stroke="#3572c9" stroke-width="1.6"/>
<text x="280" y="84" font-size="12.5" font-weight="bold" fill="#153a6e">① 输入</text>
<text x="280" y="102" font-size="12" fill="#2a4a6e">目标 TCP (x,y,z) · 工具长 tool · 可选姿态 R(az,el,roll)</text>
<text x="280" y="118" font-size="12" fill="#2a4a6e">ref = 当前姿态:取「本步行程最小」的构型</text>
<rect x="265" y="148" width="450" height="64" rx="8" fill="#f4f8fb" stroke="#7ba3c7" stroke-width="1.4"/>
<text x="280" y="172" font-size="12.5" font-weight="bold" fill="#16324f">② 冗余维准备:(θ4,θ5) 网格 + 逐行可行段探测</text>
<text x="280" y="190" font-size="12" fill="#3d5164">步长 h=0.25°;先按探测步 hp 找每行有解的连续段(位置 2° / 姿态 0.5°)</text>
<text x="280" y="206" font-size="12" fill="#3d5164">段端外扩 PAD=2° 兜住段界不确定性;段内仍按 h 精扫,密度不降</text>
<rect x="265" y="236" width="450" height="64" rx="8" fill="#f4f8fb" stroke="#7ba3c7" stroke-width="1.4"/>
<text x="280" y="260" font-size="12.5" font-weight="bold" fill="#16324f">③ 全域粗扫:每格 branch() 闭式解 θ1..θ3(≤4 支)</text>
<text x="280" y="279" font-size="12" fill="#3d5164">位置由闭式支精确命中(零残差),扫描只优化冗余维;候选有界保留 4000</text>
<rect x="265" y="324" width="450" height="64" rx="8" fill="#fdf6ec" stroke="#c99418" stroke-width="1.3"/>
<text x="280" y="348" font-size="12.5" font-weight="bold" fill="#7a5a08">④ [姿态任务] 孤立根枚举:残差变号括号 → 阻尼高斯-牛顿收根</text>
<text x="280" y="367" font-size="12" fill="#6b5312">切向相切的根不变号,按每支 ‖e‖ 最小的节点补种;精确根直接进候选池</text>
<rect x="265" y="412" width="450" height="64" rx="8" fill="#f4f8fb" stroke="#7ba3c7" stroke-width="1.4"/>
<text x="280" y="436" font-size="12.5" font-weight="bold" fill="#16324f">⑤ 局部加密 + 模式搜索:32 种子 × 窗口 ±2h(步长 h/8)</text>
<text x="280" y="455" font-size="12" fill="#3d5164">8 邻域模式搜索步长逐轮减半至 2e-4°,把每条谷底走到最低</text>
<rect x="265" y="500" width="450" height="64" rx="8" fill="#fdf6ec" stroke="#c99418" stroke-width="1.3"/>
<text x="280" y="524" font-size="12.5" font-weight="bold" fill="#7a5a08">⑥ [姿态任务] 二维牛顿收口 + escalating 加压</text>
<text x="280" y="542" font-size="12" fill="#6b5312">对工具轴切向偏差做 (θ4,θ5) 牛顿迭代,轴残差 →1e-12、位置保持精确</text>
<text x="280" y="558" font-size="12" fill="#6b5312">仍无 oerr≤0.05° 达标解则姿态权重 ×100 重扫(最多 3 轮)</text>
<rect x="265" y="588" width="450" height="64" rx="8" fill="#f4f8fb" stroke="#7ba3c7" stroke-width="1.4"/>
<text x="280" y="612" font-size="12.5" font-weight="bold" fill="#16324f">⑦ 选解:cost = Σ w·|wrap180(θ−ref)| + 1e-6·Σ|θ|</text>
<text x="280" y="630" font-size="12" fill="#3d5164">姿态任务先取 oerr≤0.05° 达标集,集内再比代价</text>
<text x="280" y="646" font-size="12" fill="#3d5164">精确根只在代价 ≤ 达标最优+2° 时接管(防漏检多付几十度)</text>
<rect x="265" y="676" width="450" height="64" rx="8" fill="#eef8f1" stroke="#1f6f43" stroke-width="1.4"/>
<text x="280" y="700" font-size="12.5" font-weight="bold" fill="#14472c">⑧ solveDyn 碰撞后处理(装了胶囊模型时)</text>
<text x="280" y="718" font-size="12" fill="#2c5c44">解先过全姿态碰撞校验:不安全 → 候选表找 ≤1.5×cost+20° 的安全解</text>
<text x="280" y="734" font-size="12" fill="#2c5c44">贴边轴二分动态边界放宽(≥5° 才值得重解,重解 cost 不升才采用)</text>
<rect x="265" y="764" width="450" height="58" rx="8" fill="#dbe9fb" stroke="#3572c9" stroke-width="1.6"/>
<text x="280" y="788" font-size="12.5" font-weight="bold" fill="#153a6e">输出:q[6] · 残差 err / oerr · 本步行程 moveSum · 候选表 list</text>
<text x="280" y="806" font-size="12" fill="#2a4a6e">徽章分级:已到达 / 姿态到达·位置最近 / 混合折中 / 不可达·最近位姿</text>
</g>
<g stroke="#5a7d9a" stroke-width="1.8">
<line x1="490" y1="124" x2="490" y2="146" marker-end="url(#pa)"/>
<line x1="490" y1="212" x2="490" y2="234" marker-end="url(#pa)"/>
<line x1="490" y1="300" x2="490" y2="322" marker-end="url(#pa)"/>
<line x1="490" y1="388" x2="490" y2="410" marker-end="url(#pa)"/>
<line x1="490" y1="476" x2="490" y2="498" marker-end="url(#pa)"/>
<line x1="490" y1="564" x2="490" y2="586" marker-end="url(#pa)"/>
<line x1="490" y1="652" x2="490" y2="674" marker-end="url(#pa)"/>
<line x1="490" y1="740" x2="490" y2="762" marker-end="url(#pa)"/>
</g>
<!-- 右侧:快路 -->
<rect x="740" y="236" width="215" height="196" rx="10" fill="#ffffff" stroke="#b06a10" stroke-width="1.6" stroke-dasharray="7 5"/>
<text x="755" y="262" font-size="13" font-weight="bold" fill="#b06a10">solveWarm 实时快路</text>
<text x="755" y="284" font-size="11.5" fill="#6b5312">拖拽中:只在上帧解附近的</text>
<text x="755" y="302" font-size="11.5" fill="#6b5312">(θ4,θ5) 递进窗口 ±4→14→40°</text>
<text x="755" y="320" font-size="11.5" fill="#6b5312">同支跟随(Σ|Δθ|≤25°)+ 残差</text>
<text x="755" y="338" font-size="11.5" fill="#6b5312">≤1e-6 + 姿态牛顿收口</text>
<text x="755" y="362" font-size="11.5" font-weight="bold" fill="#b06a10">任一校验不过 → null</text>
<text x="755" y="380" font-size="11.5" fill="#6b5312">自动回落全量扫描(③)</text>
<text x="755" y="400" font-size="11" fill="#8a9aa8">快路只负责「能快时快」,</text>
<text x="755" y="416" font-size="11" fill="#8a9aa8">松手后全量求解才是终值</text>
<path d="M 738 268 C 727 240, 727 130, 719 96" fill="none" stroke="#b06a10" stroke-width="1.5" stroke-dasharray="6 4" marker-end="url(#pao)"/>
<path d="M 847 434 C 847 480, 700 560, 721 600" fill="none" stroke="#b06a10" stroke-width="1.5" stroke-dasharray="6 4" marker-end="url(#pao)"/>
<text x="742" y="470" font-size="11" fill="#b06a10">校验不过回落</text>
<!-- 左侧:兜底 -->
<rect x="25" y="324" width="215" height="230" rx="10" fill="#ffffff" stroke="#8a5a8f" stroke-width="1.6" stroke-dasharray="7 5"/>
<text x="40" y="350" font-size="13" font-weight="bold" fill="#7a3a80">兜底:目标不可达时</text>
<text x="40" y="372" font-size="11.5" fill="#5c3a60">closest() 位置 DLS:160 迭代 ×</text>
<text x="40" y="390" font-size="11.5" fill="#5c3a60">160 次 LCG 确定性重启(同输入</text>
<text x="40" y="408" font-size="11.5" fill="#5c3a60">逐位一致)+ anchor 回拉 0.03</text>
<text x="40" y="426" font-size="11.5" fill="#5c3a60">压退化漂移 + 零空间抛光收敛</text>
<text x="40" y="450" font-size="11.5" fill="#5c3a60">closest6() 位置+姿态 6 维联合</text>
<text x="40" y="468" font-size="11.5" fill="#5c3a60">DLS(姿态按 20mm/rad 折算)</text>
<text x="40" y="492" font-size="11.5" fill="#5c3a60">closestOri() 姿态优先:闭式轴</text>
<text x="40" y="510" font-size="11.5" fill="#5c3a60">对齐 + (θ3,s,θ4) 坐标下降,</text>
<text x="40" y="528" font-size="11.5" fill="#5c3a60">姿态精确、位置取最近</text>
<text x="40" y="548" font-size="11" fill="#8a9aa8">距离差 ≤0.5mm 时优先锚定解</text>
<path d="M 263 620 C 180 620, 140 590, 133 556" fill="none" stroke="#8a5a8f" stroke-width="1.5" stroke-dasharray="6 4" marker-end="url(#pao)"/>
<text x="148" y="648" font-size="11" fill="#8a5a8f">0 采样点 / 不可达</text>
<path d="M 133 322 C 140 220, 200 130, 262 96" fill="none" stroke="#8a5a8f" stroke-width="1.5" stroke-dasharray="6 4" marker-end="url(#pao)"/>
<text x="120" y="200" font-size="11" fill="#8a5a8f">返回最近位姿</text>
<text x="490" y="860" text-anchor="middle" font-size="12.5" fill="#3d5164">热路径性能手段:轴对齐特化 rotAxis(免 Rodrigues 通用式)· sin/cos 单槽缓存 · 关节限位数组预取 · 残差 fk 复核延后到入选候选统一补算</text>
<text x="490" y="884" text-anchor="middle" font-size="12.5" fill="#3d5164">全量扫描 40~430 ms(后台 Worker);实时快路毫秒级 —— 这是拖拽能跟手的原因</text>
</svg>

After

Width:  |  Height:  |  Size: 9.1 KiB

+88
View File
@@ -0,0 +1,88 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 980 620" font-family="'Segoe UI','PingFang SC','Microsoft YaHei',sans-serif">
<rect x="0" y="0" width="980" height="620" fill="#ffffff"/>
<defs>
<marker id="ta" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse">
<path d="M0,0 L10,5 L0,10 z" fill="#8a9aa8"/>
</marker>
<marker id="to" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse">
<path d="M0,0 L10,5 L0,10 z" fill="#e08a1e"/>
</marker>
</defs>
<text x="490" y="34" text-anchor="middle" font-size="21" font-weight="bold" fill="#16324f">冗余维 (θ4,θ5) 平面:可行带、加密窗口与牛顿收口</text>
<!-- 绘图区 -->
<rect x="110" y="80" width="740" height="430" fill="#f8fafc" stroke="#b9c8d6" stroke-width="1.2"/>
<!-- 可行带(斜窄带,宽度做了夸张) -->
<polygon points="245,452 830,168 830,132 245,416" fill="#d9f2e5" opacity="0.9"/>
<text x="585" y="215" font-size="12" fill="#1f7a4d" transform="rotate(-26 585 215)">可行带:branch 判据筛掉 73~93% 的格点</text>
<!-- 不可行散点 -->
<g fill="#c3ccd4" font-size="10">
<text x="200" y="140">×</text><text x="330" y="120">×</text><text x="500" y="110">×</text>
<text x="700" y="128">×</text><text x="790" y="180">×</text><text x="170" y="300">×</text>
<text x="300" y="260">×</text><text x="430" y="210">×</text><text x="640" y="150">×</text>
<text x="220" y="470">×</text><text x="480" y="480">×</text><text x="700" y="460">×</text>
<text x="820" y="430">×</text><text x="360" y="420">×</text><text x="560" y="440">×</text>
</g>
<!-- 可行带内格点 -->
<g fill="#2f9d68">
<circle cx="270" cy="428" r="2.4"/><circle cx="310" cy="410" r="2.4"/><circle cx="352" cy="390" r="2.4"/>
<circle cx="395" cy="368" r="2.4"/><circle cx="438" cy="348" r="2.4"/><circle cx="480" cy="328" r="2.4"/>
<circle cx="522" cy="308" r="2.4"/><circle cx="565" cy="288" r="2.4"/><circle cx="608" cy="268" r="2.4"/>
<circle cx="650" cy="248" r="2.4"/><circle cx="692" cy="228" r="2.4"/><circle cx="735" cy="206" r="2.4"/>
<circle cx="778" cy="186" r="2.4"/><circle cx="812" cy="168" r="2.4"/>
<circle cx="292" cy="418" r="2.4"/><circle cx="375" cy="378" r="2.4"/><circle cx="459" cy="338" r="2.4"/>
<circle cx="543" cy="298" r="2.4"/><circle cx="628" cy="256" r="2.4"/><circle cx="712" cy="216" r="2.4"/>
<circle cx="796" cy="178" r="2.4"/>
</g>
<!-- 行可行段 -->
<g stroke="#3572c9" stroke-width="5" opacity="0.75">
<line x1="228" y1="440" x2="360" y2="376"/>
<line x1="415" y1="352" x2="548" y2="288"/>
<line x1="602" y1="264" x2="735" y2="200"/>
</g>
<text x="238" y="486" font-size="12" fill="#2a4a6e">逐行可行段 rowSpans(探测步 hp,段端外扩 PAD=2°):</text>
<text x="238" y="504" font-size="12" fill="#2a4a6e">每行通常是单一连续段,段内仍按 h 精扫 —— 密度不降、成本大降</text>
<!-- 加密窗口 -->
<rect x="478" y="278" width="92" height="92" fill="none" stroke="#e08a1e" stroke-width="1.8" stroke-dasharray="6 4"/>
<text x="580" y="368" font-size="12" fill="#b06a10">加密窗口 ±2h(步长 h/8,</text>
<text x="580" y="386" font-size="12" fill="#b06a10">小步长档 h/4)罩住种子</text>
<!-- 模式搜索折线 -->
<polyline points="500,340 524,318 512,296 540,282 560,296 578,282" fill="none" stroke="#e08a1e" stroke-width="1.8" marker-end="url(#to)"/>
<text x="700" y="330" font-size="12" fill="#b06a10">8 邻域模式搜索:</text>
<text x="700" y="348" font-size="12" fill="#b06a10">步长减半,停在窄谷谷壁</text>
<!-- 牛顿收口点 -->
<g>
<path d="M 596 244 l 4.5 10.5 11 1 -8.5 7.5 2.5 11 -9.5 -6 -9.5 6 2.5 -11 -8.5 -7.5 11 -1 z" fill="#d64545"/>
<line x1="602" y1="254" x2="640" y2="286" stroke="#d64545" stroke-width="1" stroke-dasharray="3 3"/>
<text x="648" y="296" font-size="12.5" font-weight="bold" fill="#b03030">牛顿收口点</text>
<text x="648" y="314" font-size="12" fill="#b03030">轴残差 →1e-12(位置保持精确)</text>
</g>
<!-- 坐标轴 -->
<g stroke="#8a9aa8" stroke-width="1.4">
<line x1="110" y1="510" x2="850" y2="510" marker-start="url(#ta)"/>
<line x1="110" y1="510" x2="110" y2="80" marker-end="url(#ta)"/>
<line x1="130" y1="510" x2="130" y2="516"/><line x1="245" y1="510" x2="245" y2="516"/>
<line x1="411" y1="510" x2="411" y2="516"/><line x1="576" y1="510" x2="576" y2="516"/>
<line x1="741" y1="510" x2="741" y2="516"/><line x1="830" y1="510" x2="830" y2="516"/>
<line x1="104" y1="500" x2="110" y2="500"/><line x1="104" y1="396" x2="110" y2="396"/>
<line x1="104" y1="302" x2="110" y2="302"/><line x1="104" y1="207" x2="110" y2="207"/>
<line x1="104" y1="100" x2="110" y2="100"/>
</g>
<g font-size="11.5" fill="#5c7185">
<text x="126" y="530">−111°</text><text x="238" y="530">−90°</text><text x="404" y="530">−60°</text>
<text x="569" y="530">−30°</text><text x="734" y="530">0°</text><text x="812" y="530">+16°</text>
<text x="66" y="504">−93°</text><text x="66" y="400">−60°</text><text x="66" y="306">−30°</text>
<text x="76" y="211">0°</text><text x="80" y="104">+34°</text>
<text x="840" y="552" font-size="13" font-weight="bold" fill="#16324f">θ4(J4 肘部俯仰)</text>
<text x="66" y="70" font-size="13" font-weight="bold" fill="#16324f">θ5(J5 腕俯仰)</text>
</g>
<text x="490" y="582" text-anchor="middle" font-size="12.5" fill="#3d5164">每个格点的位置解由闭式支精确给出 —— 扫描不是在「逼近」目标,而是在冗余维里「挑构型」;</text>
<text x="490" y="602" text-anchor="middle" font-size="12.5" fill="#3d5164">姿态任务的达标带实测最窄 0.98°,2° 探测步会整条漏掉(误报不可达),故姿态任务探测步降到 0.5°。</text>
</svg>

After

Width:  |  Height:  |  Size: 5.9 KiB

+80
View File
@@ -0,0 +1,80 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 980 580" font-family="'Segoe UI','PingFang SC','Microsoft YaHei',sans-serif">
<rect x="0" y="0" width="980" height="580" fill="#ffffff"/>
<defs>
<marker id="ga" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse">
<path d="M0,0 L10,5 L0,10 z" fill="#8a9aa8"/>
</marker>
</defs>
<text x="490" y="34" text-anchor="middle" font-size="21" font-weight="bold" fill="#16324f">闭式支 branch():给定 (θ4,θ5),θ1..θ3 一步解出(简化示意)</text>
<!-- 左panel -->
<text x="255" y="66" text-anchor="middle" font-size="14.5" font-weight="bold" fill="#1f4f8f">第 1 步:(θ4,θ5) ⇒ 腕心平面坐标 V</text>
<line x1="150" y1="120" x2="150" y2="330" stroke="#3572c9" stroke-width="1.6" stroke-dasharray="7 5"/>
<text x="150" y="352" text-anchor="middle" font-size="11.5" fill="#5c7185">J4 轴(+Y,⊗)</text>
<circle cx="150" cy="140" r="8" fill="#ffffff" stroke="#1f9d63" stroke-width="2"/>
<line x1="144" y1="134" x2="156" y2="146" stroke="#1f9d63" stroke-width="2"/>
<line x1="156" y1="134" x2="144" y2="146" stroke="#1f9d63" stroke-width="2"/>
<line x1="150" y1="140" x2="278" y2="188" stroke="#3572c9" stroke-width="5" stroke-linecap="round"/>
<circle cx="278" cy="188" r="8" fill="#ffffff" stroke="#1f9d63" stroke-width="2"/>
<line x1="272" y1="182" x2="284" y2="194" stroke="#1f9d63" stroke-width="2"/>
<line x1="284" y1="182" x2="272" y2="194" stroke="#1f9d63" stroke-width="2"/>
<text x="292" y="186" font-size="11.5" fill="#5c7185">J5 轴</text>
<line x1="278" y1="188" x2="330" y2="252" stroke="#3572c9" stroke-width="4" stroke-linecap="round"/>
<circle cx="330" cy="252" r="6" fill="#e08a1e"/>
<text x="344" y="258" font-size="12" font-weight="bold" fill="#b06a10">腕心方向(L = L5+tool)</text>
<path d="M 150 170 A 30 30 0 0 0 178 150.5" fill="none" stroke="#7c5cd6" stroke-width="1.6"/>
<text x="186" y="178" font-size="12" fill="#7c5cd6">θ4</text>
<path d="M 262 216 A 30 30 0 0 1 296 214" fill="none" stroke="#7c5cd6" stroke-width="1.6"/>
<text x="272" y="232" font-size="12" fill="#7c5cd6">θ5</text>
<text x="150" y="120" font-size="11.5" fill="#5c7185">肘(J4)</text>
<rect x="50" y="386" width="420" height="152" rx="8" fill="#f4f6f8" stroke="#9db0c0" stroke-width="1.2"/>
<text x="68" y="412" font-size="12.5" fill="#16324f" font-family="Consolas,monospace">u = −L·sinθ5 w = A54 − L·cosθ5</text>
<text x="68" y="436" font-size="12.5" fill="#16324f" font-family="Consolas,monospace">dx = u·cosθ4 + w·sinθ4</text>
<text x="68" y="460" font-size="12.5" fill="#16324f" font-family="Consolas,monospace">dz = A43 − u·sinθ4 + w·cosθ4</text>
<text x="68" y="484" font-size="12.5" fill="#16324f" font-family="Consolas,monospace">V = (dx, dz + A32)</text>
<text x="68" y="514" font-size="11.5" fill="#5c7185">J4∥J5 同向 ⇒ R4·R5 = R(θ4+θ5):腕心只由这两个角决定,</text>
<text x="68" y="530" font-size="11.5" fill="#5c7185">姿态任务里工具轴对齐也因此只差 2 个未知数。</text>
<!-- 右panel -->
<text x="735" y="66" text-anchor="middle" font-size="14.5" font-weight="bold" fill="#1f4f8f">第 2 步:肩部圆交 + 肘部四支 ⇒ θ1/θ2/θ3</text>
<!-- 俯视:J1 轴与 φ -->
<circle cx="600" cy="130" r="9" fill="#ffffff" stroke="#d64545" stroke-width="2"/>
<circle cx="600" cy="130" r="2" fill="#d64545"/>
<text x="576" y="112" font-size="11.5" fill="#5c7185">J1 轴(+X,⊙)</text>
<circle cx="700" cy="96" r="5" fill="#e08a1e"/>
<text x="712" y="94" font-size="11.5" fill="#b06a10">目标(柱坐标 R, φ)</text>
<line x1="600" y1="130" x2="700" y2="96" stroke="#e08a1e" stroke-width="1.6" stroke-dasharray="5 4"/>
<path d="M 640 117 A 42 42 0 0 0 636 108" fill="none" stroke="#7c5cd6" stroke-width="1.6"/>
<text x="648" y="106" font-size="12" fill="#7c5cd6">φ</text>
<text x="566" y="158" font-size="12" fill="#16324f" font-family="Consolas,monospace">θ1 = φ − atan2(cc, b)</text>
<!-- 正视:肩 C0 偏置与肘三角 -->
<circle cx="620" cy="290" r="9" fill="#ffffff" stroke="#1f9d63" stroke-width="2"/>
<line x1="614" y1="284" x2="626" y2="296" stroke="#1f9d63" stroke-width="2"/>
<line x1="626" y1="284" x2="614" y2="296" stroke="#1f9d63" stroke-width="2"/>
<text x="560" y="294" text-anchor="end" font-size="11.5" fill="#5c7185">J1(正视)</text>
<circle cx="620" cy="382" r="9" fill="#ffffff" stroke="#1f9d63" stroke-width="2"/>
<line x1="614" y1="376" x2="626" y2="388" stroke="#1f9d63" stroke-width="2"/>
<line x1="626" y1="376" x2="614" y2="388" stroke="#1f9d63" stroke-width="2"/>
<text x="560" y="386" text-anchor="end" font-size="11.5" fill="#5c7185">J2</text>
<line x1="620" y1="299" x2="620" y2="373" stroke="#8a6d3b" stroke-width="1.3" marker-start="url(#ga)" marker-end="url(#ga)"/>
<text x="632" y="341" font-size="11.5" fill="#8a6d3b">C0=120</text>
<line x1="620" y1="382" x2="700" y2="322" stroke="#3572c9" stroke-width="4" stroke-linecap="round"/>
<line x1="700" y1="322" x2="820" y2="290" stroke="#3572c9" stroke-width="4" stroke-linecap="round"/>
<circle cx="820" cy="290" r="6" fill="#e08a1e"/>
<text x="834" y="294" font-size="12" font-weight="bold" fill="#b06a10">腕心(由 V 决定)</text>
<path d="M 676 342 A 44 44 0 0 0 666 314" fill="none" stroke="#7c5cd6" stroke-width="1.6"/>
<text x="688" y="318" font-size="12" fill="#7c5cd6">σ</text>
<text x="690" y="368" font-size="11.5" fill="#5c7185">肘部三角:σ=acos(√m2)</text>
<text x="690" y="386" font-size="11.5" fill="#5c7185">θ3 四支:±σ 与 ±(π−σ)</text>
<text x="690" y="404" font-size="11.5" fill="#5c7185">(肘上/肘下 × 同号/反号镜像族)</text>
<rect x="510" y="428" width="440" height="110" rx="8" fill="#f4f6f8" stroke="#9db0c0" stroke-width="1.2"/>
<text x="528" y="454" font-size="12.5" fill="#16324f" font-family="Consolas,monospace">n = (R²+A²−Vx²−Vz²−C0²) / (2·C0),cc = C0+n</text>
<text x="528" y="478" font-size="12.5" fill="#16324f" font-family="Consolas,monospace">θ2 = atan2((λA−κn)/det, (κA+λn)/det) κ=Vx·c3, λ=Vz</text>
<text x="528" y="502" font-size="12.5" fill="#16324f" font-family="Consolas,monospace">b = −Vx·sinθ3 (θ1 由柱角 φ 反推)</text>
<text x="528" y="526" font-size="11.5" fill="#8a5433">退化防线:|Vx|&lt;1e-7、det&lt;1e-12、m2 越界 → 该支无解,直接落选</text>
<text x="490" y="560" text-anchor="middle" font-size="12.5" fill="#3d5164">示意省略了 X0/Y0 基准平移与 tool 常量;四支解的位置全部精确命中目标 ——「扫描代替数值搜索」的根基。</text>
</svg>

After

Width:  |  Height:  |  Size: 6.6 KiB

+76
View File
@@ -0,0 +1,76 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 980 750" font-family="'Segoe UI','PingFang SC','Microsoft YaHei',sans-serif">
<rect x="0" y="0" width="980" height="750" fill="#ffffff"/>
<defs>
<marker id="fa" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse">
<path d="M0,0 L10,5 L0,10 z" fill="#5a7d9a"/>
</marker>
</defs>
<text x="490" y="34" text-anchor="middle" font-size="21" font-weight="bold" fill="#16324f">离线碰撞检测(sweep_v2.py):逐层过滤漏斗</text>
<text x="490" y="58" text-anchor="middle" font-size="13" fill="#5c7185">目标:把「每姿态都做 NURBS 布尔」压到只剩真正贴 face 的零件对 —— 精确性一分不减,调用量掉几个数量级</text>
<g font-size="12.5">
<rect x="120" y="76" width="600" height="46" rx="8" fill="#dbe9fb" stroke="#3572c9" stroke-width="1.4"/>
<circle cx="145" cy="99" r="12" fill="#3572c9"/><text x="145" y="104" text-anchor="middle" fill="#ffffff" font-weight="bold">1</text>
<text x="168" y="95" font-weight="bold" fill="#153a6e">全对全候选对</text>
<text x="168" y="113" fill="#2a4a6e">19 件 ≈ 171 对(9 打印件 + 6 电机 + 台架 P01/P02),按姿态刚体变换</text>
<line x1="420" y1="122" x2="420" y2="136" stroke="#5a7d9a" stroke-width="1.6" marker-end="url(#fa)"/>
<rect x="140" y="138" width="560" height="46" rx="8" fill="#e6eef7" stroke="#5a86b5" stroke-width="1.3"/>
<circle cx="165" cy="161" r="12" fill="#5a86b5"/><text x="165" y="166" text-anchor="middle" fill="#ffffff" font-weight="bold">2</text>
<text x="188" y="157" font-weight="bold" fill="#153a6e">跳过刚性安装对</text>
<text x="188" y="175" fill="#2a4a6e">MOUNT_PAIRS 12 对:电机 ↔ 被驱动件由螺钉锁死,无相对运动</text>
<line x1="420" y1="184" x2="420" y2="198" stroke="#5a7d9a" stroke-width="1.6" marker-end="url(#fa)"/>
<rect x="160" y="200" width="520" height="46" rx="8" fill="#e6eef7" stroke="#5a86b5" stroke-width="1.3"/>
<circle cx="185" cy="223" r="12" fill="#5a86b5"/><text x="185" y="228" text-anchor="middle" fill="#ffffff" font-weight="bold">3</text>
<text x="208" y="219" font-weight="bold" fill="#153a6e">组跨度规则</text>
<text x="208" y="237" fill="#2a4a6e">该姿态中没有活动关节跨越的对跳过(零位时全部必查——静止不代表不干涉)</text>
<line x1="420" y1="246" x2="420" y2="260" stroke="#5a7d9a" stroke-width="1.6" marker-end="url(#fa)"/>
<rect x="180" y="262" width="480" height="46" rx="8" fill="#e6eef7" stroke="#5a86b5" stroke-width="1.3"/>
<circle cx="205" cy="285" r="12" fill="#5a86b5"/><text x="205" y="290" text-anchor="middle" fill="#ffffff" font-weight="bold">4</text>
<text x="228" y="281" font-weight="bold" fill="#153a6e">轴区间预剪枝(单关节扫描专用,精确安全)</text>
<text x="228" y="299" fill="#2a4a6e">沿关节轴的轴向/径向区间永不相交的对,任何角度都不可能碰撞</text>
<line x1="420" y1="308" x2="420" y2="322" stroke="#5a7d9a" stroke-width="1.6" marker-end="url(#fa)"/>
<rect x="200" y="324" width="440" height="46" rx="8" fill="#e6eef7" stroke="#5a86b5" stroke-width="1.3"/>
<circle cx="225" cy="347" r="12" fill="#5a86b5"/><text x="225" y="352" text-anchor="middle" fill="#ffffff" font-weight="bold">5</text>
<text x="248" y="343" font-weight="bold" fill="#153a6e">AABB 分离</text>
<text x="248" y="361" fill="#2a4a6e">8 角点变换后取包围盒,0.1mm 余量;分离即跳过</text>
<line x1="420" y1="370" x2="420" y2="384" stroke="#5a7d9a" stroke-width="1.6" marker-end="url(#fa)"/>
<rect x="220" y="386" width="400" height="46" rx="8" fill="#e6eef7" stroke="#5a86b5" stroke-width="1.3"/>
<circle cx="245" cy="409" r="12" fill="#5a86b5"/><text x="245" y="414" text-anchor="middle" fill="#ffffff" font-weight="bold">6</text>
<text x="268" y="405" font-weight="bold" fill="#153a6e">包围球分离</text>
<text x="268" y="423" fill="#2a4a6e">球心随组变换、半径旋转不变;球分离 ⇒ 零件必分离</text>
<line x1="420" y1="432" x2="420" y2="446" stroke="#5a7d9a" stroke-width="1.6" marker-end="url(#fa)"/>
<rect x="230" y="444" width="400" height="66" rx="8" fill="#fdf2e2" stroke="#c99418" stroke-width="1.4"/>
<circle cx="255" cy="477" r="12" fill="#c99418"/><text x="255" y="482" text-anchor="middle" fill="#ffffff" font-weight="bold">7</text>
<text x="278" y="468" font-weight="bold" fill="#7a5a08">GJK 凸包预筛(保守证书)</text>
<text x="278" y="487" fill="#6b5312">0.5mm 凸包 ⊕ 0.8mm 球:确证分离才允许跳过</text>
<text x="278" y="500" fill="#6b5312" font-size="11">可能干涉/数值退化一律回落精确布尔——结果与纯布尔一致</text>
<line x1="420" y1="510" x2="420" y2="516" stroke="#5a7d9a" stroke-width="1.6" marker-end="url(#fa)"/>
<rect x="255" y="518" width="350" height="66" rx="8" fill="#fdf2e2" stroke="#c99418" stroke-width="1.4"/>
<circle cx="280" cy="551" r="12" fill="#c99418"/><text x="280" y="556" text-anchor="middle" fill="#ffffff" font-weight="bold">8</text>
<text x="303" y="542" font-weight="bold" fill="#7a5a08">顶点侵入实锤(反方向 shortcut)</text>
<text x="303" y="561" fill="#6b5312">一方网格顶点(≤240 采样)落入对方实体 &gt;1mm</text>
<text x="303" y="574" fill="#6b5312" font-size="11">⇒ 直接判干涉,免 4~12s 布尔;只证「撞」不证「自由」</text>
<line x1="420" y1="584" x2="420" y2="590" stroke="#5a7d9a" stroke-width="1.6" marker-end="url(#fa)"/>
<rect x="275" y="592" width="300" height="52" rx="8" fill="#fbe9e7" stroke="#c0533a" stroke-width="1.5"/>
<circle cx="300" cy="618" r="12" fill="#c0533a"/><text x="300" y="623" text-anchor="middle" fill="#ffffff" font-weight="bold">9</text>
<text x="323" y="612" font-weight="bold" fill="#7c2a18">OCCT 精确布尔 Common</text>
<text x="323" y="630" fill="#8a3a26">实体交体积 &gt; 2mm³ 记干涉(附干涉质心)</text>
<text x="323" y="640" fill="#8a3a26" font-size="11">SetRunParallel(True) · 多进程分发姿态</text>
</g>
<path d="M 745 480 C 800 480, 820 540, 820 590 C 820 620, 790 630, 566 618" fill="none" stroke="#c0533a" stroke-width="1.5" stroke-dasharray="6 4" marker-end="url(#fa)"/>
<text x="806" y="470" text-anchor="middle" font-size="12" fill="#7c2a18">布尔每对 0.5~12s</text>
<text x="806" y="488" text-anchor="middle" font-size="12" fill="#7c2a18">前面每层都在为它省调用</text>
<rect x="60" y="660" width="860" height="66" rx="8" fill="#f4f6f8" stroke="#9db0c0" stroke-width="1.2"/>
<text x="490" y="684" text-anchor="middle" font-size="12.5" fill="#3d5164">扫描策略:单关节双向粗扫 2°(小批量、首个卡死角即停)+ 二分逼近 0.2° → travel.json(滑块硬限位)</text>
<text x="490" y="704" text-anchor="middle" font-size="12.5" fill="#3d5164">组合姿态收敛 6°(一个关节保持极限、扫其余,两轮迭代)→ 只写 travel_detail.json,不压缩滑块 | workers = min(12, CPU−2, 内存/2.5GB)</text>
</svg>

After

Width:  |  Height:  |  Size: 7.1 KiB

+70
View File
@@ -0,0 +1,70 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 980 640" font-family="'Segoe UI','PingFang SC','Microsoft YaHei',sans-serif">
<rect x="0" y="0" width="980" height="640" fill="#ffffff"/>
<defs>
<marker id="ca" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse">
<path d="M0,0 L10,5 L0,10 z" fill="#5a7d9a"/>
</marker>
</defs>
<text x="490" y="34" text-anchor="middle" font-size="21" font-weight="bold" fill="#16324f">实时碰撞模型:CAD 网格 → 胶囊体 + 两级宽相位</text>
<!-- 左:胶囊化手臂示意 -->
<text x="235" y="64" text-anchor="middle" font-size="14.5" font-weight="bold" fill="#1f4f8f">手臂按运动组胶囊化(fit_capsules.py 拟合)</text>
<line x1="90" y1="590" x2="400" y2="590" stroke="#8a9aa8" stroke-width="2.5"/>
<rect x="150" y="110" width="34" height="478" rx="6" fill="#e2e8ee" stroke="#9db0c0" stroke-width="1"/>
<text x="167" y="102" text-anchor="middle" font-size="11" fill="#5c7185">台架 g0(root 固定)</text>
<rect x="196" y="128" width="52" height="86" rx="26" fill="#aecbeb" stroke="#3572c9" stroke-width="1.4"/>
<text x="256" y="150" font-size="11.5" fill="#153a6e">g1 · J1 组(肩吊臂)</text>
<rect x="196" y="226" width="52" height="70" rx="26" fill="#b7dcC8" stroke="#1f9d63" stroke-width="1.4"/>
<text x="256" y="248" font-size="11.5" fill="#14573a">g2 · J2 组(肩叉)</text>
<rect x="196" y="306" width="52" height="96" rx="26" fill="#a8d5d8" stroke="#1f7a8f" stroke-width="1.4"/>
<text x="256" y="336" font-size="11.5" fill="#14525e">g3 · J3 组(大臂骨架)</text>
<rect x="196" y="412" width="52" height="78" rx="26" fill="#c9d9a8" stroke="#6f8f2f" stroke-width="1.4"/>
<text x="256" y="436" font-size="11.5" fill="#4a5f1c">g4 · J4 组(小臂骨架)</text>
<rect x="196" y="498" width="52" height="42" rx="21" fill="#f2d5a8" stroke="#c98a18" stroke-width="1.4"/>
<text x="256" y="512" font-size="11.5" fill="#7a5308">g5 · J5 组(腕偏转座)</text>
<rect x="196" y="546" width="0" height="0" fill="none"/>
<rect x="198" y="548" width="48" height="20" rx="10" fill="#f0bcb0" stroke="#c0533a" stroke-width="1.4"/>
<text x="256" y="564" font-size="11.5" fill="#7c2a18">g6 · J6 组(工具法兰)</text>
<text x="235" y="608" text-anchor="middle" font-size="11.5" fill="#5c7185">胶囊 = 线段端点 a,b + 半径 r,端点是零位 CAD 世界坐标;</text>
<text x="235" y="626" text-anchor="middle" font-size="11.5" fill="#5c7185">组 g 的胶囊随 A1..Ag 嵌套旋转(与 FK 链同一累积变换)。</text>
<!-- 右上:宽相位级联 -->
<text x="700" y="64" text-anchor="middle" font-size="14.5" font-weight="bold" fill="#1f4f8f">两级宽相位 → 胶囊精确距</text>
<rect x="470" y="80" width="470" height="58" rx="8" fill="#e6eef7" stroke="#5a86b5" stroke-width="1.3"/>
<text x="486" y="102" font-size="12.5" font-weight="bold" fill="#153a6e">第 1 级 · 零件包围球(19 球)</text>
<text x="486" y="122" font-size="11.5" fill="#2a4a6e">球心距 &gt; rad_A+rad_B+rmax_A+rmax_B+margin 才筛除(阈值推导防漏判)</text>
<line x1="705" y1="138" x2="705" y2="152" stroke="#5a7d9a" stroke-width="1.6" marker-end="url(#ca)"/>
<rect x="470" y="154" width="470" height="58" rx="8" fill="#e6eef7" stroke="#5a86b5" stroke-width="1.3"/>
<text x="486" y="176" font-size="12.5" font-weight="bold" fill="#153a6e">第 2 级 · 簇球(每零件胶囊按主轴分桶,每簇 ≤4 枚)</text>
<text x="486" y="196" font-size="11.5" fill="#2a4a6e">主轴 = 胶囊中点的幂迭代方向(5 轮);再细分实测得不偿失</text>
<line x1="705" y1="212" x2="705" y2="226" stroke="#5a7d9a" stroke-width="1.6" marker-end="url(#ca)"/>
<rect x="470" y="228" width="470" height="58" rx="8" fill="#dbe9fb" stroke="#3572c9" stroke-width="1.4"/>
<text x="486" y="250" font-size="12.5" font-weight="bold" fill="#153a6e">第 3 级 · 胶囊对:线段-线段距离(Ericson 标准式)</text>
<text x="486" y="270" font-size="11.5" fill="#2a4a6e">d(轴_a, 轴_b) &lt; r_i + r_j + margin(0.3mm) ⇒ 干涉;无 visit 回调时首对即短路</text>
<!-- 胶囊距小图 -->
<g transform="translate(490,306)">
<rect x="0" y="18" width="120" height="34" rx="17" fill="#cfe3f5" stroke="#3572c9" stroke-width="1.4"/>
<line x1="17" y1="35" x2="103" y2="35" stroke="#3572c9" stroke-width="1.2" stroke-dasharray="4 3"/>
<rect x="210" y="0" width="110" height="30" rx="15" fill="#f2d5a8" stroke="#c98a18" stroke-width="1.4"/>
<line x1="225" y1="15" x2="305" y2="15" stroke="#c98a18" stroke-width="1.2" stroke-dasharray="4 3"/>
<line x1="110" y1="30" x2="212" y2="18" stroke="#c0533a" stroke-width="1.8" marker-start="url(#ca)" marker-end="url(#ca)"/>
<text x="128" y="12" font-size="12" font-weight="bold" fill="#c0533a">d(轴间距)</text>
<text x="120" y="86" font-size="12" fill="#16324f" font-family="Consolas,monospace">自由 ⇔ d ≥ r_i + r_j + margin</text>
<text x="120" y="106" font-size="11" fill="#5c7185">胶囊并集 ⊇ 零件本体 ⇒ 「自由」可信、「干涉」需离线布尔确认</text>
</g>
<!-- 右下:规则 -->
<rect x="470" y="428" width="470" height="128" rx="8" fill="#fdf6ec" stroke="#c99418" stroke-width="1.3"/>
<text x="486" y="452" font-size="12.5" font-weight="bold" fill="#7a5a08">三条关键规则</text>
<text x="486" y="474" font-size="12" fill="#6b5312">· 只检 |Δg|≥2 的零件对(同组相对静止、零位已保证不干涉);</text>
<text x="486" y="492" font-size="12" fill="#6b5312"> 相邻对 P04×P05/P06×P07/P07×P08 同心跨轴,塞进模型零位必误报</text>
<text x="486" y="510" font-size="12" fill="#6b5312"> ⇒ 改用 adj 一维自由区间表(CAD 实测、与姿态无关)</text>
<text x="486" y="528" font-size="12" fill="#6b5312">· adj:J2 [−107°,+128°] · J4 [−112°,+112°] · J5 [−96°,+96°]</text>
<text x="486" y="546" font-size="12" fill="#6b5312">· 硬边(lo 侧相邻件自碰撞)静态值即真值;可放宽侧(撞立柱)按姿态实时二分</text>
<rect x="470" y="566" width="470" height="58" rx="8" fill="#eef8f1" stroke="#1f6f43" stroke-width="1.3"/>
<text x="486" y="588" font-size="12.5" font-weight="bold" fill="#14472c">性能:1995 胶囊 · collideFree ≈ 5ms/次(零位最坏)</text>
<text x="486" y="608" font-size="11.5" fill="#2c5c44">预分配缓冲 + 代际戳缓存(免 GC)· 轴对齐特化旋转 · 热循环标量直传免建数组</text>
</svg>

After

Width:  |  Height:  |  Size: 6.5 KiB

+72
View File
@@ -0,0 +1,72 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 980 560" font-family="'Segoe UI','PingFang SC','Microsoft YaHei',sans-serif">
<rect x="0" y="0" width="980" height="560" fill="#ffffff"/>
<defs>
<marker id="da" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse">
<path d="M0,0 L10,5 L0,10 z" fill="#5a7d9a"/>
</marker>
<marker id="dor" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse">
<path d="M0,0 L10,5 L0,10 z" fill="#e08a1e"/>
</marker>
</defs>
<text x="490" y="34" text-anchor="middle" font-size="21" font-weight="bold" fill="#16324f">动态限位:静态值只是零位快照,真边界由碰撞模型按姿态二分</text>
<!-- 数轴(J5 例) -->
<text x="490" y="70" text-anchor="middle" font-size="14" font-weight="bold" fill="#1f4f8f">以 J5 腕俯仰 + 向为例(数值取自实机数据)</text>
<line x1="120" y1="200" x2="880" y2="200" stroke="#3d5164" stroke-width="2.5" marker-start="url(#da)" marker-end="url(#da)"/>
<g font-size="11.5" fill="#5c7185" text-anchor="middle">
<text x="120" y="222">−360°</text><text x="500" y="222">0°</text><text x="880" y="222">+360°(电机限位)</text>
<line x1="500" y1="194" x2="500" y2="206" stroke="#3d5164" stroke-width="1.5"/>
<line x1="120" y1="194" x2="120" y2="206" stroke="#8a9aa8" stroke-width="1.5"/>
<line x1="880" y1="194" x2="880" y2="206" stroke="#8a9aa8" stroke-width="1.5"/>
</g>
<!-- adj 机械区间 -->
<rect x="399" y="188" width="202" height="24" rx="4" fill="#dbe9fb" opacity="0.85" stroke="#3572c9" stroke-width="1.2"/>
<text x="500" y="136" text-anchor="middle" font-size="12" fill="#2a4a6e">adj 机械区间(相邻件 CAD 实测):J5 = [−96°, +96°]</text>
<!-- 静态扫描限位 -->
<line x1="536" y1="180" x2="536" y2="220" stroke="#1f9d63" stroke-width="3"/>
<text x="536" y="250" text-anchor="middle" font-size="12" fill="#1f7a4d">零位静态扫描 +34°(撞立柱 P01_A)</text>
<!-- 动态边界 -->
<line x1="601" y1="180" x2="601" y2="220" stroke="#e08a1e" stroke-width="3"/>
<text x="672" y="272" text-anchor="middle" font-size="12" font-weight="bold" fill="#b06a10">当前姿态动态边界 +96°</text>
<!-- 当前角 -->
<path d="M 521 194 l 6 12 h -12 z" fill="#16324f"/>
<text x="489" y="172" text-anchor="end" font-size="12" font-weight="bold" fill="#16324f">当前 θ</text>
<!-- 二分括号 -->
<g stroke="#e08a1e" stroke-width="1.4" fill="none" stroke-dasharray="5 4">
<path d="M 521 216 C 521 230, 601 230, 601 224" marker-end="url(#dor)"/>
<path d="M 561 216 C 561 227, 581 227, 581 224" marker-end="url(#dor)"/>
</g>
<text x="490" y="292" text-anchor="middle" font-size="12" fill="#b06a10">dynLimit:从当前角向限位方向二分「collideFree」翻转点(tol 0.25°)</text>
<rect x="120" y="304" width="760" height="58" rx="8" fill="#f4f6f8" stroke="#9db0c0" stroke-width="1.2"/>
<text x="490" y="326" text-anchor="middle" font-size="12.5" fill="#3d5164">为什么静态限位不可信:它来自「其它轴全零」的一次性扫描。实测 J2 正向静态 +9.75°(垂臂零位撞立柱),</text>
<text x="490" y="346" text-anchor="middle" font-size="12.5" fill="#3d5164">J1=+90° 时动态边界放到 +108.75° —— 撞立柱这类「跨 ≥2 组」的约束必须按姿态重算,滑块才不锁死。</text>
<!-- solveDyn 流程 -->
<text x="490" y="394" text-anchor="middle" font-size="14.5" font-weight="bold" fill="#1f4f8f">solveDyn:静态解 → 碰撞校验 → 贴边轴放宽重解(最多 2 轮)</text>
<g font-size="11.5">
<rect x="70" y="408" width="150" height="50" rx="8" fill="#e6eef7" stroke="#5a86b5" stroke-width="1.3"/>
<text x="145" y="429" text-anchor="middle" font-weight="bold" fill="#153a6e">静态限位求解</text>
<text x="145" y="447" text-anchor="middle" fill="#2a4a6e">solveTo(全流水线)</text>
<line x1="220" y1="433" x2="240" y2="433" stroke="#5a7d9a" stroke-width="1.6" marker-end="url(#da)"/>
<rect x="242" y="408" width="150" height="50" rx="8" fill="#e6eef7" stroke="#5a86b5" stroke-width="1.3"/>
<text x="317" y="429" text-anchor="middle" font-weight="bold" fill="#153a6e">全姿态碰撞校验</text>
<text x="317" y="447" text-anchor="middle" fill="#2a4a6e">不安全→候选表选安全解</text>
<line x1="392" y1="433" x2="412" y2="433" stroke="#5a7d9a" stroke-width="1.6" marker-end="url(#da)"/>
<rect x="414" y="408" width="160" height="50" rx="8" fill="#e6eef7" stroke="#5a86b5" stroke-width="1.3"/>
<text x="494" y="429" text-anchor="middle" font-weight="bold" fill="#153a6e">贴边轴检测(0.5°内)</text>
<text x="494" y="447" text-anchor="middle" fill="#2a4a6e">非连续轴 · 非硬边</text>
<line x1="574" y1="433" x2="594" y2="433" stroke="#5a7d9a" stroke-width="1.6" marker-end="url(#da)"/>
<rect x="596" y="408" width="150" height="50" rx="8" fill="#fdf2e2" stroke="#c99418" stroke-width="1.3"/>
<text x="671" y="429" text-anchor="middle" font-weight="bold" fill="#7a5a08">dynLimit 二分边界</text>
<text x="671" y="447" text-anchor="middle" fill="#6b5312">放宽 ≥5° 才值得重解</text>
<line x1="746" y1="433" x2="766" y2="433" stroke="#5a7d9a" stroke-width="1.6" marker-end="url(#da)"/>
<rect x="768" y="408" width="150" height="50" rx="8" fill="#eef8f1" stroke="#1f6f43" stroke-width="1.3"/>
<text x="843" y="429" text-anchor="middle" font-weight="bold" fill="#14472c">放宽后重解(粗档)</text>
<text x="843" y="447" text-anchor="middle" fill="#2c5c44">cost 不升才采用</text>
</g>
<rect x="120" y="482" width="760" height="62" rx="8" fill="#fdf6ec" stroke="#c99418" stroke-width="1.3"/>
<text x="490" y="506" text-anchor="middle" font-size="12.5" fill="#6b5312">滑块夹取 clampAxis:缓存 [轴, 其余轴 1° 量化姿态] → {lo, hi};拖同一轴直接取缓存,</text>
<text x="490" y="526" text-anchor="middle" font-size="12.5" fill="#6b5312">拖过边界才重算一次二分(~40ms)。当前姿已干涉时退回机械区间 —— 否则 dynLimit 会把界外值当边界,滑块再也回不来。</text>
</svg>

After

Width:  |  Height:  |  Size: 6.2 KiB

@@ -0,0 +1,56 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 980 440" font-family="'Segoe UI','PingFang SC','Microsoft YaHei',sans-serif">
<rect x="0" y="0" width="980" height="440" fill="#ffffff"/>
<defs>
<marker id="zb" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse">
<path d="M0,0 L10,5 L0,10 z" fill="#3572c9"/>
</marker>
<marker id="zo" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6.5" markerHeight="6.5" orient="auto-start-reverse">
<path d="M0,0 L10,5 L0,10 z" fill="#e08a1e"/>
</marker>
</defs>
<text x="490" y="34" text-anchor="middle" font-size="21" font-weight="bold" fill="#16324f">工具姿态参数化:方位角 az · 仰角 el · 滚转 roll</text>
<!-- 左:球面示意 -->
<circle cx="240" cy="220" r="130" fill="#f4f8fb" stroke="#b9c8d6" stroke-width="1.4"/>
<ellipse cx="240" cy="220" rx="130" ry="40" fill="none" stroke="#b9c8d6" stroke-width="1" stroke-dasharray="4 3"/>
<line x1="240" y1="220" x2="240" y2="70" stroke="#3572c9" stroke-width="2" marker-end="url(#zb)"/>
<text x="252" y="80" font-size="12.5" fill="#2a4a6e">CAD +Z</text>
<line x1="240" y1="220" x2="400" y2="220" stroke="#8a9aa8" stroke-width="1.4"/>
<text x="406" y="224" font-size="12" fill="#5c7185">方位基线</text>
<!-- 工具轴 -->
<line x1="240" y1="220" x2="296" y2="96" stroke="#e08a1e" stroke-width="2.6" marker-end="url(#zo)"/>
<text x="306" y="94" font-size="12.5" font-weight="bold" fill="#b06a10">工具轴</text>
<text x="306" y="112" font-size="11.5" fill="#b06a10">(法兰 +Z 外伸)</text>
<!-- el 弧 -->
<path d="M 240 128 A 92 92 0 0 1 277 136" fill="none" stroke="#7c5cd6" stroke-width="1.7"/>
<text x="92" y="140" font-size="12.5" fill="#7c5cd6">el(仰角,自 +Z 抬起)</text>
<text x="92" y="158" font-size="12.5" fill="#7c5cd6">0…180°</text>
<line x1="216" y1="136" x2="258" y2="131" stroke="#7c5cd6" stroke-width="1" stroke-dasharray="3 3"/>
<!-- roll 环绕工具轴 -->
<ellipse cx="270" cy="153" rx="20" ry="9" fill="none" stroke="#d64545" stroke-width="1.7" transform="rotate(-24 270 153)"/>
<text x="92" y="196" font-size="12.5" fill="#b03030">roll(绕轴滚转)</text>
<text x="92" y="214" font-size="12.5" fill="#b03030">= θ6,绕工具轴</text>
<line x1="212" y1="198" x2="256" y2="160" stroke="#d64545" stroke-width="1" stroke-dasharray="3 3"/>
<!-- az 弧(水平面内) -->
<path d="M 362 220 A 122 38 0 0 0 330 182" fill="none" stroke="#1f9d63" stroke-width="1.7"/>
<text x="320" y="262" font-size="12.5" fill="#1f7a4d">az(绕 Z 方位角,−180…180°)</text>
<circle cx="240" cy="220" r="4" fill="#16324f"/>
<text x="160" y="248" font-size="12" fill="#5c7185">法兰中心</text>
<!-- 右:公式 -->
<rect x="470" y="70" width="470" height="180" rx="8" fill="#f4f6f8" stroke="#9db0c0" stroke-width="1.2"/>
<text x="488" y="96" font-size="13" font-weight="bold" fill="#16324f">poseMat(az, el, roll) → 工具系旋转矩阵(列 = x/y/z)</text>
<text x="488" y="122" font-size="12.5" fill="#16324f" font-family="Consolas,monospace">z = (sin el·cos az, sin el·sin az, cos el)</text>
<text x="488" y="146" font-size="12.5" fill="#16324f" font-family="Consolas,monospace">x0 = (−sin az, cos az, 0) (子午面基,恒⊥z)</text>
<text x="488" y="170" font-size="12.5" fill="#16324f" font-family="Consolas,monospace">x = R(z, roll)·x0  y = z × x</text>
<text x="488" y="200" font-size="11.5" fill="#5c7185">poseAng() 为其逆分解;el ∈ [0,180],az/roll ∈ (−180,180]。</text>
<text x="488" y="220" font-size="11.5" fill="#5c7185">界面以「工具朝外」描述;求解器内部法兰法向朝臂内,</text>
<text x="488" y="236" font-size="11.5" fill="#5c7185">两者相差绕工具 x 轴 180°(RX180),入栈/出栈各翻一次。</text>
<rect x="470" y="266" width="470" height="140" rx="8" fill="#fdf6ec" stroke="#c99418" stroke-width="1.3"/>
<text x="488" y="292" font-size="13" font-weight="bold" fill="#7a5a08">为什么姿态任务只剩 2 个未知数</text>
<text x="488" y="316" font-size="12" fill="#6b5312">· θ6 只提供绕工具轴的滚转,不改变工具轴 ⇒ 工具轴 = (R1…R5)·ẑ</text>
<text x="488" y="338" font-size="12" fill="#6b5312">· 滚转由 rollClose 闭式补出:θ6 = −atan2(N[1,0], N[0,0]),N = R5ᵀ·Rtar</text>
<text x="488" y="360" font-size="12" fill="#6b5312">· 推论:全姿态偏差 oerr ≥ 工具轴夹角(20 万随机样本验证零违反)——</text>
<text x="488" y="380" font-size="12" fill="#6b5312"> 粗扫用一次点积预筛(1−cos2°)挡掉必被拒的候选,省掉矩阵链+acos</text>
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 650 KiB

+20 -20
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -18,7 +18,7 @@
| P06_大臂骨架_螺旋体25%_墙4底5_竖打 | 螺旋体 25% | 4 / 5 | 竖打 | M4 抱紧套筒 |
| P07_小臂骨架_螺旋体30%_墙5底5_竖打 | 螺旋体 30% | 5 / 5 | 竖打 | 根部加厚顶板区 + M5 电机半包座 |
| P08_腕偏转座_螺旋体25%_墙4底5_竖打 | 螺旋体 25% | 4 / 5 | 竖打 | J5 接口孔周 |
| P09_工具法兰_螺旋体100%_墙4底5_竖打 | 螺旋体 100% | 4 / 5 | 竖打 | — |
| P09_工具法兰_螺旋体30%_墙4底5_竖打 | 螺旋体 30% | 4 / 5 | 竖打 | — |
## 通用规则
+12 -12
View File
@@ -17,27 +17,27 @@
"头型": "圆柱头内六角",
"数量": 20,
"位置": "P04/P05/P06/P07/P08 各 4 颗 -> 下级电机尾端面",
"备注": "Ø2.8 通孔 + Ø5.5 沉孔(深 4mm),旋入电机尾端面 4x M3 螺纹径向槽(电机.STEP 实测:BCD55、相位 39.9°+90k,槽宽 2.5)"
"备注": "Ø2.8 通孔 + Ø5.5 沉孔(深 4mm),旋入电机尾端面 4x M3 螺纹径向槽(电机.STEP 实测:BCD55、相位 39.9°+90k,槽宽 2.5,径向可调约 ±1.25mm)。【2026-10-03 电机厚度公差】各台电机轴向长度实测差可达 2mm,本孔的承压面已按 MOTOR_TOL=2.0 从 s=-0.6 让空到 s=-2.6(armcore.MOTOR_TOL / arm_build.REAR_SEAT),所以这 4 颗螺钉**不负责把电机压死在某个长度上**,只负责夹紧垫片:装配时在电机尾端面与承压面之间叠 0~2.6mm 的 M3 平垫(标称电机约 0.6mm),厚度 T = 58.6 - 电机实际长度 L,让前端盖正好落到前端压盖面上(压盖背面与前端盖 0.0 面压)。螺钉杆长按最坏情况 T=2.6 选:M3x10 在 T=0.6 时咬合最深,T 每加 1mm 咬合减 1mm;咬合不足 3mm 时改用 M3x12。【2026-10-05 承压面让位腔】承压面段(z=-2.6..+1.0)用 SEAT_R=35.0(= 外壁 R35)圆腔掏出垫片位。**刀半径必须等于 R35**:只要小于 R35,刀半径与外壁之间那圈环墙就必然保留并相对承压面凸起,把电机尾端面围成\"一圈围挡\"(P08 实测 1536.9mm3)—— 这是取值的必然产物,不是残留。取满 R35 后承压面成为完整 R35 平圆盘,凸台与围挡一并消失。SEAT_TOP=+1.0 用于吃掉半包开口处的 0.052cm3 残片(取 0 时该残片脱空成独立碎块)。沉孔位置与深度完全不变(Ø5.5 深 4,z=-8..-4,与圆腔 z 区间不重叠,实测残余 0.00mm³),螺钉头仍坐在沉孔底、杆穿承压面进入电机螺纹槽,咬合量不变。承压环同时由 1.95mm 窄环变 4.75mm 完整圆环,受压面积 383->974mm²。"
},
{
"id": "S03",
"规格": "M3 x 16",
"螺纹": "M3",
"长度mm": 16,
"头型": "沉头内六角",
"头型": "圆柱头内六角",
"数量": 4,
"位置": "P03 后置重载法兰 -> M1 电机尾端面",
"备注": "Ø3.4 通孔 + Ø6.5 沉孔,孔位 BCD55、相位 39.9°+90k(电机.STEP 实测螺纹径向槽),轴向锁死防拉脱;旧 35.1×40.9 矩形孔距对角两颗偏出槽位 ~4.7mm,2026-10-02 修正"
"备注": "Ø3.4 通孔 + Ø6.5 圆柱沉孔(深 5mm,头沉入 1.5mm),孔位 BCD55、相位 39.9°+90k(电机.STEP 实测螺纹径向槽),轴向锁死防拉脱;旧 35.1×40.9 矩形孔距对角两颗偏出槽位 ~4.7mm,2026-10-02 修正;头型按用户已采购件用圆柱头内六角(沉孔本为圆柱形)"
},
{
"id": "S04",
"位置": "P03 前置锁紧凸台(Ø10 x4) -> M1 电机前端面",
"备注": "Ø3.2 通孔 + Ø6.0 沉孔,BCD 55(R27.5,4 孔 17.8/107.8/197.8/287.8°);座位为舱孔前段 4 个 Ø10 局部凸台(2026-10-02 平直舱孔方案),M3x10 旋入电机前端面咬合 3.5mm",
"头型": "沉头内六角",
"备注": "Ø3.2 通孔 + Ø6.0 圆柱沉孔(深 2mm,头露出 1mm),孔位 BCD55 @17.8°+90k(电机.STEP 实测前端螺纹径向槽);头坐在沉孔底 x=9.5,M3x8 咬合电机前端面 3.5mm;2026-10-02 按用户已采购规格由 M3x10 沉头改回 M3x8 圆柱头",
"头型": "圆柱头内六角",
"数量": 4,
"螺纹": "M3",
"规格": "M3 x 10",
"长度mm": 10
"规格": "M3 x 8",
"长度mm": 8
},
{
"id": "S05",
@@ -54,10 +54,10 @@
"规格": "M4 x 16",
"螺纹": "M4",
"长度mm": 16,
"头型": "沉头内六角",
"头型": "圆柱头内六角",
"数量": 4,
"位置": "P09 末端工具法兰 -> 末端工具/夹爪",
"备注": "Ø3.7 通孔 + Ø7.0 沉孔(深 4mm),4 孔分布圆 R20"
"备注": "Ø4.4 通孔(贯穿法兰)+ Ø7.0 圆柱沉孔(深 4mm,头与法兰底面齐平),R22 @0/90/180/270°(BCD44)-> 末端工具;头型按用户已采购件用圆柱头内六角。2026-10-02 由 R20 改 R22:原 R20 与 3xS01 输出螺钉 Ø5.6 螺丝刀通道中心距 6.06mm < 所需 6.30mm,0° 孔的 Ø7.0 沉孔壁被啃穿成月牙形(圆柱面 88mm² 裂成 45.7+35.4);相位差仅 8.6°,转相位躲不开,R22 后余量 1.66mm、边壁仍余 9.5mm;同日修正通孔:原 Ø3.7 比 M4 杆 Ø4.0 还细且只到 z=105,而 M4x16 杆需走到 z=113,杆尾 8mm 埋在实心料里(干涉 147.3mm³),改为 Ø4.4 贯穿"
},
{
"id": "S07",
@@ -115,8 +115,8 @@
"螺纹": "M3",
"长度mm": 8,
"头型": "圆柱头内六角",
"数量": 4,
"位置": "P04 前端压盖 -> M2 电机前端盖(J2)",
"备注": "Ø3.4 通孔 + Ø6.0 沉孔(深 3.0,自压盖前端面 Y=161 沉到 Y=158),BCD 55(R27.5,4 孔 17.8/107.8/197.8/287.8°,与 P03→M1 的 S04 同约定);螺钉头全沉入压盖,头面 Y=158 压孔口台阶,压盖背面 Y=156 与电机前端盖外端面0.0 间隙面压贴合 —— 与后端安装板 S02 的 4xM3x10 一起把 M2 前后双向夹死,消掉大扭矩下电机绕 J2 轴点头/翘起的倾向;M3x8 穿过 3.5mm 端盖后再自攻入壳体约 4.5mm"
"数量": 16,
"位置": "P04/P05/P06/P07 前端压盖 -> M2/M3/M4/M5 电机前端盖(J2/J3/J4/J5 各 4 颗)",
"备注": "Ø3.4 通孔 + Ø6.0 圆柱沉孔(深 3.0,头 Ø5.5×3.0 全沉入),孔位 BCD55 @17.8°+90k—— 正是电机前端面 4 个 M3 螺纹径向槽的相位(与 S04 同一套,勿改);螺钉头面压在孔口台阶上(Y 轴关节 Y=158;J3 的 Z 轴版本 Z=387),压盖背面与电机前端盖外端面 0.0 间隙面压贴合—— 与后端安装板 S02 的 4xM3x10 一起把电机前后双向夹死,消掉大扭矩下电机绕 J 轴点头/翘起的倾向。J2(P04 夹 M2)/J4(P06 夹 M4)/J5(P07 夹 M5) 三处压盖几何完全相同;【2026-10-03 新增 J3】P05 夹 M3 是同一套配方的 Z 轴版本(roll 电机轴向 -Z,压盖占 Z384.0..389.0,背面 Z=389 与 M3 前端盖面压),沉孔台阶 Z=387,杆穿 2.0mm 压盖 + 3.5mm 端盖后旋入前端螺纹槽约 2.5mm。【2026-10-05d 只切敞口那一圈围挡】用户指出 M3 电机前端那圈围挡(P05 在 Z389.0..395.5 的 r30.25..35 前端定位环 + 骑在它上面的 R35 压盖外圈,Ø70 比电机 Ø63.6 凸出 3.2mm)需要删除。之前一版把整个前端(定位环 + 压盖 + 腔口下沿 389->395)都切了,电机前端没了支撑面 —— 过度;现已回退。现只切敞口(-Y)那一圈(盒切,Y 界限与 cut_open 一致),-Y 半圈从 Z=395.5 一路开到压盖背面 Z=389;背脊(+Y)侧整圈定位环保留,继续定位 M3 前端盖并作为R35 压盖唯一且足够强的支撑面(4.75mm 宽 x 0.5mm 熔接带)。压盖本体、Ø37 让位孔、4xM3x8 沉孔、腔口下沿 z_bot=389、P06 毂面 383.0 与直锁凸台 65.0 全部不动,本组 16 颗螺钉的孔位/相位/沉孔台阶/咬合量一概不变(M3 仍是后端板 4xM3x10 + 前端压盖 4xM3x8 双向面压)。不整圈削到 R31.8 的原因:那圈壁与 Loft 在 X=±35 处与 Ø70 柱面相切,整圈切完会脱空出 1.32mm3 碎块破坏单实体;且压盖根部熔接带会从 2.37mm2 缩到 0.78mm2,夹紧力会把压盖拉下来。开关 arm_model.FENCE_OPEN_CUT(2026-10-05e 起为全机 5 处共用)。。【2026-10-05e 推广到其余三处】上述\"只切敞口那半圈围挡\"的做法与 P05/M3 完全同源,已推广到 J2/M2(P04)、J4/M4(P06)、J5/M5(P07) 三处同样有 M2/M4/M5 前端压盖的电机,以及尚无前端压盖的 J6/M6(P08)(只切定位环)。切刀 cut_motor_fence() 按各关节自己的局部坐标切(y -3..+40 / z 49.4~56.1,5 处定位环都落在局部 z 49.5~56,一对参数通吃;敞口方向恒为局部 +y)。**本组 16 颗螺钉的数量、型号、孔位 BCD55、相位、沉孔台阶深度、拧紧力矩一概不变** —— 被切掉的只是压盖外侧那圈围挡环墙,压盖本体与沉孔区一根没动,夹紧力与夹紧面积不变。"
}
]
Binary file not shown.

Before

Width:  |  Height:  |  Size: 195 KiB

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 164 KiB

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 255 KiB

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 220 KiB

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 250 KiB

After

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 209 KiB

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 197 KiB

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 195 KiB

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 196 KiB

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 250 KiB

After

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 244 KiB

After

Width:  |  Height:  |  Size: 174 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 185 KiB

After

Width:  |  Height:  |  Size: 122 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 220 KiB

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 259 KiB

After

Width:  |  Height:  |  Size: 185 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 172 KiB

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

After

Width:  |  Height:  |  Size: 226 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 194 KiB

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 184 KiB

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 257 KiB

After

Width:  |  Height:  |  Size: 193 KiB

+185 -122
View File
@@ -11,14 +11,14 @@
-110.07716623688886,
128.0
],
"M": 14218.240146201913,
"F": 41.72937644104729,
"sigma": 1.3441760077937133,
"tau": 0.06539022933626484,
"M": 13909.380595143337,
"F": 40.43051800646868,
"sigma": 1.3149767824295406,
"tau": 0.06335490894195162,
"L": 526.087122895359,
"defl": 1.6350063694519446,
"defl": 1.5994895032255896,
"bend_axis": 1,
"SF": 8.927402312213866
"SF": 9.125636406924915
},
{
"截面": "吊臂颊板 @z=560",
@@ -32,77 +32,77 @@
-98.0257258064516,
127.99999999999997
],
"M": 13715.34105182334,
"F": 41.72937644104729,
"sigma": 0.922016873786273,
"tau": 0.05534987325054023,
"M": 13422.134615816658,
"F": 40.43051800646868,
"sigma": 0.9023060054615691,
"tau": 0.053627066540838125,
"L": 518.8210523704677,
"defl": 0.9212023341880543,
"defl": 0.9015088790834574,
"bend_axis": 1,
"SF": 13.014946191518016
"SF": 13.299257599268081
},
{
"截面": "肩叉腹板 @z=560",
"零件": "P04_肩叉_螺旋体30%_墙5底5_竖打",
"关节": "J2",
"A": 2217.3650000000002,
"I": 207369.82260416728,
"c": 16.750000100002925,
"A": 1607.2294,
"I": 199777.10677271383,
"c": 17.03471471510569,
"bend": 1,
"centroid": [
28.249999999999996,
124.95500000000001
27.965285384899023,
123.79905866269004
],
"M": 7818.411904486269,
"F": 34.39607710754928,
"sigma": 0.6315210117722179,
"tau": 0.023268210538780903,
"L": 384.4650327126411,
"defl": 0.9288282882988673,
"M": 7715.182607919432,
"F": 33.63905427237436,
"sigma": 0.6578628393611474,
"tau": 0.03139476008126876,
"L": 384.35971314950393,
"defl": 0.9508783541854641,
"bend_axis": 1,
"SF": 19.001743055745322
"SF": 18.240884394159178
},
{
"截面": "肩叉腹板 @z=600",
"零件": "P04_肩叉_螺旋体30%_墙5底5_竖打",
"关节": "J2",
"A": 2393.7248,
"I": 289729.609479537,
"c": 22.374104171783745,
"A": 1839.1492000000003,
"I": 278077.74717628444,
"c": 21.564804309254765,
"bend": 1,
"centroid": [
26.31610407177968,
126.97437823679647
25.50680420925067,
126.39730477005344
],
"M": 7884.930337951309,
"F": 34.39607710754928,
"sigma": 0.6089065355988086,
"tau": 0.02155390446776669,
"L": 391.71974764776274,
"defl": 0.6959926630802069,
"M": 7797.883589614678,
"F": 33.63905427237436,
"sigma": 0.6047223675535119,
"tau": 0.02743582815823835,
"L": 392.2867363986807,
"defl": 0.7192278729466778,
"bend_axis": 1,
"SF": 19.707458039022367
"SF": 19.84381700407025
},
{
"截面": "大臂旋转座横梁 @z=480",
"零件": "P05_大臂旋转座_螺旋体30%_墙4底5_竖打",
"关节": "J2",
"A": 1250.9348,
"I": 490359.70932822645,
"c": 35.00000010000009,
"A": 1048.8348,
"I": 358227.1267915601,
"c": 35.00000010000008,
"bend": 1,
"centroid": [
19.500000000000004,
165.03578024690017
19.5,
166.10246986083982
],
"M": 8644.341221650102,
"F": 34.39607710754928,
"sigma": 0.6170000060499929,
"tau": 0.041244448280856785,
"L": 420.96884917144854,
"defl": 0.5206738812710487,
"M": 8513.356526578775,
"F": 33.63905427237436,
"sigma": 0.8317836841400634,
"tau": 0.048109179261177776,
"L": 421.3881746144544,
"defl": 0.7033245693677515,
"bend_axis": 1,
"SF": 19.448946324690457
"SF": 14.426827826523379
},
{
"截面": "大臂旋转座套筒 @z=440",
@@ -116,98 +116,161 @@
19.49928077488516,
162.1529414131134
],
"M": 7224.027167183286,
"F": 28.801398145740663,
"sigma": 0.5490192396606857,
"tau": 0.04677253732065479,
"M": 7116.046120241427,
"F": 28.210271278866706,
"sigma": 0.5408127821103745,
"tau": 0.045812566443470384,
"L": 384.25524483485555,
"defl": 0.38601035205736267,
"defl": 0.38024046761743446,
"bend_axis": 1,
"SF": 21.85715751494692
"SF": 22.18882466714871
},
{
"截面": "大臂骨架中段 @z=350",
"零件": "P06_大臂骨架_螺旋体25%_墙4底5_竖打",
"关节": "J3",
"A": 1516.920000000001,
"I": 1131945.0546960002,
"c": 35.00000010000099,
"A": 1775.9400000000005,
"I": 1218698.119611809,
"c": 35.36460360574915,
"bend": 1,
"centroid": [
19.499999999999993,
119.46110407931855
19.864603505749066,
125.42235013570274
],
"M": 5824.4734611263175,
"F": 28.801398145740663,
"sigma": 0.1800940521592921,
"tau": 0.028480142142374657,
"L": 287.98631358672816,
"defl": 0.07112524430753915,
"M": 5838.021996079116,
"F": 28.210271278866706,
"sigma": 0.16940974176504492,
"tau": 0.023827047602002344,
"L": 290.61713304781716,
"defl": 0.06743118064861804,
"bend_axis": 1,
"SF": 66.63185072534252
"SF": 70.83417916215734
},
{
"截面": "大臂骨架抱箍 @z=325",
"零件": "P06_大臂骨架_螺旋体25%_墙4底5_竖打",
"关节": "J4",
"A": 562.2200000000005,
"I": 255126.52178866687,
"c": 34.31311712541806,
"A": 719.8700000000005,
"I": 364721.10908443725,
"c": 34.9936863254125,
"bend": 1,
"centroid": [
19.499999999999996,
100.98098075486462
20.18056919999444,
113.54348979676884
],
"M": 4674.1717086407425,
"F": 23.115225549656877,
"sigma": 0.6286504444087546,
"tau": 0.06167130006845235,
"L": 259.0371452768405,
"defl": 0.20489053402473298,
"M": 4766.261568803768,
"F": 22.395162749075034,
"sigma": 0.4573057553544944,
"tau": 0.046665014688224996,
"L": 264.40855086080256,
"defl": 0.15227082967198685,
"bend_axis": 1,
"SF": 19.088509531375568
"SF": 26.240649411240923
},
{
"截面": "大臂骨架过渡块 @z=365",
"零件": "P06_大臂骨架_螺旋体25%_墙4底5_竖打",
"关节": "J3",
"A": 4458.1709500000015,
"I": 1812470.99216772,
"c": 35.000057416518544,
"bend": 1,
"centroid": [
19.49994268348248,
124.61008050936232
],
"M": 6138.388109490228,
"F": 28.210271278866706,
"sigma": 0.11853648262810473,
"tau": 0.009491651933692682,
"L": 303.0677649681537,
"defl": 0.0518455542166412,
"bend_axis": 1,
"SF": 101.23465564309588
},
{
"截面": "小臂骨架中段 @z=245",
"零件": "P07_小臂骨架_螺旋体30%_墙5底5_竖打",
"关节": "J4",
"A": 1688.4280000000003,
"I": 1225116.8186629335,
"c": 35.00000010000015,
"A": 1955.5180000000007,
"I": 1288379.7804570175,
"c": 35.135670341849114,
"bend": 1,
"centroid": [
19.5,
120.60880570566229
19.364329758151037,
125.86408670234685
],
"M": 3519.6618136307306,
"F": 23.115225549656877,
"sigma": 0.10055217751682426,
"tau": 0.020535574110643336,
"L": 199.5734012002831,
"defl": 0.019071177205131774,
"M": 3553.2419643052863,
"F": 22.395162749075034,
"sigma": 0.09690119341858144,
"tau": 0.017178437694571225,
"L": 202.73130161742685,
"defl": 0.01889170908201219,
"bend_axis": 1,
"SF": 119.34102568780449
"SF": 123.83748410779553
},
{
"截面": "小臂骨架抱箍 @z=195",
"零件": "P07_小臂骨架_螺旋体30%_墙5底5_竖打",
"关节": "J5",
"A": 381.97200000000026,
"I": 75885.39226040008,
"c": 22.39173964832456,
"A": 590.9720000000002,
"I": 114405.68067274656,
"c": 22.728382247297116,
"bend": 1,
"centroid": [
19.5,
103.25743876514508
19.163357401027465,
122.73950864000324
],
"M": 2244.921562213841,
"F": 16.321781071344514,
"sigma": 0.6624160151865485,
"tau": 0.06409546146580575,
"L": 159.53235542447365,
"defl": 0.1254842711067732,
"M": 2499.415698384399,
"F": 16.14463396798306,
"sigma": 0.4965459324548058,
"tau": 0.04097816978126643,
"L": 172.60323575097078,
"defl": 0.10847696343914033,
"bend_axis": 1,
"SF": 18.11550404109807
"SF": 24.16694854527321
},
{
"截面": "小臂骨架减重槽 @z=270",
"零件": "P07_小臂骨架_螺旋体30%_墙5底5_竖打",
"关节": "J4",
"A": 3453.8644,
"I": 1944097.2364364942,
"c": 35.33095339393962,
"bend": 1,
"centroid": [
19.16904670606061,
136.30953293939396
],
"M": 3978.6632379954935,
"F": 22.395162749075034,
"sigma": 0.07230603634284398,
"tau": 0.009726132885706963,
"L": 225.66589175250482,
"defl": 0.01736999898793822,
"bend_axis": 1,
"SF": 165.96124759350914
},
{
"截面": "小臂骨架减重槽口 @z=280",
"零件": "P07_小臂骨架_螺旋体30%_墙5底5_竖打",
"关节": "J4",
"A": 1556.8381000000002,
"I": 671763.1534315376,
"c": 35.04697412971099,
"bend": 1,
"centroid": [
19.453025976175688,
162.82490726385316
],
"M": 4587.022676232362,
"F": 22.395162749075034,
"sigma": 0.23931241873732387,
"tau": 0.021577544976328973,
"L": 248.71544110712577,
"defl": 0.07039939955948334,
"bend_axis": 1,
"SF": 50.14365766438365
},
{
"截面": "腕偏转座根部 @z=150",
@@ -215,40 +278,40 @@
"关节": "J5",
"A": 877.5228499999998,
"I": 452897.5474791437,
"c": 35.00001954545841,
"c": 35.0000195454584,
"bend": 1,
"centroid": [
19.499980554542446,
153.23437984777257
],
"M": 2445.425561814797,
"F": 16.321781071344514,
"sigma": 0.18898301158149458,
"tau": 0.02789975395742319,
"M": 2430.9020288202305,
"F": 16.14463396798306,
"sigma": 0.1878606298386295,
"tau": 0.027596946281198943,
"L": 164.6512204903413,
"defl": 0.02439681521795845,
"defl": 0.024251921030086537,
"bend_axis": 1,
"SF": 63.497771040786255
"SF": 63.87714131645298
},
{
"截面": "工具法兰 @z=107",
"零件": "P09_工具法兰_螺旋体100%_墙4底5_竖打",
"零件": "P09_工具法兰_螺旋体30%_墙4底5_竖打",
"关节": "J6",
"A": 3734.962150000003,
"I": 1169654.3875857776,
"c": 35.0000685148389,
"A": 3674.473750000001,
"I": 1154943.4472954571,
"c": 35.000056371141866,
"bend": 1,
"centroid": [
19.499931585161203,
127.9986725177389
19.49994372885823,
127.99865066501017
],
"M": 1390.8472530369402,
"F": 10.722602788827801,
"sigma": 0.04161891723455653,
"tau": 0.004306309819830889,
"L": 130.23020962176682,
"defl": 0.0033611981177080657,
"M": 1389.8230238810052,
"F": 10.71458030870356,
"sigma": 0.04211797927912047,
"tau": 0.004373924419260129,
"L": 130.23018996182086,
"defl": 0.0034015031734828767,
"bend_axis": 1,
"SF": 288.3304227346958
"SF": 284.91395374110147
}
]
+56 -56
View File
@@ -12,91 +12,91 @@
"J1": {
"cn": "肩抬举",
"axis": "roll(绕X)",
"distal_mass_kg": 2.127,
"arm_mass_kg": 1.627,
"weight_N": 20.9,
"distal_mass_kg": 2.019,
"arm_mass_kg": 1.519,
"weight_N": 19.8,
"reach_tip_mm": 512.0,
"tau_gravity_Nm": 6.137,
"tau_gravity_arm_only_Nm": 3.84,
"I_distal_kgm2": 0.28468,
"tau_dynamic_Nm": 1.423,
"tau_required_static_Nm": 6.137,
"tau_required_peak_Nm": 7.561,
"duty_vs_Tcont_pct": 245.5,
"duty_vs_Tallow_pct": 306.9,
"peak_margin_x": 1.06,
"tau_gravity_Nm": 5.97,
"tau_gravity_arm_only_Nm": 3.673,
"I_distal_kgm2": 0.27885,
"tau_dynamic_Nm": 1.394,
"tau_required_static_Nm": 5.97,
"tau_required_peak_Nm": 7.364,
"duty_vs_Tcont_pct": 238.8,
"duty_vs_Tallow_pct": 298.5,
"peak_margin_x": 1.09,
"status": "OVERLOAD"
},
"J2": {
"cn": "肩俯仰",
"axis": "pitch(绕Y)",
"distal_mass_kg": 1.753,
"arm_mass_kg": 1.253,
"weight_N": 17.2,
"distal_mass_kg": 1.688,
"arm_mass_kg": 1.188,
"weight_N": 16.6,
"reach_tip_mm": 394.0,
"tau_gravity_Nm": 4.395,
"tau_gravity_arm_only_Nm": 2.478,
"I_distal_kgm2": 0.14623,
"tau_dynamic_Nm": 0.731,
"tau_required_static_Nm": 4.395,
"tau_required_peak_Nm": 5.126,
"duty_vs_Tcont_pct": 175.8,
"duty_vs_Tallow_pct": 219.8,
"peak_margin_x": 1.56,
"tau_gravity_Nm": 4.29,
"tau_gravity_arm_only_Nm": 2.372,
"I_distal_kgm2": 0.14396,
"tau_dynamic_Nm": 0.72,
"tau_required_static_Nm": 4.29,
"tau_required_peak_Nm": 5.009,
"duty_vs_Tcont_pct": 171.6,
"duty_vs_Tallow_pct": 214.5,
"peak_margin_x": 1.6,
"status": "OVERLOAD"
},
"J3": {
"cn": "大臂自转",
"axis": "roll(绕Z)",
"distal_mass_kg": 1.468,
"arm_mass_kg": 0.968,
"weight_N": 14.4,
"distal_mass_kg": 1.411,
"arm_mass_kg": 0.911,
"weight_N": 13.8,
"reach_tip_mm": 250.0,
"tau_gravity_Nm": 1.712,
"tau_gravity_arm_only_Nm": 0.742,
"I_distal_kgm2": 0.03871,
"tau_dynamic_Nm": 0.194,
"tau_required_static_Nm": 1.712,
"tau_required_peak_Nm": 1.906,
"duty_vs_Tcont_pct": 68.5,
"duty_vs_Tallow_pct": 85.6,
"peak_margin_x": 4.2,
"tau_gravity_Nm": 1.691,
"tau_gravity_arm_only_Nm": 0.721,
"I_distal_kgm2": 0.03855,
"tau_dynamic_Nm": 0.193,
"tau_required_static_Nm": 1.691,
"tau_required_peak_Nm": 1.884,
"duty_vs_Tcont_pct": 67.6,
"duty_vs_Tallow_pct": 84.5,
"peak_margin_x": 4.25,
"status": "OK"
},
"J4": {
"cn": "肘部俯仰",
"axis": "pitch(绕Y)",
"distal_mass_kg": 1.178,
"arm_mass_kg": 0.678,
"weight_N": 11.6,
"distal_mass_kg": 1.133,
"arm_mass_kg": 0.633,
"weight_N": 11.1,
"reach_tip_mm": 241.0,
"tau_gravity_Nm": 2.062,
"tau_gravity_arm_only_Nm": 0.89,
"I_distal_kgm2": 0.04258,
"tau_dynamic_Nm": 0.213,
"tau_required_static_Nm": 2.062,
"tau_required_peak_Nm": 2.275,
"duty_vs_Tcont_pct": 82.5,
"duty_vs_Tallow_pct": 103.1,
"peak_margin_x": 3.52,
"tau_gravity_Nm": 2.036,
"tau_gravity_arm_only_Nm": 0.864,
"I_distal_kgm2": 0.04239,
"tau_dynamic_Nm": 0.212,
"tau_required_static_Nm": 2.036,
"tau_required_peak_Nm": 2.248,
"duty_vs_Tcont_pct": 81.4,
"duty_vs_Tallow_pct": 101.8,
"peak_margin_x": 3.56,
"status": "OVERLOAD"
},
"J5": {
"cn": "腕部俯仰",
"axis": "pitch(绕Y)",
"distal_mass_kg": 0.832,
"arm_mass_kg": 0.332,
"weight_N": 8.2,
"distal_mass_kg": 0.823,
"arm_mass_kg": 0.323,
"weight_N": 8.1,
"reach_tip_mm": 132.0,
"tau_gravity_Nm": 0.863,
"tau_gravity_Nm": 0.862,
"tau_gravity_arm_only_Nm": 0.23,
"I_distal_kgm2": 0.01018,
"I_distal_kgm2": 0.01019,
"tau_dynamic_Nm": 0.051,
"tau_required_static_Nm": 0.863,
"tau_required_peak_Nm": 0.914,
"tau_required_static_Nm": 0.862,
"tau_required_peak_Nm": 0.913,
"duty_vs_Tcont_pct": 34.5,
"duty_vs_Tallow_pct": 43.1,
"peak_margin_x": 8.75,
"peak_margin_x": 8.76,
"status": "OK"
},
"J6": {
+12 -12
View File
@@ -13,11 +13,11 @@
| 关节 | 名称 | 轴 | 以下质量(kg) | TCP力臂(mm) | 重力力矩(N·m) | 惯性力矩(N·m) | 峰值需求(N·m) | 负荷率(对T_allow) | 峰值裕度 | 判定 |
|:---:|:---|:---|---:|---:|---:|---:|---:|---:|---:|:---:|
| J1 | 肩抬举 | roll(绕X) | 2.13 | 512 | **6.14** | 1.42 | 7.56 | **307%** | 1.06× | OVERLOAD |
| J2 | 肩俯仰 | pitch(绕Y) | 1.75 | 394 | **4.39** | 0.73 | 5.13 | **220%** | 1.56× | OVERLOAD |
| J3 | 大臂自转 | roll(绕Z) | 1.47 | 250 | **1.71** | 0.19 | 1.91 | **86%** | 4.20× | OK |
| J4 | 肘部俯仰 | pitch(绕Y) | 1.18 | 241 | **2.06** | 0.21 | 2.27 | **103%** | 3.52× | OVERLOAD |
| J5 | 腕部俯仰 | pitch(绕Y) | 0.83 | 132 | **0.86** | 0.05 | 0.91 | **43%** | 8.75× | OK |
| J1 | 肩抬举 | roll(绕X) | 2.02 | 512 | **5.97** | 1.39 | 7.36 | **298%** | 1.09× | OVERLOAD |
| J2 | 肩俯仰 | pitch(绕Y) | 1.69 | 394 | **4.29** | 0.72 | 5.01 | **214%** | 1.60× | OVERLOAD |
| J3 | 大臂自转 | roll(绕Z) | 1.41 | 250 | **1.69** | 0.19 | 1.88 | **84%** | 4.25× | OK |
| J4 | 肘部俯仰 | pitch(绕Y) | 1.13 | 241 | **2.04** | 0.21 | 2.25 | **102%** | 3.56× | OVERLOAD |
| J5 | 腕部俯仰 | pitch(绕Y) | 0.82 | 132 | **0.86** | 0.05 | 0.91 | **43%** | 8.76× | OK |
| J6 | 腕自转 | roll(绕Z) | 0.55 | 83 | **0.00** | 0.00 | 0.00 | **0%** | — | OK |
## 3. 许用连续工况下的末端负载能力
@@ -26,10 +26,10 @@
| 关节 | 仅臂身重力力矩(N·m) | 余额(N·m) | 可挂负载(kg) |
|:---:|---:|---:|---:|
| J1 | 3.84 | -1.84 | 0.00 |
| J2 | 2.48 | -0.48 | 0.00 |
| J3 | 0.74 | +1.26 | 0.51 |
| J4 | 0.89 | +1.11 | 0.47 |
| J1 | 3.67 | -1.67 | 0.00 |
| J2 | 2.37 | -0.37 | 0.00 |
| J3 | 0.72 | +1.28 | 0.52 |
| J4 | 0.86 | +1.14 | 0.48 |
| J5 | 0.23 | +1.77 | 1.37 |
| J6 | 0.00 | +2.00 | 2.46 |
@@ -38,9 +38,9 @@
## 4. 结论要点
- **超载关节:J1、J2、J4**。
- J1(肩抬举)重力 6.14 N·m,超 `T_allow` 207%;即使按峰值堵转算,裕度也只有 1.06×。
- J2(肩俯仰)重力 4.39 N·m,超 `T_allow` 120%;即使按峰值堵转算,裕度也只有 1.56×。
- J4(肘部俯仰)重力 2.06 N·m,超 `T_allow` 3%;即使按峰值堵转算,裕度也只有 3.52×。
- J1(肩抬举)重力 5.97 N·m,超 `T_allow` 198%;即使按峰值堵转算,裕度也只有 1.09×。
- J2(肩俯仰)重力 4.29 N·m,超 `T_allow` 114%;即使按峰值堵转算,裕度也只有 1.60×。
- J4(肘部俯仰)重力 2.04 N·m,超 `T_allow` 2%;即使按峰值堵转算,裕度也只有 3.56×。
- 结论对电机参数高度敏感:若实际连续额定高于假定值,请用 `ARMT_CONT` / `ARMT_ALLOW` 覆盖后重跑本脚本。
- 惯量按 Σm·d² 估,**未计入零件自身转动惯量**,对肩/肘是大臂量级的关节约低估一成,惯性力矩一栏应视为下限。
- 本版不含被动弹簧平衡。旧版报告提出的 J2 拉簧预补偿(2.48 N·m)在几何上**尚未实现**;若要保留该方案,需要在 P04/P05 之间增加拉簧连杆机构。
+4 -4
View File
@@ -9,11 +9,11 @@
},
{
"axis": "J2",
"lo": -106.0,
"lo": -105.0,
"hi": 9.0,
"continuous": false,
"zero": 0.0,
"note": "正向 +9° / 反向 -106°,总行程 115°(全对全动态扫描验证,含相邻件与电机)"
"note": "正向 +9° / 反向 -105°,总行程 114°(全对全动态扫描验证,含相邻件与电机)"
},
{
"axis": "J3",
@@ -33,11 +33,11 @@
},
{
"axis": "J5",
"lo": -95.0,
"lo": -93.0,
"hi": 34.0,
"continuous": false,
"zero": 0.0,
"note": "正向 +34° / 反向 -95°,总行程 129°(全对全动态扫描验证,含相邻件与电机)"
"note": "正向 +34° / 反向 -93°,总行程 127°(全对全动态扫描验证,含相邻件与电机)"
},
{
"axis": "J6",
+48 -38
View File
@@ -1,5 +1,5 @@
{
"generated": "2026-10-02 12:06:22",
"generated": "2026-10-04 02:00:11",
"vol_min": 2.0,
"step": 2.0,
"combo_step": 6.0,
@@ -12,17 +12,17 @@
"jams": {}
},
"J2": {
"lo": -107.5,
"lo": -106.25,
"hi": 10.38,
"jams": {
"12.0": [
[
"P01_A_立柱下段_三角形30%_墙4底5_竖打",
"P08_腕偏转座_螺旋体25%_墙4底5_竖打",
2557.1,
2401.7,
[
-52.9,
137.7,
149.9,
149.2
]
]
@@ -31,11 +31,11 @@
[
"P04_肩叉_螺旋体30%_墙5底5_竖打",
"P05_大臂旋转座_螺旋体30%_墙4底5_竖打",
16.5,
175.1,
[
46.8,
136.3,
534.6
141.0,
532.9
]
]
]
@@ -47,13 +47,13 @@
"jams": {}
},
"J4": {
"lo": -112.5,
"lo": -112.62,
"hi": 17.88,
"jams": {
"18.0": [
[
"P01_A_立柱下段_三角形30%_墙4底5_竖打",
"P09_工具法兰_螺旋体100%_墙4底5_竖打",
"P09_工具法兰_螺旋体30%_墙4底5_竖打",
6.2,
[
-82.0,
@@ -64,26 +64,26 @@
],
"-114.0": [
[
"P06_大臂骨架_螺旋体25%_墙4底5_竖打",
"P05_大臂旋转座_螺旋体30%_墙4底5_竖打",
"P07_小臂骨架_螺旋体30%_墙5底5_竖打",
56.3,
30.3,
[
19.5,
53.6,
128.0,
383.1
384.9
]
]
]
}
},
"J5": {
"lo": -96.25,
"lo": -94.75,
"hi": 35.75,
"jams": {
"36.0": [
[
"P01_A_立柱下段_三角形30%_墙4底5_竖打",
"P09_工具法兰_螺旋体100%_墙4底5_竖打",
"P09_工具法兰_螺旋体30%_墙4底5_竖打",
11.4,
[
-82.2,
@@ -92,15 +92,15 @@
]
]
],
"-98.0": [
"-96.0": [
[
"P07_小臂骨架_螺旋体30%_墙5底5_竖打",
"P08_腕偏转座_螺旋体25%_墙4底5_竖打",
328.2,
144.8,
[
59.4,
139.3,
227.9
55.5,
139.7,
257.0
]
]
]
@@ -129,7 +129,7 @@
"hold_at": -180.0,
"swept": "J2",
"direction": "lo",
"old": -107.5,
"old": -106.25,
"new": -103.0,
"jams": {}
},
@@ -159,7 +159,7 @@
"hold_at": -180.0,
"swept": "J4",
"direction": "lo",
"old": -112.5,
"old": -112.625,
"new": -109.0,
"jams": {}
},
@@ -173,6 +173,16 @@
"new": 29.0,
"jams": {}
},
{
"pass_": 0,
"hold": "J1",
"hold_at": -180.0,
"swept": "J5",
"direction": "lo",
"old": -94.75,
"new": -91.0,
"jams": {}
},
{
"pass_": 0,
"hold": "J1",
@@ -315,7 +325,7 @@
"-18.0": [
[
"P01_A_立柱下段_三角形30%_墙4底5_竖打",
"P09_工具法兰_螺旋体100%_墙4底5_竖打",
"P09_工具法兰_螺旋体30%_墙4底5_竖打",
6.2,
[
-82.0,
@@ -338,7 +348,7 @@
"-18.0": [
[
"P01_A_立柱下段_三角形30%_墙4底5_竖打",
"P09_工具法兰_螺旋体100%_墙4底5_竖打",
"P09_工具法兰_螺旋体30%_墙4底5_竖打",
6.2,
[
-82.0,
@@ -361,7 +371,7 @@
"-36.0": [
[
"P01_A_立柱下段_三角形30%_墙4底5_竖打",
"P09_工具法兰_螺旋体100%_墙4底5_竖打",
"P09_工具法兰_螺旋体30%_墙4底5_竖打",
11.4,
[
-82.2,
@@ -378,13 +388,13 @@
"hold_at": -180.0,
"swept": "J5",
"direction": "lo",
"old": -96.25,
"old": -91.0,
"new": -36.625,
"jams": {
"-36.0": [
[
"P01_A_立柱下段_三角形30%_墙4底5_竖打",
"P09_工具法兰_螺旋体100%_墙4底5_竖打",
"P09_工具法兰_螺旋体30%_墙4底5_竖打",
11.4,
[
-82.2,
@@ -437,7 +447,7 @@
"-18.0": [
[
"P01_A_立柱下段_三角形30%_墙4底5_竖打",
"P09_工具法兰_螺旋体100%_墙4底5_竖打",
"P09_工具法兰_螺旋体30%_墙4底5_竖打",
186.7,
[
-83.0,
@@ -460,7 +470,7 @@
"-18.0": [
[
"P01_A_立柱下段_三角形30%_墙4底5_竖打",
"P09_工具法兰_螺旋体100%_墙4底5_竖打",
"P09_工具法兰_螺旋体30%_墙4底5_竖打",
186.7,
[
-83.0,
@@ -483,7 +493,7 @@
"-36.0": [
[
"P01_A_立柱下段_三角形30%_墙4底5_竖打",
"P09_工具法兰_螺旋体100%_墙4底5_竖打",
"P09_工具法兰_螺旋体30%_墙4底5_竖打",
2.5,
[
-82.0,
@@ -536,7 +546,7 @@
"162.0": [
[
"P01_A_立柱下段_三角形30%_墙4底5_竖打",
"P09_工具法兰_螺旋体100%_墙4底5_竖打",
"P09_工具法兰_螺旋体30%_墙4底5_竖打",
52.0,
[
-82.4,
@@ -548,7 +558,7 @@
"-162.0": [
[
"P01_A_立柱下段_三角形30%_墙4底5_竖打",
"P09_工具法兰_螺旋体100%_墙4底5_竖打",
"P09_工具法兰_螺旋体30%_墙4底5_竖打",
52.0,
[
-82.4,
@@ -571,7 +581,7 @@
"162.0": [
[
"P01_A_立柱下段_三角形30%_墙4底5_竖打",
"P09_工具法兰_螺旋体100%_墙4底5_竖打",
"P09_工具法兰_螺旋体30%_墙4底5_竖打",
52.0,
[
-82.4,
@@ -583,7 +593,7 @@
"-162.0": [
[
"P01_A_立柱下段_三角形30%_墙4底5_竖打",
"P09_工具法兰_螺旋体100%_墙4底5_竖打",
"P09_工具法兰_螺旋体30%_墙4底5_竖打",
52.0,
[
-82.4,
@@ -1246,11 +1256,11 @@
},
{
"axis": "J2",
"lo": -106.0,
"lo": -105.0,
"hi": 9.0,
"continuous": false,
"zero": 0.0,
"note": "正向 +9° / 反向 -106°,总行程 115°(全对全动态扫描验证,含相邻件与电机)"
"note": "正向 +9° / 反向 -105°,总行程 114°(全对全动态扫描验证,含相邻件与电机)"
},
{
"axis": "J3",
@@ -1270,11 +1280,11 @@
},
{
"axis": "J5",
"lo": -95.0,
"lo": -93.0,
"hi": 34.0,
"continuous": false,
"zero": 0.0,
"note": "正向 +34° / 反向 -95°,总行程 129°(全对全动态扫描验证,含相邻件与电机)"
"note": "正向 +34° / 反向 -93°,总行程 127°(全对全动态扫描验证,含相邻件与电机)"
},
{
"axis": "J6",
File diff suppressed because one or more lines are too long
+318
View File
@@ -0,0 +1,318 @@
# 机器人手臂:运动学模拟与碰撞算法详解
> 本文基于 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/` | 本文全部算法示意图源文件 |