1. 从一句话到三维实体:text-to-cad 到底在解决什么问题
第一次听到 "text-to-cad" 这个词,很多人脑子里浮现的画面大概是:对着电脑说一句"给我画个法兰盘",屏幕上就自动长出一个带螺栓孔的三维模型。这个想象不算离谱,但真正落地的时候,它解决的问题比"语音画图"要具体得多,也有意思得多。
text-to-cad 的核心,是把自然语言描述转换成计算机辅助设计(CAD)能够识别的几何数据。这里的"CAD"不是指某个具体软件,而是指整个几何建模与工程制图的技术体系;而"text"也不只是随口一句话,它可以是结构化的参数描述、一段规格说明,甚至是一份从需求文档里摘出来的尺寸表。最终产物通常是一个可被下游工具消费的文件——可能是STEP(通用的三维交换格式)、URDF(机器人领域描述连杆与关节的格式),也可能是G-code(数控加工或 3D 打印的执行指令)。
为什么这件事值得单独拿出来做?因为传统 CAD 工作流里,从"想法"到"模型"这一段高度依赖人工。工程师打开软件,画草图、拉伸、打孔、倒角,一个中等复杂度的零件动辄半小时起步。如果需求是批量生成——比如一百个尺寸略有差异的支架,或者一批参数化的机械臂连杆——纯手工建模的时间成本会迅速失控。text-to-cad 的价值就在于把这段重复劳动自动化:用文本描述参数,用程序生成几何,人只负责定义规则和校验结果。
它适合谁?三类人最直接受益。第一类是做参数化设计的工程师,手里有大量结构相似、尺寸不同的零件;第二类是机器人方向的开发者,需要频繁生成 URDF 模型来搭建仿真环境;第三类是做增材制造或数控加工的从业者,希望从参数直接跳到 G-code,跳过中间的手工建模环节。哪怕你只是刚接触 CAD 制图入门的新手,理解这套思路也能帮你建立"几何即数据"的认知,而不是把 CAD 当成一个只能手点鼠标的黑盒。
需要先泼一盆冷水:text-to-cad 不是"AI 帮你画图"的魔法。它更像一套参数化建模的自动化管线,文本只是入口,真正的功夫在于你怎么把语言里的模糊描述,翻译成精确的、无歧义的几何参数。这一点想不清楚,后面全是坑。
2. 文本到几何的翻译链路:参数、约束与格式选择
2.1 自然语言里的"尺寸"为什么不能直接用
人说话是模糊的。"一个大概 50 毫米长的支架"——这句话里,"大概"是致命的。CAD 内核不接受"大概",它要的是确定的数值和明确的拓扑关系。所以 text-to-cad 的第一步,永远是把自然语言归一化成结构化参数。
我通常的做法是定义一个参数字典,把描述里能提取的数值全部落到键值对上。比如"长 50、宽 30、厚 5、四角各一个直径 4 的通孔,孔中心距边缘 6 毫米",翻译成结构大概是这样:
params = { "length": 50.0, "width": 30.0, "thickness": 5.0, "hole_dia": 4.0, "hole_offset": 6.0, "hole_count": 4, }注意这里我把"四角"这种空间描述,转成了hole_count加hole_offset的组合。为什么?因为几何生成代码需要的是可计算的坐标,而不是"角"这种人类概念。四个孔的中心坐标可以这样推出来:
half_l = params["length"] / 2 - params["hole_offset"] half_w = params["width"] / 2 - params["hole_offset"] centers = [ (-half_l, -half_w), ( half_l, -half_w), (-half_l, half_w), ( half_l, half_w), ]这段推导看着简单,但它体现了 text-to-cad 的核心思维:把语言里的相对描述,转成绝对坐标。凡是描述里出现"边缘""中心""对称""等距"这类词,都要在代码里找到对应的数学表达。这一步做扎实了,后面生成什么格式都只是换输出层的事。
2.2 约束比尺寸更重要
新手最容易忽略的一点:光有尺寸,模型可能是"飘"的。什么意思?你告诉程序"画一个 50 长的板",但没告诉它这块板在坐标系里的位置、朝向、和相邻零件的关系。结果就是每次生成的模型位置都不一样,装配的时候全乱套。
所以在参数化建模里,约束(constraint)和尺寸同等重要。常见的约束有几类:
- 几何约束:平行、垂直、相切、同心。比如"这个孔和那个轴同心",决定了两个特征必须共享轴线。
- 尺寸约束:长度、角度、半径。这是最直观的一类。
- 位置约束:相对于原点或某个基准面的偏移。决定了零件在全局坐标系里的落点。
在文本描述里,这些约束往往藏在形容词和介词里。"与底面垂直的立柱""沿 X 轴对称分布的两个耳片"——每一句都在施加约束。做 text-to-cad 的时候,我习惯先把描述拆成"实体 + 约束"两张清单,实体负责形状,约束负责关系,两边都齐了再动手写生成逻辑。
2.3 STEP、URDF、G-code:三种输出各管一段
同一个几何体,输出成什么格式,取决于下游要拿它干什么。这三者经常被混为一谈,其实分工很清楚。
| 格式 | 主要用途 | 关键特点 | 典型下游工具 |
|---|---|---|---|
| STEP | 通用三维交换 | 保留精确 B-rep 几何,跨软件兼容 | 各类 CAD 软件、CAM 软件 |
| URDF | 机器人建模 | 描述连杆、关节、惯性、碰撞体 | 机器人仿真环境 |
| G-code | 加工执行 | 刀具路径指令,面向机床/打印机 | 数控机床、3D 打印机 |
STEP 是"几何的通用语言"。你在这个软件里建的模型,导出 STEP 后,换个软件打开,形状基本不会变。它记录的是精确的边界表示(B-rep),不是网格近似,所以做后续的 CAM 加工或者精确装配都靠它。
URDF 则是"机器人的骨架说明书"。它不关心你零件表面多光滑,它关心的是:这个连杆的质量是多少、惯性张量怎么算、和下一个连杆通过什么关节连接、关节的旋转轴在哪。所以从 CAD 几何转 URDF 的时候,质量属性和关节定义是必须补上的信息,光有形状不够用。
G-code 是"给机器的命令"。它把几何切成一层层的路径或者一条条的刀具轨迹,输出成机器能逐行执行的指令。从几何到 G-code 中间通常还要经过切片或刀路规划,text-to-cad 如果直接输出 G-code,等于把建模和加工规划一起包了,复杂度会高不少。
我的建议是:默认输出 STEP,需要仿真再加 URDF,需要加工再走 G-code。不要一上来就追求端到端,先把几何生成这一层做稳。
3. 搭一条能跑的参数化生成管线
3.1 工具选型:为什么我优先考虑脚本化建模
要自动化生成几何,第一步是选一个能用代码驱动的建模工具。纯 GUI 的 CAD 软件虽然功能强,但很难批量调用。我这些年试下来,比较顺手的路线有这么几条:
- CadQuery:基于 Python 的参数化建模库,语法接近"描述几何"而不是"操作鼠标",写起来直观,导出 STEP 很方便。
- OpenSCAD:用脚本描述实体,适合做规则化的机械结构,社区模型多,但复杂曲面偏弱。
- FreeCAD 的 Python 接口:功能全,能覆盖从建模到导出的完整链路,学习曲线稍陡。
- 直接调用几何内核:比如通过 OCCT 的绑定做底层操作,灵活但门槛高。
选哪个,取决于你的模型复杂度和你对编程的熟悉度。如果只是规则零件批量生成,CadQuery 或 OpenSCAD 足够;如果要做复杂装配和工程图,FreeCAD 的接口更合适。我个人的默认选择是 CadQuery,因为它的 API 设计让"参数到几何"的映射非常直接,调试起来也快。
3.2 一个最小可用的生成脚本
下面这段是我常用的骨架,用 CadQuery 生成一块带四角孔的板,然后导出 STEP。你可以直接拿去改参数。
import cadquery as cq def make_plate(length, width, thickness, hole_dia, hole_offset): # 先建基础板 plate = cq.Workplane("XY").box(length, width, thickness) # 计算四个孔的中心位置 half_l = length / 2 - hole_offset half_w = width / 2 - hole_offset centers = [ (-half_l, -half_w), ( half_l, -half_w), (-half_l, half_w), ( half_l, half_w), ] # 在顶面打孔 plate = ( plate.faces(">Z") .workplane() .pushPoints(centers) .hole(hole_dia) ) return plate if __name__ == "__main__": part = make_plate(50, 30, 5, 4, 6) cq.exporters.export(part, "plate.step") print("导出完成:plate.step")这段代码有几个细节值得说。faces(">Z")是选中朝上的那个面作为工作平面,pushPoints把之前算好的坐标批量投上去,hole一次性打穿。这种"先算坐标、再批量操作"的写法,比一个个孔单独画要稳得多,也更容易参数化。
3.3 参数校验:别让错误参数悄悄生成废模型
脚本能跑通不代表结果是对的。我踩过最典型的坑是:参数之间互相矛盾,程序照样生成了一个"看起来正常"的模型,但尺寸完全不对。比如孔偏移量比板的一半还大,孔就跑到板外面去了,程序不报错,模型却是废的。
所以生成之前一定要加校验。我一般会写一个validate函数,把物理上不合理的组合挡在前面:
def validate(params): assert params["hole_offset"] < params["length"] / 2, "孔偏移超出板长" assert params["hole_offset"] < params["width"] / 2, "孔偏移超出板宽" assert params["hole_dia"] < params["thickness"] * 4, "孔径相对板厚过大" assert params["thickness"] > 0, "板厚必须为正"这些断言看着啰嗦,但能省下大量"生成了才发现不对"的时间。尤其是批量生成的时候,一个坏参数混进去,可能污染整批输出。把校验做在生成之前,是参数化管线的基本纪律。
3.4 批量生成与命名规范
批量生成时,命名混乱是另一个高频问题。我见过有人生成一百个 STEP 文件,全叫part.step、part(1).step,最后根本分不清哪个对应哪组参数。正确的做法是把关键参数编进文件名,比如:
def build_filename(params): return f"plate_L{params['length']}_W{params['width']}_T{params['thickness']}.step"这样文件名本身就是一份参数记录,回溯的时候一目了然。如果参数很多,可以只取几个关键维度,或者额外导出一份 CSV 记录每组参数和对应文件名。这个习惯在项目后期会救你很多次。
4. 从几何到 URDF:机器人场景下的额外功课
4.1 URDF 要的不只是形状
如果你做的是机器人方向,text-to-cad 的终点往往不是 STEP,而是 URDF。这里有个认知差:CAD 关心的是"零件长什么样",URDF 关心的是"机器人怎么动"。所以从几何转 URDF,必须补上三类信息。
第一类是连杆划分。一个机器人由若干连杆(link)和关节(joint)组成,你得决定哪些几何属于同一个连杆。这个划分不是几何问题,而是运动学问题——同一个连杆上的零件之间没有相对运动,不同连杆之间通过关节连接。
第二类是关节定义。关节类型(旋转、平移、固定)、旋转轴方向、运动范围,这些都要明确。URDF 里一个旋转关节大概长这样:
<joint name="joint1" type="revolute"> <parent link="base_link"/> <child link="arm_link"/> <origin xyz="0 0 0.1" rpy="0 0 0"/> <axis xyz="0 0 1"/> <limit lower="-1.57" upper="1.57" effort="10" velocity="1.0"/> </joint>origin决定关节在父连杆坐标系里的位置,axis决定绕哪个轴转,limit决定运动范围。这些参数在纯几何模型里是没有的,必须人工或按规则补上。
第三类是质量与惯性。URDF 的每个 link 都要有<inertial>块,包含质量和惯性张量。质量可以从几何体积乘密度估算,惯性张量则和形状、质量分布有关。很多从 CAD 直接导出的 URDF 之所以仿真时表现诡异,就是因为惯性参数是瞎填的。
4.2 导入仿真环境时的常见问题
URDF 做好之后,通常要导入机器人仿真环境验证。这一步的坑主要集中在坐标系和单位上。CAD 里常用的单位是毫米,而 URDF 和多数仿真环境默认用米。如果你直接把毫米数值填进 URDF,机器人会瞬间变成一千倍大小,看起来像一座山。
我的做法是在导出前统一做单位换算,把所有长度除以 1000。另外,CAD 里零件的坐标系原点和 URDF 里 link 的坐标系原点往往不一致,需要显式设置origin的偏移。这个偏移量算错,机器人装配起来就会错位,关节转起来像脱臼。
还有一个容易被忽略的点:碰撞体(collision)和视觉体(visual)可以分开定义。视觉体用精细网格,碰撞体用简化几何(比如包围盒或圆柱),这样仿真计算量会小很多。如果两者都用高精度网格,仿真会卡到怀疑人生。
4.3 一个实用的转换流程
我现在的流程大致是这样:先用参数化脚本生成每个连杆的 STEP,再写一个转换脚本,把 STEP 读进来、算质量属性、按预定义的关节关系拼成 URDF。质量属性这块,CadQuery 和 FreeCAD 都能直接算体积和重心,乘上密度就是质量,惯性张量也能一并导出。
# 伪代码示意:从几何算质量属性 volume = shape.Volume() # 单位取决于建模单位 mass = volume * density # 密度按材料给 com = shape.Center() # 重心 # 惯性张量通过几何内核的惯性计算接口获取这里要提醒一句:密度单位要和长度单位匹配。如果你建模用毫米,密度就得用"吨每立方毫米"这种别扭的单位,否则质量会差好几个数量级。我一般干脆在建模阶段就用米,省得来回换算。
5. 踩过的坑:那些文档里不会写的教训
5.1 单位不统一是万恶之源
前面提过单位问题,但它的严重程度值得单独拎出来说。我做过一个项目,几何用毫米建模,导出 STEP 没问题,但转 URDF 时忘了换算,结果仿真里机器人有几百米高,调了半天才发现是单位。更隐蔽的是混合单位:有的零件用毫米,有的用米,拼在一起表面看不出来,一装配就错位。
我的应对办法是在管线入口就锁定单位,所有参数、所有中间计算、所有输出统一用一种单位,并在代码里写死注释。宁可多写一行注释,也不要靠记忆。
5.2 浮点误差导致的"面不闭合"
参数化生成复杂几何时,偶尔会遇到导出的 STEP 在某些软件里打不开,提示"面不闭合"或"实体无效"。这通常是浮点误差累积导致的——两个本该重合的顶点差了 1e-9,几何内核就认为它们不是同一个点。
解决办法有两个方向:一是在生成时留出合理的容差,不要让特征之间刚好相切或刚好重合到极限;二是导出前做一次几何修复,很多内核都提供fix或heal接口。我一般会在导出前跑一遍有效性检查,把有问题的模型挑出来单独处理,而不是等下游报错。
5.3 参数化不等于"什么都能参数化"
刚接触这套方法时,我一度想把所有东西都参数化,结果发现有些几何特征天生不适合。比如自由曲面、复杂的过渡圆角、依赖大量人工审美的造型,硬要参数化反而会让脚本变得极其臃肿,维护成本超过手工建模。
后来我想明白了:参数化适合规则化、重复性高的结构,比如板、轴、支架、法兰、标准件。对于这些,脚本生成又快又稳。对于高度不规则的东西,老老实实手工建模,或者用脚本生成基础形状再手工微调,反而更高效。认清边界,比强行全能更重要。
5.4 版本与依赖管理
参数化脚本依赖几何库,而几何库的版本更新有时会改变 API 行为。我遇到过升级库之后,原本能跑的脚本报错,或者生成的几何微妙地变了。所以锁定依赖版本很重要,用虚拟环境或者依赖清单把版本固定下来。另外,脚本本身也要做版本管理,因为参数化模型的可复现性完全依赖脚本——脚本丢了或者改了,同样的参数就再也生成不出同样的模型。
6. 把 text-to-cad 用顺手的几个实践建议
6.1 从"半自动"开始,别追求全自动
很多人一上来就想做"输入一句话,输出完整模型"的全自动系统,结果卡在自然语言理解的模糊性上。我的建议是先做半自动:人负责把需求整理成结构化参数,程序负责从参数生成几何。这个阶段先把几何生成的稳定性和准确性做扎实,等这套跑顺了,再考虑往上加自然语言解析。
半自动的好处是可控。参数是人给的,出了问题能快速定位是参数错还是生成逻辑错。全自动的话,一句话进来,中间黑盒一大段,出错都不知道从哪查。
6.2 建立参数模板库
做久了你会发现,很多需求是重复的。今天要一块带孔板,明天要一块带槽板,结构大同小异。这时候建一个参数模板库就很有价值:把常见结构写成可复用的函数,新需求来了直接调,只改参数不改逻辑。
# 模板库示意 def plate_with_holes(length, width, thickness, holes): ... def shaft_with_steps(steps): ... def flange(outer_dia, inner_dia, bolt_count, bolt_circle_dia): ...模板库积累起来之后,生成新零件的速度会快一个量级。而且模板经过多次使用和验证,稳定性也比临时写的脚本高。
6.3 输出前一定要可视化检查
程序说"导出成功"不等于模型是对的。我养成的习惯是,每批生成之后,随机抽几个模型在查看器里打开看一眼。有时候参数算错了,模型形状会很奇怪,肉眼一看就知道。这一步花不了几分钟,但能挡住很多低级错误。
如果批量很大,可以写个脚本自动生成缩略图或者投影视图,快速扫一遍。自动化生成 + 人工抽检,是目前我觉得最稳的组合。
6.4 关于格式转换的取舍
STEP、URDF、G-code 之间的转换,不是无损的。STEP 转 URDF 会丢精度、要补质量属性;STEP 转 G-code 要经过切片或刀路规划,几何会被离散化。所以尽量在源头就把目标格式想清楚,需要什么就生成什么,减少中间转换环节。转换次数越多,出错和失真的机会越大。
如果确实需要多格式输出,建议以 STEP 作为"母版",其他格式都从 STEP 派生,而不是在派生格式之间互相转。这样至少保证几何源头是统一的。
6.5 文档和注释别省
参数化脚本最怕的是"三个月后自己都看不懂"。每个函数干什么、每个参数什么含义、单位是什么、有什么约束,都值得写清楚。我现在写这类脚本,函数头注释和参数说明是标配,关键计算步骤也会加注释。当时多写两行,后面省下的时间是以小时计的。
说到底,text-to-cad 这套东西的门槛不在某个高深算法,而在于把模糊需求翻译成精确参数、把精确参数稳定地变成几何、再把几何准确地交给下游这一整条链路的工程化。每一环都不难,难的是每一环都不出错、可复现、能维护。我自己的体会是,先把一个简单零件从文本参数到 STEP 的链路跑通,再逐步加复杂度,比一上来就搭大框架要靠谱得多。等你把这条链路走顺了,回头再看那些"AI 自动画图"的宣传,心里就有数了——真正值钱的从来不是那句描述,而是描述背后那套严谨的几何逻辑。