1. 从一段文字到三维实体:text-to-cad 到底在解决什么问题
如果你在机械设计、建筑建模或者机器人仿真领域待过一段时间,一定经历过这样的场景:脑子里已经想清楚了一个零件的形状,甚至用自然语言能把它描述得明明白白,但真正打开 CAD 软件开始画草图、拉伸、倒角、装配的时候,半小时就过去了。更别提那些需要批量生成相似结构的情况——比如做机器人仿真时,每个连杆的尺寸略有不同,却要重复几十次几乎一样的建模操作。
text-to-cad这个方向要解决的核心问题就一句话:把用自然语言描述的结构意图,自动转换成 CAD 系统能识别的几何文件。这里的“CAD 文件”不是一张截图,也不是一段描述,而是真正可以被下游工具消费的格式——比如 STEP、URDF、STL 这些。
为什么这件事值得做?因为 CAD 建模的本质是“参数化描述 + 几何运算”,而自然语言恰好擅长表达参数和约束关系。你说“一个长 100mm、宽 60mm、高 20mm 的方块,四个角倒 R5 圆角,正中间挖一个直径 30mm 的通孔”,这句话里的信息量其实已经足够生成一个精确的实体模型了。问题在于,传统 CAD 软件要求你用鼠标和键盘把这个描述“翻译”成草图约束和特征操作,而这个翻译过程是纯手工的。
我最初接触这个方向是因为一个机器人仿真项目。当时需要为一批协作机械臂生成 URDF 模型,每个臂的连杆尺寸根据工况微调,但结构拓扑完全一样。手工建模的后果就是:改一个参数,要重新走一遍草图-拉伸-装配的流程,效率极低而且容易出错。后来我开始研究能不能用 Python 脚本驱动 CAD 内核,把“描述”直接变成“模型”,这才慢慢摸到了 text-to-cad 的完整链路。
这篇文章适合谁看?如果你满足以下任意一条,接下来的内容应该对你有用:
- 会用 Python 但没怎么接触过 CAD 二次开发,想知道怎么把代码和几何建模结合起来;
- 做机器人仿真或运动学分析,需要批量生成 URDF 或 STEP 文件;
- 对“用文字生成模型”这件事好奇,想搞清楚背后的技术栈和实际可行性边界;
- 已经在用 FreeCAD、CadQuery 之类的工具,但想系统梳理一下从文本到几何的完整流程。
需要先说明一点:text-to-cad 不是一个现成的软件产品,而是一类技术方案的统称。它的实现路径有很多种,从最简单的“模板填充 + 参数替换”到复杂的“大模型解析 + 几何内核调用”都算。我会从最落地的角度出发,把整条链路拆开讲清楚,包括每一步用什么工具、为什么这样选、实际跑的时候会遇到什么坑。
2. 文本到几何的完整链路拆解:从一句话到 STEP 文件
2.1 自然语言解析:把描述拆成结构化参数
整条链路的第一步,是把一段自然语言描述转换成结构化的参数字典。这一步的难度取决于你的输入有多“规范”。如果你能接受固定句式,比如“创建一个长方体,长度 L,宽度 W,高度 H”,那用正则表达式就能搞定。但如果你想支持更自由的表达,比如“帮我画个扁平的盒子,大概一巴掌大,中间有个洞”,那就需要引入语义解析。
我在实际项目中采用的是“半结构化”策略:定义一套领域内的描述语法,允许一定的自然语言变体,但核心参数必须显式给出。比如下面这种描述:
长方体 base,长 120,宽 80,高 15 在 base 上表面中心挖圆柱孔,直径 40,深度 15 对 base 的四条竖直边倒圆角,半径 6解析这段文本的思路是:先按行切分,每行识别“操作类型 + 目标对象 + 参数列表”。操作类型包括“创建”“挖孔”“倒角”“阵列”等,目标对象是之前步骤产生的实体或面,参数用键值对提取。用 Python 实现的话,一个简单的解析器大概长这样:
import re def parse_line(line): # 匹配 "操作 目标 参数1 值1 参数2 值2 ..." pattern = r'^(\S+)\s+(\S+)\s+(.+)$' match = re.match(pattern, line.strip()) if not match: return None action, target, params_str = match.groups() params = {} for pair in re.findall(r'(\S+?)\s+(\d+\.?\d*)', params_str): params[pair[0]] = float(pair[1]) return {'action': action, 'target': target, 'params': params}这段代码很粗糙,但能说明核心逻辑:把非结构化的文字变成程序能操作的数据结构。实际项目中,我会建议把解析结果存成 JSON,方便调试和中间检查。比如上面的描述解析后大概是这样:
[ {"action": "创建", "target": "长方体", "params": {"长": 120, "宽": 80, "高": 15}}, {"action": "挖孔", "target": "上表面中心", "params": {"直径": 40, "深度": 15}}, {"action": "倒圆角", "target": "竖直边", "params": {"半径": 6}} ]这里有个经验:不要试图一步到位支持完全自由的自然语言。我试过用大模型直接解析“画一个像椅子一样的结构”,结果生成的参数完全不可控,几何内核直接报错。后来改成“模板 + 槽位填充”的方式,稳定性大幅提升。具体做法是预定义几十种常见几何操作的模板,用户输入时只需要填参数,解析器负责匹配最接近的模板。
2.2 几何内核选型:为什么我最终选了 CadQuery 而不是直接调 FreeCAD
解析出参数之后,下一步是调用几何内核生成实体。这一步的工具选型直接决定了整个项目的开发效率和运行稳定性。我前后试过三种方案,这里把对比列出来:
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| FreeCAD Python API | 功能全,支持装配和工程图 | API 冗长,文档差,版本兼容性坑多 | 需要完整 CAD 功能 |
| CadQuery | 链式调用,代码简洁,基于 OCCT | 复杂曲面支持有限 | 参数化零件、批量生成 |
| 直接调 OCCT | 最底层,最灵活 | 学习曲线极陡,开发慢 | 特殊几何运算 |
我最终选 CadQuery 作为主力工具,原因很实际:它的代码写起来像在描述几何,而不是在操作软件。比如创建一个带孔的长方体,CadQuery 的写法是:
import cadquery as cq result = ( cq.Workplane("XY") .box(120, 80, 15) .faces(">Z") .workplane() .hole(40) .edges("|Z") .fillet(6) )这段代码的逻辑非常直观:在 XY 平面建一个长方体,选上表面,挖直径 40 的孔,选竖直边,倒 R6 圆角。相比之下,FreeCAD 的 API 需要先创建文档、再创建 Body、再创建 Sketch、再 Pad、再 Pocket,每一步都要处理对象引用和坐标系变换,代码量至少翻三倍。
CadQuery 底层用的是 OCCT(Open CASCADE Technology),这是一个工业级的几何内核,生成的 STEP 文件兼容性很好,SolidWorks、UG、CATIA 都能正常打开。这一点很关键,因为很多下游工具对 STEP 文件的解析严格程度不一样,OCCT 生成的模型在兼容性上表现稳定。
2.3 从实体到 URDF:机器人仿真场景的特殊处理
如果你做的是机器人仿真,那 STEP 文件只是中间产物,最终需要的是 URDF(Unified Robot Description Format)。URDF 本质上是一个 XML 文件,描述机器人的连杆、关节、惯性参数和碰撞体。从 CadQuery 生成的实体到 URDF,中间需要补几件事:
第一,每个连杆需要独立的坐标系。CadQuery 生成的实体默认在全局坐标系下,但 URDF 要求每个 link 有自己的 origin。我的做法是在建模阶段就给每个零件单独建一个 Workplane,记录它的基准点偏移量。
第二,惯性参数需要计算。URDF 的 inertial 标签要求质量、质心和惯性矩阵。CadQuery 的Shape对象有Volume()和Center()方法,可以算出体积和质心,但惯性矩阵需要自己根据几何形状计算。对于简单几何体,可以用公式直接算;对于复杂形状,我一般用trimesh库做数值积分。
第三,碰撞体可以简化。仿真时不需要用精确的几何体做碰撞检测,用包围盒或凸包就够了。URDF 支持<collision>标签单独指定碰撞几何,我通常用box或cylinder近似,能大幅提升仿真速度。
下面是一个从 CadQuery 实体生成 URDF link 的简化流程:
import cadquery as cq import numpy as np def make_link(name, shape, mass=None): volume = shape.Volume() center = shape.Center() if mass is None: mass = volume * 1e-6 # 假设密度 1000 kg/m^3,注意单位换算 # 生成 URDF link 片段 link_xml = f''' <link name="{name}"> <inertial> <mass value="{mass:.6f}"/> <origin xyz="{center.x:.6f} {center.y:.6f} {center.z:.6f}"/> <inertia ixx="..." ixy="0" ixz="0" iyy="..." iyz="0" izz="..."/> </inertial> <visual> <geometry> <mesh filename="{name}.stl"/> </geometry> </visual> </link> ''' return link_xml这里有个坑要注意:单位换算。CadQuery 默认单位是毫米,而 URDF 的标准单位是米。如果不做换算,仿真时机器人会变成“巨人”或者“蚂蚁”。我的习惯是在导出 STL 时统一除以 1000,惯性矩阵也要按比例缩放。
2.4 批量生成与参数扫描:一次定义,多次实例化
text-to-cad 真正的效率优势体现在批量生成上。一旦你把“描述到模型”的链路跑通,就可以用循环或参数扫描的方式生成大量变体。比如做机械臂的连杆,长度从 200mm 到 400mm 分 10 档,每档生成一个 STEP 和 URDF,整个过程可以完全自动化。
我的做法是把描述文本做成模板,参数用占位符表示,然后用 Python 的string.Template或format方法填充:
template = """ 长方体 link,长 {length},宽 {width},高 {height} 在 link 两端挖圆柱孔,直径 {hole_d} """ for length in range(200, 401, 20): desc = template.format(length=length, width=60, height=40, hole_d=10) params = parse_description(desc) shape = build_shape(params) export_step(shape, f"link_{length}.step") export_urdf(shape, f"link_{length}.urdf")这段代码跑一遍,几分钟就能生成几十个模型文件。如果手工建模,同样的工作量至少需要一整天。而且参数化生成的好处是:改一个参数,所有相关模型自动更新,不会出现“改了长度忘了改孔位”这种低级错误。
3. 环境搭建与工具链配置:少走弯路的实操建议
3.1 Python 环境与 CadQuery 安装的版本匹配问题
CadQuery 的安装是我踩坑最多的地方。它依赖 OCCT 的 Python 绑定,而 OCCT 的版本和 Python 版本之间有严格的对应关系。我试过在 Python 3.11 上装 CadQuery 2.2,结果 OCCT 的二进制包不兼容,报了一堆 DLL 加载错误。后来换成 Python 3.10 + CadQuery 2.3 才稳定下来。
目前比较稳的组合是:
- Python 3.10 或 3.11
- CadQuery 2.3 或 2.4
- 通过 conda 安装(比 pip 省心很多)
conda create -n text2cad python=3.10 conda activate text2cad conda install -c conda-forge cadquery为什么推荐 conda 而不是 pip?因为 CadQuery 依赖的 OCCT 在 conda-forge 上有预编译好的二进制包,pip 安装时经常需要自己编译,在 Windows 上尤其痛苦。我试过 pip 装 CadQuery,光是解决 OCCT 的依赖就花了一个下午,最后还是在 conda 上一把过。
如果你只能用 pip,那建议先装cadquery-ocp这个包,它是 OCCT 的 Python 绑定,装好之后再装 CadQuery:
pip install cadquery-ocp pip install cadquery但要注意,cadquery-ocp的版本必须和 CadQuery 要求的版本一致,否则运行时会报ImportError。我一般会先查 CadQuery 的setup.py或pyproject.toml,确认它依赖的 OCP 版本号,然后手动指定安装。
3.2 文本解析环节的依赖选择:正则、模板还是大模型
文本解析环节的工具选择取决于你的输入规范程度。我把三种方案的适用场景整理了一下:
| 方案 | 实现难度 | 灵活性 | 稳定性 | 推荐场景 |
|---|---|---|---|---|
| 正则表达式 | 低 | 低 | 高 | 固定句式,参数明确 |
| 模板匹配 | 中 | 中 | 高 | 有限变体,领域固定 |
| 大模型解析 | 高 | 高 | 低 | 自由文本,容错要求高 |
我的建议是:从正则开始,逐步过渡到模板,大模型作为最后手段。原因很简单:正则和模板的解析结果是确定的,出了错容易排查;大模型的输出有随机性,同样的输入可能得到不同的参数,在工程场景下这是不可接受的。
如果你确实需要处理自由文本,比如用户输入“画一个大概 10 厘米见方的板子,四角圆润一点”,那可以用大模型做第一轮解析,但一定要加校验层。校验层的作用是检查参数是否在合理范围内、是否缺少必要信息、是否有矛盾。比如“直径 40 的孔”和“板宽 30”就是矛盾的,校验层应该报错而不是硬生成。
3.3 几何内核的依赖管理:OCCT 版本冲突的排查思路
OCCT 的版本冲突是 text-to-cad 项目中最隐蔽的坑。表现是:代码能跑,但生成的模型有细微错误,比如圆角变成直角、孔的位置偏移几毫米。这种问题很难通过看代码发现,因为 API 调用完全正确,问题出在内核的数值精度或算法实现上。
我的排查思路是:
- 确认 OCCT 版本:用
OCP.VERSION或cadquery.occ_impl.version查看实际加载的版本号。 - 对比不同版本的行为:如果怀疑是版本问题,建一个最小复现用例,在另一个版本的环境里跑一遍,看结果是否一致。
- 检查单位制:OCCT 内部用毫米,但某些操作(如倒角)对数值范围敏感,参数过小或过大都可能触发数值问题。
- 查看内核日志:OCCT 有日志输出,可以通过设置环境变量
OCCT_LOGGING打开,能看到具体的算法警告。
我遇到过一次典型问题:对一个薄壁件倒 R0.5 的圆角,在 OCCT 7.6 上正常,在 7.7 上直接失败。后来发现是 7.7 修改了倒角算法的容差判断逻辑,对小于某个阈值的半径直接拒绝。解决办法是把圆角半径放大到 1.0,或者在倒角前先做一次clean()操作去除微小边。
3.4 导出格式的选择:STEP、STL 还是 URDF
导出格式的选择取决于下游用途。我把常见格式的适用场景列一下:
- STEP:通用 CAD 交换格式,保留精确几何(B-Rep),适合后续在 SolidWorks、UG 里继续编辑。文件较大,但信息完整。
- STL:三角网格格式,只有表面几何,没有拓扑信息。适合 3D 打印和仿真碰撞检测。文件大小取决于网格精度。
- URDF:机器人描述格式,本质是 XML,引用 STL 或 DAE 作为视觉和碰撞几何。适合 ROS、CoppeliaSim 等仿真环境。
- IGES:老式交换格式,兼容性不如 STEP,一般不用。
我的习惯是:主模型存 STEP,仿真用 STL,机器人场景额外生成 URDF。CadQuery 对这三种格式都支持:
# 导出 STEP cq.exporters.export(shape, "model.step") # 导出 STL,指定精度 cq.exporters.export(shape, "model.stl", tolerance=0.01) # URDF 需要自己拼 XML,几何用 STLSTL 的tolerance参数很关键。默认值生成的网格比较粗糙,曲面看起来像多边形。我一般设 0.01mm,对于大多数零件够用了。如果零件很大(比如超过 500mm),可以放宽到 0.05mm,否则文件会大到难以处理。
4. 实际跑通一个案例:从描述文本到可仿真的机械臂连杆
4.1 需求描述与参数定义
假设我们要为一个六轴机械臂生成第二连杆的模型。这个连杆的结构是:一个长方体主体,一端有圆形法兰,另一端有 U 型夹爪,中间有减重孔。用自然语言描述大概是:
长方体主体,长 300,宽 80,高 60 左端面中心加圆柱法兰,直径 100,厚度 15 右端面加 U 型夹爪,开口宽度 40,深度 60 主体上表面挖两个减重孔,直径 30,间距 150 所有外边缘倒圆角 R3这段描述里的参数都是明确的,解析器可以直接提取。但有几个隐含信息需要补充:法兰的轴向是 X 方向,U 型夹爪的开口方向是 Z 方向,减重孔是通孔。这些隐含信息需要在解析规则里预定义,或者让用户在描述中显式说明。
我的做法是在解析器里维护一个“默认方向表”:法兰默认沿 X 轴,孔默认沿 Z 轴,倒角默认对所有外边缘。如果用户有特殊要求,可以在描述里加“沿 Y 轴”这样的修饰词来覆盖默认值。
4.2 建模代码的逐步实现与调试
把上面的描述翻译成 CadQuery 代码,大概是这样的:
import cadquery as cq # 主体 body = cq.Workplane("XY").box(300, 80, 60) # 左端法兰 flange = ( cq.Workplane("YZ") .circle(50) .extrude(15) .translate((-150, 0, 0)) ) # U 型夹爪:先建一个方块,再挖掉中间部分 claw_outer = ( cq.Workplane("XY") .box(60, 80, 60) .translate((150 + 30, 0, 0)) ) claw_cut = ( cq.Workplane("XY") .box(60, 40, 60) .translate((150 + 30, 0, 0)) ) claw = claw_outer.cut(claw_cut) # 合并主体、法兰、夹爪 part = body.union(flange).union(claw) # 减重孔 part = ( part.faces(">Z") .workplane() .pushPoints([(-75, 0), (75, 0)]) .hole(30) ) # 倒圆角 part = part.edges().fillet(3)这段代码有几个地方需要调试。首先是法兰的定位:cq.Workplane("YZ")创建的平面在 X=0 处,extrude(15)沿 X 正方向拉伸,所以需要translate((-150, 0, 0))把它移到主体左端面。其次是 U 型夹爪的开口方向:box(60, 40, 60)挖掉的是 Y 方向中间的部分,形成 U 型。最后是倒圆角:part.edges().fillet(3)会对所有边倒角,包括减重孔的边缘,如果不想倒孔边,需要先用edges("|Z")筛选竖直边。
实际跑的时候,fillet(3)可能会失败,因为法兰和主体的交线处有尖锐角,倒角半径 3 可能超过局部几何的最小曲率半径。解决办法是先做一次clean()去除冗余边,或者把倒角半径降到 2。
4.3 生成 URDF 并导入 CoppeliaSim 验证
模型建好后,导出 STL 并生成 URDF:
cq.exporters.export(part, "link2.stl", tolerance=0.02) urdf = f''' <?xml version="1.0"?> <robot name="arm_link2"> <link name="link2"> <visual> <geometry> <mesh filename="link2.stl" scale="0.001 0.001 0.001"/> </geometry> </visual> <collision> <geometry> <box size="0.36 0.08 0.06"/> </geometry> </collision> <inertial> <mass value="2.5"/> <origin xyz="0 0 0"/> <inertia ixx="0.01" ixy="0" ixz="0" iyy="0.02" iyz="0" izz="0.02"/> </inertial> </link> </robot> ''' with open("link2.urdf", "w") as f: f.write(urdf)注意scale="0.001 0.001 0.001",这是把毫米转成米。碰撞体用了一个简化的 box,尺寸是 0.36×0.08×0.06 米,比实际模型略大一点,保证碰撞检测不会漏。
导入 CoppeliaSim 的步骤是:菜单栏Add→Robot→Import URDF,选择生成的 URDF 文件。如果模型显示不出来,先检查 STL 路径是否正确,URDF 里的filename是相对路径,需要和 URDF 文件在同一目录下。如果模型显示但位置不对,检查origin标签的xyz和rpy参数。
我遇到过导入后模型“散架”的情况,原因是 URDF 里引用了多个 link 但没有定义 joint。对于单个连杆,只需要一个 link 就够了,不需要 joint。如果要组装完整机械臂,每个 joint 需要指定 parent link、child link、类型(revolute/prismatic/fixed)和轴向。
4.4 参数化批量生成的脚本组织方式
把上面的流程封装成函数,就可以批量生成不同尺寸的连杆:
def generate_link(length, width, height, flange_d, hole_d, output_dir): body = cq.Workplane("XY").box(length, width, height) flange = ( cq.Workplane("YZ") .circle(flange_d / 2) .extrude(15) .translate((-length / 2, 0, 0)) ) part = body.union(flange) part = ( part.faces(">Z") .workplane() .pushPoints([(-length / 4, 0), (length / 4, 0)]) .hole(hole_d) ) part = part.edges("|Z").fillet(3) step_path = f"{output_dir}/link_{length}.step" stl_path = f"{output_dir}/link_{length}.stl" cq.exporters.export(part, step_path) cq.exporters.export(part, stl_path, tolerance=0.02) return step_path, stl_path for L in [200, 250, 300, 350, 400]: generate_link(L, 80, 60, 100, 30, "./output")这个脚本跑一遍,会在./output目录下生成 5 组 STEP 和 STL 文件。每组文件的命名带长度参数,方便后续引用。如果要把这些连杆组装成完整机械臂,可以再写一个脚本生成 URDF 的 joint 部分,把各个 link 按运动学链连接起来。
5. 踩过的坑与排查经验:那些文档里不会写的问题
5.1 圆角失败与几何清理的先后顺序
圆角操作是 CadQuery 里最容易报错的地方。常见错误是StdFail_NotDone或BRep_API: command not done,原因通常是倒角半径超过了局部几何的最小曲率半径,或者边缘与其他特征相交导致算法无法处理。
我的经验是:倒角之前先做几何清理。具体操作包括:
- 用
clean()去除冗余边和微小面; - 用
fuse()合并重叠实体,避免交线处有微小间隙; - 如果倒角半径接近极限值,先试小半径,逐步增大,找到失败阈值。
还有一个技巧:分步倒角。不要一次性对所有边倒角,先倒一组,检查结果,再倒下一组。这样出问题时容易定位是哪组边导致的。
5.2 STL 网格精度与文件体积的平衡
STL 的tolerance参数控制网格精度,值越小网格越密,文件越大。我做过一个测试:一个 300mm 长的连杆,tolerance=0.01时 STL 文件 8MB,tolerance=0.05时 1.2MB,tolerance=0.1时 400KB。在 CoppeliaSim 里显示,0.05 和 0.01 的视觉差异几乎看不出来,但文件体积差了近 7 倍。
我的建议是:视觉网格用 0.05,碰撞网格用 0.1 甚至更粗。如果做 3D 打印,才需要 0.01 或更小。另外,STL 是纯文本格式,可以用二进制格式导出,体积能再小一半。CadQuery 的export函数默认是二进制 STL,不用额外设置。
5.3 URDF 导入仿真环境时的坐标系偏移
URDF 导入 CoppeliaSim 后,模型位置和预期不符是最常见的问题。原因通常是 URDF 的坐标系约定和仿真环境不一致。URDF 规定 Z 轴向上,但有些仿真环境默认 Y 轴向上。CoppeliaSim 支持 Z 轴向上,所以一般不用改,但如果你的模型在 CadQuery 里是沿 Y 轴建的,导入后就会“躺倒”。
解决办法是在建模阶段就统一坐标系:所有模型都沿 Z 轴向上建。如果零件本身是横向的,在 URDF 的origin里加旋转:
<origin xyz="0 0 0" rpy="0 1.5708 0"/>rpy是绕 X、Y、Z 轴的旋转角,单位是弧度。1.5708约等于 90 度,绕 Y 轴转 90 度可以把横向的模型立起来。
5.4 批量生成时的内存泄漏与性能优化
批量生成几十个模型时,CadQuery 可能会越来越慢,最后内存溢出。原因是 OCCT 的对象没有及时释放,Python 的垃圾回收又管不到 C++ 层的对象。我的解决办法是:每生成一个模型后,手动清理工作平面和中间对象。
import gc for params in param_list: shape = build_shape(params) export(shape, f"model_{params['id']}.step") del shape gc.collect()另外,如果只是导出文件,不需要保留 CadQuery 的 Workplane 对象,可以在导出后直接丢弃。Workplane 是链式调用的中间状态,占内存较大。
还有一个性能技巧:把倒角、挖孔这些操作放在最后做。先做布尔运算合并主体,再做细节特征。这样 OCCT 处理的几何复杂度更低,速度快很多。我试过一个零件,先倒角再合并用了 12 秒,先合并再倒角只用 3 秒。
5.5 文本解析的歧义处理与容错设计
文本解析最怕歧义。比如“在两端挖孔”,到底是 X 方向的两端还是 Y 方向的两端?如果描述里没说清楚,解析器只能猜。我的做法是:遇到歧义就报错,让用户补充说明,而不是自动选一个默认值。因为自动选错的话,用户可能到仿真阶段才发现,排查成本更高。
容错设计方面,我建议加一层“参数合理性检查”。比如:
- 孔径不能大于零件最小边长;
- 倒角半径不能大于最小壁厚的一半;
- 孔深不能超过零件厚度(除非是通孔)。
这些检查用简单的 if-else 就能实现,但能拦住 80% 的低级错误。我在项目里加了这个检查层之后,因为参数错误导致的建模失败从每天十几次降到了几乎为零。
6. 这条链路还能怎么扩展:几个实际验证过的方向
6.1 从零件到装配体:自动生成运动学链
单个零件生成跑通之后,下一步自然是装配体。我的做法是定义一个“装配描述”格式,用缩进表示层级关系:
装配 机械臂 连杆 base,长 200,宽 100,高 80 关节 joint1,类型 revolute,轴 Z,连接 base 和 link1 连杆 link1,长 300,宽 80,高 60 关节 joint2,类型 revolute,轴 Y,连接 link1 和 link2 连杆 link2,长 250,宽 60,高 50解析这个描述后,先生成每个零件的几何,再根据关节定义计算每个零件的相对位置,最后拼成完整的 URDF。关节的轴向和连接关系决定了运动学链的结构,这部分逻辑需要仔细处理,但一旦跑通,生成一个六轴机械臂的 URDF 只需要几秒钟。
6.2 结合参数优化:用仿真结果反推几何参数
text-to-cad 的另一个扩展方向是闭环优化。比如你要设计一个机械臂连杆,目标是“在满足刚度要求的前提下重量最轻”。传统做法是手工改参数、跑仿真、看结果、再改,循环很多次。用脚本可以把这个过程自动化:
- 定义参数范围(长度、宽度、壁厚等);
- 用 text-to-cad 生成模型;
- 导出到仿真环境跑静力学分析;
- 提取最大应力和重量;
- 用优化算法(如遗传算法或贝叶斯优化)更新参数;
- 重复直到收敛。
这个流程我跑过一个小规模实验,优化一个简单的悬臂梁结构,20 代之后重量比初始设计降低了 35%,而最大应力仍在安全范围内。整个过程不需要人工干预,晚上跑一夜,早上看结果就行。
6.3 与版本管理结合:几何模型的 diff 与回滚
几何模型的版本管理是个麻烦事。STEP 文件是二进制,Git 没法做 diff。我的做法是:把生成模型的描述文本和参数存成 JSON,用 Git 管理这些文本文件。需要回滚时,checkout 对应的 JSON,重新跑一遍生成脚本即可。
{ "version": "1.2", "parts": [ {"name": "link2", "length": 300, "width": 80, "height": 60, "hole_d": 30} ], "features": [ {"type": "fillet", "target": "vertical_edges", "radius": 3} ] }这样每次修改都有记录,diff 也清晰。如果两个人同时改,合并冲突也容易解决,因为改的是文本而不是二进制。
6.4 面向非标自动化的快速出图
在非标自动化行业,经常需要根据客户需求快速出方案图。传统流程是:销售提需求,工程师建模,出工程图,报价。用 text-to-cad 可以把建模环节压缩到几分钟:销售填一个参数表,脚本自动生成 STEP 和简单的三视图截图,工程师只需要审核和标注。
我帮一个做夹具设计的朋友搭过这套流程。他之前一个方案要画半天,现在填完参数等 30 秒就能看到三维模型和基本尺寸,效率提升非常明显。当然,最终的工程图还是需要人工完善,但前期方案阶段的速度完全不一样了。
这套东西的门槛其实不高,核心就是 Python 加 CadQuery,再配一个简单的文本解析器。真正花时间的是调试几何操作的边界情况和处理各种异常。但一旦跑通,对于需要批量生成相似结构的场景,节省的时间是数量级的。