← all reports
toolblade_designer.py2026-08-22

参数化自动生成工具

输入想要的长度(或杯子直径)和打印材料,自动完成参数映射、生成 STL、 网格检查、尺寸复核、渲染三视图、出工程图、写报告。这几天踩过的坑全部固化在代码里 —— 材料硬度到结构参数的换算、长度换算、可打印性下限、每一项必须复核的尺寸,不再靠人记。

长度 / 杯径+材料 Shore A 参数映射STL 网格检查尺寸复核 三视图 + 渲染工程图 HTML 报告

一、怎么用

# 我要 120mm、用 98A 的料
python blade_designer.py --length 120 --material 98

# 我不知道要多长,我要抓 Ø80 的杯子
python blade_designer.py --cup 80 --material 98

# 拿到挠度实测值后,用实测覆盖材料表的估算
python blade_designer.py --length 120 --material 98 --stiffness-ratio 5.2

# 只想看参数怎么算,先不建模(秒出)
python blade_designer.py --length 120 --material 98 --dry-run

# 看材料库
python blade_designer.py --list-materials

全套(含渲染)约 2 分钟--no-render 约 40 秒;--dry-run 立即返回。

二、参数映射是怎么算的

目标:让新件的柔顺度等于参考件(82A、S_LEN=83、基线几何 —— 就是那片手感被认可的旧红棕件)。

柔顺度 ∝ L³ / (E · 结构刚度)

结构刚度:单一缩放 k 同时作用于 WALL_T 与 RIB_T,
          梁与肋的弯曲刚度都 ∝ k³,串联后合成软化 = 1/k³
          (已用四个实测档 A/B/C/D 验证,误差 <2%)

令新件柔顺度 = 参考件:
    (L/L_ref)³ · (1/k³) · (E_ref/E) = 1
⟹   k = (L/L_ref) · (E_ref/E)^⅓

WALL_T = 2.0·k      RIB_T = 1.6·k      RIB_PITCH = 4.0 + (0.78−k)·6.0

长度换算由三个实测点拟合,残差 <0.02 mm: 总长 = 1.00729 × S_LEN + 5.563
杯径反推长度用本项目实测标定:89.4 mm 对 Ø80 约包覆 1/4 周长、120 mm 约 1/3 ⟹ 接触弧长 ≈ 0.70 × 总长 ⟹ 总长 = wrap·π·D / 0.70

材料库

 Shore A   E (MPa)    相对 82A  说明
      60       2.5     0.23×  很软,接近橡皮筋。夹持力会很低
      70       4.5     0.41×  软
      82      11.0     1.00×  ★ 参考基准:旧红棕件推定为此,手感被操作者认可
      85      14.0     1.27×  
      90      22.0     2.00×  
      95      40.0     3.64×  原设计基准。BLADE-README 的切片参数按它写的
      98      70.0     6.36×  2026-08-22 实际到手的料。硬,压不塌,摩擦偏低(估值,待实测)

>95A 的值为保守估算;请用 --stiffness-ratio 传入挠度实测值覆盖。
95A 以上不用 Gent 经验式。它的分母含 (254 − 2.54·S),S 越接近 100 分母越趋近 0,估值被急剧放大(98A 会给出 113 MPa,明显偏高)。表里取的是偏保守的 厂商典型值,并且工具会主动警告让你用挠度实测覆盖。

三、内置的安全闸

这些是 check_mesh 查不出来的 —— 网格完美通过、件却是错的。全部自动执行:

检查项要求示例件实测判定
网格水密单壳PASS / 1PASS / 1 shellPASS
总长119.7 ± 1.5119.68 mmPASS
宽度(夹套/安装座按此)23.2 ± 0.623.20 mmPASS
尖端 d=1 厚度(>0 才没塌尖)> 1.01.639 mmPASS
z=−26 实心宽(受力最大处的安全线)> 2027.41 mmPASS
凸起钩面台阶(官方实测 1.5~2)1.5 ~ 2.41.957 mm @ d=15.25PASS
梁厚 ≥ 2 条挤出线≥ 2.004.00 条PASS

另外两道闸在映射阶段就拦:梁厚不得低于 2 条挤出线(否则切片器会整段漏打薄壁), 以及 WALL_T 自动吸附到线宽整数倍(非整数倍会让切片器处理一条排不下的余量,可能欠挤或留缝)。

四、示例产物(--cup 80 --material 98

==================================================================
参数映射  →  Blade L120_98A
==================================================================
  总长             119.7 mm      S_LEN = 113.29
  材料                98A         E ≈ 70 MPa   刚度比 6.36× (参考 82A)
  几何缩放 k       0.737
  WALL_T            1.60 mm      4.00 条 0.4 线
  RIB_T             1.20 mm
  RIB_PITCH         4.26 mm
  软化: 长度 2.54× × 结构 2.15× = 5.47×   (需要 6.36×)
    · 刚度比 6.36x 来自材料表估值(98A E≈70 MPa vs 参考 82A E≈11 MPa)。建议用挠度实测覆盖
    · WALL_T 1.473 吸附到 1.60(4 条 0.4 线)
    · 合成软化 5.5x 对上需要的 6.4x ✓
    ⚠ 98A 超出可靠估算范围(>95A 时公式失真严重)。表内取的是偏保守的厂商典型值,务必做挠度实测复核
侧视
侧视
正视
正视
俯视
俯视
等轴测
等轴测
尖端
尖端
凸起平台
凸起平台

另一个例子 —— 换成软料,工具判断无需减薄,直接用基线几何:

==================================================================
参数映射  →  Blade L100_82A
==================================================================
  总长             100.0 mm      S_LEN = 93.75
  材料                82A         E ≈ 11 MPa   刚度比 1.00× (参考 82A)
  几何缩放 k       1.000
  WALL_T            2.00 mm      5.00 条 0.4 线
  RIB_T             1.60 mm
  RIB_PITCH         4.00 mm
  软化: 长度 1.44× × 结构 1.00× = 1.44×   (需要 1.00×)
    · 刚度比 1.00x 来自材料表估值(82A E≈11 MPa vs 参考 82A E≈11 MPa)。建议用挠度实测覆盖
    · 材料够软或刀身够长,无需减薄结构,采用基线几何

五、源码

单文件,约 781 行。依赖:openscadnumpy, 以及同目录的 generate_blade_arcsweep.scad / generate_v4_finish.scad / check_mesh.py

#!/usr/bin/env python3
"""Fin-Ray 抓夹刀片 · 参数化自动生成

用途
----
输入【想要的长度】和【打印材料】(可选:杯子直径、线宽),自动完成:

    参数映射 -> 生成 STL -> 网格检查 -> 尺寸复核 -> 渲染 -> 工程图 -> HTML 报告

设计意图是「把这几天踩过的坑固化下来」:材料硬度到结构参数的换算、长度到 S_LEN
的换算、可打印性下限、以及每一项必须复核的尺寸,全部写进代码,不再靠人记。

用法
----
    python blade_designer.py --length 120 --material 98
    python blade_designer.py --cup 80 --material 98A          # 由杯径反推长度
    python blade_designer.py --length 120 --material 82 --no-render
    python blade_designer.py --length 120 --material 98 --stiffness-ratio 5.2
    python blade_designer.py --list-materials

输出目录(默认 builds/<tag>/):
    Blade_<tag>.stl      成品(已钻孔)
    drawing.svg          带尺寸的侧视工程图,轮廓取自成品网格
    renders/*.png        三视图 + 等轴测 + 尖端 + 凸起平台 + 根部 + 安装孔
    report.html          自包含报告(图片内嵌),手机可直接看
    params.json          全部参数与复核结果,机器可读

依赖:openscad(渲染需要 DISPLAY)、numpy、本目录的 check_mesh.py
"""

from __future__ import annotations

import argparse
import base64
import json
import math
import os
import re
import shutil
import subprocess
import sys
import time
from dataclasses import dataclass, asdict, field
from pathlib import Path

import numpy as np

HERE = Path(__file__).resolve().parent
GEN_SCAD = HERE / "generate_blade_arcsweep.scad"
FIN_SCAD = HERE / "generate_v4_finish.scad"
CHECK_MESH = HERE / "check_mesh.py"
MEASURE = HERE.parent.parent / "03-software" / "scripts"


# ══════════════════════════════════════════════════════════════════════════
#  1. 材料库
# ══════════════════════════════════════════════════════════════════════════
# 弹性模量 MPa。低于 95A 用 Gent 经验式的量级,与厂商典型值对齐;
# 95A 以上【不用 Gent】—— 它的分母含 (254 - 2.54*S),S 越接近 100 分母越趋近 0,
# 估值被急剧放大(98A 会给出 113 MPa,明显偏高)。这里取偏保守的厂商典型值。
#
# ★ 这张表是【估算】。真值请用挠度实测覆盖:--stiffness-ratio
#   测法:两片装配脚压桌沿下、刀身水平伸出,尖端同一位置挂同一重物,
#         侧面量下沉量。ratio = 参考件下沉 / 新件下沉。
MATERIALS: dict[int, dict] = {
    60: dict(E=2.5, note="很软,接近橡皮筋。夹持力会很低"),
    70: dict(E=4.5, note="软"),
    82: dict(E=11.0, note="★ 参考基准:旧红棕件推定为此,手感被操作者认可"),
    85: dict(E=14.0, note=""),
    90: dict(E=22.0, note=""),
    95: dict(E=40.0, note="原设计基准。BLADE-README 的切片参数按它写的"),
    98: dict(E=70.0, note="2026-08-22 实际到手的料。硬,压不塌,摩擦偏低(估值,待实测)"),
}
REF_SHORE = 82  # 手感被认可的参考件硬度
REF_SLEN = 83.0  # 参考件的 S_LEN(基线几何 WALL_T=2.0 / RIB_T=1.6)

# 基线几何(generate_blade_arcsweep.scad 里的默认值)
BASE_WALL_T = 2.0
BASE_RIB_T = 1.6
BASE_RIB_PITCH = 4.0

# 长度换算:由 S_LEN=83/93.8/114 三个实测点拟合,残差 < 0.02mm
LEN_A, LEN_B = 1.00729, 5.563  # 总长 = LEN_A * S_LEN + LEN_B


def shore_to_E(shore: float) -> float:
    """Shore A -> 弹性模量 MPa。表内点直接取,表外线性插值(对数域)。"""
    keys = sorted(MATERIALS)
    if shore in MATERIALS:
        return MATERIALS[shore]["E"]
    if shore <= keys[0]:
        return MATERIALS[keys[0]]["E"]
    if shore >= keys[-1]:
        return MATERIALS[keys[-1]]["E"]
    lo = max(k for k in keys if k < shore)
    hi = min(k for k in keys if k > shore)
    t = (shore - lo) / (hi - lo)
    # 模量随硬度近似指数增长,在对数域插值更合理
    return math.exp(math.log(MATERIALS[lo]["E"]) * (1 - t) + math.log(MATERIALS[hi]["E"]) * t)


def total_to_slen(total_mm: float) -> float:
    return (total_mm - LEN_B) / LEN_A


def slen_to_total(slen: float) -> float:
    return LEN_A * slen + LEN_B


def cup_to_total(cup_dia_mm: float, wrap: float = 1 / 3) -> float:
    """由杯径反推刀身总长。

    标定依据(本项目实测):89.4mm 的刀身对 Ø80 杯子约包覆 1/4 周长,
    120mm 约包覆 1/3。两点给出 接触弧长 ≈ 0.70 x 总长。
        arc = wrap * pi * D  =>  total = wrap * pi * D / 0.70
    """
    return wrap * math.pi * cup_dia_mm / 0.70


# ══════════════════════════════════════════════════════════════════════════
#  2. 参数映射
# ══════════════════════════════════════════════════════════════════════════
@dataclass
class Design:
    total_length: float
    shore: float
    slen: float
    wall_t: float
    rib_t: float
    rib_pitch: float
    k: float                 # 几何缩放
    softening_geom: float    # 结构提供的软化倍数
    softening_len: float     # 长度提供的软化倍数
    softening_total: float
    stiffness_ratio: float   # 材料相对参考件硬多少倍
    line_width: float
    lines_in_beam: float
    cup_dia: float | None = None
    wrap: float | None = None
    warnings: list[str] = field(default_factory=list)
    notes: list[str] = field(default_factory=list)

    @property
    def tag(self) -> str:
        return f"L{self.total_length:.0f}_{self.shore:.0f}A"


def design(total_length: float, shore: float, *, line_width: float = 0.40,
           cup_dia: float | None = None, wrap: float = 1 / 3,
           stiffness_ratio: float | None = None,
           snap_to_line: bool = True) -> Design:
    """核心映射:长度 + 材料 -> 全套几何参数。

    推导
    ----
    目标:让新件的【柔顺度】等于参考件(82A、S_LEN=83、基线几何)。

      柔顺度 ∝ L³ / (E · 结构刚度)
      结构刚度按单一缩放 k 同时作用于 WALL_T 与 RIB_T:梁与肋的弯曲刚度都 ∝ k³,
      两者串联,合成软化 = 1/k³(已用四个实测档 A/B/C/D 验证,误差 <2%)。

      令新件柔顺度 = 参考件:
        (L/L_ref)³ · (1/k³) · (E_ref/E) = 1
        =>  k = (L/L_ref) · (E_ref/E)^(1/3)

    k > 1 表示材料比参考件还软或刀身很长,此时不需要减薄(k 会被夹到 1.0,
    因为加厚梁不是这套设计的意图,且会顶到 outer_solid 的包络)。
    """
    warns: list[str] = []
    notes: list[str] = []

    E = shore_to_E(shore)
    E_ref = MATERIALS[REF_SHORE]["E"]
    ratio = stiffness_ratio if stiffness_ratio else E / E_ref
    if stiffness_ratio:
        notes.append(f"刚度比 {ratio:.2f}x 来自实测(--stiffness-ratio),未用材料表估值")
    e
… (完整源码请下载上方的 blade_designer.py)

仓库位置 02-hardware/compliant-gripper-long-jaw/blade_designer.py · 参数推导详见 PARAMETRIC-DESIGN.md · 相关页:打哪一片 · 材料诊断 · Print pack