Files
Robot_Arm/src/arm_model.py
T

2043 lines
130 KiB
Python
Raw Normal View History

# -*- coding: utf-8 -*-
"""机器人手臂深度圆角优化版 V2:彻底消除所有棱角
针对用户�馈的三大问题区域进行深度优化:
1. �色零件(P04肩�)顶部方形�起 → 全圆角处�
2. 绿色/�色零件(P05/P08)底部和侧�棱角 → Loft外轮廓圆角
3. 所有连接处的尖�过渡 → 大圆角平滑过渡
4. 橙色零件(P06/P07)所有方形边缘 → 完整圆角系统
"""
import math
import os
import sys
import time
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import cadquery as cq
import arm_build as AB
from armcore import (M, C, cyl, box, ring, place, trs, bb, bbox_str, solids_of, OUT, WEB, DOCS)
from arm_build import (Joint, add, bx, Ycyl, Ybox, Yplate, prism, dogbone, sector,
FLANGE_Z, COVER_Z, HUB_FACE_Z, CLAMP_SPAN, HUB_M2_CLOCK,
BORE_R, CB_R, PCB_W, D2R, T, R, PARTS)
import human_layout as HL
from OCP.BRepAlgoAPI import BRepAlgoAPI_Common
from human_layout import (HUB, PLATE_T, R_MOT, Z_TIP, Z_WR, Z_EI, Z_SH, Z_RL, Z_R1,
Z_R3, Z_HUB3, Z_EL, Z_R6, Z_HUB6,
Z_R4, Z_CAP, Z_M6, Z_CHEST0, Z_CHEST1, X_J1_HUB, X_ARM, L1,
X_FORK_GAP, X_BAY_EXT,
Y_AXIS, Y_HUB, Y_REAR, Y_COVER, Y_H, Y_R_IN, Y_R_OUT,
Y_M6, Y_CHEST, X_CHEST, FOOT_H)
VERSION = os.environ.get("ARMVER", "v2")
ITER = int(os.environ.get("ARMITER", "3"))
AB.VERSION = VERSION
AB.ITER = ITER
Y_MID = Y_AXIS
BOSS = COVER_Z + 0.2
DISC_R = 40.0
SLEEVE_R = 35.0
# 深度圆角优化�数
CLEARANCE_GAP = 2.0 # 关节间隙 2.0mm
FILLET_TINY = 1.5 # 微�圆角(螺孔边缘)
FILLET_SMALL = 3.0 # �圆角(功能�)
FILLET_MEDIUM = 6.0 # 中圆角(结构过渡)
FILLET_LARGE = 10.0 # 大圆角(主�轮廓)
FILLET_XLARGE = 15.0 # 超大圆角(视觉主导区域)
CHAMFER_EDGE = 2.0 # 边缘倒角
def Xcyl(r, x0, x1, py, pz):
return place(place(cyl(r, 0.0, x1 - x0, 0.0, 0.0), R(90, 0, 1, 0)), T(x0, py, pz))
def Xplate(pts_yz, x0, x1):
prof = [(-p[1], p[0]) for p in pts_yz]
return place(place(prism(prof, 0.0, x1 - x0), R(90, 0, 1, 0)), T(x0, 0.0, 0.0))
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 中央让�孔
# �2026-10-03h 接�整改】根部让�槽原�切 R30.15,比腔� BAY_R=30.50 � 0.35,
# 于是压盖根部那圈 r30.15..35 的定�唇内�比腔�**内凸 0.35**,从腔里看就是
# 腔�一圈 0.5mm 宽�0.35mm 深的环槽(P04/P05/P06/P07 �有一圈,实测�阶�
# 31.2mm2 + R30.15 环带 44.4mm2,四件完全相�)。这刀是腔还是 R30.15/R30.25
# 那个年代留下的:当时必须让到电机实测外圆,压盖根部�啃�到电机。
# 现在主腔已统一到 BAY_R,电机实测 R30.15,让�槽� BAY_R ��:
# 定�唇内�与腔��一根正圆,两张圆柱�首尾相接,中间��有任何�阶,
# 对电机的�边间隙�是 0.35(唇内径 Ø61.0 > 电机 Ø60.3)。
fp = fp.cut(C(AB.BAY_R, s0 - 0.5, 0.0)) # Ø61.0 根部让�槽(= 腔径)
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, backoff=0.0):
"""优化直连法兰:增加导�倒角
backoff = 毂�沿电机轴**远离电机**的让��(2026-10-03 新增,目��有 P09 用)。
场景:J6/M6 补上�端压盖�(� p_wrist_bracket 的 roll_clamp=True),压盖�
局部 z 55.5..61.0(= 全局 Z 120.5..115),而毂�原�在局部 56.5,两者直接打架。
按 P06 给 J3 让�的�一套办法,把毂�整体�移 CLAMP_BACKOFF=5.5:
毂� 56.5 -> 62.0,压盖�端�在 61.0,中间正好留 CLAMP_CLR=1.0。
�动"外端"�标,�动"电机端"�标,�则咬��就�了:
直�凸�起点 54.942 �动�长度 54.942..56.5 -> ..62.0 加长 5.5,
进入电机输出毂的深度与改�完全一致(与 p_humerus_bone 的 J3 处���);
导�锥 54.5..54.942 �动;
3xS01 输出螺钉让�通�与 4xM2 直�沉孔的**内**端(54.0 / 58.0)�动,
�把外端(z_out+30 / z_out+40)跟�放长。
"""
disc = cyl(r_disc, 56.5 + backoff, z_out + backoff)
boss = cyl(17.8, 54.942, 56.5 + backoff)
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 + backoff)))
return hub_m2_holes(shape, j, cb_z1=z_out + 40.0 + backoff)
# ========== 躯干部分 ==========
# ========== 躯干/测试�架部分 (�置防倾翻高刚性机架) ==========
X_COL = -115.0
Y_COL = Y_AXIS # 128.0
Z_LOCK = 463.0 # 横�防脱�紧螺钉高度(立柱�� → 榫头,M4x16 自攻)
def p_chest():
"""P01_胸腔支撑立柱 (High-Rigidity Box Mast Tower - �置�架立柱):
1. 66x66mm �闭中空厚�箱型柱(�厚 11mm,内腔 44x44),中心在 (-115.0, 128.0),�于手臂正�方;
2. 顶部接收 P03 43.4x43.4x15mm 定�榫头(44x44 R4 内腔)�4x 竖�安装孔 + 1x 横�防脱�紧孔
(全部 Ø3.9 直孔:M4x16 从外部直接拧入自攻�型,无过孔/无沉孔/无预埋);
3. 底部 50x50x12mm 阶梯榫头�入 P02 榫座:环形�肩(66->50)�承载轴�力,槽�全周承载剪切;
4. ��开设两组大圆角走线工艺窗�(Z=60~150mm 与 Z=330~420mm);
5. 外棱角全部采用 R=6mm 圆角。
"""
z_bot = 22.0
z_top = Z_CHEST1 # 470.0
col = (cq.Workplane("XY", origin=(X_COL, Y_COL, z_bot))
.rect(66.0, 66.0)
.extrude(z_top - z_bot)
.edges("|Z").fillet(6.0)).val()
s = cq.Shape(col.wrapped)
cav = (cq.Workplane("XY", origin=(X_COL, Y_COL, z_bot + 10.0))
.rect(44.0, 44.0)
.extrude(z_top - z_bot + 1.0)
.edges("|Z").fillet(4.0)).val()
s = s.cut(cav)
# 底部阶梯榫头:�入 P02 榫座�环形�肩�实,轴�力走�压而� M5 螺纹
tenon = (cq.Workplane("XY", origin=(X_COL, Y_COL, 10.0))
.rect(50.0, 50.0).extrude(z_bot - 10.0)
.edges("|Z").fillet(4.0)).val()
s = s.fuse(tenon)
# 底部 M5 夹紧孔(贯通榫头):X ��对称 (−20/+16) 与榫座孔��对防�装/防转 90°
for xx in (X_COL - 20.0, X_COL + 16.0):
for yy in (Y_COL - 18.0, Y_COL + 18.0):
s = s.cut(cyl(2.75, 9.99, z_bot + 15.0, xx, yy))
# 顶部 4x 安装孔:统一 Ø3.9 直孔(M4x16 直接拧入自攻�型;�打这一个孔,无过孔/无沉孔),深 20mm
# �于 66x66 顶环 11mm 实体�内(内腔 22 → 外� 33),�开 44x44 内腔与 R4 内腔圆角;
for xx in (X_COL - 27.5, X_COL + 27.5):
for yy in (Y_COL - 13.5, Y_COL + 13.5):
s = s.cut(cyl(1.95, z_top - 20.0, z_top + 0.01, xx, yy))
# 横�防脱�紧孔(�� → 榫头):Ø3.9 贯穿 11mm �� + 榫头�径(M4x16 拧入自攻)
s = s.cut(Xcyl(1.95, X_COL - 34.0, X_COL - 20.0, Y_COL, Z_LOCK))
w_bot = (cq.Workplane("YZ", origin=(X_COL + 33.5, Y_COL, 105.0))
.rect(34.0, 70.0)
.extrude(-20.0)).val()
s = s.cut(w_bot)
w_top = (cq.Workplane("YZ", origin=(X_COL + 33.5, Y_COL, 375.0))
.rect(34.0, 70.0)
.extrude(-20.0)).val()
s = s.cut(w_top)
return s
def split_p01_poka_yoke(p01):
"""拆分 P01 为 A/B/C 三段(严格适� 200x200mm 打�机,竖直�支撑打�):
带 16mm 阶梯互�榫�(外套筒� 3.6 + 公榫� 6.9,内腔收窄 51->44 ���厚�,
�边��间隙 0.5)�以�统一 Ø3.9 直孔的分段对�孔(M4x16 从外�直接拧入自攻�型,
���与公榫��径,无过孔/沉孔/预埋,��两�对称�一颗);
孔�相对立柱中心 −16 / +22 �对称(物�防�装防呆);本体 3D 凹刻��标识。
"""
z_bot = 22.0
z_s1 = 172.0
z_s2 = 321.0
z_top = 470.0
h_lap = 16.0
# �对称对�孔�(相对立柱中心�心 -16mm 与 +22mm,�心差 6mm,�装无法穿入螺栓)
xx_bolts = (X_COL - 16.0, X_COL + 22.0)
# 分段对�孔:统一 Ø3.9 直孔(M4x16 从外�直接拧入自攻�型;���与公榫��径,
# 没有过孔/沉孔等其它孔),��两�对称;�尖伸过榫�的一段在内腔侧留 Ø4.5 让�。
def _seam_holes(part, xx, z):
return (part.cut(Ycyl(1.95, Y_COL - 33.6, Y_COL - 21.4, xx, z))
.cut(Ycyl(1.95, Y_COL + 21.4, Y_COL + 33.6, xx, z))
.cut(Ycyl(2.25, Y_COL - 21.7, Y_COL - 15.5, xx, z))
.cut(Ycyl(2.25, Y_COL + 15.5, Y_COL + 21.7, xx, z)))
# --- P01_A (下段) ---
cut_above_s1 = bx(X_COL - 50.0, X_COL + 50.0, Y_COL - 50.0, Y_COL + 50.0, z_s1, z_top + 10.0)
p01_a_base = p01.cut(cut_above_s1)
tongue1_outer = (cq.Workplane("XY", origin=(X_COL, Y_COL, z_s1))
.rect(57.8, 57.8).extrude(h_lap)
.edges("|Z").fillet(3.4).edges(">Z").chamfer(1.5)).val()
tongue1_cav = (cq.Workplane("XY", origin=(X_COL, Y_COL, z_s1 - 1.0))
.rect(44.0, 44.0).extrude(h_lap + 2.0)).val()
tongue1 = cq.Shape(tongue1_outer.wrapped).cut(tongue1_cav)
p01_a = p01_a_base.fuse(tongue1)
z_bolt1 = z_s1 + h_lap / 2.0 # 180.0
for xx in xx_bolts:
p01_a = _seam_holes(p01_a, xx, z_bolt1) # A-B �对�孔(Ø3.9,��两�)
# Engrave the front (-Y) flat face, reading VERTICALLY bottom-to-top along
# the column (user request): local +X = world +Z, local +Y = world -X, so
# the trailing "â–¶" glyph renders pointing world-up (+Z).
front_label_plane_a = cq.Plane(
origin=(X_COL, Y_COL - 33.0, 120.0),
xDir=(0, 0, 1), normal=(0, -1, 0),
)
label_a = (cq.Workplane(front_label_plane_a)
.text("P01-A TOP â–¶", 9.0, -0.8)).val()
p01_a = p01_a.cut(cq.Shape(label_a.wrapped))
# --- P01_B (中段) ---
cut_below_s1 = bx(X_COL - 50.0, X_COL + 50.0, Y_COL - 50.0, Y_COL + 50.0, z_bot - 14.0, z_s1)
cut_above_s2 = bx(X_COL - 50.0, X_COL + 50.0, Y_COL - 50.0, Y_COL + 50.0, z_s2, z_top + 10.0)
p01_b_base = p01.cut(cut_below_s1).cut(cut_above_s2)
pocket1 = (cq.Workplane("XY", origin=(X_COL, Y_COL, z_s1 - 0.1))
.rect(58.8, 58.8).extrude(h_lap + 0.6).edges("|Z").fillet(3.9)).val()
p01_b_base = p01_b_base.cut(pocket1)
tongue2_outer = (cq.Workplane("XY", origin=(X_COL, Y_COL, z_s2))
.rect(57.8, 57.8).extrude(h_lap)
.edges("|Z").fillet(3.4).edges(">Z").chamfer(1.5)).val()
tongue2_cav = (cq.Workplane("XY", origin=(X_COL, Y_COL, z_s2 - 1.0))
.rect(44.0, 44.0).extrude(h_lap + 2.0)).val()
tongue2 = cq.Shape(tongue2_outer.wrapped).cut(tongue2_cav)
p01_b = p01_b_base.fuse(tongue2)
z_bolt2 = z_s2 + h_lap / 2.0 # 329.0
for xx in xx_bolts:
p01_b = _seam_holes(p01_b, xx, z_bolt1) # A-B �对�孔(Ø3.9,��两�)
p01_b = _seam_holes(p01_b, xx, z_bolt2) # B-C �对�孔(Ø3.9,��两�)
front_label_plane_b = cq.Plane(
origin=(X_COL, Y_COL - 33.0, 240.0),
xDir=(0, 0, 1), normal=(0, -1, 0),
)
label_b = (cq.Workplane(front_label_plane_b)
.text("P01-B TOP â–¶", 9.0, -0.8)).val()
p01_b = p01_b.cut(cq.Shape(label_b.wrapped))
# --- P01_C (上段) ---
cut_below_s2 = bx(X_COL - 50.0, X_COL + 50.0, Y_COL - 50.0, Y_COL + 50.0, z_bot - 14.0, z_s2)
p01_c_base = p01.cut(cut_below_s2)
pocket2 = (cq.Workplane("XY", origin=(X_COL, Y_COL, z_s2 - 0.1))
.rect(58.8, 58.8).extrude(h_lap + 0.6).edges("|Z").fillet(3.9)).val()
p01_c = p01_c_base.cut(pocket2)
for xx in xx_bolts:
p01_c = _seam_holes(p01_c, xx, z_bolt2) # B-C �对�孔(Ø3.9,��两�)
front_label_plane_c = cq.Plane(
origin=(X_COL, Y_COL - 33.0, 390.0),
xDir=(0, 0, 1), normal=(0, -1, 0),
)
label_c = (cq.Workplane(front_label_plane_c)
.text("P01-C TOP â–¶", 9.0, -0.8)).val()
p01_c = p01_c.cut(cq.Shape(label_c.wrapped))
return p01_a, p01_b, p01_c
def make_p02_poka_yoke():
"""将 P02 拆分为三件模�化组件(�严格 <= 195x190mm,适� 200x200mm 打�床):
1. P02_Center (底座中核心): 195x190x70mm,完整容纳立柱套筒与��三角斜撑筋,核心应力区无接�;
2. P02_Front (��力臂): 145x190x22mm,宽跨� 104mm �榫头 + 预埋 M5 螺�槽;
3. P02_Rear (��力臂): 45x190x22mm,窄跨� 80mm �榫头(跨�差达 24mm 物�防呆,��混装)。
"""
z0 = 0.0
z1 = 22.0
y_width = 190.0
f_tenon_w = 44.0
f_tenon_y_span = 52.0
r_tenon_w = 32.0
r_tenon_y_span = 40.0
# 1. P02_Center (X = -195.0 .. 0.0, 195x190mm)
x_c_min = -195.0
x_c_max = 0.0
x_c_len = x_c_max - x_c_min
x_c_mid = (x_c_min + x_c_max) / 2.0
c_plate = (cq.Workplane("XY", origin=(x_c_mid, Y_COL, z0))
.rect(x_c_len, y_width).extrude(z1)
.edges("|Z and <X").fillet(8.0).edges("|Z and >X").fillet(8.0)
.edges(">Z").fillet(2.0)).val()
p02_c = cq.Shape(c_plate.wrapped)
# Socket collar (Z: 22..70mm)
z_collar_top = 70.0
collar_outer = (cq.Workplane("XY", origin=(X_COL, Y_COL, z1))
.rect(80.0, 80.0).extrude(z_collar_top - z1)
.edges("|Z").fillet(8.0).edges(">Z").chamfer(2.0)).val()
p02_c = p02_c.fuse(cq.Shape(collar_outer.wrapped))
# Front & Rear triangular gusset ribs
f_pts = [(-75.0, z1), (-15.0, z1), (-15.0, z1 + 4.0), (-75.0, 68.0)]
f_rib = (cq.Workplane("XZ", origin=(0, Y_COL, 0)).polyline(f_pts).close().extrude(19.0, both=True)).val()
p02_c = p02_c.fuse(cq.Shape(f_rib.wrapped))
r_pts = [(-155.0, z1), (-195.0, z1), (-195.0, z1 + 14.0), (-155.0, 70.0)]
r_rib = (cq.Workplane("XZ", origin=(0, Y_COL, 0)).polyline(r_pts).close().extrude(19.0, both=True)).val()
p02_c = p02_c.fuse(cq.Shape(r_rib.wrapped))
# Front wire guide
wg_pts = [(-75.5, z1 + 1.0), (-14.0, z1 + 1.0), (-14.0, z1 + 4.0), (-75.5, 68.5)]
wire_guide = (cq.Workplane("XZ", origin=(0, Y_COL, 0)).polyline(wg_pts).close().extrude(9.0, both=True)).val()
p02_c = p02_c.cut(wire_guide)
# Column socket — stepped mortise: upper 66.6 bore for the 66 box walls,
# lower 50.6 recess (floor 0.6 low) for the 50x50 bottom tenon; the recess
# rim at z=22 is the axial seat, so column weight bears on face, not threads.
socket_upper = (cq.Workplane("XY", origin=(X_COL, Y_COL, z1))
.rect(66.6, 66.6).extrude(z_collar_top - z1 + 1.0)
.edges("|Z").fillet(6.3)).val()
p02_c = p02_c.cut(socket_upper)
socket_lower = (cq.Workplane("XY", origin=(X_COL, Y_COL, 9.4))
.rect(50.6, 50.6).extrude(z1 - 9.4)
.edges("|Z").fillet(4.3)).val()
p02_c = p02_c.cut(socket_lower)
# Vertical M5 clamp bolts (offsets match the asymmetric column tenon holes:
# X −20/+16 blocks 180° flip, the ±18 Y span blocks 90° rotation).
# Threads engage the tenon 15mm — clamp only, axial load rides on the seat.
for xx in (X_COL - 20.0, X_COL + 16.0):
for yy in (Y_COL - 18.0, Y_COL + 18.0):
p02_c = p02_c.cut(cyl(2.75, -0.01, 10.5, xx, yy)).cut(cyl(5.0, -0.01, 6.0, xx, yy))
# Center clamp slots
for cy in (Y_COL - 75.0, Y_COL + 75.0):
plane = cq.Plane(origin=(x_c_mid, cy, -0.01), xDir=(0, 1, 0), normal=(0, 0, 1))
slot = cq.Workplane(plane).slot2D(28.0, 7.0).extrude(z1 + 1.0).val()
p02_c = p02_c.cut(slot)
p02_c = p02_c.cut((cq.Workplane("XY", origin=(X_COL, Y_COL - 70.0, -0.01)).rect(28.0, 80.0).extrude(6.0)).val())
# Front mortise pockets
for sign in (-1, 1):
yy = Y_COL + sign * f_tenon_y_span
front_mortise = (cq.Workplane("XY", origin=(-10.0, yy, 4.8))
.rect(21.0, f_tenon_w + 0.8).extrude(12.6).edges("|Z and <X").fillet(3.0)).val()
p02_c = p02_c.cut(front_mortise)
p02_c = p02_c.cut(cyl(2.75, -0.01, z1 + 1.0, -10.0, yy))
p02_c = p02_c.cut(cyl(5.0, z1 - 5.0, z1 + 1.0, -10.0, yy))
# Rear mortise pockets
for sign in (-1, 1):
yy = Y_COL + sign * r_tenon_y_span
rear_mortise = (cq.Workplane("XY", origin=(-185.0, yy, 4.8))
.rect(21.0, r_tenon_w + 0.8).extrude(12.6).edges("|Z and >X").fillet(3.0)).val()
p02_c = p02_c.cut(rear_mortise)
p02_c = p02_c.cut(cyl(2.75, -0.01, z1 + 1.0, -185.0, yy))
p02_c = p02_c.cut(cyl(5.0, z1 - 5.0, z1 + 1.0, -185.0, yy))
# Debossed text on P02_Center top face. The mid-plate line (y=Y_COL) is
# hidden behind the column collar and the gusset ribs, so both labels sit on
# the flat -Y side strip (y = Y_COL-68) where they stay in full view from the
# label side: REAR on the rear half, FRONT on the front half, clear of the
# clamp slot (x -101..-94) and the M5 screw counterbores.
txt_c_f = (cq.Workplane("XY", origin=(-49.0, Y_COL - 68.0, z1)).text("▲ FRONT (��) ▲", 9.0, -0.8)).val()
txt_c_r = (cq.Workplane("XY", origin=(-146.0, Y_COL - 68.0, z1)).text("▲ REAR (��) ▲", 9.0, -0.8)).val()
p02_c = p02_c.cut(cq.Shape(txt_c_f.wrapped)).cut(cq.Shape(txt_c_r.wrapped))
# 2. P02_Front (X = 0.0 .. 145.0, 145x190mm)
x_f_len = 145.0
x_f_mid = 72.5
f_plate = (cq.Workplane("XY", origin=(x_f_mid, Y_COL, z0))
.rect(x_f_len, y_width).extrude(z1)
.edges("|Z and >X").fillet(16.0).edges(">Z").fillet(2.0)).val()
p02_f = cq.Shape(f_plate.wrapped)
for cy in (Y_COL - 70.0, Y_COL + 70.0):
plane = cq.Plane(origin=(120.0, cy, -0.01), xDir=(0, 1, 0), normal=(0, 0, 1))
slot = cq.Workplane(plane).slot2D(28.0, 7.0).extrude(z1 + 1.0).val()
p02_f = p02_f.cut(slot)
for sign in (-1, 1):
yy = Y_COL + sign * f_tenon_y_span
tongue = (cq.Workplane("XY", origin=(-9.5, yy, 5.25))
.rect(20.0, f_tenon_w - 0.2).extrude(11.5).edges("|Z and <X").fillet(2.5).edges("<X").chamfer(1.5)).val()
p02_f = p02_f.fuse(cq.Shape(tongue.wrapped))
p02_f = p02_f.cut(cyl(2.75, -0.01, z1 + 1.0, -10.0, yy))
nut_pocket = (cq.Workplane("XY", origin=(-10.0, yy, -0.01)).polygon(6, 9.2).extrude(5.0)).val()
p02_f = p02_f.cut(nut_pocket)
# Top-face engraving rotated 90 deg to run across the plate along +Y (user
# request): letter tops face -X, so it reads upright from the front (+X).
# "â–²" points local +Y = world -X, i.e. toward the centre joint.
txt_f = (cq.Workplane(cq.Plane(origin=(x_f_mid, Y_COL, z1), xDir=(0, 1, 0),
normal=(0, 0, 1)))
.text("P02-FRONT â–² JOIN", 8.0, -0.8)).val()
p02_f = p02_f.cut(cq.Shape(txt_f.wrapped))
# 3. P02_Rear (X = -240.0 .. -195.0, 45x190mm)
x_r_len = 45.0
x_r_mid = -217.5
r_plate = (cq.Workplane("XY", origin=(x_r_mid, Y_COL, z0))
.rect(x_r_len, y_width).extrude(z1)
.edges("|Z and <X").fillet(16.0).edges(">Z").fillet(2.0)).val()
p02_r = cq.Shape(r_plate.wrapped)
for cy in (Y_COL - 70.0, Y_COL + 70.0):
plane = cq.Plane(origin=(-215.0, cy, -0.01), xDir=(0, 1, 0), normal=(0, 0, 1))
slot = cq.Workplane(plane).slot2D(28.0, 7.0).extrude(z1 + 1.0).val()
p02_r = p02_r.cut(slot)
for sign in (-1, 1):
yy = Y_COL + sign * r_tenon_y_span
tongue = (cq.Workplane("XY", origin=(-185.5, yy, 5.25))
.rect(20.0, r_tenon_w - 0.2).extrude(11.5).edges("|Z and >X").fillet(2.5).edges(">X").chamfer(1.5)).val()
p02_r = p02_r.fuse(cq.Shape(tongue.wrapped))
p02_r = p02_r.cut(cyl(2.75, -0.01, z1 + 1.0, -185.0, yy))
nut_pocket = (cq.Workplane("XY", origin=(-185.0, yy, -0.01)).polygon(6, 9.2).extrude(5.0)).val()
p02_r = p02_r.cut(nut_pocket)
# Same +Y rotation as the front plate; "â–¼" points local -Y = world +X,
# i.e. toward the centre joint. Along +Y the ~73 mm text fits the 190 mm
# plate width instead of overhanging its 45 mm X span.
txt_r = (cq.Workplane(cq.Plane(origin=(x_r_mid, Y_COL, z1), xDir=(0, 1, 0),
normal=(0, 0, 1)))
.text("P02-REAR â–¼ JOIN", 7.5, -0.8)).val()
p02_r = p02_r.cut(cq.Shape(txt_r.wrapped))
return p02_c, p02_f, p02_r
def p_base_foot():
"""P02_底脚 (�型防倾翻超长力臂底座 + ��大截�三角加强斜撑筋):
1. 底盘尺寸大幅扩展为 385x240x22mm (X: -240.0..+145.0, Y: 8.0..248.0);
- ��延伸至 X = +145.0mm (在手臂垂直投影�方 91.5mm),彻底��手臂�伸与抓��物时的�倾翻;
- ��延伸至 X = -240.0mm (在立柱�方 92.0mm),抵御大动�回程�仰力矩;
2. 立柱�接套筒由 12mm 浅槽大幅�级为 48mm 高加固套筒 (Z: 22.0..70.0mm,外�厚 7mm);
3. ��大三角加强斜撑筋 (Z: 68.0->22.0mm, X: -75.0->-15.0mm, 宽 38mm),抗弯力臂长达 60mm,留足安全间隙�干涉手臂�动;
4. ��大三角加强斜撑筋 (Z: 70.0->22.0mm, X: -155.0->-225.0mm, 宽 38mm),抗�仰力臂长达 70mm;
5. �斜撑表�设 18mm 宽平滑走线导槽,与 P01 下部走线窗�自然贯通;
6. 6 组 M6 工业工作�紧固腰槽 (28x7mm) + 底部�蔽走线槽;
7. 四周 R=18mm 大圆角过渡与 2.5mm 上边缘倒角。
"""
z0 = 0.0
z1 = 22.0
x_rear = -240.0
x_front = 145.0
x_len = x_front - x_rear # 385.0
x_mid = (x_rear + x_front) / 2.0 # -47.5
y_width = 240.0
# 1. 主底盘 (385 x 240 x 22 mm)
base_plate = (cq.Workplane("XY", origin=(x_mid, Y_COL, z0))
.rect(x_len, y_width)
.extrude(z1)
.edges("|Z").fillet(18.0)
.edges(">Z").fillet(2.5)).val()
s = cq.Shape(base_plate.wrapped)
# 2. 立柱外包加固套筒 (Z: 22.0..70.0 mm, 外廓 80x80mm)
z_collar_top = 70.0
collar_outer = (cq.Workplane("XY", origin=(X_COL, Y_COL, z1))
.rect(80.0, 80.0)
.extrude(z_collar_top - z1)
.edges("|Z").fillet(8.0)
.edges(">Z").chamfer(2.0)).val()
s = s.fuse(cq.Shape(collar_outer.wrapped))
# 3. ��抗倾翻三角加强斜撑筋 (Front Triangular Gusset Rib)
f_pts = [
(-75.0, z1),
(-15.0, z1),
(-15.0, z1 + 4.0),
(-75.0, 68.0),
]
f_rib = (cq.Workplane("XZ", origin=(0, Y_COL, 0))
.polyline(f_pts).close()
.extrude(19.0, both=True)).val()
s = s.fuse(cq.Shape(f_rib.wrapped))
# 4. ��抗倾翻三角加强斜撑筋 (Rear Triangular Gusset Rib)
r_pts = [
(-155.0, z1),
(-225.0, z1),
(-225.0, z1 + 4.0),
(-155.0, 70.0),
]
r_rib = (cq.Workplane("XZ", origin=(0, Y_COL, 0))
.polyline(r_pts).close()
.extrude(19.0, both=True)).val()
s = s.fuse(cq.Shape(r_rib.wrapped))
# 5. �斜撑表�走线导槽 (与 P01 �走线窗 Z=70..140mm 自然顺接)
wire_guide_pts = [
(-75.5, z1 + 1.0),
(-14.0, z1 + 1.0),
(-14.0, z1 + 4.0),
(-75.5, 68.5),
]
wire_guide = (cq.Workplane("XZ", origin=(0, Y_COL, 0))
.polyline(wire_guide_pts).close()
.extrude(9.0, both=True)).val()
s = s.cut(wire_guide)
# 6. 立柱深�槽内腔 (Pocket: 66.6 x 66.6 mm, 留 0.3mm 精密滑�间隙)
socket_box = (cq.Workplane("XY", origin=(X_COL, Y_COL, 10.0))
.rect(66.6, 66.6)
.extrude(z_collar_top - 10.0 + 1.0)
.edges("|Z").fillet(6.3)).val()
s = s.cut(socket_box)
# 7. 4x M5 底部垂直紧固螺栓孔与沉孔
for xx in (X_COL - 21.0, X_COL + 21.0):
for yy in (Y_COL - 21.0, Y_COL + 21.0):
th = cyl(2.75, -0.01, 10.5, xx, yy)
cb = cyl(5.0, -0.01, 6.0, xx, yy)
s = s.cut(th).cut(cb)
# 8. 6 组 M6 工业工作�紧固腰槽 (28 x 7 mm)
slot_positions = [
(-215.0, Y_COL - 85.0), (-215.0, Y_COL + 85.0), # �部两角
(-47.5, Y_COL - 95.0), (-47.5, Y_COL + 95.0), # 中部两侧
(120.0, Y_COL - 85.0), (120.0, Y_COL + 85.0), # �部两角
]
for cx, cy in slot_positions:
plane = cq.Plane(origin=(cx, cy, -0.01), xDir=(0, 1, 0), normal=(0, 0, 1))
slot = cq.Workplane(plane).slot2D(28.0, 7.0).extrude(z1 + 1.0).val()
s = s.cut(slot)
# 9. 底部�蔽走线通槽
wire_ch = (cq.Workplane("XY", origin=(X_COL, Y_COL - 80.0, -0.01))
.rect(28.0, 100.0)
.extrude(6.0)).val()
s = s.cut(wire_ch)
return s
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 螺�刀通�直接走腔体,拧紧更顺手。
"""
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))
# 4x M3 连接螺孔(螺�刀通�直接走两竖�之间的开放腔体,无需�钻 Ø8.5 直通隧�)
# 孔� = 电机尾端 4x M3 螺纹径�槽实测�(电机.STEP):BCD55�相� 39.9°+90k。
# 旧 35.1×40.9 矩形孔�与实际槽�差 ~4.7mm(对角两颗螺钉无处咬�),
# 2026-10-02 用户指出"螺钉没有对�"�按 STEP 实测修正。
for dy, dz in ((21.06, 17.65), (-17.65, 21.06), (-21.06, -17.65), (17.65, -21.06)):
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)
# 中央走线孔 (Ø26mm)
wire_hole = Xcyl(13.0, X_COL - 40.0, X_MOUNT + 2.0, Y_ARM, Z_ARM)
s = s.cut(wire_hole)
# 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,17.8°/107.8°/197.8°/287.8°):
# 平直舱孔的沉头座�由 4 个 Ø10 局部凸�承载——凸��于舱孔�段 8.06..11.5,
# 电机�端�(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)
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)
return s
def p_fork(j1, j2):
"""P04_肩�_螺旋体30%_墙5底5_竖打 (Shoulder Fork / L-Bracket) - 加长�让顺滑�线版:
1. 头部与身�起点自 X=5.5 �移加长至 X=10.5+X_FORK_GAP=11.5,��背�与 P03 托架端�
(X=8.5+X_BAY_EXT=11.5) 0.0 硬贴平(2026-10-02 用户两次�求"��平的";
P03 侧�槽独立加长 3mm 便于 M1 电机斜��入);
2. J1 从动法兰圆盘已删除(盘�在 column �缘形�鼓楞):J1 由 boss(Ø35.6)贯通 P03 �盖中心孔直连电机输出盘;column 顶边�� Z=646.5 与 P03 环�高,R=18mm 大圆弧顺滑过渡;
3. 头部转接凸�加长贯通 P03 �盖中心孔 (R=19.5mm),直连 J1 旋转法兰盘;
4. M2 �留�端安装�(R=35mm, 带 rear_mount_holes 螺钉孔),�端定�环鼓出圆盘已删除(�端定�由 column 端� pocket 承担),中间镂空散热;
4b. �2026-10-02 新增】M2 �端压盖:原方案电机�被�端安装�(S02 4xM3x10)���死,
大扭矩下电机绕 Y 轴在 Ø60.3 座孔里点头/翘起,固定�牢。现在�端盖外端�
(Y=156.0) �加一枚�径 R35 压盖,用 4x M3x8 在电机�端盖 BCD55 上把电机
从�端夹一次 —— 电机被��两��心 Ø60.3 端���夹紧,倾斜自由度归零;
螺钉沉在压盖沉孔内�外凸,��用 J2 轴�间隙(P05 法兰盘已相应�移);
5. 严格满足 single-solid �形拓扑与 360° 全范围零干涉 (COLLISION pairs = 0)。
"""
# 1. M2 �端安装法兰盘 (Y: 92..100, R=35mm)
rear_plate = Ycyl(35.0, Y_REAR - 8.0, Y_REAR, X_ARM, Z_SH)
# 2. M2 �端定�环 (Y: 149.5..155.5, R=35mm)
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 �端压盖(几何与�由� 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)直连电机输出盘
# 承担,螺钉走 boss 端� 4xB 孔,圆盘无传力功能已去除。
# 4. XZ 平�有机连续�线轮廓(� X_FORK_GAP 整体�移,�让顺滑过渡):
x_b, z_b = -12.65 + X_FORK_GAP, 506.0
x_a, z_a = 10.5 + X_FORK_GAP, 545.0
Lz = z_a - z_b
d0 = 0.514 * Lz
d1 = 0.0
hermite_pts = []
N_h = 16
for i in range(N_h + 1):
t = i / N_h
h00 = 2.0 * (t**3) - 3.0 * (t**2) + 1.0
h10 = t**3 - 2.0 * (t**2) + t
h01 = -2.0 * (t**3) + 3.0 * (t**2)
h11 = t**3 - t**2
xh = x_b * h00 + d0 * h10 + x_a * h01 + d1 * h11
zh = z_b + t * Lz
hermite_pts.append((xh, zh))
dx_flare = (53.5 + X_FORK_GAP) - (44.0 + X_FORK_GAP)
dz_flare = 545.0 - 490.0
R_flare = (dx_flare**2 + dz_flare**2) / (2.0 * dx_flare)
xz_wire = (cq.Workplane("XZ", origin=(0, Y_AXIS, 0))
.moveTo(10.5 + X_FORK_GAP, 545.0)
.lineTo(10.5 + X_FORK_GAP, Z_RL + 36.5)
.lineTo(28.0 + X_FORK_GAP, Z_RL + 36.5)
.radiusArc((44.0 + X_FORK_GAP, 630.0), 18.0)
.lineTo(44.0 + X_FORK_GAP, 545.0)
.radiusArc((53.5 + X_FORK_GAP, 490.0), -R_flare)
.lineTo(18.5 + X_FORK_GAP, 490.0)
.lineTo(-16.5 + X_FORK_GAP, 490.0)
.threePointArc((-15.1 + X_FORK_GAP, 498.5), (-12.65 + X_FORK_GAP, 506.0)))
for pt in hermite_pts[1:]:
xz_wire = xz_wire.lineTo(pt[0], pt[1])
xz_wire = xz_wire.close()
xz_shape = cq.Shape(xz_wire.extrude(100.0, both=True).val().wrapped)
# 5. YZ 平�包络
# �2026-10-07 按用户手绘红线改�"R3 圆角 + 竖直��",消掉顶��沿那颗尖】
# �历:2026-10-04 那版把�脸��与顶�**相切**的 Hermite 弧�,但这�曲线
# �有 9.0mm 的 Y 预算(164.5->155.5),���收 120.5mm 的�差,于是 90° 的
# 转弯全挤在起点:实测离开顶� 0.28mm �差就转到 45°,起始曲率�径 0.571mm。
# �果是顶��沿 Y=164.5 �了**全件最外凸点**,往下�脸立刻�退,
# 在 Y=164.32 处顶�与�脸之间的料厚�剩 0.027mm —— 就是用户截图圈的那颗尖。
# 2026-10-05 的"起点加密采样"把第一段 57° 折线(那其实是一层�外的�化)抹平,
# �而让它��真正的刀刃。两次都�碰了�缘**一�**棱,
# 顶�左缘(X=11.5, 73mm)与�缘(Y=91.5, 17.5mm)�是 89° 直棱,所以没解决。
# 现在的形状:顶� -> �侧 R3 圆角 -> 竖直�� Y=164.5 一路直�到 Z=566
# -> Hermite(两端切线�沿 -Z,G1) 顺收到 (155.5, 526);
# 顶�**�缘**�样倒 R3(与�缘对称,用户 2026-10-07 追加�求),
# 弧心�在背�那�斜线上(背�从 (91.5,646.5) 斜到 (92.0,526),斜 0.24°,
# 切点 Y 必须按该斜线�,�则弧端会露出 0.01mm �阶)。
# crest ��是独立的最外凸点(整����为 Y=164.5),尖由此从根上消失;
# 全件 Y 最大值�是 164.5,与 P03 止脱环 R36.5/顶646.5 的�平关系��。
# �缘圆角��料:体积 296.24 -> 296.20 cm3,Y 最�值 91.50 -> 91.51。
# 代价(实测):P04 体积 276.2 -> 296.2 cm3 (+20.0),因为填掉了原�那个内凹腔;
# 且 J1 扫掠�径由 crest 的 hypot(36.5,36.5)=51.6 增到�� (Y164.5,Z566) 的
# hypot(36.5,44.0)=57.2 (+5.6mm) —— 全行程碰撞/行程必须�扫�算完�。
# 注�:顶点绕�必须与原列表一致(先顶�������竖直段),�则多边形自交�
# 布尔�零件会碎掉(实测会掉到 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) 顶��沿 = ��所在 Y
R_TIP = 3.0 # 顶� -> 竖直�� 的圆角�径
R_BACK = 3.0 # 顶� -> 背� 的圆角�径(与�缘对称)
Z_WALL_END = 566.0 # 竖直���到此处,�往里顺收
_c = (y_b[0] - R_TIP, y_b[1] - R_TIP) # �侧圆角心 (161.5, 643.5)
# 背�那�线是斜的:(91.5, 646.5) -> (92.0, 526.0),斜 0.24°。
# �侧圆角必须切在这�线上,�则弧端点会露出 0.01mm 的�阶。
_zb0, _zb1 = Z_RL + 36.5, Z_SH # 背�上下端 Z
_yb0, _yb1 = Y_AXIS - 36.5, Y_REAR - 8.0 # 背�上下端 Y
_z_ab = _zb0 - R_BACK # 弧与背�的切点高度 643.5
_y_ab = _yb0 + (_zb0 - _z_ab) / (_zb0 - _zb1) * (_yb1 - _yb0)
_cb = (_y_ab + R_BACK, _z_ab) # �侧圆角心
yz_pts = [(_cb[0], _zb0), # 顶��缘改从圆角起点开始
(_c[0], y_b[1])] # 顶�走到�侧圆角起点 Y=161.5
for _k in range(1, 31): # R3 圆角:90° -> 0°,切线由水平转竖直
_a = math.radians(90.0 - 90.0 * _k / 30.0)
yz_pts.append((_c[0] + R_TIP * math.cos(_a), _c[1] + R_TIP * math.sin(_a)))
yz_pts.append((y_b[0], Z_WALL_END)) # 竖直�� Y=164.5 直�(直线段)
_m = 0.5 * math.hypot(y_b[0] - y_a[0], Z_WALL_END - y_a[1]) * 0.5
for _k in range(1, 121): # 底部顺收 Hermite:两端切线都沿 -Z
t = _k / 120.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] + h01 * y_a[0],
h00 * Z_WALL_END + h01 * y_a[1] - _m * (h10 + h11)))
yz_pts += [(Y_COVER - 0.5, Z_SH),
(Y_REAR - 8.0, Z_SH),
(Y_REAR - 8.0, Z_SH + 36.0)]
for _k in range(0, 30): # �侧 R3 圆角:180° -> 90°
_a = math.radians(180.0 - 90.0 * _k / 30.0)
yz_pts.append((_cb[0] + R_BACK * math.cos(_a), _cb[1] + R_BACK * math.sin(_a)))
yz_solid = Xplate(yz_pts, -20.0, 60.0)
c_int = BRepAlgoAPI_Common(yz_solid.wrapped, xz_shape.wrapped)
c_int.Build()
column = cq.Shape(c_int.Shape())
s = s.fuse(column)
# 6. 中部电机定�散热切除
# �2026-10-05f】这刀原本是独立存在的(挖 R30.5 让定�裸露)。现在主腔已�
# 统一到 BAY_R=30.50 并从承压�一路挖到�端,这刀完全�在主腔范围内 ——
# 留��改也�会多挖,但它是「�一段腔被两个���径�挖一次�的残留,
# 正是接�的��,删掉以��人以为散热��有深�。
# 通风� cut_open(第 6b 步的敞�盒切)实现,��赖这刀。
# s = s.cut(Ycyl(30.5, Y_REAR + 0.5, Y_COVER - 6.5, X_ARM, Z_SH))
# 7. M2 接��型
# �2026-10-05f】与 Joint.pocket �步:主腔统一为�一 BAY_R 圆柱,
# 从承压�一路挖到�端盖外,内���有 R35/R30.15/R30.25/R30.50 四段�阶。
# (承压�段���独挖 SEAT_R:BAY_R 从 -REAR_SEAT 起就覆盖了它,
# 承压�本身�是 R35 外�围出的完整平圆盘。)
s = j2.rear_mount_holes(s, t_plate=8.0, hole_d=2.8, cb_d=5.5, cb_depth=4.0)
s = s.cut(j2.L(cyl(AB.BAY_R, -AB.REAR_SEAT, COVER_Z + 0.6)))
# 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)都有富余,但先切�以����那些刀具�会把槽�打薄。
# 7c2.�2026-10-05 槽��跟�外轮廓,去掉�上角两段尖楔】
# x1=35 是按立柱中段定的(Z525~630,X11.5~45 全宽都在,中性� X29,
# 两片腹� X13~23 / X35~45 � 10mm)。� Z630 以上零件被 R18 圆角收窄:
# 顶��剩 X11.5~29,�往�是圆角带 X29~45。
# 原先一刀切到底,槽就横�切进圆角带;�因为槽在 Y=102/154 收尾,
# 圆角带被切�两段悬空的楔料(一头尖一头宽),从�上角看就是"一�段尖的"
# —— 它悬在槽�上方 1.7mm(Z644.76~646.5)没有支撑,尖头正好�在收尾处。
# 改法:**上段跟�外轮廓收窄**,Z>630 �切到 X29(顶��边缘),
# 圆角带 X29~45 一整���连续,中/左/�三段料都��悬空,
# 尖楔和新�阶一起消失(先��在 Z644.8 削平顶�,等于把尖顶削�一�
# 新�阶,尖头本身还在,更难看 —— 已回退)。
# 下段 X23..35 原样�留,腹��置和厚度都�动,强度结论��影�。
# 分界 Z=630 �自 xz_wire:R18 圆弧正是从 (X45,Z630) 起往左上收。
Z_TAPER = 630.0 # = 圆角起点 Z,槽��在此处收窄
X_TOP_EDGE = 29.0 # = 顶��边缘(圆角带上沿),槽�许越过
s = s.cut(hollow_channel(P04_CH["x0"], P04_CH["x1"], P04_CH["y0"], P04_CH["y1"],
P04_CH["z_floor"], Z_TAPER, tag="P04立柱下段"))
s = s.cut(hollow_channel(P04_CH["x0"], X_TOP_EDGE, P04_CH["y0"], P04_CH["y1"],
Z_TAPER, 660.0, tag="P04立柱上段(�外轮廓收窄)", fil=0.0))
# 上段 fil=0:槽宽�剩 6mm(X23~29),R4 内角圆角在 6mm 宽的槽上两侧
# �加会退化�零�积而失败(实测报 BRep_API: command not done)。
# 这里本�就是直�窄槽,�倒角也符�"跟�外轮廓"的�图。
# 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)))
# 沉孔�阶固定在 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,
roll_clamp=False):
"""Half-wrapped (�包) �曲�有机�线型 Pitch-to-Roll 结构座 (P05 & P08):
1. 顶部从动法兰与�侧 Loft 自然相切顺接;
2. 严格清除 z_plate 顶�以上与上级旋转件贴�的多余�料 (cut_top_solid),��关节大范围摆动动� 0 干涉;
3. 下方电机实行�包散热设计,-Y侧敞开,+Y侧背脊与圆柱一体化��;
4. 严格满足 single-solid �形拓扑与零干涉 (COLLISION pairs = 0)。
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 自 2026-10-03 起也传 True:M6 �端压盖补�,对侧 P09 让� CLAMP_BACKOFF
(� make_direct_hub 的 backoff),五处电机腔至此全部是「�端� + �端压盖�
���压,�方��。
"""
z_bot = z_r - 56.0 # roll 电机底端 (front collar)
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(凸�长度跟�它走)
y_cyl_max = HL.Y_AXIS + 35.0 # 163.0 (下方电机外轮廓�值)
# 1. 基础立柱:Ø70mm 实心圆柱 (Z: z_bot .. z_plate, R=35mm)
s = cyl(35.0, z_bot, z_plate, HL.X_ARM, HL.Y_AXIS)
# 2. 俯仰轴直连法兰盘:全圆柱实体 (Y: y_hub_in .. y_out, R=35mm)
disc = Ycyl(35.0, y_hub_in, y_out, HL.X_ARM, z_pitch)
s = s.fuse(disc)
# 3. �侧从底端 Ø70mm 圆柱到顶端法兰的有机相切平滑��过渡体 (Loft Morph)
Z_top = z_pitch - 35.0
z_start = z_bot
N = 16
wp = cq.Workplane("XY")
for i in range(N + 1):
t = i / float(N)
z_curr = z_start + t * (Z_top - z_start)
st = 3.0 * (t**2) - 2.0 * (t**3)
y_center = y_cyl_max + st * (y_out - y_cyl_max)
y_side = HL.Y_AXIS + st * (y_out - HL.Y_AXIS)
v_c = y_center - HL.Y_AXIS
v_s = y_side - HL.Y_AXIS
v_mid = max(v_c, v_s + 0.02)
# �缘两角 R2 圆角:把 y=123 凸包与 R35 柱�交汇的两� 90° 竖棱圆润化
# (磨掉"凸起�的"尖角,�动�缘高度/�置,打�无悬挂)
FR = 2.0
w = (cq.Workplane("XY", origin=(HL.X_ARM, HL.Y_AXIS, z_curr))
.moveTo(-35.0, v_s)
.threePointArc((0.0, v_mid), (35.0, v_s))
.lineTo(35.0, -5.0 + FR)
.radiusArc((35.0 - FR, -5.0), FR)
.lineTo(-35.0 + FR, -5.0)
.radiusArc((-35.0, -5.0 + FR), FR)
.lineTo(-35.0, v_s)
.close())
wp = wp.add(w)
loft = wp.toPending().loft()
s = s.fuse(loft.val())
# 4. 顶部填充(仅�留 y >= y_hub_in 与法兰盘对�的部分,��侵入上级关节旋转扫掠区域)
top_fill = (cq.Workplane("YZ", origin=(HL.X_ARM, 0, 0))
.moveTo(y_hub_in, Z_top)
.lineTo(y_out, Z_top)
.lineTo(y_out, z_pitch)
.lineTo(y_hub_in, z_pitch)
.close()
.extrude(35.0, both=True))
s = s.fuse(top_fill.val())
# 5. 旋转清让切除:彻底清除 z >= z_plate 且 y <= y_hub_in 范围内的多余�料,
# 消除导致与上游旋转件(P04/P07)严�干涉的立�与尖角,��顶�平整规整�
cut_top_solid = (cq.Workplane("YZ", origin=(HL.X_ARM, 0, 0))
.moveTo(-999.0, z_plate)
.lineTo(y_hub_in, z_plate)
.lineTo(y_hub_in, z_pitch + 50.0)
.lineTo(-999.0, z_pitch + 50.0)
.close()
.extrude(60.0, both=True))
s = s.cut(cut_top_solid.val())
# 6. 电机�包处� (Half-Enclosure):-Y侧敞开散热,+Y侧背脊壳体加强
cut_open = bx(HL.X_ARM - 40.0, HL.X_ARM + 40.0, HL.Y_AXIS - 40.0, HL.Y_AXIS + 3.0, z_bot + 6.0, z_r - 1.0)
s = s.cut(cut_open)
# 7. 上级电机�心圆弧�让
clearance = Ycyl(35.5, HL.Y_AXIS - 40.0, HL.Y_COVER + 0.1, HL.X_ARM, z_pitch)
s = s.cut(clearance)
# 8. 下方电机安装孔�与定�止�
# 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)
# �2026-10-03i 接�整改】front_collar_pocket 已删。
# 它切的是 cyl(BAY_R, z0=50.0, z1=56.5),而紧接�的 Joint.pocket 切的是
# cyl(BAY_R, -REAR_SEAT, COVER_Z+front_pad) —— **�一根圆柱��一个�径**,
# �者完全是�者的�集。两次�径相�的圆柱布尔相�,等于让 OCC 在腔�上
# 留下一� s=50.0 的零宽棱:实测 P05 的 R30.500 腔�被切� 5090.5 + 493.9
# 两张�(-2.6..50.0 与 50.0..55.5),P06 �样在 50.0 处裂开。�色时那�
# 棱就是腔里横穿的一�线。P04/P08 本�就没调它,腔�是完整一张�。
s = j_roll.pocket(s)
# 9. 电机定���空腔
# �2026-10-05f】原 cyl(30.5, ...) 定���空腔已删,�上:已被主腔 BAY_R 覆盖。
# 10. 俯仰轴直连法兰结构(凸�延伸至法兰盘背�之� 2.5mm,跨过间隙与法兰盘牢固熔接,
# ���实体)。凸�长度跟� y_hub_in 走:法兰盘�移时凸��步加长,
# 3xS01 输出螺钉的头��阶(局部 z=58)��长�咬��与改�完全一致。
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)))
s = hub_m2_holes(s, j_pitch, cb_z1=74.0 + 40.0)
# 11. L 型转角连接处圆角过渡(圆角 R=2.0mm):
# 平滑消除立柱顶�与竖直臂交汇处的 90° 尖角与应力集中
try:
wp_s = cq.Workplane(obj=s)
sel_l = cq.NearestToPointSelector((HL.X_ARM, y_hub_in, z_plate))
s = wp_s.edges(sel_l).fillet(2.0).val()
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:
# tag 里的电机�从 j_roll.jid �(P05->M3 / P08->M6),别�写死 M3 ——
# 写死的� P08 的构建日志会谎报自己夹的是 M3,排查时容易被带�。
s = s.fuse(motor_front_clamp(HL.X_ARM, 0.0,
tag="%s/M%d" % (name[:3], int(j_roll.jid[1])),
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) 现在都有�端压盖�环上骑�压盖 —— 两者都�切敞�(局部 +y)
# 那�圈定�环,背脊侧整圈�留,继续定�电机�端盖,并给压盖当 4.5mm 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_竖打(深度圆角优化版)
本件在链路上有两�身份,两处让�都是为�端压盖:
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, roll_clamp=True)
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. 严格�实体 (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)
# 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)))
s = hub_m2_holes(s, j3, cb_z1=58.0 + 40.0)
return s
def p_forearm_bone(j4, j5, half=None):
"""P07_�臂骨架_螺旋体30%_墙5底5_竖打 L型高刚度无干涉版(深度圆角优化版)
设计�点:
1. 改为与 P05/P08 统一的 L 型结构(L-bracket):
- +Y侧垂直背脊立柱(Y: y_hub_in..y_out),�开上级大臂骨架 P06(Y <= 155.5)
- 彻底消除原全深S弯在J4轴下方�两边�出的干涉结构(两边�出的结构全部去除)
- 垂直立柱与下方 M5 电机水平座之间采用规则折角(Corner Chamfer/Gusset)刚性连接
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
# 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,消除过薄破�问题)
z_roof_top = z_top_housing + 19.0 # 280.0 (顶�整�加高 19mm,用户确认方案4)
# 1. J5 电机��安装环
collar_front = Ycyl(35.0, HL.Y_COVER - 6.0, HL.Y_COVER - 0.5, X_ARM, Z_WR)
rear_plate = Ycyl(35.0, HL.Y_REAR - 8.0, HL.Y_REAR, X_ARM, Z_WR)
s = collar_front.fuse(rear_plate)
# 2. J4 顶部从动法兰盘 (Y: y_hub_in..y_out, R=35mm, Z: Z_EL)
disc = Ycyl(35.0, y_hub_in, y_out, X_ARM, Z_EL)
s = s.fuse(disc)
# 3. M5 电机壳体水平顶部顶� (Z: Z_WR..z_top_housing, Y: y_rear..y_front)
roof_w = (cq.Workplane("YZ", origin=(X_ARM, 0, 0))
.moveTo(y_rear, Z_WR)
.lineTo(y_rear, z_roof_top)
.lineTo(y_front - 3.0, z_roof_top)
.threePointArc((y_front - 3.0 + 3.0 * math.cos(math.pi / 4),
z_roof_top - 3.0 + 3.0 * math.sin(math.pi / 4)),
(y_front, z_roof_top - 3.0))
.lineTo(y_front, Z_WR)
.close()
.extrude(35.0, both=True))
s = s.fuse(roof_w.val())
# 4. L型垂直立柱臂 (+Y侧,Y: y_hub_in..y_out,从 Z_EL 顶端延伸至下方电机座)
vert_leg = (cq.Workplane("YZ", origin=(X_ARM, 0, 0))
.moveTo(y_hub_in, z_top_housing)
.lineTo(y_out, z_top_housing)
.lineTo(y_out, Z_EL)
.lineTo(y_hub_in, Z_EL)
.close()
.extrude(35.0, both=True))
s = s.fuse(vert_leg.val())
# 5. L型内侧圆弧加强过渡(Circular Arc Gusset,用户�求"改�圆弧而�是折角,消掉悬空��")
# 彻底�代直角折角与直斜线折角,采用与顶�和立柱�相切的平滑内凹大圆弧 (R=28.0mm)
# 起点在顶�平整� (y_arc_start, z_roof_top),精确相切至立柱内侧� (y_hub_in, z_arc_end)
# 严控全域 Z >= z_top_housing,彻底消除侵入下方旋转副的悬空�方�与多余�起�
# 注:顶�整�加高19mm(z_roof_top=280)�,R28改R14使弧�与顶��光滑相切,�出现折角
R_arc = 14.0 # �顶�加高�动�定:相切起点(144.5,280)->终点(158.5,294),�开P06环底z=297留3mm
y_arc_start = y_hub_in - R_arc
z_arc_end = z_roof_top + R_arc
p_mid_arc = (y_arc_start + R_arc * math.cos(math.pi / 4),
z_arc_end - R_arc * math.sin(math.pi / 4))
gusset_w = (cq.Workplane("YZ", origin=(X_ARM, 0, 0))
.moveTo(y_arc_start, z_roof_top)
.threePointArc(p_mid_arc, (y_hub_in, z_arc_end))
.lineTo(y_out, z_arc_end)
.lineTo(y_out, z_top_housing)
.lineTo(y_arc_start, z_top_housing)
.close()
.extrude(35.0, both=True))
s = s.fuse(gusset_w.val())
# 6. 下级 P08 旋转圆盘动��让切除(R=35.5mm,预留 0.5mm 间隙)
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 电机安装接�与定�空腔
# �2026-10-05f 接�整改】本件原先是全机唯一给腔径加 CLEARANCE_GAP(2.0) 的地方:
# front_collar_pocket(r_bore=32.25) + 一刀 Ycyl(32.5, Y_REAR-0.5, y_front-6)。
# 结果内�是 35 -> 32.5 -> 30.2 -> 32.3 -> 30.2,比兄弟件多两��阶
# (实测 scratch/bay_step_profile.py:z=5.5 处 32.5�z=49.5 处掉到 30.2�
# z=50.25 �回到 32.3)。
# 这 2.0mm 是「关节间隙�,但关节间隙是**轴�**让�(� hub_gap / CLAMP_CLR),
# 径�把腔径撑大 2mm �会让电机在腔里晃,且与 P05/P06/P08 �一致。
# 现统一走 BAY_R(30.50),与兄弟件�径,接�归零。
# �2026-10-03i】front_collar_pocket �之删除:它切 cyl(BAY_R, 50.0..56.5),
# 是 Joint.pocket 那根 cyl(BAY_R, -2.6..61.6) 的�集,��径��相�
# �会在腔�上多出一� s=50.0 的零宽棱。
s = j5.rear_mount_holes(s, t_plate=8.0, hole_d=2.8, cb_d=5.5, cb_depth=4.0)
s = j5.pocket(s)
# �2026-10-05f】原 Ycyl(30.5+CLEARANCE_GAP=32.5, ...) 整刀删除:
# 它既撑大了腔径(接�主因),�已被主腔 BAY_R 完全覆盖(纯冗余)。
# �包散热设计:-Z底部电机定�通风�
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)
# 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)))
s = hub_m2_holes(s, j4, cb_z1=74.0 + 30.0)
# 9. 工艺走线通孔
slot_z = (Z_EL + z_top_housing) / 2.0
slot = Ycyl(7.0, y_hub_in - 2.0, y_out + 2.0, X_ARM, slot_z)
s = s.cut(slot)
# ========== 关键圆角优化 ==========
try:
# �侧顶�外边缘圆角(平滑消除水平顶�与�部垂直端�交汇处的 90° 尖角,用户�求倒角改圆角)
wp_s = cq.Workplane(obj=s)
sel_rear = cq.selectors.BoxSelector(
(X_ARM - 40, y_rear - 1.0, z_roof_top - 1.0),
(X_ARM + 40, y_rear + 1.0, z_roof_top + 1.0)
)
s = wp_s.edges(sel_rear).fillet(FILLET_SMALL).val()
except Exception as e:
print(f"P07 顶��边缘圆角异常: {e}")
return s
def p_wrist_bracket(j5, j6):
"""P08_腕�转座_螺旋体25%_墙4底5_竖打(深度圆角优化版)
hub_gap=6.0:J5 处 P07 新增了 M5 �端压盖(Y 155.5..161.0),从动法兰盘背�必须
从 Y=157 �移到 Y=162 让�;J5 直�凸��步加长,3xS01 输出螺钉咬����。
roll_clamp=True�2026-10-03】:M6 补上�端压盖,与 P04/P05/P06/P07 的
M2/M3/M4/M5 �一套�方(R35�4xM3x8 BCD55 @17.8°+90k�Ø6.0 沉孔�Ø61.0 根部
让�槽),轴� -Z,压盖�全局 Z 120.5..115。此�本件是五处电机腔里**唯一**
�被�端� 4xM3x10 ���死的,电机大扭矩下会在 Ø60.3 座孔里点头;对侧
P09 让� CLAMP_BACKOFF=5.5(� make_direct_hub 的 backoff),工具端点�动。
"""
return make_pitch_to_roll_bracket(j5, j6, Z_WR, Z_R6,
name="P08_腕�转座_螺旋体25%_墙4底5_竖打",
hub_gap=6.0, roll_clamp=True)
def p_tool_flange(j6):
"""P09_工具法兰_螺旋体30%_墙4底5_竖打 深度圆角优化版
关键改进:
1. 外轮廓全圆角(R=4mm)
2. 法兰�倒角(2mm)
3. 螺孔边缘倒角(1.5mm)
"""
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 = 22.0
u = rad * math.cos(a * D2R)
v = rad * math.sin(a * D2R)
# 实际切割推迟到 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)
# 直连法兰
# �2026-10-03】M6 补上�端压盖�,J6 毂�必须让� CLAMP_BACKOFF=5.5
# (详� make_direct_hub 的 backoff)。P09 的法兰本体 z_bot/Z_HUB6 一个字没动,
# 让�的�是伸进 J6 腔里的那圈毂� —— 圆盘下移��与法兰本体在局部 65..79.5
# 大�积交�,熔接��影�;工具端点 Z_TIP=93 也�之��。
s = make_direct_hub(s, j6, r_disc=35.0, z_out=74.0, backoff=CLAMP_BACKOFF)
# 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:
# 外轮廓圆角
s = s.edges("|Z").fillet(FILLET_SMALL)
except:
pass
try:
# 法兰�倒角
s = s.edges("<Z").chamfer(CHAMFER_EDGE)
except:
pass
try:
# 顶部边缘圆角
s = s.edges(">Z").fillet(FILLET_TINY)
except:
pass
return s
# ---------------------------------------------------------------- assembly ----------------
def build():
print("=" * 80, flush=True)
print("�第一阶段 1/5】开始生�手臂�级构件 3D 几何实体模型...", flush=True)
print("=" * 80, flush=True)
t_stage1 = time.time()
print(" [0/9] 载入电机标准件模组 (STEP)...", end="", flush=True)
t0 = time.time()
motor = cq.importers.importStep(AB.SRC_STEP).val()
motor = place(motor, T(0, 0, -2.57))
AB.MOTOR, AB.HALF, AB.CLAMP = motor, None, None
print(f" 完� ({time.time()-t0:.2f}s) ✓", flush=True)
JS = {}
for tag, cn, rear, d, xd, holder, driven in HL.JOINTS:
JS[tag] = Joint(tag, cn, rear, d, xd, lug_phase=90.0 if tag == "J1" else 0.0)
j1, j2, j3, j4, j5, j6 = (JS["J%d" % k] for k in range(1, 7))
PARTS[:] = []
AB.CENTS.clear()
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_工具法兰_螺旋体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"):
AB.GRP[motor_of[t]] = par[t]
AB.GRP[driven[t]] = t
AB.GRP.update({
"P01_A_立柱下段_三角形30%_墙4底5_竖打": "root", "P01_B_立柱中段_三角形30%_墙4底5_竖打": "root", "P01_C_立柱上段_三角形30%_墙4底5_竖打": "root",
"P02_Center_底座中核心_螺旋体30%_墙5底6_平打": "root", "P02_Front_��防翻力臂_螺旋体30%_墙4底5_平打": "root", "P02_Rear_��防翻力臂_螺旋体30%_墙4底5_平打": "root",
"P03_肩�臂_螺旋体35%_墙5底5_竖打": "root"
})
AB.JAW = {}
p01_full = p_chest()
p01_a, p01_b, p01_c = split_p01_poka_yoke(p01_full)
p02_c, p02_f, p02_r = make_p02_poka_yoke()
parts_to_build = [
("P01_A_立柱下段_三角形30%_墙4底5_竖打", "立柱下段", lambda: p01_a, 0x0284c7,
"200mm适�分段A:高166mm+16mm阶梯公榫+顶端 2x Ø3.9 对�孔(M4x16 自攻)+底部50x50x12榫头+P01-A 竖排TOP刻字"),
("P01_B_立柱中段_三角形30%_墙4底5_竖打", "立柱中段", lambda: p01_b, 0x2563eb,
"200mm适�分段B:高165mm+�端公�榫+两端� 2x Ø3.9 对�孔(M4x16 自攻,�对称孔�防�装)+P01-B 竖排TOP刻字"),
("P01_C_立柱上段_三角形30%_墙4底5_竖打", "立柱上段", lambda: p01_c, 0x1d4ed8,
"200mm适�分段C:高149mm+底端�榫+底端 2x Ø3.9 对�孔(�对称孔�防�装)+顶�P03安装�(4x Ø3.9 安装孔 + 1x Ø3.9 横��紧孔)+P01-C 竖排TOP刻字"),
("P02_Center_底座中核心_螺旋体30%_墙5底6_平打", "底座核心承力座", lambda: p02_c, 0x475569,
"200mm适�中座:195x190mm核心承力座+48mm套筒+12mm阶梯榫座(轴��承载)+��三角加强筋+���对称防呆�榫槽+刻字"),
("P02_Front_��防翻力臂_螺旋体30%_墙4底5_平打", "��防翻力臂", lambda: p02_f, 0xe11d48,
"200mm适��臂:145x190mm扩展力臂+104mm宽跨��公榫+底部M5预埋螺�沉槽+P02-FRONT刻字"),
("P02_Rear_��防翻力臂_螺旋体30%_墙4底5_平打", "��防翻力臂", lambda: p02_r, 0xd97706,
"200mm适��臂:45x190mm扩展力臂+80mm窄跨��公榫+底部M5预埋螺�沉槽+P02-REAR刻字"),
("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 å½¢æˆ�å‰�å�Žå�Œå�‘é�¢åŽ‹ï¼Œæ¶ˆé™¤å¤§æ‰­çŸ©ä¸‹ç”µæœºåœ¨åº§å­”é‡Œçš„å€¾æ–œï¼›ã€�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 干涉;ã€�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 厚å£�完æ•
("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 件连接�整圈连续��实体)"),
("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电机�包散热,俯仰大范围自由�动零干涉;�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干涉;�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)
for idx, (name, cn, fn, color, note) in enumerate(parts_to_build, 1):
print(f" [{idx}/{total_parts}] 正在构建 {name} ({cn})...", end="", flush=True)
t0 = time.time()
sh = fn()
dt = time.time() - t0
vol = AB.vol_of(sh) / 1000.0
n_solids = len(solids_of(sh))
print(f" 完� ✓ ({dt:.2f}s | 体积: {vol:.1f} cm³ | 实体数: {n_solids})", flush=True)
add(name, cn, sh, color, note=note)
print(" [装�] 正在将 M1~M6 电机定�至�关节�标�姿...", end="", flush=True)
t0 = time.time()
for k, (tag, cn, rear, d, xd, holder, driven_name) in enumerate(HL.JOINTS):
j = JS[tag]
add("M%d_%s" % (k + 1, tag), "%s 电机模组" % tag, place(motor, j.t), 0x9ca3af,
printed=False, note="�用 电机.STEP")
print(f" 完� ✓ ({time.time()-t0:.2f}s)", flush=True)
print(f"-> 构件模型全部生�完�,总耗时: {time.time()-t_stage1:.2f}s\n", flush=True)
return JS
CHAIN = ["root", "J1", "J2", "J3", "J4", "J5", "J6"]
SEP = 74.0
def explode_chain(p, JS, cents, gmean):
"""爆炸图�移:立柱垂直阶梯拉开,底座��防呆拉开,电机沿轴拉出,机械臂整体�上段分离"""
nm = p["name"]
if nm == "P01_A_立柱下段_三角形30%_墙4底5_竖打":
return [0.0, 0.0, 0.0]
if nm == "P01_B_立柱中段_三角形30%_墙4底5_竖打":
return [0.0, 0.0, 60.0]
if nm == "P01_C_立柱上段_三角形30%_墙4底5_竖打":
return [0.0, 0.0, 120.0]
if nm == "P02_Center_底座中核心_螺旋体30%_墙5底6_平打":
return [0.0, 0.0, 0.0]
if nm == "P02_Front_��防翻力臂_螺旋体30%_墙4底5_平打":
return [65.0, 0.0, 0.0]
if nm == "P02_Rear_��防翻力臂_螺旋体30%_墙4底5_平打":
return [-65.0, 0.0, 0.0]
if nm == "P03_肩�臂_螺旋体35%_墙5底5_竖打":
return [0.0, 0.0, 120.0]
grp = AB.GRP.get(p["name"], "root")
if grp == "root":
return [0.0, 0.0, 0.0]
k = CHAIN.index(grp)
off = [0.0, 0.0, 120.0 - SEP * k]
j = JS.get(grp)
if j:
w = 0.0
if "��轴器" in nm or "抱�" in nm:
w = 34.0 * (1.0 if ("A" in nm[-1:]) else -1.0)
elif nm.startswith("M"):
w = 62.0
for q in range(3):
off[q] += j.d[q] * w
return off
if __name__ == "__main__":
JS = build()
print("=" * 80, flush=True)
print("��实体拓扑完整性校验】验�所有打�构件�形实体状�...", flush=True)
print("=" * 80, flush=True)
bad = 0
printed_list = [p for p in PARTS if p["printed"]]
total_pr = len(printed_list)
for idx, p in enumerate(printed_list, 1):
n = len(solids_of(p["shape"]))
if n != 1:
bad += 1
status = "�实体 ✓" if n == 1 else f"异常 ({n} 个实体) �"
print(f" [{idx}/{total_pr}] {p['name']:<14} : 体积 {p['vol']/1000.0:6.1f} cm³ | {status}", flush=True)
if bad == 0:
print(f"-> �实体检查全部通过�{total_pr}/{total_pr} 构件�为完美�一�形拓扑 ✓\n", flush=True)
else:
print(f"-> �现 {bad} 个��实体构件�\n", flush=True)
AB.explode_vec = explode_chain
bom = AB.export(JS)
# 紧固件(螺钉)实例注入:把 docs/screws.json 的 72 颗螺钉按真实孔�还原�三维实例,
# 追加进 web/mesh_arm.json 的 parts(查看器�点 BOM 螺�行高亮定�;纯数�,�动 CAD)
try:
import screw_mesh
screw_mesh.main()
except Exception as e:
print("螺钉实例注入失败(�影�主�程,�手动跑 src/screw_mesh.py):", e, flush=True)
cl = AB.collide()
try:
from build_viewer import rebuild_viewer
rebuild_viewer()
except Exception as e:
print("rebuild_viewer error:", e, flush=True)
# �达包络自动注入:AB.export 刚�写过 mesh_arm.json(包络件会被冲掉),
# 这里�跑 FK 采样 + 包络网格注入,��打包一次查看器(约 3~4s,纯数�无 CAD 计算)
try:
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
"..", "scratch"))
import ws_sweep
import gen_env_mesh
t_env = time.time()
ws_sweep.main()
gen_env_mesh.main()
from build_viewer import rebuild_viewer as _rbv
_rbv()
print(f"-> �达包络已自动注入查看器(+{time.time()-t_env:.1f}s)✓\n", flush=True)
except Exception as e:
print("�达包络自动注入失败(�影�主�程,修��手动跑 scratch/gen_env_mesh.py + src/build_viewer.py):",
e, flush=True)
print("=" * 80, flush=True)
print("✅ 机器人手臂编译�导出与查看器�布全�程完��", flush=True)
print("=" * 80, flush=True)
sys.stdout.flush()
os._exit(0)