text-to-cad实战:用Python和CadQuery实现文字转CAD模型

发布时间:2026/10/27 14:18:55
text-to-cad实战:用Python和CadQuery实现文字转CAD模型 1. 从一段文字到三维实体text-to-cad 到底在解决什么问题如果你做过机械设计、建筑建模或者机器人仿真一定经历过这样的场景脑子里已经想清楚了一个零件的形状但打开 CAD 软件之后光是画草图、约束尺寸、拉伸切除这一套流程就要花掉大半个小时。更别提改一个参数之后整个特征树跟着报错重新调整的时间比重新画还长。text-to-cad这个方向要干的事情就是让你用一段自然语言描述直接生成可用的 CAD 模型文件跳过手工建模的重复劳动。我最初接触这个方向是在做机器人仿真的时候。当时需要给一个移动底盘配一套支撑结构尺寸反复调整了七八轮每改一次就要在 CAD 里重新约束一遍草图。后来我尝试用 Python 脚本参数化生成模型虽然比手工快但每次改结构还是要改代码。再后来看到有人用大语言模型把文字描述转成 CAD 脚本我才意识到这条路可能真的能走通。text-to-cad的核心价值在于三个层面。第一层是降低建模门槛你不需要记住 SolidWorks 里某个特征命令藏在哪个菜单下只需要把需求说清楚。第二层是加速迭代参数化的模型改一个数字就能重新生成不用重新走一遍建模流程。第三层是打通上下游生成的模型可以直接导出成 STEP 格式给加工用也可以转成 URDF 给仿真用省掉了中间格式转换的折腾。这篇文章适合三类人看。一是做机械设计或工业设计的工程师想了解怎么用文字驱动建模来提效二是做机器人仿真的开发者需要快速生成 URDF 模型三是想入门参数化建模的 Python 开发者想找一个能落地的项目练手。我会从技术选型、实现路径、代码实操、踩坑经验几个角度把text-to-cad这件事讲透。2. 拆解 text-to-cad 的技术链路从文字到 STEP 文件中间发生了什么2.1 自然语言理解层把一个带孔的方块翻译成结构化参数大语言模型在这里扮演的角色不是直接画图而是把模糊的自然语言描述转成结构化的参数。比如你说帮我做一个 50 毫米长、30 毫米宽、20 毫米高的方块中间打一个直径 10 毫米的通孔模型需要提取出这些信息形状类型是长方体尺寸是 50×30×20特征是在中心位置有一个直径 10 的圆柱形切除。这一步的难点在于歧义消解。自然语言里中间可能指几何中心也可能指某个面的中心通孔可能沿 Z 轴贯穿也可能沿 X 轴。我的做法是在提示词里强制模型输出 JSON 格式的参数并且要求它把不确定的地方标注出来。比如上面那句话模型应该输出类似这样的结构{ shape: box, dimensions: {length: 50, width: 30, height: 20}, features: [ { type: hole, diameter: 10, position: center, axis: Z, through: true } ], uncertainties: [孔沿哪个轴贯穿未明确默认按Z轴处理] }这样做的好处是后续的几何生成代码只需要处理结构化数据不用再去解析自然语言。而且当模型对某个参数不确定时你可以选择让它追问用户或者按默认值处理并在输出里标注出来。2.2 几何内核层为什么选 CadQuery 而不是直接调 OpenSCAD生成 CAD 模型需要一个几何内核。常见的选择有 OpenSCAD、CadQuery、FreeCAD 的 Python API还有商业内核如 Parasolid 和 ACIS。我最终选了 CadQuery原因有几个。OpenSCAD 的语法很简单但它本质上是一个网格建模工具导出的 STL 是三角面片不是真正的 B-rep 实体。这意味着你没法在导出的模型上做精确的倒角、抽壳这些操作而且 STEP 导出也不支持。CadQuery 底层用的是 OpenCASCADE 内核生成的是真正的 B-rep 实体可以导出 STEP、IGES 这些工程格式也能做布尔运算和特征操作。FreeCAD 的 Python API 功能很全但它的 API 设计比较老旧文档也不够友好。CadQuery 的链式调用风格写起来很顺手比如cq.Workplane(XY).box(50, 30, 20).faces(Z).workplane().hole(10)这一行就完成了方块加通孔的操作可读性很好。还有一个实际考虑是部署便利性。CadQuery 可以通过 pip 直接安装依赖的 OpenCASCADE 库也有预编译的 wheel 包在 Windows 和 Linux 上都能跑。FreeCAD 的 Python 环境相对封闭想在自己的脚本里调用它的 API 需要额外配置。2.3 格式输出层STEP、STL、URDF 各自的使用场景生成的模型需要导出成什么格式取决于你的下游用途。STEP 是工程领域通用的三维模型交换格式保留了完整的 B-rep 信息可以直接导入 SolidWorks、UG、CATIA 这些软件继续编辑也可以发给加工厂做 CNC 编程。STL 是三角网格格式适合 3D 打印和快速预览但丢失了特征信息没法做参数化修改。URDF 是机器人仿真用的格式它描述的是刚体的运动学和动力学属性包括连杆的几何形状、质量、惯性矩阵、关节类型和运动范围。如果你要做机械臂仿真或者移动机器人仿真就需要把 CAD 模型转成 URDF。这里有个坑URDF 里的几何体通常是简化的碰撞体不是精确的 CAD 模型。所以我的做法是先用 CadQuery 生成精确模型导出 STEP再单独生成简化的碰撞体用于 URDF。格式用途是否保留特征典型工具STEP工程交换、CNC 加工是SolidWorks、FreeCADSTL3D 打印、快速预览否切片软件、MeshLabURDF机器人仿真否简化碰撞体Gazebo、CoppeliaSimDXF二维图纸、激光切割部分AutoCAD、LibreCAD3. 用 Python 搭一套能跑的文字转 CAD 流水线3.1 环境准备CadQuery 安装与常见依赖问题CadQuery 的安装说简单也简单说麻烦也麻烦。官方推荐用 conda 安装因为 OpenCASCADE 的依赖比较多conda 能自动处理。但我实测下来pip 安装cadquery在大多数情况下也能跑通前提是你的 Python 版本在 3.9 到 3.11 之间。Python 3.12 目前还有一些兼容性问题建议避开。pip install cadquery pip install ocp如果你在 Windows 上遇到OCP相关的 DLL 加载错误大概率是 Visual C 运行库没装全。去微软官网下载最新的 VC 运行库安装包装完之后重启终端再试。Linux 上如果报libGL相关的错误装一下libgl1-mesa-glx和libglu1-mesa就行。大语言模型的部分你可以用 OpenAI 的 API也可以用本地部署的开源模型。如果只是做原型验证用 API 最省事。本地部署的话推荐用 Ollama 跑 Llama 3 或者 Qwen 系列7B 到 14B 参数的模型在消费级显卡上就能跑生成结构化参数的任务对模型能力要求不算太高。3.2 提示词设计让模型稳定输出可解析的 JSON提示词的质量直接决定了整个流水线能不能跑通。我试过很多版本最后稳定下来的提示词结构是这样的SYSTEM_PROMPT 你是一个CAD参数提取助手。用户会用自然语言描述一个三维零件你需要提取出结构化的参数。 输出格式必须是合法的JSON包含以下字段 - shape: 基础形状类型可选值有 box, cylinder, sphere, cone, torus - dimensions: 尺寸参数字典根据形状类型不同而不同 - features: 特征列表每个特征包含 type, position, dimensions 等字段 - uncertainties: 不确定的参数列表用自然语言描述 规则 1. 所有尺寸单位默认为毫米 2. 如果用户没有指定位置默认放在原点 3. 如果用户描述有歧义在 uncertainties 里说明不要自己猜测 4. 只输出JSON不要输出任何其他文字 这个提示词的关键在于约束输出格式和强制标注不确定性。我踩过的坑是早期版本没有要求模型标注不确定性结果它经常自己脑补一些用户没说的参数比如用户说打个孔它默认直径 10 毫米但用户可能想要 5 毫米。后来加了 uncertainties 字段之后模型会主动说孔直径未指定默认按10毫米处理这样用户就知道哪里需要确认。还有一个技巧是给示例。在提示词里放两三个输入输出的例子模型输出的稳定性会明显提升。比如用户输入一个直径20毫米、高50毫米的圆柱 输出{shape: cylinder, dimensions: {diameter: 20, height: 50}, features: [], uncertainties: []}3.3 几何生成把 JSON 参数翻译成 CadQuery 代码拿到结构化参数之后下一步是生成 CadQuery 代码。这里有两种做法一种是直接根据参数动态调用 CadQuery 的 API另一种是让大语言模型生成 CadQuery 代码字符串再执行。我推荐第一种因为更可控不容易出现代码注入或者语法错误。import cadquery as cq import json def build_model(params): shape params[shape] dims params[dimensions] if shape box: result cq.Workplane(XY).box( dims[length], dims[width], dims[height] ) elif shape cylinder: result cq.Workplane(XY).cylinder( dims[height], dims[diameter] / 2 ) else: raise ValueError(f不支持的形状类型: {shape}) for feature in params.get(features, []): if feature[type] hole: result result.faces(Z).workplane().hole(feature[diameter]) elif feature[type] fillet: result result.edges().fillet(feature[radius]) return result这段代码的逻辑很直白先根据 shape 字段创建基础几何体然后遍历 features 列表逐个添加特征。faces(Z)表示选择 Z 轴正方向最上面的那个面workplane()把工作平面移到那个面上hole()在那个面上打孔。这是 CadQuery 的链式调用风格写起来很直观。3.4 导出与验证STEP 文件生成后的检查清单模型生成之后导出 STEP 只需要一行cq.exporters.export(result, output.step)但导出成功不代表模型正确。我每次生成之后都会做几个检查。第一是体积检查用result.val().Volume()拿到体积跟预期值对比。比如一个 50×30×20 的方块体积应该是 30000 立方毫米如果打了一个直径 10 的通孔体积应该减去 π×5²×20 ≈ 1570得到 28430 左右。如果体积差太多说明特征没加上或者加错了位置。第二是包围盒检查用result.val().BoundingBox()拿到模型的边界框确认尺寸跟预期一致。第三是可视化检查CadQuery 可以导出 SVG 或者 PNG 预览图我通常会在 Jupyter Notebook 里直接显示出来看一眼。from cadquery import exporters exporters.export(result, preview.svg, opt{projectionDir: (1, 1, 1)})这个 SVG 预览虽然简单但能快速看出模型的大致形状对不对。如果形状明显不对就不用浪费时间导 STEP 了。4. 从 CAD 到 URDF机器人仿真场景下的模型转换实操4.1 URDF 文件结构link、joint 和 collision 的关系URDF 描述的是一个树形结构根节点是 base_link每个子节点通过 joint 连接到父节点。每个 link 包含三部分信息visual视觉外观、collision碰撞体、inertial惯性参数。visual 决定你在仿真里看到的样子collision 决定物理引擎怎么计算碰撞inertial 决定动力学仿真时的质量和转动惯量。这里最容易踩的坑是visual 和 collision 用同一个模型。如果你直接把精确的 CAD 模型同时用作 visual 和 collision物理引擎在计算碰撞时会非常慢因为精确模型的三角面片数量可能几十万碰撞检测的计算量会爆炸。正确的做法是 visual 用精确模型collision 用简化模型比如用包围盒或者几个基本几何体拼出来。link namebase_link visual geometry mesh filenamebase_visual.stl scale0.001 0.001 0.001/ /geometry /visual collision geometry box size0.05 0.03 0.02/ /geometry /collision inertial mass value0.5/ inertia ixx0.0001 ixy0 ixz0 iyy0.0001 iyz0 izz0.0001/ /inertial /link注意scale参数。CAD 模型通常用毫米做单位但 URDF 和大多数仿真环境用米所以需要把 STL 的尺寸缩小 1000 倍。这个坑我踩过好几次模型导进去之后大了 1000 倍整个场景都被撑爆了。4.2 用 Python 脚本自动生成 URDF 文件手工写 URDF 文件很痛苦尤其是当你有十几个 link 的时候。用 Python 脚本自动生成会轻松很多。我通常用urdf_parser_py这个库来构建 URDF 结构或者直接用一个模板引擎生成 XML 字符串。from string import Template LINK_TEMPLATE Template( link name$name visual geometry mesh filename$mesh scale0.001 0.001 0.001/ /geometry /visual collision geometry box size$size/ /geometry /collision inertial mass value$mass/ inertia ixx$ixx ixy0 ixz0 iyy$iyy iyz0 izz$izz/ /inertial /link ) def generate_link(name, mesh, size, mass): return LINK_TEMPLATE.substitute( namename, meshmesh, sizesize, massmass, ixxmass*0.001, iyymass*0.001, izzmass*0.001 )惯性矩阵的计算是个麻烦事。对于简单几何体可以用公式算。比如长方体绕中心轴的转动惯量是m*(w²h²)/12。但对于复杂形状手工算就不现实了。我的做法是用 CadQuery 拿到模型的体积和重心然后用近似公式估算。如果精度要求高可以用 FreeCAD 的惯性计算功能或者用 MeshLab 的惯性矩计算工具。4.3 导入 CoppeliaSim 的注意事项CoppeliaSim以前叫 V-REP是常用的机器人仿真软件导入 URDF 的流程是Menu bar - Add - Robot - Import robot from URDF。但导入之后经常发现模型位置不对、关节方向反了、或者碰撞体不生效。第一个常见问题是坐标系不一致。URDF 用的是右手坐标系Z 轴向上但 CoppeliaSim 默认也是 Z 轴向上所以理论上是一致的。但如果你从 SolidWorks 导出 URDFSolidWorks 默认是 Y 轴向上导出的模型就会躺倒。解决办法是在导出时设置正确的坐标系或者在 CoppeliaSim 里手动旋转。第二个问题是关节类型不匹配。URDF 支持 revolute、prismatic、continuous、fixed 等关节类型CoppeliaSim 也支持这些但映射关系需要确认。比如 URDF 的 continuous 关节在 CoppeliaSim 里对应的是 revolute joint 并且没有角度限制。如果导入后发现关节转不动检查一下 joint 的 limit 设置。第三个问题是mesh 文件路径。URDF 里引用的 STL 文件路径可以是相对路径也可以是绝对路径。相对路径是相对于 URDF 文件所在目录。如果你把 URDF 和 STL 放在不同目录导入时就会找不到文件。我的习惯是把所有相关文件放在同一个目录下用相对路径引用。5. 实际跑下来容易翻车的几个地方5.1 大语言模型幻觉出不存在参数的处理策略大语言模型在提取参数时有时候会编造一些用户没说的细节。比如你说做一个支架它可能会自动加上厚度 5 毫米、圆角半径 2 毫米这些你没提的参数。这在某些场景下是好事相当于模型帮你做了合理默认但在精度要求高的场景下就是灾难。我的处理策略是分级处理不确定性。在提示词里要求模型把参数分成三类明确指定的、合理推断的、完全不确定的。明确指定的直接用合理推断的标注出来但可以用完全不确定的必须追问用户。比如{ specified: {length: 50, width: 30}, inferred: {height: 20, hole_diameter: 10}, uncertain: [孔的位置, 是否需要倒角] }这样你在生成模型之前就能看到哪些参数是模型猜的如果猜得不对可以及时调整。5.2 复杂特征组合时的布尔运算失败CadQuery 底层用的 OpenCASCADE 内核在做布尔运算时偶尔会失败。尤其是当两个特征的面几乎重合或者相切的时候内核可能算不出正确的结果直接抛异常或者生成一个空模型。我遇到最多的情况是孔打在了边缘上。比如一个 50 毫米宽的方块你打一个直径 10 毫米的孔孔中心距离边缘 4 毫米那孔就会切到边缘形成一个缺口。这种操作在几何上是合法的但 OpenCASCADE 有时候处理不好。解决办法是尽量避免让特征面重合留一点余量。如果实在避不开可以尝试用clean()方法清理一下模型或者换一种建模顺序。还有一个技巧是分步导出。如果一次布尔运算失败可以先把基础形状导出来再单独做特征操作最后用fuse()合并。虽然麻烦一点但成功率更高。5.3 单位制混乱导致的尺寸偏差单位问题是 CAD 领域的老大难。CadQuery 默认用毫米URDF 默认用米STL 文件本身不指定单位不同软件导入时可能按不同单位解释。我踩过最坑的一次是生成的模型在 CadQuery 里看是 50 毫米导出 STL 之后导入 CoppeliaSim 变成了 50 米整个场景里就看见一个巨大的方块。解决办法是在流水线的每个环节都显式指定单位。CadQuery 生成时用毫米导出 STL 时在文件名里标注单位比如base_50mm.stl生成 URDF 时统一转换成米。在 URDF 的 mesh 标签里用scale0.001 0.001 0.001把毫米转成米。这样虽然麻烦但能避免大部分单位问题。环节默认单位建议做法CadQuery 建模毫米保持默认文件名标注STL 导出无单位文件名标注 mmURDF 引用米scale 设为 0.001CoppeliaSim 导入米检查模型尺寸5.4 生成模型的几何有效性校验方法不是所有生成的模型都是有效的。有些模型看起来没问题但实际上有自相交的面、零厚度的壁、或者非流形的边。这些模型导入仿真软件后可能导致碰撞检测异常导入 CAM 软件后可能无法生成加工路径。我常用的校验方法有三种。第一是体积和包围盒检查前面已经说过。第二是STL 网格检查用trimesh库加载 STL 文件检查是否有退化三角形、重复顶点、非流形边。第三是STEP 有效性检查用 OpenCASCADE 的BRepCheck_Analyzer检查 B-rep 是否有效。import trimesh mesh trimesh.load(output.stl) print(f顶点数: {len(mesh.vertices)}) print(f面片数: {len(mesh.faces)}) print(f是否水密: {mesh.is_watertight}) print(f体积: {mesh.volume})is_watertight为 True 表示网格是封闭的没有孔洞。对于要用于 3D 打印的模型这个必须是 True。对于只用于视觉展示的模型可以放宽要求。6. 这套流水线还能怎么扩展6.1 批量生成从 Excel 参数表到几十个模型如果你需要生成一系列尺寸不同但结构相同的零件比如不同规格的法兰盘可以做一个批量生成脚本。把参数写在 Excel 或者 CSV 里每行一组参数脚本自动读取并生成对应的模型文件。import pandas as pd df pd.read_csv(flange_params.csv) for index, row in df.iterrows(): params { shape: cylinder, dimensions: {diameter: row[outer_diameter], height: row[thickness]}, features: [ {type: hole, diameter: row[bolt_hole_diameter], position: pattern} ] } model build_model(params) cq.exporters.export(model, fflange_{row[spec]}.step)这个思路在标准件库建设上特别有用。你只需要定义好参数表和模板就能批量生成几百个规格的模型比手工建模快几个数量级。6.2 反向工程从 STEP 文件反推参数化描述有时候你拿到一个 STEP 文件想修改它的参数但不知道原始建模步骤。这时候可以用 Python 读取 STEP 文件提取出几何特征再反推成参数化描述。CadQuery 可以导入 STEP 文件model cq.importers.importStep(existing_part.step)导入之后你可以遍历模型的 faces、edges、vertices分析它的几何特征。比如统计有多少个圆柱面每个圆柱面的半径和轴线位置就能推断出有多少个孔、孔径多大。这个方向我还在摸索目前能做到提取基本尺寸但复杂的特征树还原还有难度。6.3 和参数优化结合自动搜索最优结构尺寸把 text-to-cad 和优化算法结合可以做结构优化。比如你要设计一个支架在满足强度要求的前提下尽量轻。你可以定义一个参数空间板厚、孔径、圆角半径等用优化算法搜索最优参数组合每次迭代自动生成模型并做有限元分析根据分析结果调整参数。这个流程的瓶颈在有限元分析的速度。每次生成模型之后跑一次 FEA 可能要几十秒到几分钟如果参数空间很大搜索时间会很长。我的做法是先用简化的梁单元模型做快速估算缩小搜索范围再用精确的实体模型做验证。6.4 接入本地大模型离线环境下的部署方案如果你的工作环境不能联网或者你对数据隐私有要求可以用本地部署的大模型替代 API。Ollama 是目前最方便的本地模型运行工具装好之后拉一个模型就能用ollama pull qwen2.5:7b ollama serve然后在 Python 里通过 HTTP 接口调用import requests response requests.post(http://localhost:11434/api/generate, json{ model: qwen2.5:7b, prompt: SYSTEM_PROMPT \n\n用户输入 user_input, stream: False }) result response.json()[response]7B 参数的模型在参数提取任务上表现已经不错了如果你的描述比较复杂可以用 14B 或者 32B 的模型。显存不够的话可以用量化版本4-bit 量化的 7B 模型大概需要 4GB 显存大多数消费级显卡都能跑。7. 几个我反复用到的实操技巧第一个技巧是用 Jupyter Notebook 做交互式调试。CadQuery 和 Jupyter 配合得很好你可以一个 cell 一个 cell 地执行建模步骤每一步都可视化出来看效果。这比写完整脚本再跑要高效得多尤其是调试复杂特征的时候。第二个技巧是给生成的模型加元数据。STEP 文件支持自定义属性你可以把生成时的参数、时间戳、模型版本写进去。这样以后拿到一个 STEP 文件能知道它是怎么生成的方便追溯和复现。result.val().wrapped.SetProp(description, json.dumps(params))第三个技巧是建立常用形状的模板库。法兰、支架、齿轮这些常见零件你可以把它们的参数化建模代码封装成函数下次直接调用。我自己的模板库里积累了二十多个常用形状覆盖了大部分日常需求新项目直接拼装就行。第四个技巧是用 Git 管理生成的模型文件。STEP 和 STL 都是二进制文件Git 默认不好处理。可以配置 Git LFS 来管理大文件或者只提交生成脚本和参数文件模型文件按需生成。我倾向于后者因为脚本和参数是文本版本对比很方便模型文件重新生成也就几秒钟的事。这套流水线我从最初的原型到现在稳定使用大概迭代了半年多。最大的感受是text-to-cad 不是要替代 CAD 工程师而是把工程师从重复劳动里解放出来让你有更多时间花在真正需要创造力的地方。参数提取的准确率、几何生成的稳定性、格式转换的兼容性这些细节决定了这套方案能不能真正落地。如果你也在做类似的事情希望这些经验能帮你少走一些弯路。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询