资讯动态

基于Python的text-to-cad实战:从自然语言到STEP/URDF几何文件生成

发布时间:2026/10/27 15:04:18 来源:尧图企业网站定制
1. 项目缘起与核心思路拆解第一次看到 text-to-cad 这个方向我的直觉是这东西要解决的是工程设计里最枯燥、最重复、也最容易出错的那一段——把脑子里的结构描述变成一份能被 CAD 软件识别、能被仿真工具加载、能被下游流程消费的几何文件。传统做法是打开 CAD 软件一根线一根线地画一个尺寸一个尺寸地标改一次需求就得重画一遍。text-to-cad 想干的事是让你用自然语言描述一个零件或一个装配体程序自动生成对应的几何模型并导出成 STEP、URDF 这类标准格式。这个项目的核心价值不在于“炫技”而在于把设计意图和几何表达之间的那层手工翻译给省掉。适合谁来参考三类人最值得看一是做机械设计但想批量生成标准件的工程师二是做机器人仿真需要频繁调整 URDF 模型的开发者三是想用 Python 把 CAD 流程自动化的技术爱好者。哪怕你之前没碰过 CAD 二次开发只要会写一点 Python就能跟着把整条链路跑通。我先把整体思路讲清楚。text-to-cad 本质上是一条流水线输入是文本输出是几何文件中间至少经过四个环节文本解析、参数提取、几何构建、格式导出。文本解析负责把“一个长 100 宽 50 高 20 的方块中间挖一个直径 10 的通孔”这种描述拆成结构化的指令参数提取把数字和单位识别出来转成程序能用的变量几何构建调用建模内核生成实体格式导出把实体写成 STEP 或 URDF。每个环节都有坑后面会逐个拆。为什么选 Python 作为主语言因为 Python 在 CAD 自动化这块的生态最成熟。你可以用 CadQuery 做脚本化建模用 build123d 做更现代的声明式建模用 trimesh 处理网格用 urdfpy 或 yourdfpy 处理 URDF。这些库都是纯 Python 或提供 Python 绑定安装简单文档齐全社区活跃。相比之下用 C 直接调 OpenCASCADE 虽然性能好但开发效率低不适合快速迭代。用 JavaScript 调 OpenSCAD 也行但 OpenSCAD 的表达能力有限复杂曲面和布尔运算容易翻车。另一个关键选型是几何内核。STEP 文件的生成依赖精确边界表示B-rep这需要 OpenCASCADE 这样的内核。CadQuery 和 build123d 都基于 OpenCASCADE所以它们导出的 STEP 是真正的实体模型不是网格近似。URDF 则不同它描述的是机器人的连杆和关节几何部分可以用基本形状box、cylinder、sphere也可以用网格文件STL、DAE。如果你的 text-to-cad 要同时支持 STEP 和 URDF那几何构建层就得抽象出两套输出路径一套走 B-rep 导出 STEP一套走基本形状或网格导出 URDF。我试过直接用正则表达式解析文本结果遇到“直径 10 的孔深 5”和“直径 10深 5 的孔”就乱了。后来改成先用分词加词性标注再按模式匹配提取参数稳定性提升很多。这个经验说明文本解析不能只靠正则得有点 NLP 的底子。但也不用上大模型规则加轻量级解析就够用毕竟工程描述的语言相对固定。提示如果你只是想快速验证想法可以先跳过复杂的 NLP用固定模板加参数占位符的方式。比如定义“长方体(长,宽,高)”这样的函数签名让用户按格式输入。等流程跑通了再逐步放开自然语言。2. 核心细节解析与实操要点2.1 文本解析从自然语言到结构化指令文本解析是整个项目的第一道关卡也是最容易低估的部分。工程描述里常见的表达有“一个直径 20 的圆盘厚度 5”、“在方块中心打一个 M6 的螺纹孔”、“生成一个长宽高分别为 100、50、30 的盒子”。这些句子里实体类型、尺寸参数、位置关系、特征操作混在一起需要一套清晰的解析策略。我的做法是分三步走。第一步实体识别。用关键词匹配找出“方块”、“圆柱”、“圆盘”、“孔”、“槽”这些词映射到内部的实体类型枚举。第二步参数提取。用正则匹配数字加单位的组合比如(\d(?:\.\d)?)\s*(mm|cm|m)?把数值和单位分离统一转成毫米。第三步关系解析。识别“中心”、“边缘”、“偏移”、“阵列”这些位置词确定特征相对于主体的位置。这里有个细节单位处理。工程图里默认单位是毫米但用户可能写“2 厘米”或“0.1 米”。我统一在解析阶段转成毫米后续所有计算都用毫米导出时再按需转换。这样避免中间环节单位混乱。另一个细节是数字格式中文用户可能写“直径 10”英文用户写“diameter 10”得同时支持中英文关键词。import re UNIT_MAP {mm: 1.0, cm: 10.0, m: 1000.0, : 1.0} def extract_dimensions(text): pattern r(\d(?:\.\d)?)\s*(mm|cm|m)? matches re.findall(pattern, text) dims [] for value, unit in matches: dims.append(float(value) * UNIT_MAP.get(unit, 1.0)) return dims上面这段代码是参数提取的简化版。实际用的时候你得结合上下文判断哪个数字对应哪个维度。比如“长 100 宽 50 高 20”数字顺序和维度顺序一致可以按序分配。但“宽 50 长 100 高 20”就得靠关键词定位。我的经验是先找维度关键词的位置再取最近的数字准确率比纯顺序高很多。注意不要试图用一套正则搞定所有情况。工程语言变体太多建议维护一个模式库每种实体类型对应一组解析规则解析失败时给出明确的错误提示而不是静默返回错误结果。2.2 几何构建CadQuery 与 build123d 的选择几何构建是核心中的核心。CadQuery 和 build123d 都是基于 OpenCASCADE 的 Python 建模库但设计哲学不同。CadQuery 用链式调用代码像流水线build123d 用上下文管理器代码更像传统 CAD 的操作逻辑。我两个都用过最后在 text-to-cad 项目里选了 build123d原因是它的代数模式更适合程序化生成。举个例子生成一个带孔的方块。CadQuery 的写法是import cadquery as cq result (cq.Workplane(XY) .box(100, 50, 20) .faces(Z) .workplane() .hole(10))build123d 的写法是from build123d import * with BuildPart() as part: Box(100, 50, 20) with Locations((0, 0, 10)): Hole(radius5)两种写法都能得到同样的结果。但 build123d 的Locations和BuildPart上下文让位置控制更直观尤其是多个特征叠加时代码可读性更好。另外 build123d 对装配体的支持更完善导出 URDF 时更方便。几何构建的难点不在单个特征而在特征之间的布尔运算和位置关系。比如“在方块顶面中心打一个孔再在孔周围阵列四个小孔”这涉及工作平面切换、阵列参数计算、布尔减运算。我的做法是先把主体建出来再逐个添加特征每个特征都明确指定参考面和位置。不要试图一步到位分步构建虽然代码长一点但调试容易得多。参数计算方面阵列的间距和角度需要根据输入动态算。比如“四个小孔均匀分布在直径 30 的圆周上”那每个孔的位置就是(15*cos(θ), 15*sin(θ), 0)θ 取 0、90、180、270 度。这些计算在 Python 里就是几行三角函数但必须确保单位一致、角度用弧度。提示build123d 的Hole默认是通孔如果要盲孔得用CounterBoreHole或手动做布尔减。导出 STEP 前务必检查实体是否有效用part.part.is_valid()验证无效实体导出的 STEP 会在下游软件里报错。2.3 格式导出STEP 与 URDF 的差异处理STEP 和 URDF 是两种完全不同的格式导出逻辑得分开处理。STEP 是精确几何记录的是 B-rep 数据适合 CAD 软件和 CAM 流程。URDF 是机器人描述格式记录的是连杆、关节、视觉和碰撞几何适合仿真环境。导出 STEP 相对简单build123d 和 CadQuery 都提供export_step方法。关键是确保实体是有效的并且坐标系正确。我习惯在导出前把模型移到原点避免下游软件打开时找不到模型。from build123d import export_step export_step(part.part, output.step)导出 URDF 就复杂一些。URDF 的几何部分可以用基本形状也可以用网格文件。如果用基本形状得把 build123d 的实体转成 URDF 的box、cylinder、sphere标签并指定尺寸。如果用网格得先把实体导出成 STL再在 URDF 里引用 STL 文件路径。from urdfpy import URDF import numpy as np # 假设有一个连杆几何是方块 link Link( namebase_link, visual[Visual(geometryBox(size[0.1, 0.05, 0.02]))], collision[Collision(geometryBox(size[0.1, 0.05, 0.02]))] )这里有个单位坑URDF 默认单位是米而 CAD 建模常用毫米。导出时必须把毫米转成米否则模型在仿真里会大一千倍。我踩过这个坑一个 100mm 的方块在 CoppeliaSim 里变成了 100 米整个场景都装不下。另一个坑是坐标系。URDF 的坐标系是右手系Z 轴向上和 CAD 的常见坐标系一致。但有些仿真环境用 Y 轴向上导入后需要旋转。这个在导出时不好处理最好在 URDF 里加一个固定的关节来调整姿态。注意URDF 不支持复杂的 B-rep 几何所有几何最终都要转成基本形状或网格。如果你的模型有复杂曲面导出 STL 时得控制网格精度太粗会失真太细文件巨大。我一般用 0.1mm 的线性偏差和 0.5 度的角度偏差平衡质量和体积。3. 实操过程与核心环节实现3.1 环境搭建与依赖安装先把环境搭起来。我推荐用 Python 3.10 或 3.11这两个版本对 build123d 和 CadQuery 的兼容性最好。Python 3.12 有些库还没跟上容易出问题。安装方式用 pip 就行不需要 conda除非你已经在用 conda 管理环境。python -m venv text2cad-env source text2cad-env/bin/activate # Linux/macOS # text2cad-env\Scripts\activate # Windows pip install build123d cadquery urdfpy numpybuild123d 的安装会自带 OpenCASCADE 的二进制依赖不用单独装。CadQuery 也类似。urdfpy 比较老如果装不上可以用 yourdfpy 替代API 略有不同但功能更全。安装完成后跑一个简单测试from build123d import Box, export_step part Box(10, 10, 10) export_step(part, test.step) print(STEP 导出成功)如果这一步报错大概率是 OpenCASCADE 的动态库没找到。Linux 下可能需要装libgl1和libglu1-mesaWindows 下一般没问题。提示如果你在 Windows 上遇到ImportError: DLL load failed检查是否装了 Visual C Redistributable。build123d 依赖的 OCP 库需要 VC 运行时。3.2 完整流水线代码实现下面把整条流水线串起来。我写了一个简化版支持从文本生成方块和圆柱并导出 STEP 和 URDF。import re from build123d import * from urdfpy import URDF, Link, Visual, Collision, Box as UrdfBox, Cylinder as UrdfCylinder UNIT_MAP {mm: 1.0, cm: 10.0, m: 1000.0, : 1.0} def parse_text(text): 解析文本返回实体类型和参数 text text.lower() dims [float(v) * UNIT_MAP.get(u, 1.0) for v, u in re.findall(r(\d(?:\.\d)?)\s*(mm|cm|m)?, text)] if 方块 in text or box in text or 立方 in text: return box, dims[:3] elif 圆柱 in text or cylinder in text or 圆盘 in text: return cylinder, dims[:2] else: raise ValueError(f无法识别的实体类型: {text}) def build_geometry(entity_type, params): 根据类型和参数构建几何 if entity_type box: length, width, height params return Box(length, width, height) elif entity_type cylinder: radius, height params return Cylinder(radius, height) else: raise ValueError(f不支持的实体类型: {entity_type}) def export_to_step(part, filename): 导出 STEP export_step(part, filename) print(fSTEP 已导出: {filename}) def export_to_urdf(part, filename, link_namebase_link): 导出 URDF几何用基本形状 # 获取包围盒尺寸 bb part.bounding_box() size [bb.size.X / 1000, bb.size.Y / 1000, bb.size.Z / 1000] # mm - m link Link( namelink_name, visual[Visual(geometryUrdfBox(sizesize))], collision[Collision(geometryUrdfBox(sizesize))] ) robot URDF(links[link], joints[]) robot.save(filename) print(fURDF 已导出: {filename}) # 主流程 if __name__ __main__: text 生成一个长100宽50高20的方块 entity_type, params parse_text(text) part build_geometry(entity_type, params) export_to_step(part, output.step) export_to_urdf(part, output.urdf)这段代码跑通后你会得到两个文件output.step和output.urdf。STEP 可以用 FreeCAD 或中望 CAD 打开URDF 可以导入 CoppeliaSim 或 RViz 查看。3.3 参数计算与位置控制实例实际项目中位置控制比尺寸计算更麻烦。比如“在方块顶面中心打一个直径 10 的通孔”得先确定顶面的位置再在顶面中心做布尔减。build123d 里可以用Locations指定位置用Hole做减运算。with BuildPart() as part: Box(100, 50, 20) with Locations((0, 0, 10)): # 顶面中心Z10 是高度的一半 Hole(radius5)如果孔不在中心比如“在顶面偏左 20 的位置打孔”那位置就是(-20, 0, 10)。这些偏移量得从文本里解析出来或者让用户用参数指定。阵列的计算稍微复杂。比如“在顶面均匀分布 4 个直径 5 的孔分布圆直径 30”那每个孔的位置是import math radius 15 # 分布圆半径 for i in range(4): angle math.radians(i * 90) x radius * math.cos(angle) y radius * math.sin(angle) with Locations((x, y, 10)): Hole(radius2.5)这种计算在 Python 里很直接但要注意角度用弧度位置用绝对坐标。如果方块本身不在原点还得加上方块的偏移量。提示build123d 的Locations是相对于当前工作平面的。如果你切换了工作平面位置坐标的含义会变。建议在添加特征前先用Plane.XY或Plane.XZ明确指定工作平面避免坐标混乱。4. 常见问题与排查技巧实录4.1 文本解析失败怎么办文本解析失败是最常见的问题。用户输入“一个直径 10 的孔”程序可能把“10”识别成半径而不是直径。我的做法是在解析规则里明确区分“直径”和“半径”遇到“直径”就除以 2遇到“半径”直接用。如果用户没写默认按直径处理因为工程描述里直径更常见。另一个问题是单位缺失。用户写“长 100”没写单位程序默认按毫米处理。但如果用户实际想表达厘米模型就小了十倍。我的经验是在输出前加一个确认步骤把解析结果和单位打印出来让用户确认。如果是批量处理就在配置文件里设一个默认单位所有无单位数字都按默认单位算。还有一种情况是描述模糊比如“打一个孔”。没给直径也没给位置。这时候程序应该报错提示用户补充参数而不是随便给个默认值。工程场景里错误的模型比没有模型更危险。4.2 STEP 导出后打不开或显示异常STEP 导出后打不开通常是实体无效。用part.is_valid()检查如果返回 False说明布尔运算出了问题。常见原因是两个实体没有正确相交或者减运算的工具体完全在主体外部。解决办法是检查位置和尺寸确保工具体和主体有重叠。另一个原因是坐标系。如果模型不在原点附近有些 CAD 软件打开时会显示空白因为视图没对准。导出前用part.move(Location((0, 0, 0)))把模型移到原点或者至少确保包围盒中心在原点附近。还有一种情况是 STEP 版本不兼容。build123d 默认导出 AP214 或 AP203老版本 CAD 软件可能只支持 AP203。如果遇到兼容问题可以在导出时指定版本export_step(part, output.step, unitUnit.MM)4.3 URDF 导入 CoppeliaSim 后模型比例不对这是单位问题。URDF 用米CAD 用毫米导出时忘了转换模型就大了一千倍。解决办法是在导出 URDF 前把所有尺寸除以 1000。我习惯在export_to_urdf函数里统一处理确保所有几何尺寸都转成米。另一个问题是 URDF 的关节坐标系。如果模型有多个连杆每个连杆的坐标系得和关节轴对齐。否则导入后关节运动方向不对。这个在简单模型里不明显但多连杆机器人就会暴露。建议在导出前用urdfpy的Robot类检查关节的origin和axis确保和设计一致。4.4 常见问题速查表问题现象可能原因排查方法解决方案文本解析返回空关键词未匹配打印分词结果扩充关键词库尺寸偏差十倍单位未转换检查单位映射表统一转毫米STEP 打不开实体无效is_valid()检查修复布尔运算URDF 模型巨大毫米未转米检查导出尺寸除以 1000孔位置偏移工作平面错误打印位置坐标明确指定平面阵列不均匀角度计算错误检查三角函数用弧度计算注意排查问题时先把中间结果打印出来。文本解析后打印实体类型和参数几何构建后打印包围盒尺寸导出后打印文件大小。这些信息能快速定位问题环节。4.5 独家避坑技巧第一个技巧用配置文件管理解析规则。把实体关键词、单位映射、默认参数都放在 YAML 或 JSON 里改规则不用改代码。这样扩展新实体类型时只加配置就行。第二个技巧给每个生成的模型加元数据。在 STEP 文件里写入作者、日期、描述在 URDF 里写入连杆名称和质量。这些信息在下游流程里很有用尤其是批量生成时能追溯每个模型的来源。第三个技巧批量处理时加日志。每处理一个文本记录输入、解析结果、导出路径、耗时。出问题时能快速定位是哪个输入导致的。我用logging模块日志级别设 INFO输出到文件和控制台。第四个技巧模型验证自动化。导出 STEP 后用cadquery或build123d重新导入检查实体数量和体积是否和预期一致。导出 URDF 后用urdfpy加载检查连杆和关节数量。这一步能拦住大部分低级错误。5. 扩展方向与个人体会这个项目跑通后能扩展的地方很多。比如接入大模型做更自由的文本解析用 LLM 把“一个带法兰的圆筒”拆成圆筒加法兰加螺栓孔。或者接入参数化模板库把常见零件齿轮、轴承、型材做成模板文本里只填参数。再或者做 Web 界面让用户在线输入文本、预览模型、下载文件。我自己在实际操作中的体会是text-to-cad 的难点不在建模而在“理解”。文本到结构化指令这一步决定了整个系统的上限。建模和导出都是确定性操作只要参数对结果就对。但文本解析充满歧义同一个描述可能有多种理解。所以我在项目里加了一个“确认”环节把解析结果展示给用户确认后再生成模型。这个环节看起来多余但能避免大量返工。最后分享一个小技巧如果你要处理大量文本先把它们按实体类型分类同类文本用同一套解析规则。这样比用一个通用解析器处理所有文本准确率高得多。分类可以用简单的关键词匹配也可以用文本聚类。分类后每类文本的解析规则可以单独优化整体准确率能提升不少。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑