
1. 从一句话到三维模型text-to-cad 到底在解决什么问题第一次听到 “text-to-cad” 这个词很多人脑子里冒出来的画面大概是对着电脑敲一句“给我画个法兰盘”然后屏幕上就自动长出一个可以旋转的 3D 模型。这个想象不算离谱但也不完全准确。text-to-cad 本质上是一套把自然语言描述转换成结构化 CAD 数据的流程它的输出通常不是一张截图而是 STEP、URDF 这类可以被下游工具继续消费的工程文件。换句话说它解决的不是“画图”这件事而是“把人的意图翻译成机器能读的几何语言”这件事。我最早接触这个方向是因为手头有一批重复性极高的零件建模需求几十个规格相近的支架、连接件每次都要在 CAD 软件里重复拉伸、打孔、倒角。人工做不是不行但太耗时间而且容易在参数上出错。后来我尝试用 Python 脚本驱动建模再往后就自然走到了“能不能直接用文字描述生成模型”这一步。实测下来text-to-cad 目前最适合的场景是参数化程度高、结构相对规整的零件比如法兰、支架、齿轮毛坯、简单的机械臂连杆。你要是想让它一句话生成一辆完整的汽车那还是先等等。这篇文章面向三类人一是做机械设计、想提升建模效率的工程师二是做机器人仿真、需要批量生成 URDF 模型的开发者三是懂一点 Python、想把手伸进 CAD 领域的程序员。我会把整个流程拆开讲清楚——从文本解析、参数提取到几何构建、STEP 导出再到 URDF 组装和常见坑的排查。所有代码和思路都是我实际跑过、踩过坑之后整理出来的你可以直接抄作业也可以按自己的需求改。提示text-to-cad 不是一个现成的“一键神器”它更像一套需要你自己组装的流水线。理解每一环的原理比找到一个现成工具更重要。2. 整体方案设计与技术选型思路2.1 为什么选择 Python 作为主控语言做 text-to-cad第一步要决定用什么语言来串联整个流程。我选 Python理由很直接CAD 领域的开源几何内核基本都有 Python 绑定自然语言处理生态也成熟两者能在同一个进程里跑通。具体来说Python 的优势体现在三个层面。第一几何构建层。CadQuery和build123d这两个库都基于 OpenCASCADE 内核能用代码描述实体、布尔运算、倒角、抽壳最后导出 STEP。它们的 API 设计接近“写代码就是在建模”比直接调 OCCT 的 C 接口友好太多。第二文本解析层。无论是用正则提取尺寸还是用大模型做意图识别Python 都有现成的库。第三胶水层。STEP 导出之后要转 URDF、要批量处理文件、要跟仿真环境对接Python 的脚本能力刚好合适。有人会问为什么不直接用 CAD 软件自带的脚本接口比如某些软件的宏命令我的经验是那些接口通常绑定在特定软件上迁移性差而且很多不支持无头运行。你没法在服务器上批量跑。Python 方案虽然前期要搭环境但一旦跑通批量化、自动化、持续集成都很顺。2.2 STEP 与 URDF 的分工一个管几何一个管运动学很多人搞不清 STEP 和 URDF 的关系这里必须说清楚。STEP 是几何交换格式它描述的是“这个零件长什么样”——面、边、实体、尺寸。URDF 是机器人描述格式它描述的是“这些零件怎么组装、怎么运动”——连杆、关节、坐标系、惯性矩阵。两者不是替代关系而是上下游关系。在 text-to-cad 流程里我的做法是先用几何内核生成每个零件的实体导出 STEP 作为几何真值然后根据文本里描述的运动关系把 STEP 对应的几何信息组装成 URDF 的 link 和 joint。STEP 保证几何精度URDF 保证仿真可用。如果你只做静态展示STEP 就够了如果你要做机器人仿真、运动学分析URDF 是必须的。注意URDF 本身不存储复杂几何它通常引用 STL 或 DAE 网格文件。所以从 STEP 到 URDF 之间往往还要做一步网格导出。这个转换会损失精度但对仿真来说通常可以接受。2.3 文本解析策略规则优先模型兜底文本转 CAD 最容易被低估的环节是文本解析。很多人一上来就想用大模型直接输出建模代码结果发现稳定性很差——同样的描述两次生成的代码可能不一样参数还可能飘。我的策略是规则优先模型兜底。对于结构化的描述比如“直径 50 毫米、厚度 10 毫米、中心孔直径 20 毫米的法兰盘”用正则表达式提取数字和单位就够了稳定且可预测。对于模糊描述比如“一个差不多手掌大小的支架”才交给语言模型去推断大致尺寸范围。这样既保证了常见情况的可靠性又保留了对模糊输入的处理能力。具体实现上我会先定义一套参数模板每个模板对应一种零件类型模板里列出必需的参数名和单位。解析时先尝试用正则匹配这些参数匹配不全再调用模型补全。这套组合拳打下来实测解析成功率比纯模型方案高不少。2.4 几何构建方案CadQuery 与 build123d 的取舍几何构建这块我主要用 CadQuery偶尔用 build123d。两者的选择取决于项目复杂度。CadQuery 的链式 API 写起来很顺适合快速构建规则零件build123d 的上下文管理器风格更接近传统 CAD 的操作逻辑适合构建有复杂装配关系的模型。举个例子画一个带中心孔的法兰盘CadQuery 的写法是先创建一个圆柱再创建一个圆柱作为孔然后做布尔减运算最后导出。整个过程不到十行代码。build123d 的写法类似但语法结构不同。我建议新手先从 CadQuery 入手文档全、例子多、社区活跃。等你需要更精细的装配控制时再考虑 build123d。3. 核心细节解析与实操要点3.1 参数提取单位换算与容错处理参数提取看起来简单实际上坑很多。第一个坑是单位。用户可能说“5 厘米”也可能说“50mm”还可能只说“50”。如果不做统一后面建模尺寸会乱套。我的做法是定义一个单位映射表把所有长度统一换算成毫米因为 STEP 和大多数 CAD 内核默认用毫米。第二个坑是数字格式。中文用户可能写“直径五十”也可能写“直径 50”还可能写“φ50”。正则要覆盖这些变体。第三个坑是缺省值。如果用户没说厚度你得有个合理的默认值或者明确报错让用户补充。我倾向于对关键参数报错对次要参数给默认值并在输出里标注哪些是推断的。import re UNIT_MAP { mm: 1.0, 毫米: 1.0, cm: 10.0, 厘米: 10.0, m: 1000.0, 米: 1000.0, in: 25.4, 英寸: 25.4, } def extract_dimension(text, keyword): pattern rf{keyword}\s*[为是:]?\s*(\d\.?\d*)\s*(mm|cm|m|in|毫米|厘米|米|英寸)? match re.search(pattern, text, re.IGNORECASE) if not match: return None value float(match.group(1)) unit match.group(2) or mm return value * UNIT_MAP.get(unit, 1.0)这段代码是我实际用的简化版。注意keyword可以是“直径”“外径”“厚度”等调用时分别提取。实测下来覆盖八成以上的常见描述没问题。3.2 几何构建布尔运算的顺序很关键用代码建几何模型布尔运算的顺序直接影响结果。我踩过的一个坑是先倒角再打孔结果倒角把孔边缘也处理了导致孔口形状不对。正确的顺序应该是先做主体、再打孔、最后倒角。这个顺序跟传统 CAD 建模的逻辑一致但在代码里容易被忽略因为代码写起来是一行一行往下走的。另一个要点是坐标系。CadQuery 默认在 XY 平面建草图沿 Z 轴拉伸。如果你要建的零件方向不对可以在导出前做旋转但更好的做法是在建模时就选对工作平面。我习惯把主要轴向对齐 Z 轴这样后续组装 URDF 时坐标系转换少一些。import cadquery as cq def make_flange(outer_d, inner_d, thickness): result ( cq.Workplane(XY) .circle(outer_d / 2) .extrude(thickness) .faces(Z) .workplane() .circle(inner_d / 2) .cutThruAll() ) return result这段代码建了一个法兰盘外圆拉伸成柱体然后在顶面画内圆贯穿切除。逻辑清晰参数直观。你可以把outer_d、inner_d、thickness换成从文本提取的值就完成了从文字到几何的第一步。3.3 STEP 导出精度与兼容性导出 STEP 时有两个参数值得注意精度和版本。精度太低曲面会变成多边形精度太高文件体积暴涨。对于大多数机械零件我用的线性精度是 0.01 毫米角度精度是 0.1 度。这个设置下文件大小和几何质量比较平衡。版本方面AP214 兼容性最好大多数 CAD 软件都能读。AP203 更老但更通用。如果你不确定下游用什么软件选 AP214 通常没错。导出代码如下cq.exporters.export(result, flange.step, cq.exporters.ExportTypes.STEP)一行搞定。但要注意导出前确保模型是有效的实体不是壳体或线框。如果布尔运算出了问题导出的 STEP 可能是空的或者破损的。我习惯在导出前加一步result.val().isValid()检查。3.4 URDF 组装link 与 joint 的映射逻辑从 STEP 到 URDF核心工作是定义 link 和 joint。每个零件对应一个 linklink 的视觉几何用 STL 网格表示碰撞几何可以简化。joint 描述两个 link 之间的运动关系比如旋转关节、平移关节、固定关节。我的做法是在文本描述里识别运动关系词比如“绕 Z 轴旋转”对应 revolute joint“固定连接”对应 fixed joint。然后根据零件之间的相对位置计算 joint 的原点和轴向。这一步需要一些空间想象力但一旦模板建好后续就是填参数的事。提示URDF 的惯性矩阵如果随便填仿真时会出现奇怪的抖动。建议用几何近似计算或者用工具自动生成。别直接抄网上的默认值。4. 完整实操流程从文本到可仿真模型4.1 环境搭建Python 与几何库安装先把环境搭起来。我用的 Python 版本是 3.10太新的版本有时候几何库还没适配。安装 CadQuery 最省事的方式是用 conda因为它的依赖里有 OCCT 的二进制包pip 装有时候会编译失败。conda create -n text2cad python3.10 conda activate text2cad conda install -c conda-forge cadquery pip install numpy trimesh urdfpytrimesh用来做网格转换urdfpy用来读写 URDF。如果你要用语言模型做文本解析再装对应的客户端库。这套环境我在 Ubuntu 和 Windows 上都跑过conda 方案最稳。4.2 文本解析实战一个法兰盘的完整解析假设输入是“创建一个法兰盘外径 80 毫米内径 30 毫米厚度 12 毫米材料钢。” 解析流程分三步识别零件类型、提取尺寸参数、提取材料信息。零件类型用关键词匹配“法兰”对应法兰模板。尺寸参数用前面的extract_dimension函数逐个提取。材料信息暂时不影响几何但可以存进元数据后续导出时写入 STEP 的属性里。text 创建一个法兰盘外径 80 毫米内径 30 毫米厚度 12 毫米材料钢 outer_d extract_dimension(text, 外径) inner_d extract_dimension(text, 内径) thickness extract_dimension(text, 厚度) print(outer_d, inner_d, thickness) # 80.0 30.0 12.0实测这段能正确输出。如果用户写“外径 8 厘米”也会被换算成 80 毫米。这就是单位映射表的作用。4.3 几何生成与 STEP 导出实操拿到参数后调用前面的make_flange函数生成实体然后导出 STEP。我通常会把中间结果也存下来方便排查问题。flange make_flange(outer_d, inner_d, thickness) if flange.val().isValid(): cq.exporters.export(flange, flange.step) print(STEP 导出成功) else: print(几何无效检查参数)跑完这一步你会得到一个flange.step文件用任意 CAD 软件都能打开。我试过用 FreeCAD 和在线 STEP 查看器打开几何都正确。4.4 从 STEP 到 URDF网格转换与关节定义URDF 需要网格文件所以先把 STEP 转成 STL。CadQuery 可以直接导出 STLcq.exporters.export(flange, flange.stl, cq.exporters.ExportTypes.STL)然后写 URDF。一个最简单的单 link URDF 长这样robot nameflange_robot link nameflange_link visual geometry mesh filenameflange.stl/ /geometry /visual collision geometry mesh filenameflange.stl/ /geometry /collision inertial mass value0.5/ inertia ixx0.001 ixy0 ixz0 iyy0.001 iyz0 izz0.001/ /inertial /link /robot如果是多零件装配就加多个 link 和对应的 joint。joint 的 origin 要根据零件相对位置计算。这部分我建议先用简单模型验证跑通了再上复杂装配。4.5 批量生成循环处理多个描述text-to-cad 的真正价值在批量。你可以把多个描述放在一个列表里循环解析、建模、导出。我做过一个测试二十个不同规格的法兰盘从解析到导出 STEP 总共不到三十秒。人工建模的话一个可能就要几分钟。descriptions [ 法兰盘外径 80内径 30厚度 12, 法兰盘外径 100内径 40厚度 15, # ... ] for i, desc in enumerate(descriptions): od extract_dimension(desc, 外径) id_ extract_dimension(desc, 内径) th extract_dimension(desc, 厚度) part make_flange(od, id_, th) cq.exporters.export(part, fflange_{i}.step)这段代码可以直接跑。注意文件名要唯一否则会覆盖。5. 常见问题与排查技巧实录5.1 几何无效布尔运算失败的几种原因最常见的问题是布尔运算失败导出的 STEP 是空的。原因通常有三个一是两个实体没有相交做减运算时找不到交集二是参数导致几何退化比如内径大于外径三是精度设置不当导致面与面之间出现微小缝隙。排查方法先检查参数是否合理再检查布尔运算的两个实体是否真的重叠。我习惯在布尔运算前打印两个实体的包围盒确认它们有交集。如果还不行调低精度或者换一种建模顺序试试。5.2 单位混乱尺寸差十倍甚至千倍单位问题是新手最容易踩的坑。用户说“5”你以为是毫米结果人家想说的是厘米。解决办法是在解析阶段就统一单位并且在输出里明确标注最终尺寸。我还会在解析失败时给出提示让用户补充单位。另一个隐蔽的单位问题是角度。URDF 里的旋转用弧度但用户可能说“旋转 90 度”。转换时要乘pi/180。这个错误不会报错但仿真结果会完全不对。5.3 URDF 导入仿真环境报错常见原因速查报错信息可能原因解决办法mesh file not foundSTL 路径不对用绝对路径或确认相对路径基准inertia matrix not positive definite惯性矩阵填错用几何近似重新计算joint axis is zero vector关节轴向未定义指定非零轴向如 0 0 1link has no inertial缺少惯性信息补充 inertial 标签XML parse error标签未闭合检查 XML 语法这张表是我从多次调试中总结的覆盖了大部分常见报错。遇到新问题先看报错信息里的关键词再对照排查。5.4 性能优化批量处理时的内存管理批量生成时如果模型很多内存会涨。原因是 CadQuery 的实体对象不会自动释放。我的做法是每处理完一个模型就显式删除对象并调用垃圾回收。import gc del part gc.collect()另外导出 STEP 时可以用低精度模式加快速度最后再对关键零件用高精度重新导出。这个策略在批量场景下很有效。5.5 文本解析的边界情况模糊描述怎么处理用户可能说“一个大一点的法兰盘”没有具体数字。这时候规则解析就失效了需要模型兜底。我的做法是维护一个尺寸范围表比如“小”对应 20-50 毫米“大”对应 100-200 毫米然后取中间值。同时输出里标注“尺寸为推断值”提醒用户确认。如果连零件类型都不明确比如“做个连接件”那就只能报错让用户补充信息。text-to-cad 不是万能的明确输入才能有明确输出。6. 工具链扩展与进阶方向6.1 与仿真环境的对接要点生成的 URDF 最终要导入仿真环境。导入前有几个检查点网格文件路径是否正确、惯性参数是否合理、关节限位是否设置。我习惯先在简单的可视化工具里加载一遍确认模型能正常显示再导入复杂的仿真环境。如果仿真环境对网格格式有要求比如只支持 DAE那就用trimesh做转换。转换时注意材质和纹理虽然机械零件通常不需要但有些环境会检查。6.2 参数化模板库的积累思路text-to-cad 的效率取决于模板库的丰富程度。我建议从自己最常做的零件类型开始每做一个就存一个模板。模板包括参数定义、建模函数、默认值、校验规则。积累到十几个模板就能覆盖大部分日常需求。模板的命名要规范比如flange_template、bracket_template参数名要统一比如都用outer_d、thickness。这样解析层和建模层能解耦换零件类型时只需要换模板。6.3 从单零件到装配体复杂度管理单零件跑通后自然会想搞装配体。装配体的难点在于零件之间的约束关系和坐标系对齐。我的建议是先用固定关节把所有零件拼起来确认位置正确再逐步替换成运动关节。每加一个关节就验证一次别一次性全加上去。装配体的 URDF 文件会很长建议用脚本生成别手写。脚本里把每个 link 和 joint 的参数化改一个尺寸就能重新生成整个装配体。这才是 text-to-cad 在装配体层面的真正价值。提示装配体的碰撞检测很吃性能碰撞几何尽量用简单形状代替复杂网格。视觉几何可以精细碰撞几何要精简。7. 我在实际项目中的几点体会这套流程我跑了大概半年最大的体会是text-to-cad 的瓶颈不在建模而在文本解析的鲁棒性。建模部分有成熟的库照着文档写就行文本解析要面对千奇百怪的输入需要不断积累规则和边界情况。我的做法是每次遇到解析失败就把那个案例加进测试集慢慢把覆盖率提上去。另一个体会是别追求一步到位。先做单零件再做简单装配最后做复杂装配。每步都验证每步都留退路。我见过有人一上来就想搞整机生成结果卡在坐标系对齐上好几天。分解问题逐个击破才是工程化的做法。最后分享一个小技巧生成的 STEP 文件建议用零件类型加时间戳命名比如flange_20250101_120000.step。这样批量生成时不会覆盖回溯也方便。配合版本管理工具整个设计过程都能追踪。这个习惯看起来小但在实际项目里能省很多事。