
1. 从一句话到三维实体text-to-cad 到底在解决什么问题第一次听到 text-to-cad 这个词很多人脑子里浮现的画面大概是对着电脑说一句给我画个法兰盘屏幕上就自动长出一个带螺栓孔的三维模型。这个想象不算离谱但真正落地到工程实践里它要解决的问题比语音画图要具体得多也有意思得多。text-to-cad 的核心命题是把自然语言描述转换成结构化的 CAD 几何数据。这里的CAD 几何数据不是一张截图也不是一段渲染视频而是能被下游工具真正读取、编辑、装配、仿真的东西——比如 STEP 文件、URDF 描述、参数化脚本。换句话说它要产出的是可计算的几何而不是看起来像几何的图片。这件事为什么值得做因为传统 CAD 工作流里从想法到模型之间横着一道很高的门槛。你得会软件操作、懂建模逻辑、熟悉约束关系一个简单的支架可能就要画半小时。而在很多场景下——比如机器人仿真、批量零件生成、教学演示、快速原型验证——人们需要的往往不是精雕细琢的工业级模型而是一个结构正确、尺寸合理、能直接导入下游工具的基础几何体。text-to-cad 瞄准的正是这个空档。从热搜词也能看出这个方向的真实需求分布一边是CAD、STEP、URDF、agent这些偏技术侧的关键词另一边是cad制图初学入门、cad安装教程、cad转pdf这类偏使用侧的长尾词。这说明关注 text-to-cad 的人群其实分两类一类是想用 AI 自动生成模型的开发者另一类是被 CAD 操作门槛卡住的普通用户。这两类人的诉求不一样但都指向同一个痛点——建模这件事能不能更省事一点。这篇文章会围绕 text-to-cad 的完整链路展开它背后的技术构成是什么、为什么输出格式要选 STEP 和 URDF、一个可用的 agent 该怎么搭、实际跑起来会遇到哪些坑、以及怎么把它接到机器人仿真这类真实场景里。内容会尽量往实操靠能抄的配置和代码我都会给出来同时把为什么这么做讲清楚避免只给步骤不给理由。提示text-to-cad 目前还不是一句话生成工业级零件的成熟方案它更擅长生成结构清晰、参数明确的基础几何体。把它当成建模加速器而不是建模替代品预期会更合理。2. 拆解 text-to-cad 的技术链路从文本到几何要过几道关2.1 文本理解层把口语描述翻译成结构化参数用户输入的自然语言往往是模糊的。比如做一个 100 毫米长、带四个孔的板子这句话里隐含了大量没说的信息板子多宽多厚孔多大孔在什么位置孔是通孔还是沉头孔所以 text-to-cad 的第一步不是画图而是参数抽取与补全。常见做法是让大语言模型把自然语言解析成一个结构化的 JSON 或字典字段包括几何类型、尺寸参数、特征列表。举个实际会用的中间表示{ shape: plate, length: 100, width: 60, thickness: 5, features: [ {type: hole, diameter: 6, count: 4, pattern: corner, offset: 10} ], unit: mm }这个中间层非常关键。它把人话和几何内核解耦了——语言模型只负责理解意图几何生成只负责按参数建模。这样做的好处是当用户说孔再大一点时你只需要改diameter字段而不用重新训练整个模型。这里有个经验不要让语言模型直接输出 CAD 脚本代码。早期我试过让模型直接生成 CadQuery 或 OpenSCAD 代码结果经常出现语法错误、变量未定义、单位混乱的问题调试成本极高。改成先出 JSON再由确定性代码生成几何之后稳定性提升非常明显。语言模型擅长的是理解意图不是写严谨的几何代码把这两件事分开各司其职。2.2 几何生成层参数化建模内核的选择拿到结构化参数后就需要一个几何内核把它变成真正的三维实体。目前主流的几条路线方案特点适合场景CadQueryPython 原生基于 OCCT代码即模型参数化零件、批量生成OpenSCAD脚本化语法简单社区大教学、简单几何、快速验证build123dCadQuery 的继任者API 更现代新项目、复杂特征FreeCAD 脚本功能全可调用完整工作台需要工程图、装配的场景我个人的选择是CadQuery 或 build123d原因是它们底层都跑在 OpenCASCADEOCCT上而 OCCT 是工业界公认的几何内核导出的 STEP 文件兼容性最好。OpenSCAD 虽然上手快但它本质上是网格建模CSG导出的模型在需要精确 B-rep 的场合会吃亏。用 CadQuery 生成上面那块带孔板子代码大概是这样import cadquery as cq result ( cq.Workplane(XY) .box(100, 60, 5) .faces(Z) .workplane() .rect(80, 40, forConstructionTrue) .vertices() .hole(6) ) cq.exporters.export(result, plate.step)这段代码的逻辑很直白先建一个 100×60×5 的长方体选中顶面在顶面上画一个 80×40 的构造矩形取它的四个顶点打孔。整个过程是确定性的只要参数对结果就一定对。2.3 格式输出层为什么 STEP 和 URDF 是绕不开的两个格式生成几何只是中间产物真正交付给下游的格式才是决定这套流程能不能用起来的关键。热搜词里STEP和URDF同时出现其实点出了两个完全不同的下游场景。STEP是 CAD 领域的通用交换格式几乎所有的机械设计软件都能读。它的优势是保留精确的 B-rep 几何信息尺寸、圆角、孔位都不会丢。如果你生成的模型要拿去加工、出图、做装配STEP 是首选。URDF则是机器人领域的描述格式它描述的不是一个零件长什么样而是一个机器人由哪些连杆和关节组成、它们怎么连接、各自的惯性参数是多少。URDF 里引用的几何体通常是 STL 或 DAE 网格而不是 STEP。这就带来一个实际问题text-to-cad 生成的模型怎么变成 URDF 能用的东西答案是中间要做一次格式转换。STEP 转 STL 可以用 CadQuery 或 FreeCAD 的命令行工具完成# STEP 转 STL import cadquery as cq shape cq.importers.importStep(plate.step) cq.exporters.export(shape, plate.stl, tolerance0.01)tolerance参数控制网格精度值越小网格越细、文件越大。做仿真时一般取 0.01 到 0.001 之间太粗会导致碰撞检测不准太细会让仿真变慢。2.4 Agent 编排层把上面三层串成一个能对话的系统单次生成不难难的是让它变成一个能连续对话、能改参数、能记住上下文的 agent。热搜词里agent、agent架构、agent记忆、agent tool反复出现说明大家真正关心的是怎么把这套流程工程化。一个最小可用的 text-to-cad agent 通常包含这几个部分意图解析工具调用语言模型把用户输入转成结构化参数几何生成工具接收参数调用 CadQuery 生成模型格式转换工具按需导出 STEP / STL / URDF记忆模块保存当前模型的参数状态支持把长度改成 120这类增量修改校验工具检查生成结果是否合法比如孔是否超出板面记忆模块是很多人会忽略的一环。没有它用户每次说改一下都得重新描述整个模型体验很差。实现上不需要多复杂一个字典存当前参数就够了关键是每次修改后要更新这个字典。3. 手把手搭一个能跑通的 text-to-cad 最小系统3.1 环境准备依赖装不对后面全是坑先把环境搭起来。我推荐用 Python 3.10 或 3.11太新的版本有时候 OCCT 的 wheel 还没跟上。python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate pip install cadquery pip install openai # 或你用的其他模型 SDKCadQuery 的安装是第一个坎。它依赖 OCCT在部分系统上需要预编译的二进制包。如果pip install cadquery报错优先检查是不是 Python 版本太新或太旧。实测 3.10 和 3.11 最稳。注意不要试图从源码编译 OCCT除非你有充足的时间和编译经验。直接用官方 wheel 是最省事的路。3.2 意图解析给语言模型一个明确的输出契约这一步的目标是让模型稳定输出 JSON。关键在于 prompt 里要把 schema 写死并给出示例。我常用的模板大致是这样SYSTEM_PROMPT 你是一个 CAD 参数解析器。 用户会用自然语言描述一个零件你需要输出 JSON。 JSON 必须包含以下字段 - shape: 几何类型可选 plate / cylinder / box / bracket - dimensions: 尺寸字典单位统一为 mm - features: 特征列表每个特征包含 type 和参数 只输出 JSON不要输出任何解释。如果用户没提到某个尺寸用合理默认值补全。 这里有个细节默认值的选择会直接影响可用性。比如用户说做个板子没说厚度你默认 5mm 还是 10mm我一般按常见工程习惯给板厚 5mm、孔径 6mm、边距 10mm。这些值不一定对但至少能生成一个看起来合理的模型用户可以在此基础上改。解析完拿到 JSON 后一定要做一次字段校验。语言模型偶尔会漏字段或给错类型比如把count写成字符串4。加一层校验能避免后面几何生成时崩溃def validate_params(params): required [shape, dimensions] for key in required: if key not in params: raise ValueError(f缺少必要字段: {key}) # 数值字段强制转 float for k, v in params[dimensions].items(): params[dimensions][k] float(v) return params3.3 几何生成把参数映射到 CadQuery 调用有了干净参数生成几何就是纯工程活了。核心思路是写一个分发函数根据shape字段调用不同的建模逻辑def build_model(params): shape params[shape] dims params[dimensions] if shape plate: model cq.Workplane(XY).box( dims[length], dims[width], dims[thickness] ) # 处理孔特征 for feat in params.get(features, []): if feat[type] hole: model model.faces(Z).workplane().rect( dims[length] - 2 * feat[offset], dims[width] - 2 * feat[offset], forConstructionTrue ).vertices().hole(feat[diameter]) return model elif shape cylinder: return cq.Workplane(XY).circle(dims[radius]).extrude(dims[height]) else: raise ValueError(f不支持的形状: {shape})这段代码看起来简单但里面藏着几个容易踩的坑。第一faces(Z)选的是 Z 方向最高的面如果模型方向不对选面会失败。第二打孔时构造矩形的尺寸要减去边距否则孔会跑到板子外面。第三多个特征叠加时顺序很重要先打孔再倒角和先倒角再打孔结果可能不一样。3.4 导出与验证生成完不算完得确认它真的能用模型生成后导出只是第一步验证才是保证质量的关键。我一般会做三层检查几何合法性用 CadQuery 的isValid()检查实体是否有效尺寸合理性检查包围盒尺寸是否和输入参数一致下游兼容性把导出的 STEP 重新导入一次确认没有丢失特征def export_and_verify(model, path): if not model.val().isValid(): raise ValueError(生成的几何体无效) bb model.val().BoundingBox() print(f包围盒: {bb.xlen:.2f} x {bb.ylen:.2f} x {bb.zlen:.2f}) cq.exporters.export(model, path) # 回读验证 reimported cq.importers.importStep(path) print(回读成功实体数量:, len(reimported.val().Solids()))回读这一步很多人会跳过但它能抓到不少问题。比如某些复杂特征在导出时可能被简化回读后实体数量对不上就说明有问题。4. 把生成的模型接进机器人仿真URDF 转换的实操细节4.1 URDF 到底需要什么不只是几何很多人以为 URDF 就是把模型文件塞进去其实远不止。URDF 描述的是一个运动学树它需要link连杆每个刚体包含视觉几何、碰撞几何、惯性参数joint关节连接两个 link定义运动类型旋转/平移、轴向、限位坐标系关系每个 link 的原点在哪joint 的轴朝哪text-to-cad 生成的通常只是单个零件的几何要变成 URDF你得手动或自动补上关节和惯性信息。惯性参数尤其容易被忽略但它在动力学仿真里至关重要。一个简单的长方体惯性张量可以按公式算def box_inertia(mass, x, y, z): # 长方体绕质心的惯性张量对角项 Ixx mass * (y**2 z**2) / 12 Iyy mass * (x**2 z**2) / 12 Izz mass * (x**2 y**2) / 12 return Ixx, Iyy, Izz如果惯性参数随便填仿真里机器人可能会抖得厉害或者关节力矩异常。这是我在做仿真时踩过的真实坑——模型看着没问题一跑动力学就发散最后发现是惯性张量填错了。4.2 STEP 转 STL 再进 URDF 的完整流程URDF 里引用的几何一般是 STL 或 DAE。完整流程是# 1. 生成 STEP model build_model(params) cq.exporters.export(model, part.step) # 2. 转 STL注意精度 shape cq.importers.importStep(part.step) cq.exporters.export(shape, part.stl, tolerance0.001, angularTolerance0.1) # 3. 写 URDF urdf_template ?xml version1.0? robot namegenerated link namebase_link visual geometry mesh filenamepart.stl/ /geometry /visual collision geometry mesh filenamepart.stl/ /geometry /collision inertial mass value{mass}/ inertia ixx{ixx} ixy0 ixz0 iyy{iyy} iyz0 izz{izz}/ /inertial /link /robot tolerance和angularTolerance这两个参数要一起调。只调tolerance的话曲面上的三角形可能还是很大。实测tolerance0.001、angularTolerance0.1是个不错的平衡点既能保证曲面光滑文件又不会太大。4.3 导入仿真环境时的常见问题模型进仿真环境后最常见的问题有三个第一个是单位问题。CAD 里默认是毫米但很多仿真环境默认用米。一个 100mm 的板子直接导进去会变成 100 米机器人瞬间变成巨型建筑。解决办法是在导出 STL 时统一缩放或者在 URDF 里加scale属性mesh filenamepart.stl scale0.001 0.001 0.001/第二个是坐标系原点问题。CadQuery 生成的模型原点通常在几何中心或某个角点但 URDF 里 link 的原点决定了旋转中心。如果原点不对关节转起来会绕着奇怪的位置转。建议在建模时就明确原点位置或者在 URDF 里用origin标签调整。第三个是碰撞几何太复杂。直接用视觉网格做碰撞检测计算量会很大。实践中通常用简化几何比如包围盒、圆柱做碰撞体视觉用精细网格。这个在 URDF 里就是collision和visual分开写。5. 实测中那些文档不会告诉你的坑5.1 语言模型的想当然参数幻觉怎么防语言模型有个毛病你问它要参数它会给但给的不一定对。比如你说做个能装下手机的盒子它可能给你一个 150×80×10 的板子——尺寸看着合理但完全不是盒子。这种参数幻觉在 text-to-cad 里特别常见。我的应对办法是加一层语义校验。生成参数后用规则检查关键约束。比如盒子必须有六个面或者至少是个封闭体板子的厚度不能大于长度。这些规则不复杂但能挡掉大部分离谱结果def semantic_check(params): dims params[dimensions] if params[shape] plate: if dims[thickness] dims[length] / 2: raise ValueError(板厚不合理可能参数解析错误) if params[shape] box: if height not in dims: raise ValueError(盒子缺少高度参数)另一个办法是在 prompt 里明确要求模型如果不确定输出 null 而不是猜。这样至少能知道哪些参数是模型编的可以追问用户。5.2 几何内核的边界情况什么时候会失败CadQuery 虽然强大但有些操作会失败。我遇到过的典型情况孔打在了边缘上如果孔的位置加上半径超过了板面边界hole()会报错或生成无效几何倒角半径过大倒角半径超过相邻边长度的一半操作会失败布尔运算的共面问题两个面完全重合时做布尔运算结果可能不稳定这些问题的共同点是参数在数学上合法但在几何上不可行。解决办法是在生成前做几何可行性检查比如打孔前先算一下孔边缘到板边的距离def check_hole_fit(plate_len, plate_wid, offset, hole_dia): margin offset - hole_dia / 2 if margin 1: # 至少留 1mm 边距 raise ValueError(f孔边距过小: {margin:.2f}mm)5.3 批量生成时的性能问题如果你要批量生成几十上百个模型会发现 CadQuery 每次调用都要初始化几何内核速度很慢。优化思路有两个一是复用 Workplane 对象避免重复初始化。二是并行化用多进程跑生成任务from multiprocessing import Pool def generate_one(params): model build_model(params) path foutput/{params[id]}.step cq.exporters.export(model, path) return path with Pool(4) as p: results p.map(generate_one, param_list)实测 4 进程能把批量生成速度提升 3 倍左右。注意不要开太多进程OCCT 本身会吃内存进程太多反而会拖慢。5.4 版本兼容性STEP 不是万能交换格式虽然 STEP 兼容性很好但不同软件对 STEP 的支持程度不一样。我遇到过 CadQuery 导出的 STEP 在某些软件里打开后圆角丢失、孔位偏移的情况。这通常是因为 STEP 有不同的协议版本AP203、AP214、AP242不同软件默认读的版本不同。CadQuery 默认导出的是 AP214兼容性较好。如果下游软件读不了可以试试显式指定cq.exporters.export(model, part.step, opt{write_pcurves: False})write_pcurves关掉后文件会更干净兼容性通常更好但会丢失一些参数化曲线信息。这个取舍要看下游用途。6. 这套方案还能往哪些方向延伸text-to-cad 目前能做到的是结构清晰的基础几何体生成但它的延伸空间很大。一个方向是参数化模板库。与其让模型每次从零生成不如预置一批常用零件模板法兰、支架、齿轮坯模型只负责填参数。这样稳定性和速度都会好很多。热搜词里盘扣cad插件、cad插件这类词其实反映的就是用户对现成模板的需求。另一个方向是多轮对话式修改。现在的实现大多是一次性生成但真实使用中用户会不断调整。把孔改成 8 个厚度加到 10mm整体放大 1.5 倍——这些增量修改需要 agent 有状态记忆和参数追踪能力。实现上不难关键是设计好参数版本管理。还有一个方向是和仿真流程深度集成。生成模型只是第一步后面还有装配、运动学验证、动力学仿真。如果 text-to-cad 能直接输出带关节和惯性参数的完整 URDF甚至能自动生成仿真场景那价值会大很多。这也是URDF、agent这些词频繁出现在热搜里的原因——大家要的不是孤立的模型而是能直接跑起来的完整方案。我自己在实际项目里的体会是text-to-cad 的价值不在于替代建模而在于消除重复劳动。那些结构固定、只是尺寸不同的零件用这套流程生成比手动画快得多而且不会出错。但真正需要创意和复杂曲面的设计还是得靠人。把 AI 放在它擅长的位置比指望它什么都能干要靠谱得多。最后分享一个小技巧如果你要长期用这套流程建议把每次生成的参数和结果都存下来形成一个参数-模型数据集。积累多了之后你会发现很多需求其实是重复的直接查历史记录比重新生成快得多。这个习惯我坚持了半年现在常用零件的生成基本是秒级响应。