R2:底板 / 刀片布局布线脚本、快照、截图与规划文档更新(含外壳、打印件、布线归档)

Co-Authored-By: Claude Opus 5.5 <[email protected]>
这个提交包含在:
zhou yang
2026-10-04 19:24:45 +08:00
共同撰写人 Claude Opus 5.5
父节点 cd01640c40
当前提交 cf7cc9cf90
共修改 759 个文件,包含 76962 行新增和 5299 行删除
+184
查看文件
@@ -0,0 +1,184 @@
# -*- coding: utf-8 -*-
u"""总装里 EndPlate_v10 → **v12**(多了 A 组 8 + C 组 4 个 Ø4.2×6 螺母孔),并核「螺丝拧得进去」。
摆位与 place_v10.py 完全相同(西:不转、外表面 −38.4;东:绕 z 180°、外表面 136.4、dy=2×51.345)。
判据:
1. 两块端板装配 box 与 v10 时一致;
2. 干涉仍是 360 基线;
3. ⭐ **同轴核对**:在装配坐标下,把端板上 12 个 R2.1 螺母孔的轴线,
与南板/北板/顶盖上 Ø3.4 过孔的轴线逐一配对 —— 同方向、横向偏差 < 0.05 才算拧得进去。
这是「孔打出来了」和「螺丝能穿过去」之间的那一步,包围盒/干涉/直方图都回答不了。
"""
import io
import os
import sys
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', write_through=True)
import pythoncom
import win32com.client as wc
OUT = r'C:\Users\35138\.claude\jobs\11836093\tmp'
DIR = u'D:\\DCDC\\新建文件夹\\5blades\\'
ASM = 'PhoneRail_5blades'
EMPTY = wc.VARIANT(pythoncom.VT_DISPATCH, None)
IDENT = [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0]
ROT_Z180 = [-1.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 1.0]
FAN_CY = 51.345
WEST_OUT, EAST_OUT = -38.4, 136.4
EP_Y0, EP_Y1 = -1.655, 104.345
def prop(o, n):
v = getattr(o, n)
return v() if type(v).__name__ in ('method', 'function', 'builtin_function_or_method') else v
def D(x):
return x if hasattr(x, '_oleobj_') else wc.Dispatch(x)
def P(*a):
print(*a)
app = wc.GetActiveObject('SldWorks.Application')
doc = None
for d in app.GetDocuments or []:
if ASM in (prop(d, 'GetTitle') or ''):
doc = d
break
e0 = wc.VARIANT(pythoncom.VT_BYREF | pythoncom.VT_I4, 0)
app.ActivateDoc3(prop(doc, 'GetTitle'), False, 0, e0)
doc = app.ActiveDoc
did = app._oleobj_.GetIDsOfNames('GetMathUtility')
mu = wc.Dispatch(app._oleobj_.Invoke(did, 0, pythoncom.DISPATCH_METHOD, 1))
d2 = mu._oleobj_.GetIDsOfNames('CreateTransform')
def mk(a16):
v = wc.VARIANT(pythoncom.VT_ARRAY | pythoncom.VT_R8, list(a16))
return wc.Dispatch(mu._oleobj_.Invoke(d2, 0, pythoncom.DISPATCH_METHOD, 1, v))
def setx(c, a16):
xf = mk(a16)
try:
c.SetTransformAndSolve2(xf)
except Exception:
c.Transform2 = xf
doc.EditRebuild3
return [round(v * 1000.0, 3) for v in c.GetBox(False, False)]
P(u'[1] 删 v10')
for c in list(doc.GetComponents(True)):
nm = prop(c, 'Name2')
if 'ENDPLATE' in nm.upper():
doc.ClearSelection2(True)
doc.Extension.SelectByID2(nm, 'COMPONENT', 0, 0, 0, False, 0, EMPTY, 0)
doc.Extension.DeleteSelection2(0)
P(u' 删掉 %s' % nm)
doc.EditRebuild3
EP = DIR + u'EndPlate_v13.SLDPRT'
P(u'\n[2] 插 v12 ×2')
allok = True
for tag, rot, want in ((u'西', IDENT, [WEST_OUT, EP_Y0, -10.0, 0.0, EP_Y1, 72.0]),
(u'东', ROT_Z180, [98.0, EP_Y0, -10.0, EAST_OUT, EP_Y1, 72.0])):
c = doc.AddComponent4(EP, '', 0.0, 0.0, 0.0)
if c is None:
P(u' ⛔ %s AddComponent4 → None' % tag); sys.exit(4)
b0 = setx(c, rot + [0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0])
if tag == u'西':
t = (WEST_OUT - b0[0], 0.0, -10.0 - b0[2])
else:
t = (EAST_OUT - b0[3], 2.0 * FAN_CY, 0.0)
b = setx(c, rot + [t[0] / 1000.0, t[1] / 1000.0, t[2] / 1000.0, 1.0, 0.0, 0.0, 0.0])
ok = all(abs(b[i] - want[i]) < 0.05 for i in range(6))
allok &= ok
P(u' %s端板 x %.2f~%.2f y %.2f~%.2f z %.2f~%.2f %s'
% (tag, b[0], b[3], b[1], b[4], b[2], b[5], u'✅' if ok else u'⛔ 期望 %s' % want))
# ── [3] 同轴核对 ──────────────────────────────────────────────────────────
def world_holes(comp, radius):
u"""组件上指定半径的圆柱面 → 装配坐标下的 (轴向单位向量, 轴上一点, 面box)"""
xf = list(prop(comp, 'Transform2').ArrayData)
R = xf[0:9]
T = [xf[9] * 1000, xf[10] * 1000, xf[11] * 1000]
out = []
body_list = comp.GetBodies2(0) or []
for bd in body_list:
for f in prop(D(bd), 'GetFaces'):
s = prop(D(f), 'GetSurface')
if not prop(s, 'IsCylinder'):
continue
cp = list(prop(s, 'CylinderParams'))
if abs(cp[6] * 1000 - radius) > 0.01:
continue
p = [cp[0] * 1000, cp[1] * 1000, cp[2] * 1000]
a = cp[3:6]
# 列主序:本地 (x,y,z) → 全局 = x*col0 + y*col1 + z*col2 + T
gp = [p[0] * R[0] + p[1] * R[3] + p[2] * R[6] + T[0],
p[0] * R[1] + p[1] * R[4] + p[2] * R[7] + T[1],
p[0] * R[2] + p[1] * R[5] + p[2] * R[8] + T[2]]
ga = [a[0] * R[0] + a[1] * R[3] + a[2] * R[6],
a[0] * R[1] + a[1] * R[4] + a[2] * R[7],
a[0] * R[2] + a[1] * R[5] + a[2] * R[8]]
out.append((ga, gp))
return out
P(u'\n[3] 同轴核对:端板 R2.1 螺母孔 ↔ 侧板/顶盖 R1.7 过孔')
nuts, thru = [], []
for c in doc.GetComponents(True):
nm = prop(c, 'Name2').upper()
if 'ENDPLATE' in nm:
nuts += [(nm, *h) for h in world_holes(c, 2.1)]
elif any(k in nm for k in ('SOUTHPANEL', 'NORTHPANEL', 'TOPPANEL')):
thru += [(nm, *h) for h in world_holes(c, 1.7)]
P(u' 螺母孔 %d 个(期望 12)/ 板上过孔 %d 个(期望 7+7+4=18,其中 B 组 6 个无对象)'
% (len(nuts), len(thru)))
matched = 0
for nm, ga, gp in nuts:
ai = max(range(3), key=lambda i: abs(ga[i]))
best = None
for tn, ta, tp in thru:
if max(range(3), key=lambda i: abs(ta[i])) != ai:
continue
lat = max(abs(gp[i] - tp[i]) for i in range(3) if i != ai)
if best is None or lat < best[0]:
best = (lat, tn)
ok = best is not None and best[0] < 0.05
matched += ok
o = [i for i in range(3) if i != ai]
P(u' %-16s 轴%s @ (%s=%.2f, %s=%.2f) ↔ %-16s 偏 %.3f %s'
% (nm[:16], 'xyz'[ai], 'xyz'[o[0]], gp[o[0]], 'xyz'[o[1]], gp[o[1]],
best[1][:16] if best else '-', best[0] if best else -1, u'✅' if ok else u'⛔'))
P(u' 同轴 %d / %d %s' % (matched, len(nuts), u'✅' if matched == 12 else u'⛔'))
allok &= (matched == 12)
# ── [4] 干涉 ──────────────────────────────────────────────────────────────
P(u'\n[4] 干涉(基线 360)')
idm = doc.InterferenceDetectionManager
idm.TreatCoincidenceAsInterference = False
idm.IncludeMultibodyPartInterferences = False
idm.TreatSubAssembliesAsComponents = True
idm.IgnoreHiddenBodies = True
idm.UseTransform = False
try:
n = idm.GetInterferenceCount
except AttributeError:
dd = idm._oleobj_.GetIDsOfNames('GetInterferenceCount')
n = idm._oleobj_.Invoke(dd, 0, pythoncom.DISPATCH_PROPERTYGET, 1)
idm.Done()
P(u' 干涉 %s %s' % (n, u'✅' if n == 360 else u'⛔'))
allok &= (n == 360)
e3 = wc.VARIANT(pythoncom.VT_BYREF | pythoncom.VT_I4, 0)
w3 = wc.VARIANT(pythoncom.VT_BYREF | pythoncom.VT_I4, 0)
doc.Extension.SaveAs3(prop(doc, 'GetPathName'), 0, 1, EMPTY, EMPTY, e3, w3)
P(u'\n存盘 err=%s 总判 %s' % (e3.value, u'✅ 全过' if allok else u'⛔ 有项没过'))