ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

text-to-cad 实战:从自然语言到 STEP/URDF/G-code 的三段式管线

text-to-cad 实战:从自然语言到 STEP/URDF/G-code 的三段式管线

1. 从一句话到三维模型:text-to-cad 到底在解决什么问题

第一次听到 “text-to-cad” 这个词,很多人脑子里蹦出来的画面可能是:对着电脑说一句“给我画个法兰盘”,然后 CAD 软件里就自动生成了一张图纸。这个理解方向没错,但实际落地远没有这么简单。text-to-cad 本质上是一套把自然语言描述转换成结构化三维模型数据的技术链路,它的输出通常不是一张 DWG 图纸,而是 STEP、URDF、G-code 这类可以被下游工具直接消费的中间格式。

我接触这个方向是从一个很具体的需求开始的:手头有一批参数化零件,每次改尺寸都要手动在 CAD 里重新拉伸、打孔、倒角,重复劳动太多。后来想,能不能用一段文字描述直接驱动模型生成?试了几轮之后发现,text-to-cad 真正的价值不在于“替代画图”,而在于把设计意图变成可复用的数据流。你描述的是“一个外径 80、内径 40、厚度 10 的法兰,均布 6 个直径 8 的孔”,系统输出的是带完整几何拓扑的 STEP 文件,这个文件可以进仿真、可以进切片、可以进机器人仿真环境。

它适合谁?三类人最值得关注:一是做参数化设计的机械工程师,二是搞机器人仿真需要批量生成 URDF 的技术人员,三是做增材制造想把文字需求直接转成 G-code 的创客。如果你只是偶尔画两张二维图,那 text-to-cad 对你的边际收益不大;但如果你面对的是成百上千个变体零件,这套思路能省掉大量重复操作。

需要先明确一点:text-to-cad 不是某一个软件的功能按钮,而是一条从语义解析到几何生成再到格式导出的完整管线。下面我按自己实际搭过的流程,把每个环节拆开讲。

2. 整体架构设计:为什么是“解析—建模—导出”三段式

2.1 核心思路与方案选型

我试过两种路线。第一种是端到端生成,用大模型直接输出 CAD 脚本或网格数据;第二种是分阶段管线,先把文字解析成结构化参数,再调用几何内核建模,最后按目标格式导出。实测下来,第一种路线在简单形状上能跑通,但一旦涉及孔位阵列、倒角、装配关系,生成结果就很不稳定,而且出错之后几乎没法调试。

所以我最终采用的是三段式架构:

  • 语义解析层:把自然语言拆成“实体类型 + 尺寸参数 + 特征操作”的结构化描述
  • 几何建模层:用参数化建模内核根据结构化描述生成三维实体
  • 格式导出层:把实体转换成 STEP、URDF、G-code 等目标格式

这个设计的核心考量是可调试性。文字解析错了,我只需要改解析规则;几何生成错了,我只需要调建模参数;导出格式不对,我只需要换导出器。每一层都可以单独验证,不会出现“一句话进去、一坨乱码出来、完全不知道哪里错了”的情况。

提示:如果你刚开始做 text-to-cad,强烈建议不要一上来就追求端到端。先把中间的结构化描述定义清楚,后面所有工作都会轻松很多。

2.2 各层职责与数据流

结构化描述我用的是 JSON,字段设计参考了常见 CAD 特征的参数组织方式。一个典型的法兰描述长这样:

{ "entity": "flange", "params": { "outer_diameter": 80, "inner_diameter": 40, "thickness": 10, "holes": { "count": 6, "diameter": 8, "pcd": 60 } }, "units": "mm" }

这个 JSON 就是整条管线的“合同”。解析层负责产出它,建模层负责消费它,导出层不关心它怎么来的。这样做的好处是,哪天我想换一个更好的语言模型来解析文字,只要它还能输出同样结构的 JSON,后面两层完全不用动。

几何建模层我选的是基于OpenCASCADE的内核,因为它对 STEP 格式的支持最成熟,布尔运算也稳定。URDF 导出则需要额外处理关节和连杆的层级关系,这部分后面单独讲。G-code 导出相对独立,本质上是把三维模型切片后生成路径,我一般用现成的切片引擎来处理。

2.3 为什么不用直接生成网格

有人会问:现在网格生成模型那么多,为什么不直接文字生成 STL?我的经验是,网格数据不可参数化。你拿到一个 STL,想改一个孔的直径,只能重新生成整个网格。而 STEP 是边界表示(B-rep),每个面、每条边都是带参数的,改一个尺寸只需要更新对应参数。对于需要反复迭代的设计场景,这个差别是致命的。

另外,URDF 和 G-code 对几何精度的要求不同。URDF 关心的是碰撞体和视觉体的近似,G-code 关心的是切片路径,两者都需要从精确的 B-rep 模型出发做不同处理。如果中间只有网格,很多下游操作会变得很别扭。

3. 语义解析层:把“人话”变成“机器能懂的参数”

3.1 解析策略:规则优先还是模型优先

我一开始用纯规则做解析,写了一大堆正则表达式。简单句还能应付,比如“直径 50 的圆盘”,但一旦出现“外径 80 内径 40 厚度 10 均布 6 个直径 8 的孔”这种复合描述,规则就开始打架。后来改成模型抽取 + 规则校验的混合策略:先用语言模型把句子里的实体、尺寸、数量、位置关系抽出来,再用规则检查参数是否完整、单位是否明确、数值是否在合理范围内。

这个混合策略的关键在于校验环节。语言模型有时候会把“内径”和“外径”搞反,或者漏掉单位。规则校验会检查:外径是否大于内径?孔的数量是否是整数?孔径是否小于壁厚?这些检查能拦掉大部分低级错误。

3.2 参数完整性检查清单

在实际操作中,我发现以下几类问题出现频率最高,建议在解析层就做好拦截:

问题类型典型表现处理方式
单位缺失“直径 80”没说是 mm 还是 inch默认 mm,但在输出中标注假设
参数矛盾内径大于外径直接报错,要求重新描述
特征冲突孔的位置超出实体范围计算孔边距,超出则报错
数量模糊“几个孔”没有具体数字追问或使用默认值并标注
术语歧义“大孔”没有具体尺寸要求补充具体数值

注意:不要小看单位问题。我踩过一次坑,一批零件默认用了 inch,结果导出 STEP 之后进仿真,尺寸差了 25.4 倍,排查了半天才发现是单位没统一。

3.3 从描述到 JSON 的实操示例

假设输入是:“一个长 100 宽 60 高 20 的方块,四个角各有一个直径 6 的通孔,孔中心距边缘 10。”

解析层的工作流程是:

  1. 识别实体类型:方块(box)
  2. 抽取主尺寸:长 100、宽 60、高 20
  3. 识别特征操作:通孔(through hole)
  4. 抽取孔参数:直径 6、数量 4
  5. 推断孔位置:四角,中心距边缘 10
  6. 生成结构化 JSON

对应的 JSON 如下:

{ "entity": "box", "params": { "length": 100, "width": 60, "height": 20, "holes": { "type": "through", "diameter": 6, "count": 4, "positions": [ {"x": 10, "y": 10}, {"x": 90, "y": 10}, {"x": 10, "y": 50}, {"x": 90, "y": 50} ] } }, "units": "mm" }

这里的位置计算是解析层做的:孔中心距边缘 10,所以四个角的坐标分别是 (10,10)、(90,10)、(10,50)、(90,50)。这个计算逻辑要写清楚,因为不同人对“距边缘”的理解可能不一样——是孔中心到边缘,还是孔边到边缘?我默认按孔中心算,并在文档里注明。

4. 几何建模层:从参数到实体的关键操作

4.1 建模内核的选择与初始化

几何建模层我用的 OpenCASCADE,Python 绑定用的是 pythonocc。初始化流程大概是:

from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeBox, BRepPrimAPI_MakeCylinder from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Cut from OCC.Core.gp import gp_Pnt, gp_Ax2, gp_Dir # 创建方块 box = BRepPrimAPI_MakeBox(100, 60, 20).Shape() # 创建圆柱并做布尔减 for pos in hole_positions: cylinder = BRepPrimAPI_MakeCylinder(3, 20, gp_Ax2(gp_Pnt(pos.x, pos.y, 0), gp_Dir(0, 0, 1))).Shape() box = BRepAlgoAPI_Cut(box, cylinder).Shape()

这段代码的逻辑很直接:先建主实体,再逐个建特征实体,最后用布尔运算把特征从主实体上“挖”掉。但实际操作中有几个细节要注意。

4.2 布尔运算的稳定性技巧

布尔运算是几何建模里最容易出问题的环节。我遇到过圆柱和方块表面刚好相切导致运算失败的情况,也遇到过孔的位置太靠近边缘导致生成薄壁碎片。几个实用技巧:

  • 留余量:做减运算的圆柱,高度要比主实体厚度多出一点,确保完全穿透。比如方块高 20,圆柱高度用 22,两端各多 1mm。
  • 避免相切:孔中心到边缘的距离不要刚好等于孔径的一半,稍微错开一点,避免相切。
  • 分步运算:如果有多个孔,不要一次性做所有布尔运算,逐个做,每做一步检查一下实体是否有效。
  • 容差设置:OpenCASCADE 有默认容差,如果模型尺寸很小(比如微米级),需要手动调整容差参数。

提示:布尔运算失败时,先检查两个实体是否有重叠体积。如果只是面接触,运算结果可能为空或报错。

4.3 特征顺序对结果的影响

建模顺序会影响最终结果,尤其是涉及倒角和圆角的时候。我的经验是:先做减运算,再做倒角。如果先倒角再打孔,孔可能会切掉倒角的一部分,导致边缘不完整。正确的顺序是:

  1. 创建主实体
  2. 执行所有减运算(孔、槽、腔)
  3. 执行倒角和圆角
  4. 检查实体有效性

这个顺序不是绝对的,但大多数情况下能避免特征干涉。如果倒角之后还需要打孔,那就要重新评估顺序。

4.4 模型有效性检查

每次布尔运算之后,我都会跑一遍有效性检查:

from OCC.Core.BRepCheck import BRepCheck_Analyzer analyzer = BRepCheck_Analyzer(shape) if not analyzer.IsValid(): print("模型无效,需要排查")

无效模型导出 STEP 后,下游工具可能读不出来,或者读出来是破面。与其等到下游报错,不如在建模层就拦住。

5. 格式导出层:STEP、URDF、G-code 的差异化处理

5.1 STEP 导出:最直接的路径

STEP 导出是最成熟的,OpenCASCADE 自带导出器:

from OCC.Core.STEPControl import STEPControl_Writer, STEPControl_AsIs writer = STEPControl_Writer() writer.Transfer(shape, STEPControl_AsIs) writer.Write("output.step")

这里的关键参数是STEPControl_AsIs,表示按原样导出,不做额外处理。如果模型有装配关系,需要额外处理装配层级。对于单实体零件,这样导出就够了。

STEP 的好处是通用性强,几乎所有 CAD 软件都能读。我试过用 FreeCAD、中望 CAD、SolidWorks 分别打开同一个 STEP 文件,几何信息都能正确识别。但要注意,STEP 不保留特征树,导出的是一堆面片和边,想改参数只能回到生成端重新生成。

5.2 URDF 导出:机器人仿真的特殊需求

URDF 是机器人描述格式,它关心的不是精确几何,而是连杆、关节、碰撞体、视觉体的层级关系。从 text-to-cad 生成 URDF,需要额外做几件事:

  • 定义连杆:每个独立运动的部件作为一个 link
  • 定义关节:link 之间的连接关系,包括关节类型(旋转、平移、固定)和运动范围
  • 简化碰撞体:URDF 的碰撞体通常用简单几何(方块、圆柱、球)近似,不需要精确 B-rep
  • 指定视觉体:视觉体可以用网格文件,通常导出为 STL 或 DAE

我一般的工作流是:先用 text-to-cad 生成精确 STEP 模型,再根据装配关系手动或半自动地定义 URDF 结构。完全自动从文字生成 URDF 目前还比较困难,因为关节类型和运动范围很难从自然语言中准确推断。

注意:URDF 导入 CoppeliaSim 时,路径问题是最常见的坑。建议用相对路径,并且把网格文件和 URDF 放在同一个目录下。

5.3 G-code 导出:从模型到制造路径

G-code 导出本质上是切片加路径规划。text-to-cad 生成的模型需要先导出为 STL,再进切片引擎。我常用的流程是:

  1. 从 B-rep 模型导出 STL(注意设置合适的网格精度)
  2. 用切片引擎(如 CuraEngine、PrusaSlicer)生成 G-code
  3. 根据打印机参数调整速度、温度、层高

这里的关键是网格精度。STL 是三角网格,精度太低会导致圆弧变成多边形,精度太高会导致文件过大。我的经验是:对于一般零件,弦高公差设 0.01mm,角度公差设 0.5 度,能在文件大小和精度之间取得平衡。

格式主要用途精度要求导出难度
STEPCAD 交换、仿真高,B-rep低
URDF机器人仿真中,简化碰撞体中
G-code增材制造中,切片路径中
STL3D 打印、可视化中,三角网格低

6. 实操全流程:从一句话到可用的 STEP 文件

6.1 环境准备与依赖安装

我用的环境是 Python 3.10 + pythonocc-core + numpy。安装 pythonocc 在 Windows 上稍微麻烦一点,推荐用 conda:

conda install -c conda-forge pythonocc-core

如果不用 conda,pip 安装可能会缺依赖。Linux 下相对简单,apt 装好 OpenCASCADE 开发库之后 pip 装 pythonocc 基本能跑通。

6.2 完整代码示例

下面是一个从文字描述到 STEP 导出的最小可运行示例:

import json from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeBox, BRepPrimAPI_MakeCylinder from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Cut from OCC.Core.gp import gp_Pnt, gp_Ax2, gp_Dir from OCC.Core.STEPControl import STEPControl_Writer, STEPControl_AsIs from OCC.Core.BRepCheck import BRepCheck_Analyzer # 假设解析层已经产出结构化描述 spec = { "entity": "box", "params": { "length": 100, "width": 60, "height": 20, "holes": { "type": "through", "diameter": 6, "count": 4, "positions": [ {"x": 10, "y": 10}, {"x": 90, "y": 10}, {"x": 10, "y": 50}, {"x": 90, "y": 50} ] } }, "units": "mm" } # 建模 p = spec["params"] shape = BRepPrimAPI_MakeBox(p["length"], p["width"], p["height"]).Shape() hole_r = p["holes"]["diameter"] / 2 hole_h = p["height"] + 2 # 留余量 for pos in p["holes"]["positions"]: cyl = BRepPrimAPI_MakeCylinder( hole_r, hole_h, gp_Ax2(gp_Pnt(pos["x"], pos["y"], -1), gp_Dir(0, 0, 1)) ).Shape() shape = BRepAlgoAPI_Cut(shape, cyl).Shape() # 有效性检查 if not BRepCheck_Analyzer(shape).IsValid(): raise RuntimeError("模型无效") # 导出 STEP writer = STEPControl_Writer() writer.Transfer(shape, STEPControl_AsIs) writer.Write("output.step") print("STEP 文件已生成")

这段代码跑通之后,你会得到一个带四个通孔的方块 STEP 文件。用 FreeCAD 打开,应该能看到完整的几何。

6.3 参数计算的实际案例

再举一个法兰的例子,说明参数计算过程。输入描述:“外径 120,内径 60,厚度 15,均布 8 个直径 10 的孔,孔中心圆直径 90。”

计算过程:

  • 外半径 = 120 / 2 = 60
  • 内半径 = 60 / 2 = 30
  • 孔半径 = 10 / 2 = 5
  • 孔中心圆半径 = 90 / 2 = 45
  • 孔中心坐标:角度间隔 360 / 8 = 45 度
  • 第 i 个孔的角度 = i * 45 度
  • x = 45 * cos(角度),y = 45 * sin(角度)

这些计算在解析层完成,建模层直接消费坐标。这样做的好处是,如果以后要改孔的数量,只需要改解析层的计算逻辑,建模层不用动。

7. 常见问题与排查技巧实录

7.1 布尔运算失败怎么办

这是最高频的问题。表现是BRepAlgoAPI_Cut返回空形状或者抛异常。排查顺序:

  1. 检查两个实体是否有实际重叠体积,而不只是面接触
  2. 检查实体是否有效,用BRepCheck_Analyzer验证
  3. 尝试调整容差,BRepAlgoAPI_Cut可以设置模糊容差
  4. 如果还不行,把减运算的实体稍微移动一点,避开相切位置

我遇到过一次,圆柱和方块的一个面刚好共面,导致布尔运算结果不稳定。把圆柱沿轴向移动 0.01mm 之后就正常了。

7.2 STEP 导出后下游读不出来

通常是模型本身有问题。先用BRepCheck_Analyzer检查,如果模型有效但下游还是读不出来,可能是 STEP 版本问题。OpenCASCADE 默认导出 AP214,有些老软件只支持 AP203。可以在导出时指定协议:

writer.Transfer(shape, STEPControl_AsIs) # 或者尝试其他协议

另外,文件名不要用中文和特殊字符,有些下游工具对路径编码支持不好。

7.3 URDF 导入仿真环境后位置不对

URDF 的坐标系定义和 CAD 的坐标系定义可能不一致。CAD 通常 Z 轴向上,URDF 通常也是 Z 轴向上,但有些仿真环境默认 Y 轴向上。导入之后如果发现模型躺倒了,检查一下坐标系约定。

还有一个常见问题是单位。URDF 默认用米,CAD 默认用毫米。导出 URDF 时要把尺寸除以 1000,否则模型会大 1000 倍。

7.4 G-code 路径异常

G-code 路径异常通常出在切片参数上。检查:

  • 层高是否合理(一般 0.1-0.3mm)
  • 喷嘴直径是否和切片参数匹配
  • 模型是否封闭(非封闭模型切片会出问题)
  • 模型是否在打印平台范围内

我踩过一次坑,模型导出 STL 时精度设得太低,圆弧变成了明显的多边形,切片后路径抖动很厉害。把弦高公差从 0.1mm 调到 0.01mm 之后就顺滑了。

7.5 常见问题速查表

问题现象可能原因排查方向
布尔运算返回空实体无重叠或相切检查位置关系,留余量
STEP 导出失败模型无效或路径含特殊字符检查模型有效性,换路径
URDF 导入后尺寸不对单位不一致毫米转米
URDF 导入后姿态不对坐标系约定不同检查轴向定义
G-code 路径抖动STL 精度太低提高网格精度
模型面数过多网格精度太高降低弦高公差
孔位置偏移坐标计算错误检查解析层计算逻辑

8. 工具链选型与扩展思路

8.1 建模内核对比

内核优势劣势适用场景
OpenCASCADE开源,STEP 支持好学习曲线陡通用 CAD 生成
CGAL几何算法丰富偏学术,工程化弱研究性项目
FreeCAD API上手快,Python 友好依赖 FreeCAD 环境快速原型
CadQuery语法简洁,参数化强底层还是 OCC参数化零件

我目前主力用 OpenCASCADE,因为可控性最强。如果只是做简单零件,CadQuery 的语法更友好,写起来快很多。

8.2 从单零件到装配体

单零件跑通之后,下一步自然是装配体。装配体的 text-to-cad 需要额外处理:

  • 零件间的位置关系:配合、对齐、距离
  • 装配层级:子装配和总装配的关系
  • 干涉检查:零件之间是否有碰撞

这部分我还在摸索,目前的做法是先用文字分别生成每个零件,再手动或半自动地定义装配关系。完全自动的装配体生成,难点在于自然语言中的位置描述往往很模糊,比如“把轴插进孔里”,到底插多深、什么配合,都需要额外推断。

8.3 批量生成的工程化考虑

如果要做批量生成,比如一次生成 100 个不同尺寸的法兰,需要考虑:

  • 模板化:把常用零件做成模板,只改参数
  • 队列处理:生成任务排队,避免内存爆掉
  • 错误重试:某个零件生成失败时自动重试或跳过
  • 日志记录:每个零件的生成参数和结果都要记录,方便追溯

我用 Python 的 concurrent.futures 做并行生成,但要注意 OpenCASCADE 不是线程安全的,每个线程需要独立的建模上下文。简单做法是用多进程,每个进程独立初始化。

9. 我踩过的坑与实操心得

第一个坑是单位不统一。前面提过,一批零件默认用了 inch,导出 STEP 后进仿真尺寸差了 25.4 倍。后来我在解析层强制要求标注单位,没标注的一律按 mm 处理,并在输出中打印警告。

第二个坑是布尔运算的顺序。有一次先倒了角再打孔,结果孔切掉了倒角的一部分,边缘出现了薄壁碎片。后来改成先打孔再倒角,问题消失。这个顺序不是绝对的,但大多数情况下先减后倒更安全。

第三个坑是URDF 的碰撞体简化。一开始我直接把精确模型作为碰撞体导出,结果仿真时碰撞检测特别慢。后来改成用简单几何近似碰撞体,视觉体用精确模型,仿真速度提升了一个数量级。

第四个坑是G-code 的模型封闭性。有一个模型因为布尔运算出了一个小破面,肉眼看不出来,但切片时路径直接乱掉。后来每次导出 STL 之前都跑一遍封闭性检查,不通过就不切片。

提示:text-to-cad 的产出物最终是要给下游用的,所以下游能读、能用比生成得快更重要。宁可多花几秒做有效性检查,也不要生成一堆废文件。

10. 这个方向后续还能怎么扩展

目前我跑通的链路是“文字 → 结构化参数 → 单零件 STEP/URDF/G-code”。下一步想尝试的是从装配描述直接生成装配体,比如“一个减速箱,输入轴和输出轴平行,中心距 80”,系统自动推断齿轮参数和箱体尺寸。这个难度不小,因为涉及工程计算的推断,不只是几何生成。

另一个方向是反向链路:从现有 STEP 文件反推出结构化描述,再用文字描述去修改。这样就能实现“打开一个现有模型,说一句‘把孔径改成 12’,模型自动更新”。这个在参数化设计场景下很有用,技术上也比正向生成容易一些,因为几何信息是现成的,只需要做特征识别和参数提取。

还有一个实用扩展是和现有 CAD 软件集成。比如生成 STEP 之后自动导入中望 CAD 或 FreeCAD 做后续处理,或者把 G-code 直接推送到打印机。这些集成本身不难,主要是接口适配的工作量。

我个人在实际操作中的体会是,text-to-cad 目前最适合的场景是参数化变体生成,而不是从零开始的全新设计。你给它一个明确的参数化模板,它能快速生成大量变体;但你让它凭空设计一个复杂零件,它还很吃力。把预期放在“提效工具”而不是“替代设计师”,心态会好很多,落地也会顺利很多。

返回列表