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
+86 -5
View File
@@ -24,7 +24,7 @@ except Exception:
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import cadquery as cq
from armcore import (M, C, V, trs, place, bb, bbox_str, cyl, ring, box, holes,
solids_of, mesh_of, OUT, WEB, DOCS, SRC_STEP, HALF_STEP, CLAMP_STEP)
solids_of, mesh_of, MOTOR_TOL, OUT, WEB, DOCS, SRC_STEP, HALF_STEP, CLAMP_STEP)
from OCP.gp import gp_Trsf, gp_Vec, gp_Pnt, gp_Dir, gp_Ax1
from OCP.BRepAlgoAPI import BRepAlgoAPI_Common
from OCP.GProp import GProp_GProps
@@ -50,6 +50,37 @@ PCB_W = M["pcba_w"] + 0.8 # 64.40 square pocket
HUB_M2_CLOCK = [(360.0 - a) % 360.0 for a in M["m2_clock"]] # phase of the mirrored half
HUB_PILOT_D = M["neck_d"] - 0.4 # 35.6 -> the half's pilot lands in a Ø36 recess
PINCH_D = 2.6 # M3 clearance in printed material
# 承压面让位(完整背景见 Joint.pocket 的 docstring):
# REAR_LAND = 0.6 腔口直壁台肩高度,**保持原设计值不变**(让位不靠加高台肩)
# REAR_SEAT = 2.6 尾端面->承压面 = 0.6 原台肩 + 2.0 公差垫片位
# R35_SEAT = 35.0 后端板 R35 外壁(承压面外径)
# SEAT_R = 35.0 承压面段(z=-REAR_SEAT..SEAT_TOP)的腔口半径 = **等于外壁 R35**。
# SEAT_TOP = 1.0 圆腔顶端越过电机尾端面(z=0) 1mm。
#
# 【2026-10-05 用户第三次指出:SEAT_R 必须是 35,"凸台+围挡"要一起弄平】
# 之前取 32.2(64.4 方腔的内切圆)以为这样能保住连接壁,实测**恰恰相反**:
# 32.2 与外壁 35 之间那圈 2.8mm 宽的环墙完整保留,而承压面只挖到 32.2,
# 于是这圈环墙相对承压面【凸起 2.6mm】,把电机尾端面整个围在里面 ——
# 这正是用户截图里的"一圈围挡"(P08 该圈实测 1536.9 mm3)。它不是残留,
# 而是 SEAT_R 取小值的必然产物:只要 SEAT_R < R35,环墙就一定存在。
# 用户要求:"把凸台、围挡一起弄平,做成和电机两头齐平一起的平面"。
# 故 SEAT_R 取满 R35:承压面段整圈挖到外壁,承压面变成**完整 R35 平圆盘**,
# 环墙(凸台)自然消失,电机尾端面与承压面之间再无任何凸起。
#
# 【为什么这次取 R35 不会重演2026-10-04 的"肩叉顶部没连上"】
# 那次我切的是 z=-2.6..0 且**只切到 z=0 就停**,导致环墙在 z=0..1.0 还残留一截
# 0.052cm3 的薄片(Y93.0~95.8 的半包开口处),那片薄片脱空成为独立碎块。
# 现在 SEAT_TOP=+1.0 把那一截一起吃掉:实测五件全部单实体、零碎片。
# 且承力桥改为由**臂身本体**(r>35)承担 —— 实测 band 段 r36..58 有料的方位
# P04 10/36、P05/P06/P07/P08 各 15/36,其余方位由 z=+1.0 以上的杯体连接,
# 不存在"整圈被切断"的通槽(那正是 2026-10-04 的错误形态)。
# 连带好处:承压环由「1.95mm 窄环」变「完整 R35 圆环 4.75mm」,受压面积 383->974mm²。
REAR_LAND = 0.6
REAR_SEAT = REAR_LAND + MOTOR_TOL # 2.6 尾端面->后端板承压面:公差垫片位
R35_SEAT = 35.0 # 后端板 R35 外壁(承压面外径)
SEAT_R = R35_SEAT # 35.0 承压面段腔口 = 外壁:凸台+围挡一并消除
SEAT_TOP = 1.0 # 圆腔顶端越过电机尾端面 1mm(吃掉开口处残片)
BORE_Z0 = 5.5 # 座孔起点:R35 圆腔挖到此处,与 R30.15 座孔相接
PARTS = []
CENTS = {}
@@ -204,9 +235,59 @@ class Joint:
return (self.rear[0] + self.d[0] * s, self.rear[1] + self.d[1] * s, self.rear[2] + self.d[2] * s)
def pocket(self, shape, front_pad=0.6):
"""Cut the motor pocket out of a proximal link body."""
c = [cyl(BORE_R, 5.5, COVER_Z - 3.5), cyl(CB_R, COVER_Z - 3.5, COVER_Z + front_pad),
box(-PCB_W / 2, PCB_W / 2, -PCB_W / 2, PCB_W / 2, -0.6, M["pcba_t"] + 0.4)]
"""Cut the motor pocket out of a proximal link body.
REAR_SEAT = 电机尾端面(局部 z=0)到后端板承压面的距离。它不是定位面而是
**垫片位**:公差 MOTOR_TOL=2.0(armcore)要靠它消化 —— 电机比标称短的那一份,
就垫在尾端面与这个承压面之间(见 armcore.MOTOR_TOL 的说明)。原值 0.6 只能吃
0.6mm,短 1mm 以上的电机装不进、长 0.6mm 以上的电机会顶死后端板而压溃。
注意它同时是承压面的高度,所以让空 2.0mm 要求 t_plate >= 8
(见各零件 rear_mount_holes 的 t_plate)。
【2026-10-04 整改:承压面段改用 R35 圆腔,凸台彻底消失】
上一版把 64.4 **方**腔整体从 -0.6 加深到 -2.6(纯掏腔),方腔壁以外
z=-2.6..0 之间那圈 R35 料一动没动,于是相对新承压面整整凸起 2.6mm ——
这就是 P04~P08 后端那圈"悬空凸台"(用户截图红圈,3D 打印时该处是悬空
薄壁,既要支撑又公差难保证)。
为什么之前试"把凸台切掉"总失败(几何实测,别再重复):
- 64.4 方腔的**内切圆半径恰好 = 32.2**,所以方腔在 45° 方向已经吃穿到
r=45.5,R35 壁只剩**四条边中点附近**的料。实测 r=34 处材料沿 z 的
区间(承压面 -2.6):P06 的 0°/180° 是 -8.0..12.0(贯通,是臂身本体),
80°/100° 是 -8.0..-0.0(**只比承压面多 2.6mm,这段就是凸台**),
其余 20~160° 是 -8.0..-2.6(承压环本体,齐平)。
- 因此整段全截面切会把 0/90 方向那条**贯通到 z=12 的承力桥**一起切断
-> P05/P08 碎成 2 块(实测)。这不是"承压环是唯一连接",
而是我的刀具没区分"桥"和"悬空料"。
现在的做法(一刀,不做角度分区):
承压面所在的 z=-REAR_SEAT..SEAT_TOP 段,腔口取 **SEAT_R = R35 = 外壁**
的圆,把整圈挖到外壁,承压面因此变成**完整 R35 平圆盘**。
- 关键:**SEAT_R 必须等于 R35**。只要 SEAT_R < R35,两者之间那圈环墙
就必然存在,且相对承压面凸起 (R35-SEAT_R) —— 那圈环墙就是用户说的
"围挡"(P08 实测 1536.9mm3),不是残留而是我的取值必然产物。
- SEAT_TOP 取 +1.0(越过电机尾端面 1mm):2026-10-04 那版取 0 时,
半包开口处(Y93.0~95.8)会残留一截 0.052cm3 薄片脱空成碎块;
抬到 +1.0 后五件全部单实体零碎片。
- 承力改由**臂身本体**承担:band 段内 r36..58 有料的方位 P04 10/36、
其余四件各 15/36,其余方位靠 z>+1.0 的杯体连接,不存在整圈切断的通槽。
(2026-10-04 那版"肩叉顶部没连上"就是通槽形态,务必不要重现。)
【2026-10-05b 去掉 64.4 方腔,电机腔内壁恢复完整圆弧面】
此前 z=0..pcba_t 还叠了一层 `box(±32.2)` 方腔,理由是"PCB 是方的"。
**该假设不成立**:实测电机 STEP 已把 PCB 体积含在本体轮廓内,不会超出
投影范围,所以方腔纯属多挖。它带来两个副作用(用户截图 P06/P07/P08 腔内观感):
1. 方腔半宽 32.2 < 外壁 R35,在四个方位留下一圈 2.8mm 环形台肩;
2. 方腔对角 45.5 > R35,四个角把外壁咬穿,形成四个凸角。
两者叠加使内壁呈"圆—方—圆"三段,与同族零件的整体圆弧语言不一致。
现改为**单一 R35 圆腔从承压面一直挖到座孔起点 BORE_Z0(=5.5)**,
与其上的 R30.15 座孔正交相接,内壁成为两段同心正圆(仅一处正常台阶),
承压面到外壁之间再无任何凸起或方角。P04 走自己那套刀具,本来就无方腔。
"""
c = [cyl(BORE_R, BORE_Z0, COVER_Z - 3.5), cyl(CB_R, COVER_Z - 3.5, COVER_Z + front_pad),
# 承压面段 + 尾部过渡段:SEAT_R(=R35 外壁) 圆腔,从承压面一路挖到座孔起点。
# 取满 R35 才能让承压面与外壁齐平成一个平面;取小于 R35 就会留下
# 一圈凸起的"围挡"(见 docstring)。
cyl(SEAT_R, -REAR_SEAT, BORE_Z0)]
return shape.cut(*[self.L(s) for s in c])
def rear_mount_holes(self, shape, t_plate=8.0, hole_d=2.8, cb_d=5.5, cb_depth=4.0):
@@ -452,7 +533,7 @@ def collide():
("P05_大臂旋转座_螺旋体30%_墙4底5_竖打", "M3_J3"), ("P06_大臂骨架_螺旋体25%_墙4底5_竖打", "M3_J3"),
("P06_大臂骨架_螺旋体25%_墙4底5_竖打", "M4_J4"), ("P07_小臂骨架_螺旋体30%_墙5底5_竖打", "M4_J4"),
("P07_小臂骨架_螺旋体30%_墙5底5_竖打", "M5_J5"), ("P08_腕偏转座_螺旋体25%_墙4底5_竖打", "M5_J5"),
("P08_腕偏转座_螺旋体25%_墙4底5_竖打", "M6_J6"), ("P09_工具法兰_螺旋体100%_墙4底5_竖打", "M6_J6"),
("P08_腕偏转座_螺旋体25%_墙4底5_竖打", "M6_J6"), ("P09_工具法兰_螺旋体30%_墙4底5_竖打", "M6_J6"),
("P01_A_立柱下段_三角形30%_墙4底5_竖打", "P01_B_立柱中段_三角形30%_墙4底5_竖打"), ("P01_B_立柱中段_三角形30%_墙4底5_竖打", "P01_C_立柱上段_三角形30%_墙4底5_竖打"),
("P01_A_立柱下段_三角形30%_墙4底5_竖打", "P02_Center_底座中核心_螺旋体30%_墙5底6_平打"),
("P02_Center_底座中核心_螺旋体30%_墙5底6_平打", "P02_Front_前向防翻力臂_螺旋体30%_墙4底5_平打"),
+457 -99
View File
@@ -60,6 +60,200 @@ def XB(a0, a1, y0, y1, z0, z1):
return bx(X_ARM + a0, X_ARM + a1, y0, y1, z0, z1)
# ------------------------------------------------------------------ 减重(镂空)--------
# 三个手法件(P04/P06/P07)在关节根部都留着一大块"实心过渡料":壁厚最深到 15~17.5mm,
# 而 gyroid 填充只有 25~35%,等于用整块实心去抗一个安全系数 19~122 的截面。
# 下面两个 helper 把这块料掏掉,规则与 P03 的"帐篷形减重窗"一致:
# 1) 不留封闭空腔 —— 减重腔要么朝上敞开,要么是有进出口的窗,打印不憋气、不积料;
# 2) 斜面自支撑 —— 斜面与水平面夹角 >= 52°(与竖直面夹角 <= 38°),免支撑;
# 3) 内角 R4 圆角 —— 挖出来的洞会把圆截面变成尖角,Kt 从 ~1.2 抬到 ~2.5,
# 圆角把应力集中压回去;
# 4) 只吃"距表面 > 8mm"的厚料,四周至少留 8mm 壁 —— 受力纤维一根不碰。
HOLLOW_WALL = 8.0 # 减重腔与原外表面的最小间距(= 留下的最小壁厚)
HOLLOW_FILLET = 4.0 # 减重腔内角圆角,与 P03 竖板减重窗一致
# 三处减重腔的开度。这三个件都是绕 Y 轴弯,弯矩在 X 方向拉压,距中性面越远的料
# 贡献越大,所以"少挖、挖窄"是恢复强度最省料的做法。
# 数值由 scratch/tune_hollow.py 按"槽区最差安全系数 >= 无槽基线的 95%"扫出来:
# P04 档1 X23..35 Y102..154 Z>=525 -> 95% 基线,保留 69% 的减重量
# P07 档1 X−3..43 Y103..153 Z>=255 -> 96% 基线,保留 76%
# P06 档1 抬高窗底到 Z362(不是收窄) -> 100% 基线,保留 56%
# 窗底原来压在 Z358,而该处截面安全系数最低;抬高 4mm 比横向收窄 8mm 划算得多。
P04_CH = dict(x0=23.0, x1=35.0, y0=102.0, y1=154.0, z_floor=525.0) # 肩叉立柱
P07_CH = dict(x0=-3.0, x1=43.0, y0=103.0, y1=153.0, z_floor=255.0) # 小臂顶板
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 hollow_channel(x0, x1, y0, y1, z_floor, z_open, tag="", fil=HOLLOW_FILLET):
"""朝上敞开的减重槽:从 z_open 往下切到 z_floor,槽底水平(朝上受支撑)、侧壁竖直。
竖打(模型 Z = 打印 Z)下这个形状是零支撑的:槽底是水平面、槽壁全是竖直面,
顶部完全敞开因而不构成封闭空腔。切完该件变成 U 形槽钢而不是实心料:
抗弯靠两侧 X 向腹板(正好落在弯矩拉压区),中间中性层掏空几乎不损失 I。
x0/x1/y0/y1 用世界坐标;z_open 取一个保证贯通顶面的值。
"""
tool = bx(x0, x1, y0, y1, z_floor, z_open)
if fil > 0:
try:
tool = cq.Workplane(obj=tool).edges("|Z").fillet(fil).val()
except Exception as e:
print(" [减重槽] %s 内角圆角跳过: %s" % (tag, e), flush=True)
print(" [减重槽] %s: X%.1f..%.1f Y%.1f..%.1f 自 Z%.1f 向上敞开 (四周留壁 >=%.0fmm)"
% (tag, x0, x1, y0, y1, z_floor, HOLLOW_WALL), flush=True)
return tool
def hollow_tent_window(pts_xz, y0, y1, tag="", fil=HOLLOW_FILLET):
"""帐篷形减重窗:XZ 平面画轮廓,沿 Y 双向切穿。
轮廓下宽上窄(拱形)——窗顶上方的料形成一个尖顶,斜面与水平夹角 >= 52° 自支撑,
只有顶部一小段平顶需要桥接(<=20mm,与 P03 同)。轮廓在 X 方向不能吃满整个截面,
两侧必须留墙,否则该高度上零件会被切断、失去单实体。
"""
# XZ 工作平面的法线是 -Y,extrude 会朝 -Y 走,所以起点放在 y1 才能覆盖 y0..y1
tool = (cq.Workplane("XZ", origin=(0, y1, 0)).polyline(pts_xz).close()
.extrude(y1 - y0).val())
if fil > 0:
try:
tool = cq.Workplane(obj=tool).edges("|Y").fillet(fil).val()
except Exception as e:
print(" [减重窗] %s 内角圆角跳过: %s" % (tag, e), flush=True)
print(" [减重窗] %s: XZ 轮廓 %d 点, 沿 Y %.1f..%.1f 切穿 (帐篷形, 斜面自支撑)"
% (tag, len(pts_xz), y0, y1), flush=True)
return cq.Shape(tool.wrapped)
# 各关节"电机前端夹紧"统一配方(J2/J3/J4/J5 通用,J1 由 P03 自己那套 S04 凸台实现)。
# 电机前端盖外端面:Y 轴关节(J2/J4/J5)在 Y_COVER=156,压盖朝 +Y 生长;
# Z 轴关节(J3,roll 电机,轴向 -Z)在 Z_R-56,压盖朝 -Z 生长(-Z 侧是从动件)。
# 压盖背面与之 0.0 间隙面压,螺钉只提供压紧力。
# 沉孔相位按【关节局部相位】定义(BCD55,72.2°+90k):换算到全局后 Y 轴关节是
# 17.8°+90k(与 P03→M1 的 S04 一致),Z 轴关节是同一组数落在全局 (x,y) 平面,
# 两种轴向的物理指向完全一致 —— 全机同一套孔位约定,改一处必须同步另一处。
CLAMP_BCD = 27.5
CLAMP_CLOCK = (17.8, 107.8, 197.8, 287.8) # Y 轴关节的全局 (x,z) 相位
CLAMP_CLOCK_LOCAL = tuple((360.0 - a) % 360.0 for a in CLAMP_CLOCK) # = 72.2+90k
CLAMP_ROOT = 0.5 # 根部薄环厚度:与前端定位环前端面共面,保证熔接成一体
CLAMP_T = 5.0 # 压盖本体厚度
CLAMP_CLR = 1.0 # 从动件让位间隙:与 Y 轴关节 hub_gap=6.0 同一个道理
CLAMP_BACKOFF = CLAMP_T + CLAMP_CLR - CLAMP_ROOT # =5.5 从动件毂面整体后移量
# 【2026-10-05e】"电机前端围挡"统一整改开关,作用于全机 5 处电机前端(J2/J3/J4/J5/J6)。
# 围挡本体 = 各零件前端的 **前端定位环**(`front_collar_pocket` 把座孔从 R30.15 扩到
# R30.25 时留下的那圈 r30.25..35、6mm 高的环墙)。它比电机前端盖 Ø60.3 凸出 4.75mm,
# 把电机前端盖整圈围住;J2/J3/J4/J5 上还骑着一枚 R35 前端压盖(Ø70 再凸 3.2mm)。
# 用户 2026-10-05 先在 J3/M3 尾部指出,2026-10-05d 只切敞口那一圈解决;本次把同一刀
# 推广到其余四处(J2/M2、J4/M4、J5/M5、J6/M6),几何完全同构。
# 为什么不整圈削到与电机齐平(R35 -> R31.8):
# 1. 那圈壁与本体在两个方位与外壁**相切**,整圈切完那两片切向楔形会脱空成孤立碎块
# (实测 1.32mm3),破坏单实体拓扑;
# 2. 压盖根部只有 0.5mm 熔接带,环削窄后连接面从 2.37mm2 掉到 0.78mm2,
# 4xM3x8 的夹紧力会把压盖从本体上拉下来。
# 所以只切**敞口那一半**:背脊侧定位环继续定位电机前端盖、并作为压盖唯一且足够强的
# 支撑/熔接面;压盖本体、Ø37 让位孔、4xM3x8 沉孔、腔口下沿全部不动。
# 切口位置见 cut_motor_fence()。设 False 取消(恢复原来的整圈围挡)。
FENCE_OPEN_CUT = True
def cut_motor_fence(shape, j, tag, name3, z0=49.4, z1=56.1):
"""【2026-10-05e】只切掉电机前端围挡(前端定位环)在**敞口那一半**的环墙。
与 2026-10-05d 给 P05/M3 那一刀完全同源 —— 当时那记全局盒切
(X_ARM±40, Y_AXIS-40..Y_AXIS+3, Z388.9..395.6) 换算到 J3 局部系正好是
x±40 / y -3..+40 / z 49.4..56.1。本函数把它从"P05 专用全局盒"泛化成
"按关节局部坐标切半个环",J2/J4/J5/J6 于是同一刀通用。
局部坐标约定(见 trs/place):z 沿电机轴、指向电机前端盖(外端面在 z=COVER_Z=56),
x/y 在垂直于轴的平面内。**局部 +y 一律是半包敞口侧** —— 实测 5 处电机舱的
本体(环外 r=37/40 有料处 = 背脊侧)方位角全部落在局部角 180..360 = 局部 -y 侧:
J2/M2 背脊 240..315 J3/M3 背脊 225..315 J4/M4 背脊 210..330
J5/M5 背脊 210..330 J6/M6 背脊 195..345
故敞口方向无需逐件配置,统一取局部 +y。
为什么一对 z 参数能覆盖全机:5 处电机的前端定位环都恰好落在**局部 z 49.5..56**
(Y 轴电机前端环 Y150..156 -> z50..56;roll 电机 Z395.5..389 -> z49.5..56)。
被切掉的只有 r30.25..35 那一圈环墙在敞口侧的部分;压盖本体(4xM3x8 沉孔段)、
Ø37 让位孔、腔口下沿、背脊侧整圈定位环全部不动。
切口边界取在局部 y=-3.0 而不是 0:环壁在局部 y=-3 处仍与背脊侧实体连通,
切出来的是真正的缺口而不是零厚度缝,单实体拓扑天然保持
(P05 实测切完 solids=1;2026-10-05c 整圈削 R31.8 那版会掉出 1.32mm3 碎块)。
"""
if not FENCE_OPEN_CUT:
return shape
shape = shape.cut(j.L(bx(-40.0, 40.0, -3.0, 40.0, z0, z1)))
print(" [围挡] %s: 只切敞口侧那一圈前端定位环(局部 y -3..+40, z %.1f..%.1f);"
"背脊侧整圈保留定位前端盖并支撑压盖,压盖本体/沉孔/腔口下沿不变"
% (name3, z0, z1), flush=True)
return shape
def motor_front_clamp(px, pz, plate_t=CLAMP_T, tag="", axis="Y", py=0.0, z_face=None):
"""电机前端压盖:把"只被后端安装板单面锁死"改成前后双向面压夹紧。
原设计里每个关节的电机都只有后端板 4xM3x10(S02)锁在尾端面上,前端盖完全裸露,
大扭矩下电机绕自身轴在 Ø60.3 座孔里点头/翘起。这里在电机前端盖外端面加一枚
与后端板同径的 R35 压盖,用 4xM3x8(BCD55)在前端盖上再夹一次 —— 电机被前后两块
同心 Ø60.3 端面双向夹死,倾斜自由度归零;夹紧力全部走面压,螺钉不参与定位。
两个轴向共用同一套尺寸,只换刀具体系(不重复实现任何半径/相位):
axis="Y"(J2/J4/J5):关节轴过 (px, pz),前端盖外端面在 Y_COVER,压盖朝 +Y 长;
axis="Z"(J3 roll,轴向 -Z):关节轴过 (px, py),前端盖外端面在 z_face,压盖朝 -Z 长。
局部坐标(s 从前端盖外端面起算,s>0 一律"背离电机",两个轴向同号):
- 压盖 R35,s 从 -CLAMP_ROOT(与前端定位环前端面共面,保证熔接成一体)到 plate_t;
- 背面精确落在 s=0,不让根部那 0.5mm 啃到端盖 R30.15 的外圆;
- Ø37 中央让位孔(R18.5)让开从动件的直锁凸台 R17.8 与 3xS01 输出螺钉头;
- 4xM3x8:Ø3.4 通孔 + Ø6.0 沉孔深 3.0(自前端面沉到 s=2),圆柱头全沉入,
头面压孔口台阶,中间 2.0mm 压盖本体受压。
"""
if axis == "Y":
def C(r, s0, s1, du=0.0, dv=0.0):
return Ycyl(r, Y_COVER + s0, Y_COVER + s1, px + du, pz + dv)
elif axis == "Z":
def C(r, s0, s1, du=0.0, dv=0.0):
return cyl(r, z_face - s1, z_face - s0, px + du, py + dv)
else:
raise ValueError("motor_front_clamp: axis 只能是 Y 或 Z,收到 %r" % (axis,))
s0, s1 = -CLAMP_ROOT, plate_t
fp = C(35.0, s0, s1)
fp = fp.cut(C(18.5, s0 - 0.5, s1 + 0.5)) # Ø37 中央让位孔
fp = fp.cut(C(30.15, s0 - 0.5, 0.0)) # Ø60.3 让位槽
for deg in CLAMP_CLOCK_LOCAL:
rad = math.radians(-deg) # 全局相位 = -局部相位
fu, fv = CLAMP_BCD * math.cos(rad), CLAMP_BCD * math.sin(rad)
fp = fp.cut(C(3.0, 2.0, s1 + 0.5, fu, fv)) # Ø6.0 沉孔
fp = fp.cut(C(1.7, s0 - 0.5, 2.0, fu, fv)) # Ø3.4 通孔
where = ("Y%.1f..%.1f" % (Y_COVER - CLAMP_ROOT, Y_COVER + plate_t)) if axis == "Y" \
else ("Z%.1f..%.1f" % (z_face - plate_t, z_face + CLAMP_ROOT))
print(" [前端压盖] %s: R35 %s, 4xM3x8 BCD55@%s (%s 轴)"
% (tag, where, "/".join("%.1f" % d for d in CLAMP_CLOCK), axis), flush=True)
return fp
def hub_m2_holes(shape, j, cb_z1, th_z1=58.0, th_z0=54.0, cb_z0=58.0,
th_r=1.4, cb_r=2.8):
"""在从动毂上开 4x M2 直锁螺钉孔:Ø2.8 通孔 + Ø5.6 沉孔,BCD 见 AB.M["m2_bcd"]。
全机 5 处(J1 叉板 / J2+J3 直连毂 / J4+J5 直连毂)原本各抄一份同样的循环,
只有沉孔顶 z 与销孔顶 z 两处随件而异 —— 抄一次错一处就是一处真 bug。
现在统一走这里,各调用点只传自己那两个 z。
cb_z0 默认 58.0 = th_z1,台阶因此落在通孔顶端;P03 叉板那一处例外
(沉孔台阶固定在 63.0 不随凸台前移,见 p_fork 的注释),故两个 z 都可传。
"""
for a in AB.HUB_M2_CLOCK:
u = AB.M["m2_bcd"] / 2.0 * math.cos(a * AB.D2R)
v = AB.M["m2_bcd"] / 2.0 * math.sin(a * AB.D2R)
shape = shape.cut(j.L(cyl(th_r, th_z0, th_z1, u, v)))
shape = shape.cut(j.L(cyl(cb_r, cb_z0, cb_z1, u, v)))
return shape
def make_direct_hub(shape, j, r_disc=35.0, z_out=74.0):
"""优化直连法兰:增加导向倒角"""
disc = cyl(r_disc, 56.5, z_out)
@@ -67,13 +261,7 @@ def make_direct_hub(shape, j, r_disc=35.0, z_out=74.0):
chamfer_cone = cyl(18.5, 54.5, 54.942)
shape = shape.fuse(j.L(disc)).fuse(j.L(boss)).cut(j.L(chamfer_cone))
shape = shape.cut(j.L(cyl(3.5, 54.0, z_out + 30.0)))
for a in AB.HUB_M2_CLOCK:
u = AB.M["m2_bcd"] / 2.0 * math.cos(a * AB.D2R)
v = AB.M["m2_bcd"] / 2.0 * math.sin(a * AB.D2R)
th = cyl(1.4, 54.0, 58.0, u, v)
cb = cyl(2.8, 58.0, z_out + 40.0, u, v)
shape = shape.cut(j.L(th)).cut(j.L(cb))
return shape
return hub_m2_holes(shape, j, cb_z1=z_out + 40.0)
# ========== 躯干部分 ==========
@@ -825,14 +1013,14 @@ def p_girdle(j1):
# Ø60.7 圆柱孔壁从前端面平直贯通到舱底(无窄颈、无台阶、无喇叭锥);
# 舱底 = 一个完整的平坦环面(X=5.06+X_BAY_EXT=8.06,与插槽末端墙共面,中开 Ø39 凸台过孔)
# ——即电机前限位面。电机轴向由后端法兰 4xM3x16 约束;
# 前端锁紧由 4 个 Ø10 局部凸台上的 M3 沉头螺钉承担(见下方凸台段)。
# 前端锁紧由 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,17.8°/107.8°/197.8°/287.8°):
# 平直舱孔的沉头座位由 4 个 Ø10 局部凸台承载——凸台位于舱孔前段 8.06..11.5,
# 电机前端面(5.0)之前 3mm,电机斜插路径不经过;M3x10 沉头从凸台端面旋入
# 电机前端面,咬合 3.5mm,兼作电机前端防转定位。
# 电机前端面(5.0)之前 3mm,电机斜插路径不经过;M3x8 圆柱头坐在凸台沉孔
# 底(x=9.5)旋入电机前端面,咬合 3.5mm,兼作电机前端防转定位。
for deg in (17.8, 107.8, 197.8, 287.8):
rad = math.radians(deg)
fy = Y_ARM + 27.5 * math.cos(rad)
@@ -899,27 +1087,10 @@ def p_fork(j1, j2):
front_collar = Ycyl(35.0, Y_COVER - 6.5, Y_COVER - 0.5, X_ARM, Z_SH)
s = rear_plate.fuse(front_collar)
# 2b. 【2026-10-02 新增】M2 前端压盖 (Y: 155.5..161.0, R=35,与后端板同径)
# 根部与 front_collar 前端面 (Y_COVER-0.5=155.5) 共面熔接成一体,无缝隙、无悬浮面。
# 压盖背面 (Y_COVER=156.0) 与电机前端盖外端面 0.0 间隙面压贴合;Ø37 中央让位孔
# 让出 P05 的 J2 直锁凸台 (R17.8) 与 3xS01 输出螺钉头;Ø60.3 让位槽把压盖背面
# 精确落在 Y=156.0,不让根部那 0.5mm 啃到电机前端盖 R30.15 的外圆。
FP_Y0 = Y_COVER - 0.5 # 155.5 压盖根部(= front_collar 前端面,保证熔接)
FP_Y1 = Y_COVER + 5.0 # 161.0 压盖前端面(与 P05 法兰盘 162.0 留 1.0mm 动间隙)
FP_BORE = 27.5 # 4xM3 分布圆半径 (BCD55,与 P03→M1 的 S04 同约定)
fp = Ycyl(35.0, FP_Y0, FP_Y1, X_ARM, Z_SH)
fp = fp.cut(Ycyl(18.5, FP_Y0 - 0.5, FP_Y1 + 0.5, X_ARM, Z_SH)) # Ø37 中央让位孔
fp = fp.cut(Ycyl(30.15, FP_Y0 - 0.5, Y_COVER, X_ARM, Z_SH)) # Ø60.3 让位槽
for deg in (17.8, 107.8, 197.8, 287.8):
rad = math.radians(deg)
fu, fz = X_ARM + FP_BORE * math.cos(rad), Z_SH + FP_BORE * math.sin(rad)
# Ø6.0 沉孔深 3.0 (自前端面 FP_Y1 往 -Y 沉到 Y=158.0):M3 圆柱头(Ø5.5x3.0)
# 全沉入压盖,头面 Y=158.0 压在 Ø3.4 孔口台阶上;压盖背面(156.0)压电机前端盖,
# 中间 2.0mm 压盖本体受压 —— 夹紧力全部走面压,螺钉只提供压紧力不提供定位。
fp = fp.cut(Ycyl(3.0, Y_COVER + 2.0, FP_Y1 + 0.5, fu, fz))
fp = fp.cut(Ycyl(1.7, Y_COVER - 0.5, Y_COVER + 2.0, fu, fz))
# 压盖在第 7 步 M2 接口成型(座孔/让位槽)之后才熔接,避免被那些 R30.15~30.5 的
# 圆柱刀具蹭掉根部一圈 0.1mm 薄边。
# 2b. 【2026-10-02 新增】M2 前端压盖(几何与理由见 motor_front_clamp)。
# 必须在第 6/7 步所有 R30.15~30.5 圆柱刀具走完之后才熔接,否则压盖根部会被
# front_collar_pocket / c_m2 啃掉一圈 0.1mm 薄边(见 7b)。
fp = motor_front_clamp(X_ARM, Z_SH, tag="P04/M2")
# 3. J1 从动法兰圆盘已删除:原 R=36.5 圆盘盘面在 column 前缘形成圆盘鼓楞,
# J1 与 P04 的连接由 boss(Ø35.6 贯通 P03 前盖中心孔 R=19.5)直连电机输出盘
@@ -964,14 +1135,34 @@ def p_fork(j1, j2):
xz_shape = cq.Shape(xz_wire.extrude(100.0, both=True).val().wrapped)
# 5. YZ 平面包络
yz_pts = [
(Y_AXIS - 36.5, Z_RL + 36.5),
(Y_AXIS + 36.5, Z_RL + 36.5),
(Y_COVER - 0.5, Z_SH + 36.0),
(Y_COVER - 0.5, Z_SH),
(Y_REAR - 8.0, Z_SH),
(Y_REAR - 8.0, Z_SH + 36.0),
]
# 【2026-10-04 视觉整改】电机上方那块 4048mm2 的大平面改成 Hermite 弧面。
# 原来第 2->3 点之间是一条直线 (164.5,646.5)->(155.5,526),沿 X 方向拉成
# 4048mm2 的平板(实测法向 (0,1,-0.07),带 4 度斜度),正对电机上方 ——
# 两侧被 xz_wire 的 R18 圆弧挤出去,形成"中间一块平、两边鼓"的观感。
# 改法:把这段直线换成三次 Hermite 曲线,两端切线分别沿 Y(接上面 Z=646.5
# 的顶段)与沿 Z(接下面 Y=155.5 的竖直段),保证 G1 连续无缝;
# 中部向 +Y 外凸约 3mm,弧度观感与两侧 R18 一致。
# 为什么不用单段圆弧:A 处切线要沿 Y、B 处要沿 Z,单个圆心无法同时满足
# (解出来 R=9 与 R=120.5 矛盾),必须用 Hermite/Bezier —— 与 P05/P06
# 的 Loft 过渡同一套做法。
# 注意:顶点绕序必须与原列表一致(先 y_b 后 y_a),否则多边形自交、
# 布尔后零件会碎掉(实测会掉到 48cm3 / 3 块实体)。
y_a = (Y_COVER - 0.5, Z_SH + 36.0) # (155.5, 526.0) 曲线终点
y_b = (Y_AXIS + 36.5, Z_RL + 36.5) # (164.5, 646.5) 曲线起点
_d = math.hypot(y_b[0] - y_a[0], y_b[1] - y_a[1])
_m0y, _m1z = -0.5 * _d * 0.35, 0.5 * _d * 0.5 # 切线:起点沿 -Y,终点沿 +Z
yz_pts = [(Y_AXIS - 36.5, Z_RL + 36.5)]
for k in range(13):
t = k / 12.0
h00 = 2 * t ** 3 - 3 * t ** 2 + 1
h10 = t ** 3 - 2 * t ** 2 + t
h01 = -2 * t ** 3 + 3 * t ** 2
h11 = t ** 3 - t ** 2
yz_pts.append((h00 * y_b[0] + h10 * _m0y + h01 * y_a[0],
h00 * y_b[1] + h11 * _m1z + h01 * y_a[1]))
yz_pts += [(Y_COVER - 0.5, Z_SH),
(Y_REAR - 8.0, Z_SH),
(Y_REAR - 8.0, Z_SH + 36.0)]
yz_solid = Xplate(yz_pts, -20.0, 60.0)
c_int = BRepAlgoAPI_Common(yz_solid.wrapped, xz_shape.wrapped)
@@ -984,33 +1175,60 @@ def p_fork(j1, j2):
s = s.cut(Ycyl(30.5, Y_REAR + 0.5, Y_COVER - 6.5, X_ARM, Z_SH))
# 7. M2 接口成型
# 承压面起点用 AB.REAR_SEAT(=0.6 台肩 + 2.0 公差),与 Joint.pocket 同源:
# P04 走的是自己那套 BORE_R 圆柱刀具(没调 Joint.pocket),这里必须同步改,
# 否则 J2 关节的电机公差位仍是旧的 0.6,与 J3~J6 不一致。
# 【2026-10-05】承压面段加一刀 SEAT_R(=R35) 圆腔,SEAT_TOP=+1.0。
# 取满 R35 才能让承压面与外壁齐平成**一个平面**(用户要求:把凸台、围挡
# 一起弄平,做成和电机两头齐平的平面)。取小于 R35 必然留下一圈凸起
# 2.6mm 的围挡环墙 —— 详见 Joint.pocket 的 docstring。
s = j2.front_collar_pocket(s, r_bore=30.25, z0=50.0, z1=56.5)
s = j2.rear_mount_holes(s, t_plate=8.0, hole_d=2.8, cb_d=5.5, cb_depth=4.0)
c_m2 = [cyl(BORE_R, -0.6, COVER_Z - 3.5), cyl(CB_R, COVER_Z - 3.5, COVER_Z + 0.6)]
c_m2 = [cyl(BORE_R, -AB.REAR_SEAT, COVER_Z - 3.5), cyl(CB_R, COVER_Z - 3.5, COVER_Z + 0.6),
cyl(AB.SEAT_R, -AB.REAR_SEAT, AB.SEAT_TOP)]
s = s.cut(*[j2.L(item) for item in c_m2])
# 7b. M2 前端压盖熔接(见 2b):必须在 6/7 步所有 R30.15~30.5 圆柱刀具走完之后,
# 否则压盖根部会被 front_collar_pocket / c_m2 啃掉一圈 0.1mm 薄边。
s = s.fuse(fp)
# 7b2.【2026-10-05e】删掉前端"围挡"在敞口侧的那半圈环墙(见 FENCE_OPEN_CUT)。
# 围挡 = front_collar_pocket 把座孔扩到 R30.25 后留下的那圈 r30.25..35、6mm 高
# 环墙(Y149.5..155.5),比电机前端盖 Ø60.3 凸 4.75mm,把前端盖整圈围住;
# 上面还骑着 R35 压盖(Ø70 再凸 3.2mm)。切敞口(局部 +y = 全局 -Z)那半圈后,
# 背脊(局部 -y = 全局 +Z)侧整圈保留:继续定位前端盖、并作为压盖唯一且足够强
# 的支撑面(4.75mm 宽 x 0.5mm 熔接带,本次完全不碰)→ M2 仍是
# 后端板 4xM3x10 + 前端压盖 4xM3x8 双向面压;压盖本体/Ø37 孔/4xM3x8 沉孔/
# 腔口下沿 Y155.5 全部不动。
s = cut_motor_fence(s, j2, "P04/M2", "P04")
# 7c. 【2026-10-02 新增】立柱减重槽(见 hollow_channel)。
# 立柱 Z525..635 整段是壁厚最深处达 17.5mm 的实心料,而 J2 在 z=560/600 的
# 安全系数有 19。挖成朝上敞开的 U 形槽后立柱变成槽钢:抗弯的两片 X 向腹板
# 正好落在弯矩拉压区(z=560 截面 X13..45,中性面在 X29,腹板占 X13~23/X35~45),
# 被掏掉的正是中性层附近几乎不参与抗弯的料。
# 槽底 Z525 以下保留整块实心过渡到电机座;顶部敞开故非封闭腔、免支撑。
# 必须在 8 步 J1 凸台/螺钉孔之前切:槽的 X23..35 范围离 J1 凸台(X<=11.5)与
# 3xS01 沉孔(X12..14)都有富余,但先切可以保证后面那些刀具不会把槽壁打薄。
s = s.cut(hollow_channel(P04_CH["x0"], P04_CH["x1"], P04_CH["y0"], P04_CH["y1"],
P04_CH["z_floor"], 660.0, tag="P04立柱"))
# 8. 头部转接凸台加长(贯通 P03 前盖中心孔,跨越装配缝直连 J1 输出盘)
boss = cyl(17.8, 54.942, 61.5 + X_FORK_GAP)
s = s.fuse(j1.L(boss))
s = s.cut(j1.L(cyl(3.5, 54.0, 74.0 + 30.0)))
for a in AB.HUB_M2_CLOCK:
u = AB.M['m2_bcd'] / 2.0 * math.cos(a * AB.D2R)
v = AB.M['m2_bcd'] / 2.0 * math.sin(a * AB.D2R)
th = cyl(1.4, 54.0, 63.0 + X_FORK_GAP, u, v)
# 沉孔台阶固定在 63.0(不随前移):M2x10 旋入电机输出毂的咬合量与改前一致;
# 螺钉头(63..65)相对叉板背面(64.5)外露 0.5mm,位于 2mm 叉缝空隙内,无干涉。
cb = cyl(2.8, 63.0, 74.0 + 40.0 + X_FORK_GAP, u, v)
s = s.cut(j1.L(th)).cut(j1.L(cb))
# 沉孔台阶固定在 63.0(不随前移):M2x10 旋入电机输出毂的咬合量与改前一致;
# 螺钉头(63..65)相对叉板背面(64.5)外露 0.5mm,位于 2mm 叉缝空隙内,无干涉。
s = hub_m2_holes(s, j1, cb_z1=74.0 + 40.0 + X_FORK_GAP,
th_z1=63.0 + X_FORK_GAP, cb_z0=63.0)
return s
def make_pitch_to_roll_bracket(j_pitch, j_roll, z_pitch, z_r, name="bracket", hub_gap=1.0):
def make_pitch_to_roll_bracket(j_pitch, j_roll, z_pitch, z_r, name="bracket", hub_gap=1.0,
roll_clamp=False):
"""Half-wrapped (半包) 双曲面有机流线型 Pitch-to-Roll 结构座 (P05 & P08):
1. 顶部从动法兰与前侧 Loft 自然相切顺接;
2. 严格清除 z_plate 顶面以上与上级旋转件贴面的多余材料 (cut_top_solid),保证关节大范围摆动动态 0 干涉;
@@ -1020,9 +1238,15 @@ def make_pitch_to_roll_bracket(j_pitch, j_roll, z_pitch, z_r, name="bracket", hu
hub_gap = 从动法兰盘背面与上级电机前端盖之间的轴向让位间隙。
默认 1.0(贴合防磨)。P05 传 6.0:J2 处 P04 新增了 M2 前端压盖
(Y 155.5..161.0),必须让出这 6.0mm 才能装下;见 p_shoulder_roll_base。
roll_clamp = 本件作为 roll 电机【前端夹紧件】时为 True(J3/P05 夹 M3)。
roll 电机(J3/J6)轴向是 -Z,前端盖外端面在 z_r-56,-Z 侧是从动件:
压盖朝 -Z 长(Z_COVER-5..Z_COVER),从动件毂面必须整体后移 CLAMP_BACKOFF=5.5
让位(与 Y 轴关节 hub_gap=6.0 同一个道理,只是基准间隙 0.5 而非 1.0)。
P08 传 False:J6 的 M6 前端压盖尚未做(从动件 P09 让位要改工具法兰直锁凸台)。
"""
z_bot = z_r - 56.0 # roll 电机底端 (front collar)
z_plate = z_r + 7.0 # roll 电机顶端安装板 (rear plate top)
z_plate = z_r + 8.0 # roll 电机顶端安装板 (rear plate top);见第 8 步 t_plate 说明
y_out = HL.Y_HUB + HL.PLATE_T # 174.0 (俯仰直连法兰外侧面)
y_hub_in = HL.Y_COVER + hub_gap # 157.0 / (P05) 162.0 从动法兰内侧面
z_hub_in = y_hub_in - HL.Y_REAR # 法兰盘背面的电机局部 z(凸台长度跟着它走)
@@ -1096,7 +1320,10 @@ def make_pitch_to_roll_bracket(j_pitch, j_roll, z_pitch, z_r, name="bracket", hu
s = s.cut(clearance)
# 8. 下方电机安装孔位与定位止口
s = j_roll.rear_mount_holes(s, t_plate=7.0, hole_d=2.8, cb_d=5.5, cb_depth=4.0)
# t_plate=8.0:Joint.pocket 的尾端面承压面让空了 MOTOR_TOL=2.0,承压面到螺钉沉孔
# 台阶只剩 t_plate-cb_depth-REAR_SEAT = 8-4-2.6 = 1.4mm 膜厚;给到 8 而不是原来的
# 7,是为了不让这层膜退化成 0.4mm。7->8 让 P05/P08 的杯体外壳顶 z_plate 抬 1mm。
s = j_roll.rear_mount_holes(s, t_plate=8.0, hole_d=2.8, cb_d=5.5, cb_depth=4.0)
s = j_roll.front_collar_pocket(s, r_bore=30.25, z0=50.0, z1=56.5)
s = j_roll.pocket(s)
@@ -1109,12 +1336,7 @@ def make_pitch_to_roll_bracket(j_pitch, j_roll, z_pitch, z_r, name="bracket", hu
boss = cyl(17.8, 54.942, z_hub_in + 2.5)
s = s.fuse(j_pitch.L(boss))
s = s.cut(j_pitch.L(cyl(3.5, 54.0, 74.0 + 30.0)))
for a in AB.HUB_M2_CLOCK:
u = AB.M["m2_bcd"] / 2.0 * math.cos(a * AB.D2R)
v = AB.M["m2_bcd"] / 2.0 * math.sin(a * AB.D2R)
th = cyl(1.4, 54.0, 58.0, u, v)
cb = cyl(2.8, 58.0, 74.0 + 40.0, u, v)
s = s.cut(j_pitch.L(th)).cut(j_pitch.L(cb))
s = hub_m2_holes(s, j_pitch, cb_z1=74.0 + 40.0)
# 11. L 型转角连接处圆角过渡(圆角 R=2.0mm):
# 平滑消除立柱顶面与竖直臂交汇处的 90° 尖角与应力集中
@@ -1125,19 +1347,60 @@ def make_pitch_to_roll_bracket(j_pitch, j_roll, z_pitch, z_r, name="bracket", hu
except Exception as e:
print(f"[{name}] L型转角圆角异常: {e}")
# 12. roll 电机(M3)前端压盖:与 P04/P06/P07 的 M2/M4/M5 压盖同一套配方,
# 只是 roll 电机轴向为 -Z,压盖朝 -Z 长(见 motor_front_clamp 的 axis="Z")。
# 熔接必须放在最后:第 6/7/8/9 步所有 R30.15~30.5 圆柱刀具都走完之后才接,
# 否则压盖根部那 0.5mm 薄环会被 front_collar_pocket / 定子孔啃掉一圈。
if roll_clamp:
s = s.fuse(motor_front_clamp(HL.X_ARM, 0.0, tag="%s/M3" % name[:3],
axis="Z", py=HL.Y_AXIS, z_face=z_r - COVER_Z))
# 13.【2026-10-05d】只切"围挡"那一圈:敞口(-Y)侧的前端定位环 + 压盖根部薄环。
# 用户 2026-10-05 指出这圈环墙把 M3 前端盖和 P06 的 J3 凸台围在中间;我第一版
# 把整个前端(定位环 + 压盖 + 腔口下沿 389->395)都切了,电机前端没了支撑面 ——
# 过度。现在只切敞口这一圈,Y 界限与第 6 步 cut_open 完全一致
# (Y_AXIS-40 .. Y_AXIS+3),保证 -Y 半圈从 Z=395.5 一路开到压盖背面 Z=389;
# 背脊(+Y)侧整圈定位环保留:
# - 继续定位 M3 前端盖(内孔 r30.25,前端盖 Ø60.3 -> 单边 0.10mm);
# - 继续当 R35 压盖唯一且足够强的支撑面(4.75mm 宽 x 0.5mm 熔接带,本次改动
# 完全不碰)→ M3 仍是后端板 4xM3x10 + 前端压盖 4xM3x8 双向面压夹紧;
# - 压盖本体 / Ø37 让位孔 / 4xM3x8 沉孔 / 腔口下沿 z_bot=389 全部不动,
# 压盖敞口侧的端面从 Z=389.5 降到 389 与背面齐平,视觉上不再凸出。
# 为什么不能整圈削到与电机齐平(R35->R31.8,见 FENCE_OPEN_CUT 处的说明):
# ① 这圈壁与第 3 步 Loft 在 X=±35 处与 Ø70 柱面**相切**,整圈切完那两片切向楔形
# (实测 1.32mm3)会脱空成孤立碎块,破坏单实体拓扑;
# ② 压盖根部只有 0.5mm 熔接带,环削到 1.55mm 宽后连接面从 2.37mm2 掉到 0.78mm2,
# 4xM3x8 的夹紧力会把压盖从本体上拉下来。
# 附带效果:盒切把 Loft 在 X=±35 与 Ø70 柱面相切的那两片切向楔形一起带走
# (楔形位于 Y_AXIS 附近 < Y_AXIS+3),不会留下孤立碎块 -> 单实体拓扑保持。
# 13.【2026-10-05e】删掉前端"围挡"在敞口侧的那半圈环墙(见 FENCE_OPEN_CUT)。
# P05(M3) 有前端压盖、环上骑着压盖;P08(M6) 还没有 M6 前端压盖(roll_clamp=False),
# 环是光板 —— 两者都只切敞口(局部 +y)那半圈定位环,背脊侧整圈保留,
# 继续定位电机前端盖。P05 的压盖因此仍有 4.75mm x 0.5mm 的熔接带作支撑面。
if roll_clamp or FENCE_OPEN_CUT:
s = cut_motor_fence(s, j_roll, "%s/M3" % name[:3], name[:3])
return s
def p_shoulder_roll_base(j2, j3, half=None):
"""P05_大臂旋转座_螺旋体30%_墙4底5_竖打(深度圆角优化版)
hub_gap=6.0:J2 处 P04 新增了 M2 前端压盖(前端夹紧电机,消除大扭矩下的倾斜),
压盖占 Y 155.5..161.0,所以从动法兰盘背面必须从 Y=157 后移到 Y=162(留 1.0mm 动间隙)。
P08 保持默认 1.0 —— J5 处 P07 没有前端压盖,不需要这段轴向空间。
本件在链路上有两重身份,两处让位都是为前端压盖:
1. hub_gap=6.0(从动侧):J2 处 P04 新增了 M2 前端压盖(前端夹紧电机,消除大扭矩下的
倾斜),压盖占 Y 155.5..161.0,所以从动法兰盘背面必须从 Y=157 后移到 Y=162
(留 1.0mm 动间隙)。
2. roll_clamp=True(前端夹紧侧):P05 自己抱着 M3(J3,-Z 轴),原来也只有后端板
4xM3x10 单面锁死;现在按 motor_front_clamp 的统一配方在 M3 前端盖外端面
(Z=389)加一枚 R35 压盖(Z 384..389,4xM3x8 BCD55),与后端板前后双向面压,
倾斜自由度归零。代价是对侧从动件 P06 的 J3 毂面整体后移 CLAMP_BACKOFF=5.5
(Z 388.5→383.0),见 p_humerus_bone。
P08 保持默认 —— J5 处 P07 的前端压盖已做(只需 6.0 的 hub_gap),而 J6 的 M6
前端压盖尚未做,故 roll_clamp 传 False。
"""
return make_pitch_to_roll_bracket(j2, j3, Z_SH, Z_R3,
name="P05_大臂旋转座_螺旋体30%_墙4底5_竖打",
hub_gap=6.0)
hub_gap=6.0, roll_clamp=True)
def p_humerus_bone(j3, j4):
@@ -1145,11 +1408,17 @@ def p_humerus_bone(j3, j4):
受 J3 (大臂自转, -Z轴, hub@Z_HUB3=380) 驱动,
向下延伸构成大臂主体骨架,底端抱紧 M4 (肘部俯仰电机, +Y轴, 轴线位于 Z_EL=332.0)。
1. 借鉴 P05 大臂旋转座几何放样算法:
- 顶端结合面 (Z = 388.5mm):与 J3 轴径严格相切的 Ø70mm 正圆形轮廓,彻底消除原先外突的方形水平阶梯;
- 顶端结合面 (Z = 383.0mm):与 J3 轴径严格相切的 Ø70mm 正圆形轮廓,彻底消除原先外突的方形水平阶梯;
- 底端结合面 (Z = 367.0mm):平滑展开融入 M4 电机套筒的 70x63.5mm 方形截面;
- 过渡区 (Z: 367.0..388.5mm):采用 Hermite s(t) = 3t² - 2t³ 驱动 20 层放样切片连续渐变;
- 过渡区 (Z: 367.0..383.0mm):采用 Hermite s(t) = 3t² - 2t³ 驱动 20 层放样切片连续渐变;
2. 下部抱紧套筒与安装板 (R=35mm),电机定子 100% 裸露散热开窗;
3. 严格单实体 (solids = 1) 且与上下级旋转构件及电机实现动态 0 碰撞干涉。
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. 严格单实体 (solids = 1) 且与上下级旋转构件及电机实现动态 0 碰撞干涉。
"""
# 1. M4 底部抱紧套筒与后端安装板 (R=35mm)
rear_m4 = Ycyl(35.0, Y_REAR - 8.0, Y_REAR, X_ARM, Z_EL)
@@ -1157,7 +1426,10 @@ def p_humerus_bone(j3, j4):
s = rear_m4.fuse(front_m4)
# 顶端结合面与过渡高度
z_top = 388.5
# 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)
@@ -1199,17 +1471,50 @@ def p_humerus_bone(j3, j4):
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)
# 6. J3 直连法兰结构(凸台延伸至 59.5 跨过结合面牢固熔接,保持单实体与零干涉)
boss = cyl(17.8, 54.942, 59.5)
# 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】删掉前端"围挡"在敞口侧的那半圈环墙(见 FENCE_OPEN_CUT)。
# 配方与 P04/P07 的 M2/M5 完全同源(同一套 front_collar_pocket + motor_front_clamp),
# 局部 z 50..56 的 r30.25..35 环墙切掉敞口(局部 +y = 全局 -Z)那半圈;背脊侧整圈
# 保留,继续定位 M4 前端盖并支撑压盖 → M4 前后双向面压不变。
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%(这段本来就不缺强度),减重收益纯属锦上添花。
# 保留 P06_WIN_XZ 与本段注释,需要减重时把开关改回 True 即可复原。
if P06_HOLLOW_WIN:
s = s.cut(hollow_tent_window(P06_WIN_XZ, 80.0, 175.0, tag="P06过渡块"))
else:
print(" [减重窗] P06过渡块: 已关闭(2026-10-03 视觉整改),该段恢复实心", flush=True)
# 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)))
for a in AB.HUB_M2_CLOCK:
u = AB.M["m2_bcd"] / 2.0 * math.cos(a * AB.D2R)
v = AB.M["m2_bcd"] / 2.0 * math.sin(a * AB.D2R)
th = cyl(1.4, 54.0, 58.0, u, v)
cb = cyl(2.8, 58.0, 58.0 + 40.0, u, v)
s = s.cut(j3.L(th)).cut(j3.L(cb))
s = hub_m2_holes(s, j3, cb_z1=58.0 + 40.0)
return s
@@ -1224,9 +1529,14 @@ def p_forearm_bone(j4, j5, half=None):
2. 下方 M5 电机实行半包散热结构,底部与内腔开窗散热
3. J4 轴俯仰动态行程从受限的 ±54° 大幅释放至无死角大范围摆动(零干涉)
4. 满足单实体 (solids=1) 与装配零碰撞。
5.【2026-10-02】M5 前端压盖(4xM3x8 BCD55)消除"电机只被后端板单面锁死"的倾斜隐患;
J4 从动侧整体让位:y_hub_in 由 158.5 后移到 J4_CLEAR=162.0,让出 P06 的 M4 前端压盖
(前端面 Y=161.0),留 1.0mm 动间隙。J4 直连凸台同步加长,3xS01 输出螺钉咬合量不变。
"""
y_out = Y_HUB + PLATE_T # 174.0
y_hub_in = HL.Y_COVER + 0.5 + CLEARANCE_GAP # 158.5
# J4 从动侧让位基准:必须 >= P06 的 M4 前端压盖前端面(Y_COVER+5.0=161.0) + 1.0mm
J4_CLEAR = Y_COVER + 6.0 # 162.0
y_hub_in = J4_CLEAR
y_front = Y_COVER - 0.5 # 155.5
y_rear = Y_REAR - 8.0 # 92.0
z_top_housing = Z_WR + 39.0 # 261.0 (顶面加厚 4.0mm,消除过薄破面问题)
@@ -1289,6 +1599,16 @@ def p_forearm_bone(j4, j5, half=None):
p08_clearance = Ycyl(35.5, 156.0, y_out + 2.0, X_ARM, Z_WR)
s = s.cut(p08_clearance)
# 6b. 【2026-10-02 新增】顶板根部减重槽(见 hollow_channel)。
# 顶板在 Z255..280 一整段是壁厚最深 15mm 的实心料,而 J4 在 z=245 的安全系数
# 高达 122、z=195 也有 24。挖成朝上敞开的 U 形槽,顶板变成槽钢:
# 槽底 Z255 以下保留整块实心到电机座,抗弯的两片 X 向腹板(X-15..-7 / X47..55)
# 留在弯矩拉压区。Y 上限取 156 而非核心的 166,是为了避开 y_hub_in=158.5 起的
# 竖直立柱本体(立柱是 J4 从动法兰的承力柱,不能掏)。
# 槽顶 Z280 就是顶板上表面,敞口向上,免支撑、非封闭腔。
s = s.cut(hollow_channel(P07_CH["x0"], P07_CH["x1"], P07_CH["y0"], P07_CH["y1"],
P07_CH["z_floor"], 300.0, tag="P07顶板"))
# 7. M5 电机安装接口与定子空腔
s = j5.front_collar_pocket(s, r_bore=30.25 + CLEARANCE_GAP, z0=50.0, z1=56.5)
s = j5.rear_mount_holes(s, t_plate=8.0, hole_d=2.8, cb_d=5.5, cb_depth=4.0)
@@ -1299,16 +1619,24 @@ def p_forearm_bone(j4, j5, half=None):
vent_cut = bx(X_ARM - 40.0, X_ARM + 40.0, y_rear + 8.0, y_front - 6.0, Z_WR - 40.0, Z_WR - 25.0)
s = s.cut(vent_cut)
# 8. J4 直连法兰结构(凸台延伸至 60.0 跨过间隙与法兰盘牢固熔接,保持单实体)
boss = cyl(17.8, 54.942, 60.0)
# 7b. 【2026-10-02 新增】M5 前端压盖 4xM3x8(BCD55):M5 原来只被后端板单面锁死,
# 大扭矩下会绕 J5 轴在座孔里点头。现在前端也面压夹一次(见 motor_front_clamp)。
# 必须在第 6 步 p08_clearance(R35.5 让位孔,Y>=156)之后熔接,否则压盖会被削掉。
# P08 的 J5 从动侧已相应后移到 Y>=162(make_pitch_to_roll_bracket 的 hub_gap=6.0)。
s = s.fuse(motor_front_clamp(X_ARM, Z_WR, tag="P07/M5"))
# 7c2.【2026-10-05e】删掉前端"围挡"在敞口侧的那半圈环墙(见 FENCE_OPEN_CUT)。
# 与 P04/P06 同源。注意本件的 front_collar_pocket 用了 r_bore=30.25+CLEARANCE_GAP
# =32.25(2.0mm 关节间隙),环壁因此更薄(r32.25..35),切法与配方完全一致。
s = cut_motor_fence(s, j5, "P07/M5", "P07")
# 8. J4 直连法兰结构(凸台延伸到法兰盘背面之后 2.0mm 跨过间隙与法兰盘牢固熔接,
# 保持单实体)。凸台长度跟着 y_hub_in 走:法兰盘后移时凸台同步加长,
# 3xS01 输出螺钉的头面台阶(局部 z=58)、杆长、咬合量与改前完全一致。
boss = cyl(17.8, 54.942, (y_hub_in - HL.Y_REAR) + 2.0)
s = s.fuse(j4.L(boss))
s = s.cut(j4.L(cyl(3.5, 54.0, 74.0 + 30.0)))
for a in AB.HUB_M2_CLOCK:
u = AB.M["m2_bcd"] / 2.0 * math.cos(a * AB.D2R)
v = AB.M["m2_bcd"] / 2.0 * math.sin(a * AB.D2R)
th = cyl(1.4, 54.0, 58.0, u, v)
cb = cyl(2.8, 58.0, 74.0 + 40.0, u, v)
s = s.cut(j4.L(th)).cut(j4.L(cb))
s = hub_m2_holes(s, j4, cb_z1=74.0 + 30.0)
# 9. 工艺走线通孔
slot_z = (Z_EL + z_top_housing) / 2.0
@@ -1331,12 +1659,18 @@ def p_forearm_bone(j4, j5, half=None):
def p_wrist_bracket(j5, j6):
"""P08_腕偏转座_螺旋体25%_墙4底5_竖打(深度圆角优化版)"""
return make_pitch_to_roll_bracket(j5, j6, Z_WR, Z_R6, name="P08_腕偏转座_螺旋体25%_墙4底5_竖打")
"""P08_腕偏转座_螺旋体25%_墙4底5_竖打(深度圆角优化版)
hub_gap=6.0:J5 处 P07 新增了 M5 前端压盖(Y 155.5..161.0),从动法兰盘背面必须
从 Y=157 后移到 Y=162 让位;J5 直锁凸台同步加长,3xS01 输出螺钉咬合量不变。
"""
return make_pitch_to_roll_bracket(j5, j6, Z_WR, Z_R6,
name="P08_腕偏转座_螺旋体25%_墙4底5_竖打",
hub_gap=6.0)
def p_tool_flange(j6):
"""P09_工具法兰_螺旋体100%_墙4底5_竖打 深度圆角优化版
"""P09_工具法兰_螺旋体30%_墙4底5_竖打 深度圆角优化版
关键改进:
1. 外轮廓全圆角(R=4mm)
2. 法兰面倒角(2mm)
@@ -1344,23 +1678,47 @@ def p_tool_flange(j6):
"""
z_bot = Z_HUB6 - PLATE_T - 9.0
s = cyl(35.0, z_bot, Z_HUB6, X_ARM, Y_AXIS)
P09_M4 = []
# 4x M4螺栓孔
#
# 分布圆半径 20 -> 22(BCD 40 -> 44):R20 与 3xS01 输出螺钉的 Ø5.6 螺丝刀通道
# (make_direct_hub 里 cyl(2.8, 58.0, z_out+40) 在 BCD29=R14.5、相位 351.4/232.3/112.1°)
# 咬穿沉孔。0° 那个孔中心 (39.5,128) 到 351.4° 通道中心 (33.84,125.83) 只有 6.06mm,
# 而两孔半径之和是 3.5+2.8=6.30 —— 差 0.24mm,Ø7.0 沉孔壁被啃掉一块,
# 完整圆柱面(88mm²)裂成 45.7+35.4 两片,即用户截图里的月牙孔。
# 该相位差只有 8.6°,转动 M4 孔相位无法躲开(0/90/180/270 与 45 度错开全部仍 <6.30),
# 只能加大分布圆半径:R22 时最小中心距 7.96mm,余量 1.66mm。
# 边壁仍充足:R35 法兰外径 - (R22 + 沉孔 3.5) = 9.5mm。
for a in (0, 90, 180, 270):
rad = 20.0
rad = 22.0
u = rad * math.cos(a * D2R)
v = rad * math.sin(a * D2R)
h = cyl(1.85, z_bot - 1.0, z_bot + 12.0, X_ARM + u, Y_AXIS + v)
cb = cyl(3.5, z_bot - 1.0, z_bot + 4.0, X_ARM + u, Y_AXIS + v)
s = s.cut(h).cut(cb)
# 实际切割推迟到 make_direct_hub 之后(见下)。这里只算出 4 个孔位。
P09_M4.append((X_ARM + u, Y_AXIS + v))
# 定位止口 + 走线孔
pilot = cyl(10.0, z_bot - 1.0, z_bot + 2.5, X_ARM, Y_AXIS)
thru = cyl(5.0, z_bot - 1.0, Z_HUB6 + 1.0, X_ARM, Y_AXIS)
s = s.cut(pilot).cut(thru)
# 直连法兰
s = make_direct_hub(s, j6, r_disc=35.0, z_out=74.0)
# 4x M4 孔(必须在 make_direct_hub 之后切)
#
# 直连法兰那步 fuse 了 R35 圆盘(局部 z=56.5..74 -> 全局 z=102..121 一侧),
# 会把先切的孔重新填死:分段求交显示 z=97..102 无干涉、z=102 以上整根杆都埋在料里
# (每段 12.57 mm^3 = pi*2^2*1,全截面被埋)。
#
# 孔径与深度同时修正:旧值 Ø3.7 比 M4 杆 Ø4.0 还细,且只到 z=105,
# 而 M4x16 头面在 z=97、杆要走到 z=113 —— 杆尾 8mm 无处可去(干涉 147.3 mm^3)。
# Ø4.4 是 M4 标准间隙孔(ISO 273 medium ≈ Ø4.3)。
# 孔顶要盖过 M4x16 的杆尾:头面 z=97 + 杆长 16 = z=113,故开到 Z_HUB6+4=115,
# 比杆尾多留 2mm(实测开到 112 时最后 1mm 杆尾仍撞料 37.7mm³)。
for (fx, fy) in P09_M4:
s = s.cut(cyl(2.2, z_bot - 1.0, Z_HUB6 + 4.0, fx, fy))
s = s.cut(cyl(3.5, z_bot - 1.0, z_bot + 4.0, fx, fy))
# ========== 关键圆角优化 ==========
try:
@@ -1408,7 +1766,7 @@ def build():
AB.GRP = {}
par = {"J1": "root", "J2": "J1", "J3": "J2", "J4": "J3", "J5": "J4", "J6": "J5"}
driven = {"J1": "P04_肩叉_螺旋体30%_墙5底5_竖打", "J2": "P05_大臂旋转座_螺旋体30%_墙4底5_竖打", "J3": "P06_大臂骨架_螺旋体25%_墙4底5_竖打",
"J4": "P07_小臂骨架_螺旋体30%_墙5底5_竖打", "J5": "P08_腕偏转座_螺旋体25%_墙4底5_竖打", "J6": "P09_工具法兰_螺旋体100%_墙4底5_竖打"}
"J4": "P07_小臂骨架_螺旋体30%_墙5底5_竖打", "J5": "P08_腕偏转座_螺旋体25%_墙4底5_竖打", "J6": "P09_工具法兰_螺旋体30%_墙4底5_竖打"}
motor_of = {"J1": "M1_J1", "J2": "M2_J2", "J3": "M3_J3",
"J4": "M4_J4", "J5": "M5_J5", "J6": "M6_J6"}
for t in ("J1", "J2", "J3", "J4", "J5", "J6"):
@@ -1441,17 +1799,17 @@ def build():
("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 锁死)、大散热窗与对流槽;无封闭空腔、免支撑"),
("P04_肩叉_螺旋体30%_墙5底5_竖打", "肩叉", lambda: p_fork(j1, j2), 0x2563eb,
"齐平插槽+ M2 前端压盖版:叉板背面与P03托架端面(11.5)0.0硬贴平(用户要求外观齐平),P03侧插槽独立加长3mm,M1电机可倾斜卡入,J1输出凸台贯通直连电机,J1从动法兰圆盘鼓楞已删(接缝区与P03止脱环齐径齐高R36.5/顶646.5),单实体全角度零干涉;【2026-10-02 新增】M2 前端压盖(R35, Y155.5..161.0):电机前端盖外端面加 4xM3x8 沉孔(BCD55)夹紧,与后端安装板 4xM3x10 形成前后双向面压,消除大扭矩下电机在座孔里的倾斜"),
"齐平插槽+ M2 前端压盖+ 立柱减重槽版:叉板背面与P03托架端面(11.5)0.0硬贴平(用户要求外观齐平),P03侧插槽独立加长3mm,M1电机可倾斜卡入,J1输出凸台贯通直连电机,J1从动法兰圆盘鼓楞已删(接缝区与P03止脱环齐径齐高R36.5/顶646.5),单实体全角度零干涉;【2026-10-02】M2 前端压盖(R35, Y155.5..161.0):电机前端盖外端面加 4xM3x8 沉孔(BCD55)夹紧,与后端安装板 4xM3x10 形成前后双向面压,消除大扭矩下电机在座孔里的倾斜;【2026-10-02】立柱减重槽(X23~35 / Y102~154 / 自Z525向上敞开):立柱Z525~635整段壁厚峰值17.5mm,J2在z=560/600的安全系数有19,改成U形槽钢后361->289cm3(-20%),抗弯的两片X向腹板正好落在弯矩拉压区(z=560截面X13~45、中性面X29,腹板占X13~23/X35~45),掏掉的正是中性层附近几乎不参与抗弯的料;槽底Z525以下保留整块实心过渡到电机座,槽底水平朝上、侧壁竖直、顶部敞开故零支撑且非封闭腔;开度按「减重区最差安全系数 >= 无槽基线 95%」选定(X23~35 比最初的 X21~37 收窄 2mm,强度从 88% 回到 95%);【2026-10-04 视觉整改】电机上方那块 4048mm2 的大平面改成 Hermite 弧面(全行程重扫 907s 确认 J1~J6 行程逐项不变):原来 yz_pts 里(164.5,646.5)->(155.5,526) 是一条直线,沿 X 拉成 4048mm2 平板(法向 (0,1,-0.07)、带 4° 斜度),正对电机上方,两侧被 xz_wire 的 R18 圆弧挤出去,形成「中间一块平、两边鼓」的观感。改成三次 Hermite(两端切线分别沿 Y/Z,与上下水平段 G1 连续),中部向 +Y 外凸约 3mm,弧度与两侧 R18 一致;单段圆弧无法同时满足两端切线(解出 R=9 与 R=120.5 矛盾)故用 Hermite。P04 体积 282.1->274.3cm³(弧面替代平面削掉的料);【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 件连接壁整圈连续、单实体)"),
("P05_大臂旋转座_螺旋体30%_墙4底5_竖打", "大臂旋转座", lambda: p_shoulder_roll_base(j2, j3, None), 0x059669,
"V3圆角+ J2前端夹紧配合:Loft外轮廓R=10mm,L型转角内部2mm倒角,后缘凸包两角90°竖棱磨R2圆角(结构/后缘高度不变,打印无悬挂);J2 从动法兰盘背面由 Y=157 后移至 Y=162(hub_gap=6.0,给 P04 新增的 M2 前端压盖让位,J2 直锁凸台同步加长到 Y=165.5,3xS01 输出螺钉咬合量不变);保证 J2 全角度 0 干涉"),
"V3圆角+ J2前端夹紧配合:Loft外轮廓R=10mm,L型转角内部2mm倒角,后缘凸包两角90°竖棱磨R2圆角(结构/后缘高度不变,打印无悬挂);J2 从动法兰盘背面由 Y=157 后移至 Y=162(hub_gap=6.0,给 P04 新增的 M2 前端压盖让位,J2 直锁凸台同步加长到 Y=165.5,3xS01 输出螺钉咬合量不变);保证 J2 全角度 0 干涉;【2026-10-05d 只切敞口那一圈围挡】用户指出 M3 电机前端(另一头)也有一圈围挡:Z389.0~395.5 的 r30.25~35 前端定位环(6mm 高环墙) + 骑在它上面的 R35 压盖外圈(Ø70 比电机 Ø63.6 凸 3.2mm),把 M3 前端盖连同穿过 Ø37 孔的 P06 J3 凸台围在中间。第一版把整个前端(定位环+压盖+腔口下沿 389->395)都切了属过度(电机前端失去支撑面、M3 退回单面锁死),已回退;现只切敞口(-Y)那一圈(盒切,Y 界限与 cut_open 一致),-Y 半圈从 Z=395.5 一路开到压盖背面 Z=389,背脊(+Y)侧整圈保留作前端盖定位与压盖唯一且足够强的支撑面(4.75mm 宽 x 0.5mm 熔接带);压盖本体/Ø37 孔/4xM3x8 沉孔/腔口下沿 389.0 全部不动,P05 只少 3.2cm3(154.8->151.6),仍单实体。不整圈削到 R31.8 的原因:那圈壁与 Loft 在 X=±35 处与 Ø70 柱面相切,整圈切完会脱空出 1.32mm3 碎块破坏单实体;且压盖根部熔接面会从 2.37mm2 缩到 0.78mm2,4xM3x8 夹紧力会把压盖拉下来。开关 FENCE_OPEN_CUT(2026-10-05e 起为全机 5 处共用)。【2026-10-03】M3 前端压盖(R35, Z384.0..389.0):J3 roll 电机原来也只有后端板 4xM3x10 单面锁死(S02),大扭矩下绕 J3 轴在 Ø60.3 座孔里点头;现按 P04/P06/P07 同一配方在 M3 前端盖外端面(Z=389)加 4xM3x8(BCD55)沉孔压盖,与后端板前后双向面压,倾斜自由度归零(配方唯一差别是 roll 电机轴向 -Z,压盖朝 -Z 长,见 motor_front_clamp axis=\"Z\");代价是对侧 P06 的 J3 毂面整体后移 5.5mm 让位;【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 件连接壁整圈连续、单实体)"),
("P06_大臂骨架_螺旋体25%_墙4底5_竖打", "大臂骨架", lambda: p_humerus_bone(j3, j4), 0x0d9488,
"Loft有机过渡版:借鉴P05圆弧变方形放样算法,顶端Ø70正圆与J3平齐无台阶,底端渐变融入M4抱紧套筒与壳体,单实体0干涉"),
"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 件连接壁整圈连续、单实体)"),
("P07_小臂骨架_螺旋体30%_墙5底5_竖打", "小臂骨架", lambda: p_forearm_bone(j4, j5, None), 0x7c3aed,
"V2 L型结构:+Y侧背脊立柱避让大臂骨架,过渡区采用与顶板和立柱双相切的R=28mm平滑内凹大圆弧,彻底消除直斜线折角与悬空阶梯小块,顶板前后外缘2mm倒角改R3圆角,顶板整条加高19mm(z261->280)加强连接且gusset弧R28->R14重定相切融入,J4圆盘外沿与立柱外棱不倒圆保持原始尖角,下方M5电机半包散热,俯仰大范围自由运动零干涉"),
"V2 L型结构 + 顶板减重槽:+Y侧背脊立柱避让大臂骨架,过渡区采用与顶板和立柱双相切的R=28mm平滑内凹大圆弧,彻底消除直斜线折角与悬空阶梯小块,顶板前后外缘2mm倒角改R3圆角,顶板整条加高19mm(z261->280)加强连接且gusset弧R28->R14重定相切融入,J4圆盘外沿与立柱外棱不倒圆保持原始尖角,下方M5电机半包散热,俯仰大范围自由运动零干涉;【2026-10-02】顶板根部Z255~280开朝上敞开的减重槽(X-3~43 / Y103~153, Y上限取153避开y_hub_in=158.5起的竖直立柱本体):顶板由实心变槽钢,288->230cm3(-20%),槽底以下保留整块实心到电机座,抗弯靠两侧各10mm的X向腹板;槽底水平朝上、侧壁竖直、顶部敞开故零支撑且非封闭腔;开度按「减重区最差安全系数 >= 无槽基线 95%」选定(X-3~43 比最初的 X-7~47 收窄 4mm,强度从 88% 回到 96%);【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 件连接壁整圈连续、单实体)"),
("P08_腕偏转座_螺旋体25%_墙4底5_竖打", "腕部转接座", lambda: p_wrist_bracket(j5, j6), 0xd97706,
"V2圆角:Loft外轮廓R=10mm,L型转角内部2mm倒角,彻底清除立壁保证J5全角度0干涉"),
("P09_工具法兰_螺旋体100%_墙4底5_竖打", "末端工具法兰", lambda: p_tool_flange(j6), 0xdc2626,
"V2圆角:外轮廓R=3mm,法兰面倒角2mm"),
"V2圆角:Loft外轮廓R=10mm,L型转角内部2mm倒角,彻底清除立壁保证J5全角度0干涉;【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 件连接壁整圈连续、单实体)"),
("P09_工具法兰_螺旋体30%_墙4底5_竖打", "末端工具法兰", lambda: p_tool_flange(j6), 0xdc2626,
"V2圆角:外轮廓R=3mm,法兰面倒角2mm;【2026-10-02】打印填充由100%降到30%:该截面安全系数288是全机裕度最大的件,满填充纯属浪费(件号同步改为P09_工具法兰_螺旋体30%)"),
]
total_parts = len(parts_to_build)
+10
View File
@@ -70,6 +70,16 @@ LUG_R = C["lug_r1"] + 1.0 # radial keep-out where the bolt lugs
CLR = dict(bore_slip=0.20, pilot=0.10, hole_3=3.4, hole_27=2.8, wall_min=2.0)
FIT = dict(motor_bore_d=M["hous_d"] + 2 * 0.15, cover_cb_d=M["cover_d"] + 2 * 0.35)
# 电机【轴向长度】公差(用户 2026-10-03 给的现场口径:各台电机厚度差可达 2mm)。
# 这条链是"后端板螺钉 + 前端压盖螺钉"两个刚性夹持面,螺钉头一旦坐死在沉孔台阶上
# 就再也不能进给,所以 2mm 没法靠"拧紧"消化,只能靠两件事吃掉(见 arm_build.Joint.pocket
# 与 motor_front_clamp):
# 长出来的 0~+2mm -> 后端板 4xM3 螺钉头下垫 0~2mm 垫片圈(螺钉被垫高 -> 电机被拉回)
# 短下去的 0~-2mm -> 电机尾端面与后端板承压面之间垫 0~2mm 垫片(承压面已按 MOTOR_TOL
# 让空,见 Joint.pocket 的 REAR_SEAT)
# 两边都不动压盖/凸台的几何,关节轴向长度不变。
MOTOR_TOL = 2.0
# ---------------- geometry helpers --------------------------------------------------------------------
def _nrm(v):
+1 -1
View File
@@ -112,7 +112,7 @@ JOINTS = [
("J3", "大臂自转", (X_ARM, Y_AXIS, Z_R3), (0, 0, -1), (1, 0, 0), "P05_大臂旋转座_螺旋体30%_墙4底5_竖打", "P06_大臂骨架_螺旋体25%_墙4底5_竖打"),
("J4", "肘部俯仰", (X_ARM, Y_REAR, Z_EL), (0, 1, 0), (1, 0, 0), "P06_大臂骨架_螺旋体25%_墙4底5_竖打", "P07_小臂骨架_螺旋体30%_墙5底5_竖打"),
("J5", "腕部俯仰", (X_ARM, Y_REAR, Z_WR), (0, 1, 0), (1, 0, 0), "P07_小臂骨架_螺旋体30%_墙5底5_竖打", "P08_腕偏转座_螺旋体25%_墙4底5_竖打"),
("J6", "腕自转", (X_ARM, Y_AXIS, Z_R6), (0, 0, -1), (1, 0, 0), "P08_腕偏转座_螺旋体25%_墙4底5_竖打", "P09_工具法兰_螺旋体100%_墙4底5_竖打"),
("J6", "腕自转", (X_ARM, Y_AXIS, Z_R6), (0, 0, -1), (1, 0, 0), "P08_腕偏转座_螺旋体25%_墙4底5_竖打", "P09_工具法兰_螺旋体30%_墙4底5_竖打"),
]
AXIS = {t[0]: dict(rear=t[2], d=t[3], xdir=t[4]) for t in JOINTS}
+45 -15
View File
@@ -13,12 +13,14 @@ docs/screws.json 是紧固件清单的唯一事实源(规格 / 长度 / 数量
台阶 z=63(J1, p_fork th 54..63) / 58(其余, th 54..58);头深藏在 Ø5.6 螺丝刀通道内
S02 P04~P08 4xM3 :下级电机 rear_mount_holes:沉孔 Ø5.5 从 -t_plate-1 到 -t_plate+4,
台阶 z = -t_plate+4 = -4(t_plate=8)/ -3(t_plate=7),头沉入 1mm
S03 P03 后法兰 4xM3 :J1 局部 (R27.5 @39.9°+90k, -5.5) (沉孔 Ø6.5 台阶 x=-56.5,法兰外端面 -61)
S04 P03 前置锁紧凸台 4xM3 :J1 局部 (R27.5 @17.8/107.8/197.8/287.8°, 62.5)
(沉孔 Ø6.0 在 Ø10 凸台端面 x=11.5,M3x10 旋入电机前端面咬合 3.5mm)
S03 P03 后法兰 4xM3 :J1 局部 (R27.5 @39.9°+90k, -5.5) (Ø6.5 圆柱沉孔台阶 x=-56.5,法兰外端面 -61,
圆柱头沉入 1.5mm)
S04 P03 前置锁紧凸台 4xM3 :J1 局部 (R27.5 @17.8/107.8/197.8/287.8°, 60.5)
(Ø6.0×2 圆柱沉孔在 Ø10 凸台内,台阶 x=9.5,M3x8 圆柱头露 1mm,旋入电机前端面咬合 3.5mm)
S05 P01 分段对锁 8xM4x16:Ø3.9 直孔 x∈{-131,-93}, z∈{180,329}(母口壁与公榫壁同径),
前后各拧一颗,头在 y=95 / y=161 外露(搭接壁薄不开沉孔),直接拧入自攻成型
S06 P09 工具法兰 4xM4:R20 @0/90/180/270°,台阶 z=97(沉孔 Ø7.0 深 4mm,法兰底面 z=93)
S06 P09 工具法兰 4xM4:R22 @0/90/180/270°(BCD44,原 R20 会与 S01 的 Ø5.6 通道咬穿沉孔),台阶 z=97(Ø7.0×4 圆柱沉孔 + Ø4.4 贯穿通孔,法兰底面 z=93,
M4 圆柱头与法兰底面齐平)
S07 P02 中核心 4xM5 :x∈{-135,-99}, y∈{110,146},台阶 z=6(Ø10 沉孔深 6mm,头沉入 1mm)
S08 P02 力臂 4xM5 :(-10,76/180) + (-185,88/168),台阶 z=17(Ø10 沉孔深 5mm,头与顶面齐平)
S09 P03 安装台 4xM4x16:x∈{-142.5,-87.5}, y∈{114.5,141.5},台阶 z=479(P03 鞍座顶面,
@@ -27,6 +29,11 @@ docs/screws.json 是紧固件清单的唯一事实源(规格 / 长度 / 数量
沿 +X Ø3.9 直孔穿 11mm 后壁 + 榫头同径,直接拧入自攻(榫头内咬合 4.7mm)
S10 底座腰槽 6xM6 :3 件底座的 6 组 28x7 通腰槽中心(-97.5/120/-215 × 53/203/58/198),
头面 z=22(底座顶面;腰槽为通槽无沉孔,桌面紧固螺栓头必然外露)
S12 电机前端压盖 4xM3x8:J2(P04 夹 M2) / J3(P05 夹 M3) / J4(P06 夹 M4) / J5(P07 夹 M5) 各 4 颗。
配方唯一事实源 = arm_model.motor_front_clamp(R35、4xM3x8 BCD55 @17.8°+90k、Ø6.0 沉孔
深 3.0、沉孔台阶 = 前端盖外端面 +2.0mm、压盖背面 0.0 面压);J3 是同一套配方的 Z 轴版本
(台阶落在 Z=(z_r-56)-2=387),半径/相位直接取 arm_model 的 CLAMP_BCD/CLAMP_CLOCK。
孔位必须显式算全局 (x,z)/(x,y) 而不能用 to_global,原因见 instances() 内的注释。
长度处理:S08/S10 螺杆按真实长度会伸到底座底面(z=0)以下悬空(工作台/预埋螺母未建模),
这里按可见段截断到 z=0,头部与孔位仍按 BOM 规格,避免出现"悬空螺杆"。
@@ -50,6 +57,7 @@ _ROOT = os.path.dirname(_HERE)
sys.path.insert(0, _HERE)
import human_layout as HL # noqa: E402
from arm_model import CLAMP_BCD, CLAMP_CLOCK # noqa: E402 压盖孔位唯一事实源
X_COL = -115.0 # 立柱中心(与 arm_model.py 一致)
Y_COL = HL.Y_AXIS # 128.0
@@ -202,19 +210,39 @@ def instances(m2_bcd, m2_clock):
for deg in (17.8, 107.8, 197.8, 287.8):
u = 27.5 * math.cos(math.radians(deg))
v = 27.5 * math.sin(math.radians(deg))
out.append(("S04", to_global("J1", u, v, 62.5), d1, "root",
out.append(("S04", to_global("J1", u, v, 60.5), d1, "root",
"P03 前置锁紧凸台", "BCD55 @%.1f° -> M1 前端面" % deg))
for u, v in REAR_SLOTS:
out.append(("S03", to_global("J1", u, v, -5.5), tuple(-c for c in d1), "root",
"P03 后置法兰", "BCD55 @39.9° 螺纹槽 -> M1 尾端面"))
# S12 P04 前端压盖 -> M2 前端盖(J2):沉孔台阶 Y=158(局部 z=58),与 S04 同 BCD55 相位
d2 = frame("J2")[1]
for deg in (17.8, 107.8, 197.8, 287.8):
u = 27.5 * math.cos(math.radians(deg))
v = 27.5 * math.sin(math.radians(deg))
out.append(("S12", to_global("J2", u, v, 58.0), d2, "J1",
"P04 前端压盖", "BCD55 @%.1f° -> M2 前端面(前端夹紧)" % deg))
# S12 电机前端压盖 -> 电机前端盖:J2(P04 夹 M2) / J3(P05 夹 M3) / J4(P06 夹 M4) /
# J5(P07 夹 M5) 各 4 颗。J2/J4/J5 三处压盖几何完全相同,J3 是同一套配方的 Z 轴版本。
# R35、沉孔台阶都在"前端盖外端面 + 2.0mm":Y 轴是 Y_COVER+2=158,Z 轴是 (z_r-56)-2=387。
# 4xM3x8 @BCD55 17.8°+90k、沉孔 Ø6.0 深 3.0、压盖背面与前端盖 0.0 面压,
# 相位与 S04(P03→M1 前端锁紧)同一套约定,全机 BCD55 孔位一致。
#
# 孔位必须按 motor_front_clamp 的写法直接算全局 (x,z)/(x,y),不能用 to_global:
# 压盖成孔是 fu = px + BCD*cos、fv = pz/py + BCD*sin,即 sin 项直接加在**全局**该轴上;
# 而 frame() 的 y 轴 = d × x,对 J2/J4/J5(d=+Y, x=+X)算出 y=(0,0,-1),
# 经 to_global 会把 v 映到全局 -z,与成孔方向差一个符号
# -> 12 颗螺钉实例整体偏 2*27.5*sin(17.8°) = 16.81mm,每颗都落在两孔之间的实体上
# (布尔校验:M3 圆柱头沉在 Ø6.0 沉孔里却与压盖实体重叠 71.27 mm^3,装不进去)。
# 故此处显式用全局平面复刻成孔公式,与建模脚本一一对应。
for tag, prox in (("J2", "P04 肩叉"), ("J3", "P05 大臂旋转座"),
("J4", "P06 大臂骨架"), ("J5", "P07 小臂骨架")):
dt = frame(tag)[1]
rear = tuple(HL.AXIS[tag]["rear"])
roll = HL.AXIS[tag]["d"][1] == 0 # J3/J6 是 roll 电机(轴向 -Z)
for deg in CLAMP_CLOCK:
u = CLAMP_BCD * math.cos(math.radians(deg))
v = CLAMP_BCD * math.sin(math.radians(deg))
hp = (rear[0] + u, rear[1] + v, rear[2] - 58.0) if roll \
else (rear[0] + u, rear[1] + 58.0, rear[2] + v)
out.append(("S12", hp, dt, GRP_PAR[tag],
"%s 前端压盖" % prox,
"BCD55 @%.1f° -> %s 电机前端盖(前端夹紧,%s 轴)"
% (deg, tag, "Z" if roll else "Y")))
for x in (X_COL - 16.0, X_COL + 22.0):
for z in (180.0, 329.0):
@@ -224,10 +252,12 @@ def instances(m2_bcd, m2_clock):
"P01 立柱分段缝", "z=%.0f 背面 +Y:Ø3.9 直孔(M4x16 直接拧入自攻)" % z))
for deg in (0.0, 90.0, 180.0, 270.0):
x = HL.X_ARM + 20.0 * math.cos(math.radians(deg))
y = HL.Y_AXIS + 20.0 * math.sin(math.radians(deg))
# BCD44(R22):原 R20 与 S01 输出螺钉 Ø5.6 通道中心距仅 6.06 < 6.30,
# 沉孔壁被咬穿成月牙形;加大到 R22 后余量 1.66mm。见 p_tool_flange 注释。
x = HL.X_ARM + 22.0 * math.cos(math.radians(deg))
y = HL.Y_AXIS + 22.0 * math.sin(math.radians(deg))
out.append(("S06", (x, y, 97.0), (0.0, 0.0, -1.0), "J6",
"P09 工具法兰", "R20 @%.0f° -> 末端工具" % deg))
"P09 工具法兰", "R22 @%.0f° -> 末端工具" % deg))
for x in (-135.0, -99.0):
for y in (110.0, 146.0):
+27 -1
View File
@@ -55,6 +55,10 @@ VIEWS = {
"user_p06_yz": "?az=0&po=1.5708&rd=260&tx=18.5&ty=350&tz=-110&xray=0",
"user_p06_iso": "?az=-0.65&po=1.25&rd=260&tx=-15&ty=350&tz=-128&xray=0",
"user_p07_creases": "?az=-0.65&po=1.25&rd=260&tx=30&ty=270&tz=-130&xray=0",
# 电机腔内壁(隐藏全部电机,从腔内看 R35/R30.15 两段同心圆弧是否连续)
"bay_p06_inside": "?az=1.5708&po=1.5708&rd=250&tx=18.5&ty=345&tz=-128&xray=0&hide=J1,J2,J3,J4,J5,J6",
"bay_p07_inside": "?az=1.5708&po=1.5708&rd=240&tx=18.5&ty=235&tz=-128&xray=0&hide=J1,J2,J3,J4,J5,J6",
"bay_p08_inside": "?az=1.5708&po=1.5708&rd=210&tx=18.5&ty=175&tz=-128&xray=0&hide=J1,J2,J3,J4,J5,J6",
"user_p04_side": "?az=1.5708&po=1.5708&rd=300&tx=18.5&ty=520&tz=-128&xray=0&hide=胸板,底脚,肩吊臂",
"user_p04_bottom": "?az=0.5&po=2.0&rd=240&tx=18.5&ty=490&tz=-128&xray=0&hide=胸板,底脚,肩吊臂",
"user_p04_bottom2": "?az=1.2&po=2.0&rd=260&tx=18.5&ty=490&tz=-128&xray=0&hide=胸板,底脚,肩吊臂",
@@ -67,10 +71,26 @@ VIEWS = {
"p03_light_rear": "?az=0.0&po=1.45&rd=300&tx=-70&ty=548&tz=-128&xray=0&hide=P01,P02,P04,P05,P06,P07,P08,P09,M1,M2,M3,M4,M5,M6,S0,S1,ENV",
"p03_light_join": "?az=0.62&po=1.15&rd=420&tx=18.5&ty=520&tz=-128&xray=0",
"p03_light_xray": "?az=0.62&po=1.15&rd=300&tx=-70&ty=548&tz=-128&xray=1&hide=P01,P02,P04,P05,P06,P07,P08,P09,M1,M2,M3,M4,M5,M6,S0,S1,ENV",
# 减重版(2026-10-02):P04 立柱槽 / P06 过渡块窗 / P07 顶板槽 的孤立视图
# 俯视看朝上敞开的槽口,斜视看槽壁与留下来的抗弯腹板
"hollow_p04_top": "?az=0.0&po=0.05&rd=250&tx=18.5&ty=585&tz=-128&xray=0&hide=P01,P02,P03,P05,P06,P07,P08,P09,M1,M2,M3,M4,M5,M6,S0,S1,ENV",
"hollow_p04_iso": "?az=0.7&po=1.05&rd=280&tx=18.5&ty=585&tz=-128&xray=0&hide=P01,P02,P03,P05,P06,P07,P08,P09,M1,M2,M3,M4,M5,M6,S0,S1,ENV",
"hollow_p07_top": "?az=0.0&po=0.05&rd=230&tx=18.5&ty=270&tz=-128&xray=0&hide=P01,P02,P03,P04,P05,P06,P08,P09,M1,M2,M3,M4,M5,M6,S0,S1,ENV",
"hollow_p07_iso": "?az=0.7&po=1.05&rd=260&tx=18.5&ty=275&tz=-128&xray=0&hide=P01,P02,P03,P04,P05,P06,P08,P09,M1,M2,M3,M4,M5,M6,S0,S1,ENV",
"hollow_p06_iso": "?az=0.7&po=1.05&rd=190&tx=18.5&ty=368&tz=-128&xray=0&hide=P01,P02,P03,P04,P05,P07,P08,P09,M1,M2,M3,M4,M5,M6,S0,S1,ENV",
# P03↔立柱 安装接口(新版 4xM5 从腔体上方拧入):俯视看螺钉落位
"p03_mount_top": "?az=0.9&po=1.30&rd=190&tx=-115&ty=476&tz=-128&xray=0&hide=P02,P04,P05,P06,P07,P08,P09,M1,M2,M3,M4,M5,M6,ENV",
"p03_mount_iso": "?az=2.2&po=0.95&rd=230&tx=-115&ty=486&tz=-128&xray=0&hide=P02,P04,P05,P06,P07,P08,P09,M1,M2,M3,M4,M5,M6,ENV",
"p03_mount_loose": "?az=0.9&po=0.42&rd=260&tx=-115&ty=500&tz=-128&xray=0&expl=60&hide=P02,P04,P05,P06,P07,P08,P09,M1,M2,M3,M4,M5,M6,ENV",
"j3_前端压盖_装配": "?az=0.7&po=2.72&rd=190&tx=19.5&ty=396&tz=-128&xray=0&hide=立柱,底座,肩关节安装悬臂,肩叉,大臂骨架,小臂骨架,腕部转接座,末端工具法兰,M1,M2,M4,M5,M6,包络经纬网",
"j3_前端压盖_全件": "?az=0.7&po=2.6&rd=300&tx=19.5&ty=420&tz=-128&xray=0",
# 承压面下沉 2mm 留下的"悬空凸台"(2026-10-04 整改):沿电机轴线从尾端面方向看,
# 四个视图分别对应 P04/P05/P06/P07 的后端。ax/po 沿轴向,rd 拉近到 R35 座圈。
# 沿电机轴线从尾端面看过去:承压面(R35圆面) + 4xS02 沉孔,凸台消失后应是干净圆面
"boss_p04": "?az=0.55&po=1.30&rd=150&tx=19.5&ty=490&tz=-158&xray=0&hide=P01,P02,P03,P05,P06,P07,P08,P09,M3,M4,M5,M6,ENV",
"boss_p05": "?az=1.5708&po=1.5708&rd=190&tx=18.5&ty=392&tz=-128&xray=0&hide=P01,P02,P03,P04,P06,P07,P08,P09,M2,M4,M5,M6,ENV",
"boss_p06": "?az=1.5708&po=1.5708&rd=190&tx=18.5&ty=332&tz=-128&xray=0&hide=P01,P02,P03,P04,P05,P07,P08,P09,M1,M2,M3,M5,M6,ENV",
"boss_p07": "?az=0&po=0.0&rd=210&tx=19.5&ty=215&tz=-128&xray=0&hide=P01,P02,P03,P04,P05,P06,P08,P09,M1,M2,M3,M4,M6,ENV",
"svg_side": f"file:///{DOCS}/sheet_side.svg",
"svg_joint": f"file:///{DOCS}/sheet_joint.svg",
"svg_exploded": f"file:///{DOCS}/sheet_exploded.svg",
@@ -82,17 +102,23 @@ want = sys.argv[1:] or list(VIEWS)
def take_shot(nm):
q = VIEWS[nm]
out = "%s/%s.png" % (SHOTS, nm)
import time
# 不预先删除旧图:Edge 的 --screenshot 本来就会覆盖,而 os.remove 在带
# safe-delete 垫片的环境里会把"删到回收站"的失败抛出来,直接打断整轮渲染。
import time, tempfile
t_bust = int(time.time() * 1000)
target = q if q.startswith("file://") else "file:///" + HTML.replace("\\", "/") + q + (
f"&still=1&_t={t_bust}" if "?" in q else f"?still=1&_t={t_bust}")
# 每次用独立 profile:共用默认 profile 时并发实例会互相抢锁,表现为随机 FAIL。
udd = tempfile.mkdtemp(prefix="eshot")
cmd = [EDGE, "--headless=new", "--use-gl=angle", "--use-angle=d3d11",
"--disable-cache", "--disable-application-cache", "--disk-cache-size=0",
"--user-data-dir=" + udd,
"--enable-gpu-rasterization", "--ignore-gpu-blocklist",
"--hide-scrollbars", "--force-device-scale-factor=1",
"--window-size=1500,860", "--virtual-time-budget=20000",
"--screenshot=" + out, target]
r = subprocess.run(cmd, capture_output=True, timeout=180)
shutil.rmtree(udd, ignore_errors=True)
ok = os.path.exists(out) and os.path.getsize(out) > 20000
err = (r.stderr or b"").decode("utf-8", errors="ignore").strip()[:90].replace("\n", " ")
sz = os.path.getsize(out) if os.path.exists(out) else 0
+9 -1
View File
@@ -395,12 +395,20 @@ def cuts():
("大臂旋转座套筒 @z=440", "P05_大臂旋转座_螺旋体30%_墙4底5_竖打", (0, 0, 1), (HL.X_ARM, Y, 440.0), "J3", 1),
("大臂骨架中段 @z=350", "P06_大臂骨架_螺旋体25%_墙4底5_竖打", (0, 0, 1), (HL.X_ARM, Y, 350.0), "J3", 1),
("大臂骨架抱箍 @z=325", "P06_大臂骨架_螺旋体25%_墙4底5_竖打", (0, 0, 1), (HL.X_ARM, Y, 325.0), "J4", 1),
# 【2026-10-02 补】P06 原减重窗在 Z358~374,原有剖面(350/325)全在它下面,
# 根本校核不到被挖的区域。z=365 穿过该段过渡块,承载同 J3。
# 【2026-10-03】减重窗已取消、该段恢复实心,此剖面保留为该高度的安全裕度监控点。
("大臂骨架过渡块 @z=365", "P06_大臂骨架_螺旋体25%_墙4底5_竖打", (0, 0, 1), (HL.X_ARM, Y, 365.0), "J3", 1),
("小臂骨架中段 @z=245", "P07_小臂骨架_螺旋体30%_墙5底5_竖打", (0, 0, 1), (HL.X_ARM, Y, 245.0), "J4", 1),
("小臂骨架抱箍 @z=195", "P07_小臂骨架_螺旋体30%_墙5底5_竖打", (0, 0, 1), (HL.X_ARM, Y, 195.0), "J5", 1),
# 【2026-10-02 补】P07 减重槽在 Z255~280,同理原有剖面(245/195)没覆盖到。
# z=270 穿过槽体本身,承载同 J4;z=280 是槽口所在的顶板上边缘,壁最薄。
("小臂骨架减重槽 @z=270", "P07_小臂骨架_螺旋体30%_墙5底5_竖打", (0, 0, 1), (HL.X_ARM, Y, 270.0), "J4", 1),
("小臂骨架减重槽口 @z=280", "P07_小臂骨架_螺旋体30%_墙5底5_竖打", (0, 0, 1), (HL.X_ARM, Y, 280.0), "J4", 1),
("腕偏转座根部 @z=150", "P08_腕偏转座_螺旋体25%_墙4底5_竖打", (0, 0, 1), (HL.X_ARM, Y, 150.0), "J5", 1),
# P09 包络 z=93..121,剖面取 107(法兰中截面)。原 -55 落在零件之外,
# 剖切返回空,工具法兰其实一直没被校核过。
("工具法兰 @z=107", "P09_工具法兰_螺旋体100%_墙4底5_竖打", (0, 0, 1), (HL.X_ARM, Y, 107.0), "J6", 1),
("工具法兰 @z=107", "P09_工具法兰_螺旋体30%_墙4底5_竖打", (0, 0, 1), (HL.X_ARM, Y, 107.0), "J6", 1),
]
+1 -1
View File
@@ -60,7 +60,7 @@ MOUNT_PAIRS = {
("P05_大臂旋转座_螺旋体30%_墙4底5_竖打", "M3_J3"), ("P06_大臂骨架_螺旋体25%_墙4底5_竖打", "M3_J3"),
("P06_大臂骨架_螺旋体25%_墙4底5_竖打", "M4_J4"), ("P07_小臂骨架_螺旋体30%_墙5底5_竖打", "M4_J4"),
("P07_小臂骨架_螺旋体30%_墙5底5_竖打", "M5_J5"), ("P08_腕偏转座_螺旋体25%_墙4底5_竖打", "M5_J5"),
("P08_腕偏转座_螺旋体25%_墙4底5_竖打", "M6_J6"), ("P09_工具法兰_螺旋体100%_墙4底5_竖打", "M6_J6"),
("P08_腕偏转座_螺旋体25%_墙4底5_竖打", "M6_J6"), ("P09_工具法兰_螺旋体30%_墙4底5_竖打", "M6_J6"),
}
# 网格化预筛参数:外扩必须 ≥ 网格化偏差,判"分离"才保守可信
+17 -4
View File
@@ -520,6 +520,13 @@ for (const p of DATA.parts) {
for (let i = 0; i < p.v.length; i++) { pos[i*3] = p.v[i][0]; pos[i*3+1] = p.v[i][2]; pos[i*3+2] = -p.v[i][1]; }
p.triCount = p.t ? p.t.length : 0;
p.vertCount = p.v.length;
/* 外形三围:模型坐标系 X×Y×Z 包围盒(装配零位、打印朝向 Z=高),悬停面板显示用 */
{ let x0=1e9,y0=1e9,z0=1e9,x1=-1e9,y1=-1e9,z1=-1e9;
for (let i = 0; i < p.v.length; i++) { const a = p.v[i];
if (a[0]<x0)x0=a[0]; if (a[0]>x1)x1=a[0];
if (a[1]<y0)y0=a[1]; if (a[1]>y1)y1=a[1];
if (a[2]<z0)z0=a[2]; if (a[2]>z1)z1=a[2]; }
p.dims = [x1-x0, y1-y0, z1-z0]; }
const isEnv = p.name.slice(0, 4) === 'ENV_'; // 可达包络:运动学边界,不随关节动
/* 索引按顶点数降位:单件 ≤65535 顶点时 Uint16 合法(92 件实测最大 47483),
索引内存与上传带宽减半;超大件保留 Uint32 兜底。 */
@@ -686,6 +693,11 @@ function pick(e) {
的 style.background 全刷一遍,纯属多余 DOM 写入。 */
let bomHlRow = null;
function bomHlClear() { if (bomHlRow) { bomHlRow.style.background = ''; bomHlRow = null; } }
function dimsStr(p) {
if (!p.dims) return '';
const f = d => String(Math.round(d * 10) / 10);
return '外形 ' + f(p.dims[0]) + ' × ' + f(p.dims[1]) + ' × ' + f(p.dims[2]) + ' mm(X·Y·Z)';
}
function hover(e) {
const o = pick(e); if (o === hov) return; hov = o;
const n = document.getElementById('note');
@@ -693,7 +705,8 @@ function hover(e) {
const p = o.userData;
n.style.display = 'block';
n.innerHTML = '<b>#' + p.idx + ' ' + p.cn + '</b><br><small>' + p.name +
' · ' + (p.printed ? '3D打印件' : '电机/已有件') + '<br>' + p.note + '</small>';
' · ' + (p.printed ? '3D打印件' : '电机/已有件') +
(dimsStr(p) ? '<br>' + dimsStr(p) : '') + '</small>';
}
function hidePart(m) {
if (!m) return;
@@ -835,7 +848,7 @@ for (const [gt, test] of groups) {
const p = m.userData;
const r = document.createElement('div'); r.className = 'row';
r.innerHTML = '<input type="checkbox" checked><span class="chip" style="background:#' +
p.color.toString(16).padStart(6,'0') + '"></span><label title="' + p.note + '">' +
p.color.toString(16).padStart(6,'0') + '"></span><label title="' + p.name + '">' +
'<span class="' + (p.printed?'pr':(p.name[0]==='M'?'mo':'ex')) + '">' + p.cn + '</span></label>' +
'<span class="q">#' + p.idx + '</span>';
const chk = r.querySelector('input');
@@ -867,7 +880,7 @@ if (scrAll.length) {
const ms = bySpec[sid], p0 = ms[0].userData;
const r = document.createElement('div'); r.className = 'row';
r.innerHTML = '<input type="checkbox" checked><span class="chip" style="background:#' +
p0.color.toString(16).padStart(6,'0') + '"></span><label title="' + p0.note + '">' +
p0.color.toString(16).padStart(6,'0') + '"></span><label>' +
'<span class="ex">' + sid + ' ' + p0.cn.split(' · ')[0] + '</span></label>' +
'<span class="q">' + ms.length + ' 颗</span>';
const chk = r.querySelector('input');
@@ -925,7 +938,7 @@ for (const p of DATA.parts) {
tr.innerHTML = '<td>' + p.idx + '</td><td>' + p.cn + '</td><td>' + (p.vol/1000).toFixed(1) + '</td><td>' +
(p.vol*1.24e-3).toFixed(0) + '</td>';
tr.dataset.idx = p.idx;
tr.title = p.note + ' / ' + p.name;
tr.title = p.name + (dimsStr(p) ? ',' + dimsStr(p) : '');
tb.appendChild(tr);
}
document.getElementById('sum').innerHTML =