单文件约束取消后,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 段逐字节一致。
418 lines
24 KiB
Python
418 lines
24 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""P03 肩吊臂:板式桁架 + 侧开式电机托架 + M1 后置法兰。"""
|
||
import cadquery as cq
|
||
import math
|
||
from arm.cad.helpers import cyl
|
||
from arm.cad.arm_build import Yplate, bx
|
||
from arm.kinematics import X_BAY_EXT, Y_AXIS, Z_CHEST1, Z_R1, Z_RL
|
||
from arm.cad.part_tools import Xcyl, make_surface_tie_bridge
|
||
from arm.cad.parts.p01_column import X_COL, Y_COL, Z_LOCK
|
||
|
||
|
||
def p_girdle(j1):
|
||
"""P03_肩关节安装悬臂 (轻量化板式桁架版 + 侧开式电机托架):
|
||
1. 10mm 后端重载法兰盘 (X: -61.0..-51.0, 74x74mm);
|
||
2. 主干由 66mm 实心块改为双侧 6mm 桁架竖板 (Y = Y_ARM ± 27..33),外轮廓保持旧版
|
||
"主干 ∪ 侧筋"并集不变 —— 只减材料不加体积,零新增干涉风险;实心 728 -> 286 cm³ (-61%);
|
||
3. 竖板帐篷形减重窗 (顶/底斜面 ≥52°,自支撑;仅 20mm 平顶可桥接);
|
||
4. 底部 9mm 承力鞍座 + 43.4x43.4 定位榫头 (配 P01_C 44x44 内腔,单边 0.3mm 间隙);
|
||
鞍座底面 66x66 与立柱顶环带承压面积 2403mm² —— 竖向载荷走面压,螺栓只防上抬/防滑;
|
||
5. 4x 安装孔 (X_COL±27.5, Y_COL±13.5):Ø3.9 直孔,M4x16 从上方直接拧入自攻成型
|
||
(鞍座与立柱顶环同径,无过孔/沉孔/预埋),可拆装;
|
||
6. 下腹中央龙骨筋 (12mm 中位腹板,自鞍座斜拉至法兰下缘) + 顶部横向锁紧环抱筋 (66x9x10mm,
|
||
与两侧竖板外面齐平、顶面压在筋板脊线上,仅顶前棱一道 2mm 倒角);
|
||
7. 侧开式电机托架 (+Y侧全开, X: -51.0..11.5,前端随 X_BAY_EXT 前延 3mm),
|
||
M1 电机可从侧方斜插卡入装配(插槽比电机长 3mm),由后端法兰 4xM3x16 锁死;
|
||
电机舱 Ø60.7 从后端直通到前端面(2026-10-02 用户裁定"去掉台阶改成平的":无环形台阶、
|
||
无喇叭锥;原前置止脱锁紧法兰 4xM3 随之取消,电机轴向由后端 4xM3x16 约束);
|
||
9. 两侧大面积散热舷窗 (34x20mm) + 顶部双斜向对流排热槽 (30x6.5mm),确保散热畅通;
|
||
10. Ø26mm 中央走线孔;竖板间为开放腔体(无封闭空腔),M3 螺丝刀通道直接走腔体,拧紧更顺手。
|
||
11. 三处外凸扎带拱桥(10x8mm、站出 3.2mm、桥下 3.2x1.8 穿带隧道):①桁架竖板面 Y=95、
|
||
②后置法兰面 Y=91、③Ø73 舱体 −Y 母线 Y=91.5 —— 安装面按各处真实表面取;原来统一按
|
||
Y=95 放,②③ 被筒壁吞成露出 0mm、且压在 34x20 散热舷窗的贯通孔上,等于没有固定点。
|
||
"""
|
||
X_MOUNT = Z_R1 # -51.0 (Rear face of M1)
|
||
T_FLANGE = 10.0 # 10mm thick high-strength mounting flange
|
||
X_BACK = X_MOUNT - T_FLANGE # -61.0
|
||
Y_ARM = Y_AXIS # 128.0
|
||
Z_ARM = Z_RL # 610.0
|
||
Y_P_IN = 27.0 # 桁架竖板内表面 (Y_ARM ± 27)
|
||
Y_P_OUT = 33.0 # 桁架竖板外表面 (与旧主干齐平)
|
||
T_SADDLE = 9.0 # 底部鞍座厚度
|
||
TENON = 43.4 # 定位榫头边长(P01_C 内腔 44x44 R4,单边留 0.3mm)
|
||
WIN_R = 4.0 # 竖板减重窗内角圆角(消除内尖角应力集中,Kt 由 ~2.5 降到 ~1.2)
|
||
BOLT_DX, BOLT_DY = 27.5, 13.5 # 4x 安装孔相对立柱中心偏移:落在顶环 11mm 实体壁内
|
||
|
||
# 1. 主安装法兰盘 (74x74mm, 四角倒圆 R=8mm)
|
||
# 背面那圈 2mm 倒角已去掉:板料只伸进法兰 0.1mm,倒角面与板背面相交会在
|
||
# Z≈574.9 挤出一批 2mm 短棱,正是这批短棱让整条交接内棱网络倒不出圆角;
|
||
# 同时它跨过 R8 角部圆弧,就是角上那道"卷起来"的怪倒角。
|
||
flange = (cq.Workplane("YZ", origin=(X_BACK, Y_ARM, Z_ARM))
|
||
.rect(74.0, 74.0)
|
||
.extrude(T_FLANGE)
|
||
.edges("|X").fillet(8.0)).val()
|
||
s = cq.Shape(flange.wrapped)
|
||
|
||
# 2. 双侧桁架竖板 (6mm):旧"斜挑支撑主干"与"大三角加强筋"轮廓同段挤出 → 自动并集成单块侧板
|
||
# 主干的后缘两点取在加强筋斜边的延长线上,使外轮廓顶部成为一整条直线
|
||
# (否则后缘 (-148,550) 处有 5° 折点、留下 5mm 台阶)。
|
||
# Z_CREST = 侧筋脊线高度,同时是顶筋 2mm 顶前倒角的下沿:倒角从 (X-71,Z_CREST)
|
||
# 斜上到 (X-69,Z_CREST+2) = 顶筋顶面,斜坡面与倒角面几乎相切,顶部读起来是一整张面。
|
||
Z_CREST = Z_ARM + 33.0 # 643(初版 642;顶筋整体上移 3mm 让出扳手空间后跟着抬)
|
||
|
||
RIB_SLOPE = (Z_CREST - 575.0) / ((X_BACK - 10.0) - (X_COL - 15.0))
|
||
|
||
def rib_line_z(x):
|
||
return Z_CREST + RIB_SLOPE * (x - (X_BACK - 10.0))
|
||
|
||
trunk_pts = [
|
||
(X_COL - 33.0, Z_CHEST1),
|
||
(X_COL + 33.0, Z_CHEST1),
|
||
(X_COL + 33.0, 505.0),
|
||
(X_BACK + 0.1, 565.0),
|
||
(X_BACK + 0.1, Z_ARM - 5.0),
|
||
(X_COL - 10.0, rib_line_z(X_COL - 10.0)),
|
||
(X_COL - 33.0, rib_line_z(X_COL - 33.0)),
|
||
]
|
||
rib_pts = [
|
||
(X_COL - 33.0, Z_CHEST1), # 底边两端与主干/鞍座对齐 (±33):原来收进 3mm,
|
||
(X_COL + 33.0, Z_CHEST1), # 会在鞍座顶面上留下 0.7/3mm 短棱,卡住整条底棱圆角
|
||
(X_BACK + 0.1, 565.0),
|
||
(X_BACK + 0.1, Z_CREST),
|
||
(X_BACK - 10.0, Z_CREST), # 脊线 (-71,643):正好落在顶筋前面与 2mm 倒角下沿的交点
|
||
(X_COL - 15.0, 575.0),
|
||
(X_COL - 30.0, 530.0),
|
||
]
|
||
for sign in (-1, 1):
|
||
ya = Y_ARM + sign * Y_P_IN
|
||
yb = Y_ARM + sign * Y_P_OUT
|
||
y0, y1 = min(ya, yb), max(ya, yb)
|
||
for prof in (trunk_pts, rib_pts):
|
||
s = s.fuse(cq.Shape(Yplate(prof, y0, y1).wrapped))
|
||
|
||
# 3. 竖板减重窗 (帐篷形:斜面 ≥52° 自支撑、仅 20mm 平顶可桥接;避开鞍座与 M5 螺母柱)
|
||
# 6 个内角全部 R4 圆角:把内尖角应力集中 (Kt≈2.5) 降到 ≈1.2,同时圆角面仍是竖直/自支撑面
|
||
win_pts = [
|
||
(X_COL - 24.0, 498.0), (X_COL + 23.0, 498.0), (X_COL + 25.0, 522.0),
|
||
(X_COL + 10.0, 545.0), (X_COL - 10.0, 545.0), (X_COL - 25.0, 524.0),
|
||
]
|
||
for sign in (-1, 1):
|
||
yc = Y_ARM + sign * 0.5 * (Y_P_IN + Y_P_OUT)
|
||
tool = (cq.Workplane("XZ", origin=(0, yc, 0)).polyline(win_pts).close()
|
||
.extrude(Y_P_OUT - Y_P_IN + 1.0, both=True)
|
||
.edges("|Y").fillet(WIN_R).val())
|
||
s = s.cut(cq.Shape(tool.wrapped))
|
||
|
||
# 4. 底部承力鞍座:坐实立柱顶面,把两竖板、龙骨与榫头连成整体
|
||
s = s.fuse(bx(X_COL - 33.0, X_COL + 33.0, Y_ARM - Y_P_OUT, Y_ARM + Y_P_OUT,
|
||
Z_CHEST1, Z_CHEST1 + T_SADDLE))
|
||
|
||
# 5. 下腹部纵向抗弯龙骨筋 (12mm 中位腹板,自鞍座斜拉至法兰背面;交接内棱在融合后做真 3D 圆角)
|
||
keel_pts = [
|
||
(X_COL + 33.0, Z_CHEST1), # (-82, 470)
|
||
(X_COL + 33.0, 495.0), # (-82, 495)
|
||
(X_BACK + 0.1, 573.0), # (-60.9, 573) 斜拉至法兰背面
|
||
(X_BACK + 0.1, 594.4), # (-60.9, 594.4) 顶端贴法兰
|
||
(X_COL + 42.1, 585.0), # (-72.9, 585)
|
||
(X_COL + 20.0, Z_CHEST1), # (-95, 470) 背面一路斜到底:原来在 (-95,480) 有个折点,
|
||
] # 折点离鞍座顶面只 1mm,R2.5 圆角爬不上去 → 龙骨底棱整组倒不出
|
||
s = s.fuse(cq.Shape(Yplate(keel_pts, Y_ARM - 6.0, Y_ARM + 6.0).wrapped))
|
||
|
||
# 7. 顶部横向锁紧环抱横筋:Y 向与两竖板外面齐平 (Y_ARM±33)、前面与筋板脊线共面 (X=-71)、
|
||
# 顶面 Z=645 压住脊线 (Z=643) 并与 2mm 顶前倒角相切 → 顶筋与两侧脊线合成一整块。
|
||
# 整体比初版抬高 3mm (底面 633→636):4x M3 后端沉孔 Ø6.5 跨在 Z 627.2..633.7 上,
|
||
# 底面 633 会压住孔口上沿 0.7mm、扳手让位只剩负间隙;抬到 636 后退到孔口上方 2.3mm、
|
||
# 离螺钉轴线 5.55mm。顶面 645 不越过法兰 R8 角部圆弧在 Y=95 处的 645.9 上界。
|
||
tie_rib = (cq.Workplane("YZ", origin=(X_BACK - 10.0, Y_ARM, Z_ARM + 30.5))
|
||
.rect(66.0, 9.0)
|
||
.extrude(10.0)
|
||
.edges(cq.selectors.BoxSelector((X_BACK - 10.5, Y_ARM - 34.0, Z_ARM + 34.5),
|
||
(X_BACK - 9.5, Y_ARM + 34.0, Z_ARM + 35.5)))
|
||
.chamfer(2.0)).val()
|
||
s = s.fuse(cq.Shape(tie_rib.wrapped))
|
||
|
||
# 6.y 体-体交接凹棱:融合完成后对"凹棱"直接做真 3D 圆角。
|
||
# 原先这里是手工沿棱 fuse 的 10 条"正方形−四分之一圆"焊珠小体(_cove),已整体删除:
|
||
# 焊珠端面是平的、悬在半空,两条互相垂直的焊珠在角上互不搭接 → 圆角"缺一块",
|
||
# 并与相邻体的棱互相穿插留 1~3mm² 碎面。真圆角在面边界自然收口、多棱相交处由 OCC
|
||
# 生成角部补片。
|
||
from cadquery import selectors as _sel
|
||
from OCP.BRepAdaptor import BRepAdaptor_Surface
|
||
from OCP.GeomAbs import GeomAbs_Plane
|
||
from OCP.TopAbs import TopAbs_REVERSED
|
||
|
||
def _outward(f, sh):
|
||
"""平面面的外法向:先按 Orientation 修正,再用"面中心外移 1mm 是否仍在实体内"兜方向。"""
|
||
ad = BRepAdaptor_Surface(f.wrapped)
|
||
if ad.GetType() != GeomAbs_Plane:
|
||
return None
|
||
ax = ad.Plane().Axis().Direction()
|
||
n = cq.Vector(ax.X(), ax.Y(), ax.Z())
|
||
if f.wrapped.Orientation() == TopAbs_REVERSED:
|
||
n = n * -1.0
|
||
c = f.Center()
|
||
if cq.Solid(sh.wrapped).isInside(cq.Vector(c.x + n.x, c.y + n.y, c.z + n.z), 1e-6):
|
||
n = n * -1.0
|
||
return n
|
||
|
||
def _fillet_reflex(lo, hi, radii, tag, eps=0.9):
|
||
"""倒出 lo~hi 区域内的凹棱(两面夹 270° 材料)。判据:从棱中点沿 (n1-n2) 外探 eps,
|
||
落在实体内 = 凹、落在外 = 凸。区域内凹棱先整批一次倒(半径逐级降级,且倒完验拓扑:
|
||
OCC 会给出"没抛异常但面已坏"的结果),建不出时按相连棱分组逐组倒。"""
|
||
nonlocal s
|
||
|
||
def _pick(sh):
|
||
edges = sh.Edges()
|
||
idx = {}
|
||
for i, e in enumerate(edges):
|
||
idx[e.hashCode()] = i
|
||
adj = [[] for _ in edges]
|
||
for f in sh.Faces():
|
||
for ee in f.Edges():
|
||
k = idx.get(ee.hashCode())
|
||
if k is not None:
|
||
adj[k].append(f)
|
||
box = _sel.BoxSelector(lo, hi)
|
||
out = []
|
||
for i, e in enumerate(edges):
|
||
if e.Length() <= 6.0 or len(adj[i]) != 2 or not box.filter([e]):
|
||
continue
|
||
n1 = _outward(adj[i][0], sh)
|
||
n2 = _outward(adj[i][1], sh)
|
||
if n1 is None or n2 is None:
|
||
continue
|
||
dx, dy, dz = n1.x - n2.x, n1.y - n2.y, n1.z - n2.z
|
||
ln = math.sqrt(dx * dx + dy * dy + dz * dz)
|
||
if ln < 1e-6:
|
||
continue
|
||
p = e.Center()
|
||
if cq.Solid(sh.wrapped).isInside(
|
||
cq.Vector(p.x + dx / ln * eps, p.y + dy / ln * eps,
|
||
p.z + dz / ln * eps), 1e-6):
|
||
out.append(e)
|
||
return out
|
||
|
||
es0 = _pick(s)
|
||
if not es0:
|
||
print(" [P03 真圆角] %-14s 区域内无凹棱,跳过" % tag, flush=True)
|
||
return
|
||
|
||
def _groups(ee):
|
||
"""按共端点把凹棱连成链/网络:一组一次倒,OCC 才能在交汇处生成角部补片;
|
||
逐条倒会在交汇处留下断口(看着就像圆角倒一半停了)。"""
|
||
pts = [e.Vertices() or [e.Center()] for e in ee]
|
||
par = list(range(len(ee)))
|
||
|
||
def find(a):
|
||
while par[a] != a:
|
||
par[a] = par[par[a]]
|
||
a = par[a]
|
||
return a
|
||
|
||
for i in range(len(ee)):
|
||
for j in range(i + 1, len(ee)):
|
||
if any(pi.distance(pj) < 0.05 for pi in pts[i] for pj in pts[j]):
|
||
par[find(i)] = find(j)
|
||
g = {}
|
||
for i in range(len(ee)):
|
||
g.setdefault(find(i), []).append(ee[i])
|
||
return list(g.values())
|
||
|
||
def _do(edges_, rr):
|
||
"""倒完立刻验拓扑:OCC 会给出"没抛异常但面已坏"的结果,必须挡下来。"""
|
||
try:
|
||
ns = cq.Shape(cq.Solid(s.wrapped).fillet(rr, edges_).wrapped)
|
||
except Exception:
|
||
return None
|
||
return ns if cq.Solid(ns.wrapped).isValid() else None
|
||
|
||
# 先试"区域内全部凹棱一次倒"(探针实测这样能整批成型,且只重建一次,
|
||
# 不会像分批那样把同一张面反复重算坏掉);不行再按相连棱分组逐组倒。
|
||
for rr in radii:
|
||
ns = _do(es0, rr)
|
||
if ns is not None:
|
||
s = ns
|
||
print(" [P03 真圆角] %-14s %d 条凹棱 R%.1f ✓ 整批一次成型"
|
||
% (tag, len(es0), rr), flush=True)
|
||
return
|
||
|
||
n_ok, n_grp, left = 0, 0, len(es0)
|
||
for _ in range(24):
|
||
es = _pick(s)
|
||
if not es:
|
||
break
|
||
got = False
|
||
for g in sorted(_groups(es), key=lambda g: -sum(e.Length() for e in g)):
|
||
for rr in radii:
|
||
ns = _do(g, rr)
|
||
if ns is None:
|
||
continue
|
||
s = ns
|
||
n_ok += len(g)
|
||
n_grp += 1
|
||
print(" [P03 真圆角] %-14s 第%d组 %d 条 R%.1f ✓"
|
||
% (tag, n_grp, len(g), rr), flush=True)
|
||
got = True
|
||
break
|
||
if got:
|
||
break
|
||
if not got:
|
||
left = len(es)
|
||
break
|
||
print(" [P03 真圆角] %-14s 共 %d 组 %d 条凹棱倒圆,剩 %d 条保持尖棱"
|
||
% (tag, n_grp, n_ok, left), flush=True)
|
||
|
||
XR, YC, ZS = X_BACK, Y_ARM, Z_CHEST1 + T_SADDLE # -61, 128, 479
|
||
# —— 鞍座顶面 (Z=479) 上的交接内棱:龙骨三条 + 两竖板内侧两条,一次真圆角 ——
|
||
# 半径上限 2.0:龙骨侧面 Y=122 与 M5 螺钉头外缘 Y=118.75 只隔 3.25mm,
|
||
# 原来的 R3 焊珠会顶到螺帽承压边,R2.0 留 1.25mm。
|
||
_fillet_reflex((X_COL - 34.0, Y_ARM - 40.0, ZS - 3.0),
|
||
(X_COL + 34.0, Y_ARM + 40.0, ZS + 7.0),
|
||
(2.0, 1.5), "鞍座顶面交接")
|
||
# —— 法兰背面 (X=-61) 上的交接内棱:只倒竖板/龙骨的竖直棱 ——
|
||
# 顶筋底面那条水平棱 (Z=636) 不倒:4x M3 沉孔 Ø6.5 在 Z 627.2..633.7,
|
||
# 倒上 R2.5 会把螺钉上方 5.55mm 的扳手让位吃掉 2.5mm。
|
||
# 上界取 631:竖板内棱被顶筋底面切成 573..636 / 636..643 两段,只倒下面长的那段
|
||
# ——把 636..643 的 7mm 短段并进来,整批就从"一次成型"退化成逐组、且少倒 6 条。
|
||
_fillet_reflex((X_BACK - 3.0, Y_ARM - 40.0, 570.0),
|
||
(X_BACK + 3.0, Y_ARM + 40.0, 631.0),
|
||
(2.5, 2.0, 1.5), "竖板/龙骨↔法兰")
|
||
_fillet_reflex((X_BACK - 3.0, Y_ARM - 40.0, Z_ARM + 34.5),
|
||
(X_BACK + 3.0, Y_ARM + 40.0, Z_ARM + 35.5),
|
||
(1.5, 1.0), "顶筋顶↔法兰")
|
||
|
||
# 与立柱顶部配合的定位榫头 (43.4x43.4x15mm):配 P01_C 44x44 R4 内腔,单边 0.3mm 滑配
|
||
boss = (cq.Workplane("XY", origin=(X_COL, Y_COL, Z_CHEST1 - 15.0))
|
||
.rect(TENON, TENON)
|
||
.extrude(15.0)
|
||
.edges("|Z").fillet(4.2)).val()
|
||
s = s.fuse(cq.Shape(boss.wrapped))
|
||
|
||
# 4x 安装孔:统一 Ø3.9 直孔(M4x16 从腔体正上方拧入自攻成型;鞍座与立柱顶环同径、无沉孔)
|
||
# 孔位 (X_COL±27.5, Y_COL±13.5) 同时满足:①在立柱顶环实体壁内(内腔 22 → 外壁 33);
|
||
# ②在 P03 两竖板之间、避开中间龙骨,从腔体正上方用扳手可直接拧紧。
|
||
for xx in (X_COL - BOLT_DX, X_COL + BOLT_DX):
|
||
for yy in (Y_COL - BOLT_DY, Y_COL + BOLT_DY):
|
||
s = s.cut(cyl(1.95, Z_CHEST1 - 0.5, Z_CHEST1 + T_SADDLE + 0.5, xx, yy))
|
||
|
||
# 1x 横向防脱锁紧:榫头 −X 侧向内 Ø3.9x14 直孔(与立柱后壁 Ø3.9 孔同轴,M4x16 拧入自攻)
|
||
s = s.cut(Xcyl(1.95, X_COL - TENON / 2.0 - 1.0, X_COL - TENON / 2.0 + 14.0, Y_COL, Z_LOCK))
|
||
|
||
# 【线束过线孔】榫头/鞍座中央 22x22mm 竖向贯通方孔 (直通 P01 立柱 44x44mm 内腔,容纳 20x20mm 接插件)
|
||
saddle_wire = (cq.Workplane("XY", origin=(X_COL, Y_COL, Z_CHEST1 - 16.0))
|
||
.rect(22.0, 22.0)
|
||
.extrude(T_SADDLE + 17.0)
|
||
.edges("|Z").fillet(3.0)).val()
|
||
s = s.cut(cq.Shape(saddle_wire.wrapped))
|
||
|
||
# 4x M3 连接螺孔(螺丝刀通道直接走两竖板之间的开放腔体,无需再钻 Ø8.5 直通隧道)
|
||
# 4x M3 连接螺孔(螺丝刀通道直接走两竖板之间的开放腔体,无需再钻 Ø8.5 直通隧道)
|
||
# 孔位 = 电机尾端 4x M3 螺纹径向槽实测位(电机.STEP):BCD55。
|
||
# 【2026-10-07 整改】M1 电机沿 +X 轴旋转 5.35° 后,4x M3 后端孔完全规整对齐 45°/135°/225°/315° 正四角
|
||
# (dy, dz = ±19.45, ±19.45),水平与竖直完全齐平规整,且与旋转后的 M1 尾端 4xM3 槽完全同心
|
||
for dy, dz in ((19.45, 19.45), (-19.45, 19.45), (-19.45, -19.45), (19.45, -19.45)):
|
||
py = Y_ARM + dy
|
||
pz = Z_ARM + dz
|
||
th = Xcyl(1.7, X_BACK - 0.5, X_MOUNT + 4.0, py, pz)
|
||
cb = Xcyl(3.25, X_BACK - 0.5, X_BACK + 4.5, py, pz)
|
||
s = s.cut(th).cut(cb)
|
||
|
||
# 6. 前向电机全包托架 (X: -51.0..8.5+X_BAY_EXT=11.5,Ø60.7 舱孔平直贯通到端面)
|
||
# 端面随插槽加长前延 X_BAY_EXT;叉板背面(10.5+X_FORK_GAP=11.5)与端面 0.0 硬贴平
|
||
# (用户两次要求"搞成平的";J1 转动摩擦由用户知悉,发涩时可微松 M2)
|
||
X_FRONT = 8.5 + X_BAY_EXT
|
||
R_POD = 36.5
|
||
R_BORE = 30.35
|
||
|
||
cradle_outer = (cq.Workplane("YZ", origin=(X_MOUNT - 0.1, Y_ARM, Z_ARM))
|
||
.circle(R_POD)
|
||
.extrude(X_FRONT - X_MOUNT + 0.1)
|
||
.edges(">X").chamfer(1.5)).val()
|
||
s = s.fuse(cq.Shape(cradle_outer.wrapped))
|
||
|
||
# 电机定子配合内腔(最终状态,2026-10-02 用户裁定"去掉台阶改成平的+保留固定电机的面"):
|
||
# Ø60.7 圆柱孔壁从前端面平直贯通到舱底(无窄颈、无台阶、无喇叭锥);
|
||
# 舱底 = 一个完整的平坦环面(X=5.06+X_BAY_EXT=8.06,与插槽末端墙共面,中开 Ø39 凸台过孔)
|
||
# ——即电机前限位面。电机轴向由后端法兰 4xM3x16 约束;
|
||
# 前端锁紧由 4 个 Ø10 局部凸台上的 M3 圆柱头螺钉承担(见下方凸台段)。
|
||
motor_pocket = Xcyl(R_BORE, X_MOUNT - 0.2, 5.06 + X_BAY_EXT, Y_ARM, Z_ARM)
|
||
s = s.cut(motor_pocket)
|
||
|
||
# 4x 前置 M3 锁紧螺钉凸台(BCD=55mm,跟随 M1 电机旋转 5.35° -> 22.82°/112.82°/202.82°/292.82°):
|
||
# 平直舱孔的沉头座位由 4 个 Ø10 局部凸台承载——凸台位于舱孔前段 8.06..11.5,
|
||
# 电机前端面(5.0)之前 3mm,电机斜插路径不经过;M3x8 圆柱头坐在凸台沉孔
|
||
# 底(x=9.5)旋入电机前端面,咬合 3.5mm,与旋转后的 M1 前端盖实测 4xM3 螺孔精确同心
|
||
for deg in (22.82, 112.82, 202.82, 292.82):
|
||
rad = math.radians(deg)
|
||
fy = Y_ARM + 27.5 * math.cos(rad)
|
||
fz = Z_ARM + 27.5 * math.sin(rad)
|
||
s = s.fuse(Xcyl(5.0, 5.06 + X_BAY_EXT, X_FRONT, fy, fz))
|
||
th = Xcyl(1.6, 5.06 + X_BAY_EXT, X_FRONT, fy, fz)
|
||
cb = Xcyl(3.0, X_FRONT - 2.0, X_FRONT, fy, fz)
|
||
s = s.cut(th).cut(cb)
|
||
|
||
# J1 输出凸台过孔 (Ø39 × 3.44——J1 凸台与 3xM2 输出螺钉的必经孔,非台阶;
|
||
# 原 6.7mm 长颈与半圆台肩已随舱底前移一并消除)
|
||
hub_clearance = Xcyl(19.5, 4.8, X_FRONT + 0.5, Y_ARM, Z_ARM)
|
||
s = s.cut(hub_clearance)
|
||
|
||
# 8. 电机托架 +Y 侧全开(侧开式半包,替代原封闭全包筒):
|
||
# M1 电机可从 +Y 侧方直线滑入托架完成装配——原封闭筒电机物理上无法装入。
|
||
# 轴向约束:后端法兰 4xM3(电机尾端面)单向锁死(前置止脱法兰随平直舱孔要求取消);
|
||
# 径向约束:Ø60.7 精密滑配托架底 + 重力贴底。开口前端至 5.06+X_BAY_EXT=8.06
|
||
# (比电机前端面多 3mm 斜插余量,电机可倾斜卡入),止脱法兰环随之前移、环宽保持 3.44mm。
|
||
s = s.cut(bx(X_MOUNT, 5.06 + X_BAY_EXT, Y_ARM, Y_ARM + 60.0, Z_ARM - 60.0, Z_ARM + 60.0))
|
||
|
||
# 7. 散热系统:双侧大视窗 + 顶部斜向对流排热槽
|
||
for sign in (-1, 1):
|
||
win_plane = cq.Plane(
|
||
origin=(-24.5, Y_ARM + sign * 35.0, Z_ARM),
|
||
xDir=(1, 0, 0),
|
||
normal=(0, sign, 0)
|
||
)
|
||
win_cut = cq.Workplane(win_plane).slot2D(34.0, 20.0).extrude(16.0, both=True).val()
|
||
s = s.cut(win_cut)
|
||
|
||
for deg in (45, 135):
|
||
rad = math.radians(deg)
|
||
slot_plane = cq.Plane(
|
||
origin=(-24.5, Y_ARM + 33.0 * math.cos(rad), Z_ARM + 33.0 * math.sin(rad)),
|
||
xDir=(1, 0, 0),
|
||
normal=(0, math.cos(rad), math.sin(rad))
|
||
)
|
||
slot_cut = cq.Workplane(slot_plane).slot2D(30.0, 6.5).extrude(15.0, both=True).val()
|
||
s = s.cut(slot_cut)
|
||
|
||
# 8. 实体表面外凸式扎带拱桥 (10x8mm 拱形穿带座,一体成型,扎带从桥下 3.2x1.8 隧道穿过):
|
||
# 安装面一律取"该处的真实表面"——原来三处全按竖板面 Y=95 放,而 ②③ 落在 Ø73 舱体上,
|
||
# 舱面母线只到 Y=91.5,桥体 3.2mm 被筒壁整段吞掉(露出 0.0mm、穿带孔被壁填死)= 没有固定点。
|
||
# 组①:桁架竖板平面段 X=-85, Z=570, 贴 Y=95 平面,竖直穿带 —— 与 P04 三处同配方,不动
|
||
# 组②:后置法兰 −Y 平面 X=-56, Z=610, 贴 Y=91 (法兰 74 宽 → 面在 128-37)
|
||
# 选法兰面而不是舱面:法兰是 10mm 整实料;且线束过了 X=-61 必须绕开 Y=91 这道 4mm 台阶,
|
||
# 贴法兰面后法兰带内线皮净空 2.78mm,走舱面径向则线要外拱、只剩 0.33mm。
|
||
# X 向留 1.0mm 不越法兰前后脸,Z 向 606..614 在 R8 圆角之间 (581..639) 全平面。
|
||
# 组③:舱体 −Y 母线实料段 X=+5.5, Z=610, 贴 Y=91.5 切点,动静接缝前锚固点
|
||
# 离舷窗前缘 (最外到 X=-8.3) 10.3mm、离前端 1.5 倒角 (X=10 起) 1.0mm;
|
||
# 穿带孔由沿 X 改成沿 Z —— 此处线束沿 X 走,孔轴必须垂直于线才抱得住。
|
||
Y_FACE_FLANGE = Y_ARM - 37.0 # 91.0 法兰 −Y 承面
|
||
Y_TAN = Y_ARM - R_POD # 91.5 舱体最外的 −Y 母线(切点)
|
||
for tx, ys, tz, wx in ((-85.0, Y_ARM - Y_P_OUT, 570.0, 10.0),
|
||
(-56.0, Y_FACE_FLANGE, 610.0, 8.0),
|
||
(5.5, Y_TAN, 610.0, 7.0)):
|
||
s = s.fuse(make_surface_tie_bridge(tx, ys, tz, w_x=wx, l_z=8.0, h_y=3.2,
|
||
slot_w=3.2, slot_h=1.8, tunnel_dir='Z'))
|
||
|
||
return s
|
||
|
||
|
||
JOINT = 'root'
|
||
|
||
|
||
def specs(JS):
|
||
"""本件的 (件号, 中文名, 构建 thunk, 配色, 说明) 清单;顺序即 BOM 顺序。"""
|
||
j1 = JS["J1"]
|
||
return [
|
||
("P03_肩吊臂_螺旋体35%_墙5底5_竖打", "肩关节安装悬臂", lambda: p_girdle(j1), 0x4b5563,
|
||
"轻量化板式桁架版:主干由 66mm 实心块改为双侧 6mm 桁架竖板+帐篷减重窗(斜面≥52°自支撑)+R4内角,外轮廓与旧版并集完全一致,实心 728->286cm³;底部 9mm 承力鞍座(Ø3.9 安装孔)+12mm 中位龙骨筋(自鞍座斜拉至法兰下缘)+40~45°外扩斜撑;保留 10mm 后置法兰、侧开式电机托架、Ø60.7 直通电机舱(平孔无台阶)(M1 从+Y滑入,后端 4xM3x16 锁死)、大散热窗与对流槽;【2026-10-07 电机旋转整改】M1 电机旋转 5.35°:后置法兰 4xM3 连接孔完全规整对齐 45°/135°/225°/315° 正四角(dy,dz=±19.45mm),前端 4xM3 锁紧凸台同步旋转至 22.82°+90k,消除原有倾斜,与 M1 前后端盖螺孔精确同心;无封闭空腔、免支撑"),
|
||
]
|