2026-09-26 18:05:21 +08:00
|
|
|
# -*- coding: utf-8 -*-
|
2026-09-27 04:48:49 +08:00
|
|
|
"""机器人手臂深度圆角优化版 V2:彻底消除所有棱角
|
|
|
|
|
针对用户反馈的三大问题区域进行深度优化:
|
|
|
|
|
1. 蓝色零件(P04肩叉)顶部方形突起 → 全圆角处理
|
|
|
|
|
2. 绿色/青色零件(P05/P08)底部和侧面棱角 → Loft外轮廓圆角
|
|
|
|
|
3. 所有连接处的尖锐过渡 → 大圆角平滑过渡
|
|
|
|
|
4. 橙色零件(P06/P07)所有方形边缘 → 完整圆角系统
|
2026-09-26 18:05:21 +08:00
|
|
|
"""
|
|
|
|
|
import math
|
|
|
|
|
import os
|
|
|
|
|
import sys
|
2026-09-27 04:48:49 +08:00
|
|
|
import time
|
2026-09-26 18:05:21 +08:00
|
|
|
import cadquery as cq
|
2026-10-15 03:31:20 +08:00
|
|
|
from arm.cad import arm_build as AB
|
|
|
|
|
from arm.paths import OUT, WEB, DOCS
|
|
|
|
|
from arm.constants import M, C
|
|
|
|
|
from arm.cad.helpers import cyl, box, ring, place, trs, bb, bbox_str, solids_of
|
|
|
|
|
from arm.cad.arm_build import (Joint, add, bx, Ycyl, Ybox, Yplate, prism, dogbone, sector,
|
2026-09-26 18:05:21 +08:00
|
|
|
FLANGE_Z, COVER_Z, HUB_FACE_Z, CLAMP_SPAN, HUB_M2_CLOCK,
|
|
|
|
|
BORE_R, CB_R, PCB_W, D2R, T, R, PARTS)
|
2026-10-15 03:31:20 +08:00
|
|
|
from arm import kinematics as HL
|
2026-09-26 18:05:21 +08:00
|
|
|
from OCP.BRepAlgoAPI import BRepAlgoAPI_Common
|
2026-10-15 03:31:20 +08:00
|
|
|
from arm.kinematics import (HUB, PLATE_T, R_MOT, Z_TIP, Z_WR, Z_EI, Z_SH, Z_RL, Z_R1,
|
2026-09-26 18:05:21 +08:00
|
|
|
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,
|
2026-10-02 04:39:12 +08:00
|
|
|
X_FORK_GAP, X_BAY_EXT,
|
2026-09-26 18:05:21 +08:00
|
|
|
Y_AXIS, Y_HUB, Y_REAR, Y_COVER, Y_H, Y_R_IN, Y_R_OUT,
|
2026-09-27 04:48:49 +08:00
|
|
|
Y_M6, Y_CHEST, X_CHEST, FOOT_H)
|
2026-09-26 18:05:21 +08:00
|
|
|
|
2026-09-27 04:48:49 +08:00
|
|
|
VERSION = os.environ.get("ARMVER", "v2")
|
|
|
|
|
ITER = int(os.environ.get("ARMITER", "3"))
|
2026-09-26 18:05:21 +08:00
|
|
|
AB.VERSION = VERSION
|
|
|
|
|
AB.ITER = ITER
|
|
|
|
|
|
2026-10-14 22:03:18 +08:00
|
|
|
# ------------------------------------------------ 逐件模块(几何已拆出)
|
2026-10-15 03:31:20 +08:00
|
|
|
from arm.cad.parts import PART_MODULES
|
|
|
|
|
from arm.cad.part_tools import (
|
2026-10-14 22:03:18 +08:00
|
|
|
BOSS, CHAMFER_EDGE, CLAMP_BACKOFF, CLAMP_BCD, CLAMP_CLOCK, CLAMP_CLOCK_LOCAL, CLAMP_CLR,
|
|
|
|
|
CLAMP_ROOT, CLAMP_T, CLEARANCE_GAP, DISC_R, FENCE_OPEN_CUT, FILLET_LARGE, FILLET_MEDIUM,
|
|
|
|
|
FILLET_SMALL, FILLET_TINY, FILLET_XLARGE, HOLLOW_FILLET, HOLLOW_WALL, SLEEVE_R, XB, Xcyl,
|
|
|
|
|
Xplate, Y_MID, cut_motor_fence, hollow_channel, hollow_tent_window, hub_m2_holes,
|
|
|
|
|
make_direct_hub, make_pitch_to_roll_bracket, motor_front_clamp,
|
|
|
|
|
)
|
2026-10-15 03:31:20 +08:00
|
|
|
from arm.cad.parts.p01_column import X_COL, Y_COL, Z_LOCK, p_chest, split_p01_poka_yoke
|
|
|
|
|
from arm.cad.parts.p02_base import make_p02_poka_yoke, p_base_foot
|
|
|
|
|
from arm.cad.parts.p03_girdle import p_girdle
|
|
|
|
|
from arm.cad.parts.p04_fork import P04_CH, p_fork
|
|
|
|
|
from arm.cad.parts.p05_roll_base import p_shoulder_roll_base
|
|
|
|
|
from arm.cad.parts.p06_humerus import P06_HOLLOW_WIN, P06_WIN_XZ, p_humerus_bone
|
|
|
|
|
from arm.cad.parts.p07_forearm import P07_CH, p_forearm_bone
|
|
|
|
|
from arm.cad.parts.p08_wrist import p_wrist_bracket
|
|
|
|
|
from arm.cad.parts.p09_flange import p_tool_flange
|
2026-10-14 22:03:18 +08:00
|
|
|
# 兼容旧工具:scratch/ 的诊断脚本按 arm_model.<符号> 取用件构造函数与开关
|
2026-09-27 04:48:49 +08:00
|
|
|
|
2026-09-26 18:05:21 +08:00
|
|
|
def build():
|
2026-09-27 04:48:49 +08:00
|
|
|
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()
|
2026-09-26 18:05:21 +08:00
|
|
|
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
|
2026-09-27 04:48:49 +08:00
|
|
|
print(f" 完成 ({time.time()-t0:.2f}s) ✓", flush=True)
|
2026-09-26 18:05:21 +08:00
|
|
|
|
|
|
|
|
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"}
|
|
|
|
|
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"):
|
2026-09-27 04:48:49 +08:00
|
|
|
AB.GRP[motor_of[t]] = par[t]
|
2026-09-26 18:05:21 +08:00
|
|
|
AB.JAW = {}
|
2026-10-15 03:31:20 +08:00
|
|
|
# 逐件模块:几何、件号/配色/说明、装配帧归属都在 arm/cad/parts/ 里,
|
2026-10-14 22:03:18 +08:00
|
|
|
# 这里只按 BOM 顺序收集 specs(),并把件号登记到装配帧图上。
|
|
|
|
|
parts_to_build = []
|
|
|
|
|
for _m in PART_MODULES:
|
|
|
|
|
for _spec in _m.specs(JS):
|
|
|
|
|
AB.GRP[_spec[0]] = _m.JOINT
|
|
|
|
|
parts_to_build.append(_spec)
|
|
|
|
|
|
2026-09-26 18:05:21 +08:00
|
|
|
|
2026-09-27 17:21:50 +08:00
|
|
|
total_parts = len(parts_to_build)
|
2026-09-27 04:48:49 +08:00
|
|
|
for idx, (name, cn, fn, color, note) in enumerate(parts_to_build, 1):
|
2026-09-27 17:21:50 +08:00
|
|
|
print(f" [{idx}/{total_parts}] 正在构建 {name} ({cn})...", end="", flush=True)
|
2026-09-27 04:48:49 +08:00
|
|
|
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()
|
2026-09-26 18:05:21 +08:00
|
|
|
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")
|
2026-09-27 04:48:49 +08:00
|
|
|
print(f" 完成 ✓ ({time.time()-t0:.2f}s)", flush=True)
|
|
|
|
|
print(f"-> 构件模型全部生成完成,总耗时: {time.time()-t_stage1:.2f}s\n", flush=True)
|
2026-09-26 18:05:21 +08:00
|
|
|
return JS
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CHAIN = ["root", "J1", "J2", "J3", "J4", "J5", "J6"]
|
2026-09-27 04:48:49 +08:00
|
|
|
SEP = 74.0
|
2026-09-26 18:05:21 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def explode_chain(p, JS, cents, gmean):
|
2026-09-27 17:21:50 +08:00
|
|
|
"""爆炸图偏移:立柱垂直阶梯拉开,底座前后防呆拉开,电机沿轴拉出,机械臂整体随上段分离"""
|
|
|
|
|
nm = p["name"]
|
2026-09-28 03:33:27 +08:00
|
|
|
if nm == "P01_A_立柱下段_三角形30%_墙4底5_竖打":
|
2026-09-27 17:21:50 +08:00
|
|
|
return [0.0, 0.0, 0.0]
|
2026-09-28 03:33:27 +08:00
|
|
|
if nm == "P01_B_立柱中段_三角形30%_墙4底5_竖打":
|
2026-09-27 17:21:50 +08:00
|
|
|
return [0.0, 0.0, 60.0]
|
2026-09-28 03:33:27 +08:00
|
|
|
if nm == "P01_C_立柱上段_三角形30%_墙4底5_竖打":
|
2026-09-27 17:21:50 +08:00
|
|
|
return [0.0, 0.0, 120.0]
|
2026-09-28 03:33:27 +08:00
|
|
|
if nm == "P02_Center_底座中核心_螺旋体30%_墙5底6_平打":
|
2026-09-27 17:21:50 +08:00
|
|
|
return [0.0, 0.0, 0.0]
|
2026-09-28 03:33:27 +08:00
|
|
|
if nm == "P02_Front_前向防翻力臂_螺旋体30%_墙4底5_平打":
|
2026-09-27 17:21:50 +08:00
|
|
|
return [65.0, 0.0, 0.0]
|
2026-09-28 03:33:27 +08:00
|
|
|
if nm == "P02_Rear_后向防翻力臂_螺旋体30%_墙4底5_平打":
|
2026-09-27 17:21:50 +08:00
|
|
|
return [-65.0, 0.0, 0.0]
|
2026-09-28 03:33:27 +08:00
|
|
|
if nm == "P03_肩吊臂_螺旋体35%_墙5底5_竖打":
|
2026-09-27 17:21:50 +08:00
|
|
|
return [0.0, 0.0, 120.0]
|
|
|
|
|
|
2026-09-26 18:05:21 +08:00
|
|
|
grp = AB.GRP.get(p["name"], "root")
|
|
|
|
|
if grp == "root":
|
|
|
|
|
return [0.0, 0.0, 0.0]
|
|
|
|
|
k = CHAIN.index(grp)
|
2026-09-27 17:21:50 +08:00
|
|
|
off = [0.0, 0.0, 120.0 - SEP * k]
|
2026-09-26 18:05:21 +08:00
|
|
|
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()
|
2026-09-27 04:48:49 +08:00
|
|
|
|
|
|
|
|
print("=" * 80, flush=True)
|
|
|
|
|
print("【单实体拓扑完整性校验】验证所有打印构件流形实体状态...", flush=True)
|
|
|
|
|
print("=" * 80, flush=True)
|
2026-09-26 18:05:21 +08:00
|
|
|
bad = 0
|
2026-09-27 04:48:49 +08:00
|
|
|
printed_list = [p for p in PARTS if p["printed"]]
|
2026-09-27 17:21:50 +08:00
|
|
|
total_pr = len(printed_list)
|
2026-09-27 04:48:49 +08:00
|
|
|
for idx, p in enumerate(printed_list, 1):
|
2026-09-26 18:05:21 +08:00
|
|
|
n = len(solids_of(p["shape"]))
|
|
|
|
|
if n != 1:
|
|
|
|
|
bad += 1
|
2026-09-27 04:48:49 +08:00
|
|
|
status = "单实体 ✓" if n == 1 else f"异常 ({n} 个实体) ❌"
|
2026-09-27 17:21:50 +08:00
|
|
|
print(f" [{idx}/{total_pr}] {p['name']:<14} : 体积 {p['vol']/1000.0:6.1f} cm³ | {status}", flush=True)
|
2026-09-27 04:48:49 +08:00
|
|
|
if bad == 0:
|
2026-09-27 17:21:50 +08:00
|
|
|
print(f"-> 单实体检查全部通过!{total_pr}/{total_pr} 构件均为完美单一流形拓扑 ✓\n", flush=True)
|
2026-09-27 04:48:49 +08:00
|
|
|
else:
|
|
|
|
|
print(f"-> 发现 {bad} 个非单实体构件!\n", flush=True)
|
|
|
|
|
|
2026-09-26 18:05:21 +08:00
|
|
|
AB.explode_vec = explode_chain
|
|
|
|
|
bom = AB.export(JS)
|
2026-09-28 03:33:27 +08:00
|
|
|
|
|
|
|
|
# 紧固件(螺钉)实例注入:把 docs/screws.json 的 72 颗螺钉按真实孔位还原成三维实例,
|
|
|
|
|
# 追加进 web/mesh_arm.json 的 parts(查看器可点 BOM 螺丝行高亮定位;纯数据,不动 CAD)
|
2026-10-15 03:31:20 +08:00
|
|
|
from arm.dataio import screw_mesh
|
|
|
|
|
screw_mesh.main()
|
2026-09-26 18:05:21 +08:00
|
|
|
cl = AB.collide()
|
2026-10-15 03:31:20 +08:00
|
|
|
from arm.viewer.build_viewer import rebuild_viewer
|
|
|
|
|
rebuild_viewer()
|
2026-09-28 03:33:27 +08:00
|
|
|
# 可达包络自动注入:AB.export 刚重写过 mesh_arm.json(包络件会被冲掉),
|
|
|
|
|
# 这里重跑 FK 采样 + 包络网格注入,再重打包一次查看器(约 3~4s,纯数据无 CAD 计算)
|
2026-10-15 03:31:20 +08:00
|
|
|
from arm.analysis import ws_sweep, gen_env_mesh
|
|
|
|
|
t_env = time.time()
|
|
|
|
|
ws_sweep.main()
|
|
|
|
|
gen_env_mesh.main()
|
|
|
|
|
rebuild_viewer()
|
|
|
|
|
print(f"-> 可达包络已自动注入查看器(+{time.time() - t_env:.1f}s)✓\n", flush=True)
|
2026-09-27 04:48:49 +08:00
|
|
|
print("=" * 80, flush=True)
|
|
|
|
|
print("✅ 机器人手臂编译、导出与查看器发布全流程完成!", flush=True)
|
|
|
|
|
print("=" * 80, flush=True)
|
2026-10-15 03:31:20 +08:00
|
|
|
|
|
|
|
|
# OCC/cadquery 的静态析构在解释器正常退出时必定段错误(实测:光是
|
|
|
|
|
# `python -c "import arm.cad.helpers"` 就返回 139,与本项目代码无关)。所以入口
|
|
|
|
|
# 脚本跑完后显式刷缓冲再 os._exit —— 否则全流程已经成功、exit code 却是 139,
|
|
|
|
|
# 会把上游脚本的 set -e 判成失败。只在 __main__ 出口生效, 被 import 时不触发。
|
2026-09-27 00:28:49 +08:00
|
|
|
sys.stdout.flush()
|
2026-09-26 18:05:21 +08:00
|
|
|
os._exit(0)
|