Files
Robot_Arm/arm/cad/parts/p06_humerus.py
T
cbb 649bd69810 refactor(layout): src/ 收成顶层包 arm/,入口统一 python -m;提升承担构建的 scratch 脚本
单文件约束取消后,Python 侧没必要再靠 16 处 sys.path.insert 维持扁平模块互认。
现在按职责分包:
  arm/paths.py 路径唯一来源(含新增 CACHE、TPL)
  arm/constants.py 实测接口数值(原 armcore 的 M/C/CLR/FIT/MOTOR_TOL 段)
  arm/cad/{helpers,arm_build,part_tools,parts/} 几何与装配
  arm/kinematics.py(原 human_layout.py,唯一运动学事实源)
  arm/analysis/{sweep_v2,strength_check,torque_balance,finalize_travel,ws_sweep,gen_env_mesh}
  arm/dataio/{screw_mesh,draw_sheet}   arm/viewer/{build_viewer,shot}
  arm/build_model.py(原 arm_model.py)

顺带修掉的既有缺陷(都是实测撞出来的,不是预防性改动):
- arm_model 第二次 sys.path 跳到 gitignore 的 scratch/ 去 import 构建必需的包络脚本:
  ws_sweep/gen_env_mesh 提升进 arm/analysis/,缓存挪到 data/cache/(gitignore,可重生成)。
- finalize_travel / draw_sheet 顶层就是"算完写文件",一次 importlib 冒烟测试就把
  docs/travel.json 重写了一遍;现在被 import 直接抛错。
- strength_check 用 OUT.replace("/out","/web") 靠字符串形状推路径,改成直接用 WEB。
- build_viewer 里 WORKSPACE 使用点漏改导致 NameError —— 已由构建关卡抓到并修掉。

os._exit 的处置与计划里的假设相反:实测光是 `python -c "import arm.cad.helpers"`
就会在解释器退出时段错误(exit 139,OCC 静态析构),全流程跑完也一样。所以入口
脚本尾部保留 flush + os._exit,并把原因写进注释;被 import 时不生效。

关卡(tests/):
- verify.py 一条命令跑三层:产物 md5、IK 数值 exact 段、截图。
- 截图基线重做并分级:同一构建连跑两轮实测 102 张逐字节可复现(要求 md5 相同),
  3 张线束 x-ray 不可复现(mean<=0.0017、无像素差>8)改为与参考像素比容差;
  这 3 张不再入库,免得每次跑关卡都脏 git。
- 新增 ?view=sheet / ?sheet=N / ?theme / ?guide=N 四个 URL 参数:工程图与装配向导
  占了约一半 CSS 和 700 行 JS,原先没有任何截图覆盖,拆它们等于没有关卡。
- 关卡灵敏度已验:改坏 IK 基线一个数 -> FAIL;换旧版求解器 -> 5 处 exact 差异。

实测:python -m arm.build_model 退出码 0,全部产物 md5 与基线一致,
105 视图截图全过,IK exact 段逐字节一致。
2026-10-15 03:31:20 +08:00

157 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""P06 大臂骨架:J3 滚转 -> J4 俯仰的 Loft 空心梁。"""
from arm.cad import arm_build as AB
import cadquery as cq
from arm.cad.helpers import cyl
from arm.cad.arm_build import COVER_Z, Ycyl, bx
from arm.kinematics import X_ARM, Y_AXIS, Y_COVER, Y_REAR, Z_EL, Z_R3
from arm.cad.part_tools import (
CLAMP_BACKOFF, CLAMP_CLR, CLAMP_T, cut_motor_fence, hub_m2_holes,
make_surface_tie_bridge_neg_x, motor_front_clamp,
)
P06_WIN_XZ = [(3.0, 362.0), (37.0, 362.0), (37.0, 371.0),
(28.0, 374.0), (12.0, 374.0), (3.0, 371.0)] # 大臂过渡块
P06_HOLLOW_WIN = False # 2026-10-03 起关闭:拱形窗正面看是贯通黑洞,不雅观(详见 p_upper_bone 5c)
def p_humerus_bone(j3, j4):
"""P06_大臂骨架_螺旋体25%_墙4底5_竖打 (Humerus Bone - Loft 圆弧转方有机过渡版):
受 J3 (大臂自转, -Z轴, hub@Z_HUB3=380) 驱动,
向下延伸构成大臂主体骨架,底端抱紧 M4 (肘部俯仰电机, +Y轴, 轴线位于 Z_EL=332.0)。
1. 借鉴 P05 大臂旋转座几何放样算法:
- 顶端结合面 (Z = 383.0mm):与 J3 轴径严格相切的 Ø70mm 正圆形轮廓,彻底消除原先外突的方形水平阶梯;
- 底端结合面 (Z = 367.0mm):平滑展开融入 M4 电机套筒的 70x63.5mm 方形截面;
- 过渡区 (Z: 367.0..383.0mm):采用 Hermite s(t) = 3t² - 2t³ 驱动 20 层放样切片连续渐变;
2. 下部抱紧套筒与安装板 (R=35mm),电机定子 100% 裸露散热开窗;
3.【2026-10-03】J3 毂面整体后移 CLAMP_BACKOFF=5.5(顶端 388.5→383.0,直锁凸台
59.5→65.0):P05 新增 M3 前端压盖,占 Z 384..389,原先 388.5 的毂面只剩 0.5mm
让位放不下压盖。配方与 Y 轴三处(J2/J4/J5 令从动法兰盘 Y157→162)完全同源:
压盖 5.0 + 动间隙 1.0 - 原基准 0.5 = 5.5。凸台同步加长,3xS01 输出螺钉的头面
台阶(局部 s=58)、杆长、咬合量与改前完全一致,J3 直锁与零干涉不受影响。
压盖中央 Ø37 让位孔(R18.5)也让开 R17.8 凸台,单边余 0.7mm。
4.【2026-10-09】−X 侧线束走廊加一座外凸扎带拱桥 (y=102.5, Z=345):站出 3.2mm、桥下
3.2×1.8 穿带隧道沿 Y 贯通(与 P03/P04 同配方)。走廊线心随之由 x=-17.5 提到 -20.7:
原线皮离本件 −X 面只剩 0.2mm,扎带没有容身处。
5. 严格单实体 (solids = 1) 且与上下级旋转构件及电机实现动态 0 碰撞干涉。
"""
# 1. M4 底部抱紧套筒与后端安装板 (R=35mm)
rear_m4 = Ycyl(35.0, Y_REAR - 8.0, Y_REAR, X_ARM, Z_EL)
front_m4 = Ycyl(35.0, Y_COVER - 6.0, Y_COVER - 0.5, X_ARM, Z_EL)
s = rear_m4.fuse(front_m4)
# 顶端结合面与过渡高度
# z_top 由 M3 前端盖外端面反推:压盖占 [Z_COVER-CLAMP_T, Z_COVER],再留 CLAMP_CLR
# 动间隙 -> 383.0(改前 388.5,与前端盖只有 0.5mm,放不下压盖)。
z_cover3 = Z_R3 - COVER_Z # 389.0 M3 前端盖外端面
z_top = z_cover3 - CLAMP_T - CLAMP_CLR # 383.0 J3 毂面(= 388.5 - CLAMP_BACKOFF)
z_bot = Z_EL + 35.0 # 367.0
# 2. 下部实心基座块 (Z: Z_EL..z_bot + 0.5)
lower_block = bx(X_ARM - 35.0, X_ARM + 35.0, Y_REAR - 8.0, Y_COVER - 0.5, Z_EL, z_bot + 0.5)
s = s.fuse(lower_block)
# 3. 借鉴 P05 的 Loft 放样过渡体:圆弧变方形(20 层平滑放样,保持严格对称 4 段式截面拓扑)
N = 20
wp = cq.Workplane("XY")
for i in range(N + 1):
t = i / float(N)
z_curr = z_bot + t * (z_top - z_bot)
st = 3.0 * (t**2) - 2.0 * (t**3)
# 前侧:t=0 为平直面 (Y=155.5, v=27.5),t=1 渐变为与 Ø70 正切的圆弧顶点 (Y=163.0, v=35.0)
v_sf = 27.5 * (1.0 - st) + 0.05 * st
v_cf = max(27.5 * (1.0 - st) + 35.0 * st, v_sf + 0.05)
# 后侧:从 t=0 的平直面 (Y=92.0, v=-36.0) 渐变到 t=1 的圆弧 (Y=93.0, v=-35.0)
v_sr = -36.0 * (1.0 - st) - 0.05 * st
v_cr = min(-36.0 * (1.0 - st) - 35.0 * st, v_sr - 0.05)
w = (cq.Workplane("XY", origin=(X_ARM, Y_AXIS, z_curr))
.moveTo(-35.0, v_sf)
.threePointArc((0.0, v_cf), (35.0, v_sf))
.lineTo(35.0, v_sr)
.threePointArc((0.0, v_cr), (-35.0, v_sr))
.close())
wp = wp.add(w)
loft = wp.toPending().loft()
s = s.fuse(loft.val())
# 4. 电机中间定子散热裸露切除 (100% 裸露)
# 【2026-10-05f】原 Ycyl(30.5, ...) 定子散热切除已删:主腔统一 BAY_R=30.50
# 且从承压面挖到前端,这刀完全落在主腔内,属同段腔被两个半径各挖一次的残留。
# 通风由敞口盒切实现,不依赖它。
# 5. M4 接口成型
# 【2026-10-03i 接缝整改】front_collar_pocket 已删,理由同 p_shoulder_roll_base:
# 它与 Joint.pocket 切的是同一根 BAY_R 圆柱,是后者的子集,两次同半径布尔
# 相减会在腔壁上留下 s=50.0 的零宽棱(本件实测 R30.500 裂成 5168.4 + 493.9 两张面)。
s = j4.rear_mount_holes(s, t_plate=8.0, hole_d=2.8, cb_d=5.5, cb_depth=4.0)
s = j4.pocket(s)
# 【2026-10-07 修复:仅切除敞口侧承压面环墙,保护侧壁杜绝缺口漏螺丝】
# 仿照 P05 设计,只在敞口半侧(局部 y>=0)将承压面段(z: -REAR_SEAT..0)切平,
# 彻底消除阻碍电机装配的凸起围挡;臂身侧壁(局部 y<0)绝对不动,杜绝切穿侧壁留下缺口!
s = s.cut(j4.L(bx(-40.0, 40.0, 0.0, 40.0, -AB.REAR_SEAT, 0.0)))
# 5b. 【2026-10-02 新增】M4 前端压盖 4xM3x8(BCD55):M4 原来只被后端板单面锁死,
# 大扭矩下会绕 J4 轴在 Ø60.3 座孔里点头。现在前端也面压夹一次(见 motor_front_clamp)。
# 熔接放在所有 R30.15~30.5 圆柱刀具之后,理由同 p_fork 7b。P07 的 J4 从动侧
# 已相应后移到 Y>=162(见 p_forearm_bone 的 J4_CLEAR),与压盖前端面留 1.0mm。
s = s.fuse(motor_front_clamp(X_ARM, Z_EL, tag="P06/M4"))
# 5b2.【2026-10-05e / 2026-10-07 修复】删掉前端"围挡"在敞口侧的那半圈环墙(见 FENCE_OPEN_CUT)。
s = cut_motor_fence(s, j4, "P06/M4", "P06")
# 5c. 【2026-10-02 新增】电机上方过渡块减重窗(见 hollow_tent_window)。
# Z360..385 这段 Loft 过渡块壁厚最深 15.8mm,是 P06 唯一一块可掏的厚料
# (Z332..367 的下部本体已经被 Ø61 定子孔掏成 4.5mm 薄壁,没得再掏)。
# 但它正压在 J3 毂下面,所以三条红线:
# 1) 窗顶最高只到 Z374 —— J3 的 Ø70 毂面在 Z380..388.5,窗顶留 6mm 整块
# 实心托住毂面承压,绝不碰毂面和它后面的 3xS01 直锁螺钉;
# 2) 窗的 X 范围 3..37 —— 截面本体 X-15..55,两侧各留 18mm 竖墙,
# 该高度上零件才不会被切成两半(必须单实体);
# 3) 拱形(下宽上窄)轮廓,斜面与水平 >= 52° 自支撑,只有顶部 X12..28 的
# 16mm 平顶需要桥接(与 P03 同量级)。
# 窗底取 Z362 而不是更低:Z360 附近是全段安全系数最低的截面,
# 抬高窗底 4mm 比横向收窄 8mm 更划算(减重量只少 3cm3,安全系数从 92% 回到 100%)。
# 窗沿 Y 双向切穿(Y87..172 超出本体,前后都贯通),顺带把这段的定子散热口
# 一起打开。
# 【2026-10-03 关闭】用户实机视点确认这个拱形窗在正面看就是一个贯通的矩形黑洞,
# 位于J4 关节正上方、整机最显眼的落脚点,视觉上像"零件破了个洞",非常不雅观。
# 权衡后恢复实心:代价只有 161->183cm3(+22cm3 / +27g PLA),而强度本来就已经是
# 无窗基线的 100%(这段本来就不缺强度),减重收益纯属锦上添花。
# 6. J3 直连法兰结构(凸台延伸至 65.0 跨过结合面牢固熔接,保持单实体与零干涉)
# 凸台随 J3 毂面一起后移 CLAMP_BACKOFF=5.5(59.5→65.0):进入电机输出毂的深度
# 仍是 s54.942..56.0 那 1.06mm,3xS01 的头面台阶(s=58)/杆长/咬合量一律不变。
boss = cyl(17.8, 54.942, 59.5 + CLAMP_BACKOFF)
chamfer_cone = cyl(18.5, 54.5, 54.942)
s = s.fuse(j3.L(boss)).cut(j3.L(chamfer_cone))
s = s.cut(j3.L(cyl(3.5, 54.0, 58.0 + 30.0)))
s = hub_m2_holes(s, j3, cb_z1=58.0 + 40.0)
# 9. −X 侧线束走廊扎带拱桥(与 P03/P04 同一配方:站出 3.2mm、桥下 3.2×1.8 穿带隧道、孔沿 Y)
# 走廊原来贴 x=-17.5 走,离本件 −X 面 (X_ARM-35 = -15.5) 只剩 2.0mm —— 线皮净空 0.2mm,
# 扎带没有容身处。桥站出后线心改到 x=-20.5(桥顶 -18.7 + 线半径 1.8),与 P03 舱段同一处理。
# 落点 Z=345 在 lower_block 的 −X 平面上 (Z332..367.5 / Y92..155.5):足迹 Z341..349 离块体
# 下缘 9.0mm、Y97.5..107.5 离 Y 边 5.5mm,实测平面度 0.00mm、背后 10.35mm 整实料(未打穿内腔)。
s = s.fuse(make_surface_tie_bridge_neg_x(X_ARM - 35.0, 102.5, 345.0,
w_y=10.0, l_z=8.0, h_x=3.2))
return s
JOINT = 'J3'
def specs(JS):
"""本件的 (件号, 中文名, 构建 thunk, 配色, 说明) 清单;顺序即 BOM 顺序。"""
j3 = JS["J3"]
j4 = JS["J4"]
return [
("P06_大臂骨架_螺旋体25%_墙4底5_竖打", "大臂骨架", lambda: p_humerus_bone(j3, j4), 0x0d9488,
"Loft有机过渡版:借鉴P05圆弧变方形放样算法,顶端Ø70正圆与J3平齐无台阶,底端渐变融入M4抱紧套筒与壳体;【2026-10-03】取消电机上方Loft过渡块(Z360~385)的拱形减重窗,恢复实心:该窗沿Y贯通,正面看是整机最显眼处的矩形黑洞(用户视觉整改),代价仅 +22cm3/+27g,强度仍为无窗基线100%;【2026-10-03】J3 毂面整体后移 5.5mm(顶端 388.5→383.0,直锁凸台 59.5→65.0):给 P05 新增的 M3 前端压盖(Z384..389)让位,留 1.0mm 动间隙——与 Y 轴三处(J2/J4/J5 令从动法兰盘 Y157→162)完全同源,换算式同为「压盖 5.0 + 动间隙 1.0 - 原基准 0.5」;凸台同步加长,3xS01 头面台阶(s=58)/杆长/咬合量与 J3 直锁关系一律不变;【2026-10-05 承压面让位腔改为 R35 圆腔、凸台与围挡一并消除】电机尾端垫片位(REAR_SEAT=2.6)用圆腔掏出承压面。**刀半径必须等于外壁 R35**(arm_build.SEAT_R):取小于 R35(曾试 32.2 = 64.4 方腔内切圆)时,刀半径与外壁之间那圈 2.8mm 环墙完整保留,而承压面只挖到刀半径,于是该环墙相对承压面【凸起】并把电机尾端面围住 —— 这就是用户截图里的一圈围挡(P08 实测 1536.9mm3)。它不是残留,而是 SEAT_R<R35 的必然产物。现 SEAT_R=35.0、SEAT_TOP=+1.0(越过尾端面 1mm,吃掉半包开口处那截0.052cm3 残片):承压面段整圈挖到外壁,承压面成为完整 R35 平圆盘,凸台与围挡一起消失,电机尾端面与承压面之间再无凸起。承力改由臂身本体承担(band 段 r36..58 有料方位 P04 10/36、其余各 15/36,其余方位靠 z>+1.0 杯体连接,无整圈通槽)。沉孔 z 区间(-8..-4)与圆腔(-2.6..+1.0)不重叠,S02 完好。连带:承压环由「方腔内切 32.2 − 沉孔外缘 30.25 = 1.95mm 窄环」变为「完整 R35 圆环 4.75mm」,受压面积 383->974mm²(2.5 倍)。【2026-10-04 承压面让位腔改为 32.2 圆腔】电机尾端垫片位(REAR_SEAT=2.6)之前用 64.4 **方**腔掏出来,方腔壁以外 z=-2.6..0 那圈料相对新承压面凸起 2.6mm,打印时是悬空薄壁(用户反馈:难打印、需支撑、公差差)。现承压面段改用**SEAT_R=32.2 圆腔**(= 64.4 方腔的内切圆半径,z=-2.6..0 与方腔平接)把整圈挖到承压面高度,凸台零残留;z=0 以上 PCBA 腔仍保持 64.4 方(PCB 是方的必须方腔)。**关键:刀半径必须取 32.2 而不是 R35** —— 中间试过 R35,结果把 r=32.2..35 的连接壁整圈切断:实测 P04 在 z=-2.6..+1.0 只剩 240~320° 方位有料,其余 360° 全掏空,后端法兰与杯壁之间出现 3.6mm 宽通槽 + 225° 一片 0.5mm 孤立薄片(用户截图:肩叉顶部「没连上」)。取 32.2 后 r=32.2..35 这圈 2.8mm 厚壁完整保留,360° 连续,既无凸台又与上方臂身连成一体(实测 5/5 件连接壁整圈连续、单实体)"),
]