☰
text-to-cad:自然语言生成STEP与URDF的实战指南
2026/10/8 5:13:31 网站建设 项目流程

1. 项目缘起与核心思路拆解

第一次听到 text-to-cad 这个词,我的直觉是:终于有人把大语言模型和传统 CAD 工作流之间的那堵墙凿开了一个口子。过去几年,参数化建模、脚本化建模已经不算新鲜事,OpenSCAD、CadQuery、build123d 这些工具让“用代码画图”成为可能,但门槛依然摆在那里——你得懂 Python,得熟悉几何内核的 API,还得理解 B-rep 的构造逻辑。text-to-cad 想做的事情更激进:你用人话描述一个零件,它帮你生成可用的 CAD 文件,直接输出 STEP 或者 URDF,中间不需要你手写一行建模代码。

这个项目的核心价值在于把“自然语言”翻译成“几何体”。它解决的不是渲染问题,也不是简单的二维绘图,而是实打实的三维实体建模。输出格式选 STEP 和 URDF 这两个,本身就说明了定位:STEP 是工业界通用的三维数据交换格式,几乎所有主流 CAD 软件都能读;URDF 则是机器人描述格式,用来定义机器人的连杆、关节和运动学结构。换句话说,这个工具瞄准的是机械设计、机器人仿真、快速原型这几个场景。

适合谁来用?我梳理了一下,大概三类人最受益。第一类是机械工程师,手头有大量重复性的标准件建模需求,比如法兰、支架、齿轮毛坯,用自然语言批量生成比手动拉伸切除快得多。第二类是机器人方向的开发者和学生,需要快速搭建 URDF 模型导入 CoppeliaSim 或者 Gazebo 做仿真,以前要么手动写 URDF 写到吐,要么用 SolidWorks 导出再转换,现在一句话就能生成基础结构。第三类是做 AI 应用落地的开发者,想在自己的产品里集成“文字生成三维模型”的能力,text-to-cad 提供了一个可参考的技术路径。

从技术选型上看,这个项目大概率走的是“LLM 生成建模脚本 + 几何内核执行”的路线。为什么这么说?因为直接让神经网络输出 STEP 文件的顶点和面片数据,目前既不现实也不可靠。STEP 文件背后是精确的 B-rep 表示,涉及曲面求交、拓扑缝合、公差处理,这些操作必须交给成熟的几何内核(比如 OpenCASCADE)来完成。所以合理的架构是:LLM 负责理解自然语言,生成结构化的建模指令或者 Python 脚本,然后调用 CadQuery、build123d 这类基于 OpenCASCADE 的库来执行,最后导出 STEP 或 URDF。这样做的好处是生成结果可控、可验证、可修改,而不是一个无法编辑的黑盒网格。

提示:如果你之前只用过 GUI 类 CAD 软件,从没接触过脚本化建模,建议先花半小时了解一下 CadQuery 的基本语法。不需要精通,但至少要知道 Workplane、Sketch、extrude 这些概念对应的是什么操作,后面理解 text-to-cad 的生成逻辑会顺畅很多。

2. 核心技术点深度解析与实操要点

2.1 自然语言到建模指令的映射逻辑

text-to-cad 最核心的难点,也是整个项目最有技术含量的部分,就是怎么把“一个边长 50 毫米、中心带直径 20 毫米通孔的方形法兰盘”这句话,变成几何内核能执行的建模步骤。这里面涉及几个层次的转换。

第一层是实体识别。LLM 需要从句子里提取出关键几何要素:形状类型(方形、圆形、圆柱)、尺寸参数(边长、直径、厚度)、位置关系(中心、同心、偏移)、特征操作(通孔、倒角、阵列)。这一步现在的大模型做得已经不错了,但前提是提示词工程要到位。我实测下来,如果直接把用户输入扔给模型,让它自由发挥生成 Python 代码,出错率很高,经常出现变量未定义、单位混乱、坐标系搞反的问题。

第二层是建模顺序规划。一个零件往往不是一步就能建出来的,需要拆解成合理的特征树。比如上面那个法兰盘,正确的顺序是先拉伸一个方形底板,再在中心打孔,如果有倒角或者圆角,还要考虑放在最后做还是中间做。顺序错了,轻则模型不对,重则几何内核直接报错。我见过不少失败案例,就是因为把孔的特征放在了底板拉伸之前,导致布尔运算找不到目标实体。

第三层是代码生成与校验。生成的 Python 脚本需要符合目标库的 API 规范,变量命名要一致,单位要统一(通常用毫米),还要处理异常情况。比较稳妥的做法是让 LLM 输出结构化的 JSON 描述,再由一个确定性的代码生成器把它翻译成 CadQuery 脚本。这样虽然多了一步,但稳定性提升非常明显。JSON 里可以定义shape_type、dimensions、features这些字段,代码生成器根据字段值调用对应的建模函数,避免 LLM 直接写代码时“自由发挥”带来的不确定性。

2.2 STEP 与 URDF 导出的关键差异

很多人以为 STEP 和 URDF 只是格式不同,导出逻辑应该差不多。实际上这两个格式的用途和数据结构差异很大,处理方式完全不一样。

STEP 文件描述的是静态的几何形状,关注的是实体本身的精确边界表示。导出 STEP 时,你只需要把建好的三维实体传给导出函数,指定单位(毫米或米),剩下的交给几何内核。CadQuery 里一行cq.exporters.export(result, "output.step")就能搞定。但要注意,STEP 有 AP203、AP214、AP242 等多个协议版本,不同 CAD 软件对版本的兼容性不一样。我一般默认用 AP214,兼容性最好,SolidWorks、中望 CAD、Fusion 360 都能正常读取。

URDF 就复杂多了。它描述的不是一个静态零件,而是一个机器人系统,包含连杆(link)和关节(joint)的层级结构。每个 link 有自己的视觉几何、碰撞几何、惯性参数,每个 joint 有类型(旋转、平移、固定)、轴向、限位。text-to-cad 如果要从自然语言生成 URDF,那用户输入的就不能只是“一个法兰盘”,而应该是“一个两轮差速机器人,底盘长 300 宽 200 高 100,两个驱动轮直径 80 安装在底盘两侧”。这已经超出了单纯 CAD 建模的范畴,进入了机器人结构设计的领域。

实际操作中,我建议把 URDF 生成分成两步走。第一步先用 text-to-cad 生成各个零件的 STEP 模型,比如底盘、轮子、支架。第二步再根据机器人的拓扑结构,把这些模型组装成 URDF。URDF 里的视觉几何可以直接引用 STL 或者 DAE 文件,碰撞几何可以用简化后的包围盒或者圆柱体代替,这样既保证了仿真时的视觉效果,又不会让碰撞检测的计算量爆炸。

对比维度STEPURDF
描述对象单个静态实体机器人系统(连杆+关节)
核心数据B-rep 边界表示运动学树结构
单位约定毫米或米米(ROS 生态默认)
常用场景机械设计、加工制造机器人仿真、运动规划
导出难点曲面缝合、公差处理惯性参数计算、关节轴定义
推荐库CadQuery、build123durdfpy、yourdfpy

2.3 Python 环境搭建与依赖管理

text-to-cad 这类项目对 Python 环境有一定要求,不是随便装个 Python 就能跑起来的。我踩过的坑主要集中在几何内核的依赖上。OpenCASCADE 是一个庞大的 C++ 库,Python 绑定(pythonocc-core 或者 OCP)的安装在不同操作系统上差异很大。

Windows 环境下,最省心的方式是用 conda 安装。conda install -c conda-forge cadquery这一条命令就能把 CadQuery 和它依赖的 OCP 全部装好,不需要手动编译。如果你用 pip 装,大概率会在编译 OCP 的时候卡住,因为 Windows 上缺少合适的 C++ 编译工具链。我试过用 pip 装 cadquery,折腾了两个小时最后还是换回了 conda。

Linux 环境下选择多一些。Ubuntu 22.04 上可以用 apt 装 python3-opencascade,也可以用 conda。如果你在 Docker 里跑,建议直接基于condaforge/miniforge3镜像来构建,把环境依赖写进 Dockerfile,这样换机器的时候一键重建,不用重新踩坑。

macOS 上我建议用 Homebrew 装 miniforge,然后 conda 创建虚拟环境。Apple Silicon 芯片的兼容性现在基本没问题了,OCP 有 arm64 的预编译包,直接装就行。

Python 版本选择上,我推荐 3.10 或者 3.11。3.12 虽然也能跑,但部分几何库的 wheel 包还没跟上,可能会遇到编译问题。3.8 太老了,一些新的类型注解语法不支持,写代码的时候会别扭。

# 创建虚拟环境(以 conda 为例) conda create -n text2cad python=3.11 conda activate text2cad # 安装核心依赖 conda install -c conda-forge cadquery ocp pip install openai pydantic urdfpy # 验证安装 python -c "import cadquery as cq; print(cq.__version__)"

注意:如果你所在的环境无法访问 conda 的默认源,可以配置国内镜像源加速下载。但不要混用 pip 和 conda 安装同一个包,否则容易出现版本冲突,尤其是 OCP 这种底层库。

3. 完整实操流程与核心环节实现

3.1 从一句话到一个 STEP 文件

我拿一个实际例子来走一遍完整流程。假设用户输入是:“生成一个长 100 毫米、宽 60 毫米、高 20 毫米的长方体,四个角做半径 5 毫米的圆角,中心打一个直径 10 毫米的通孔。”

第一步,构造提示词,让 LLM 输出结构化的 JSON 描述。提示词里要明确告诉模型:所有尺寸单位是毫米,坐标系原点放在长方体底面中心,Z 轴朝上。这些约定如果不提前说清楚,模型每次生成的坐标系都不一样,后续组装的时候会非常痛苦。

import json from openai import OpenAI client = OpenAI() system_prompt = """你是一个 CAD 建模助手。用户会用自然语言描述一个零件, 你需要输出 JSON 格式的建模指令。JSON 结构如下: { "shape_type": "box" | "cylinder" | "sphere", "dimensions": {"length": float, "width": float, "height": float}, "features": [ {"type": "fillet", "radius": float, "edges": "all_vertical"}, {"type": "hole", "diameter": float, "position": [x, y], "through": true} ], "unit": "mm" } 所有尺寸单位为毫米。坐标系原点位于底面中心,Z 轴朝上。""" user_input = "生成一个长100毫米、宽60毫米、高20毫米的长方体,四个角做半径5毫米的圆角,中心打一个直径10毫米的通孔。" response = client.chat.completions.create( model="gpt-4o", messages=[ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_input} ], response_format={"type": "json_object"} ) spec = json.loads(response.choices[0].message.content) print(json.dumps(spec, indent=2, ensure_ascii=False))

跑完这段,我得到的 JSON 大概是这样的:

{ "shape_type": "box", "dimensions": {"length": 100, "width": 60, "height": 20}, "features": [ {"type": "fillet", "radius": 5, "edges": "all_vertical"}, {"type": "hole", "diameter": 10, "position": [0, 0], "through": true} ], "unit": "mm" }

第二步,写一个确定性的代码生成器,把 JSON 翻译成 CadQuery 脚本。这一步不涉及任何 AI,纯逻辑映射,保证每次生成的代码都是合法且一致的。

import cadquery as cq def build_from_spec(spec): dims = spec["dimensions"] length = dims["length"] width = dims["width"] height = dims["height"] # 创建基础长方体,原点在底面中心 result = ( cq.Workplane("XY") .box(length, width, height, centered=(True, True, False)) ) # 处理特征 for feature in spec["features"]: if feature["type"] == "fillet": radius = feature["radius"] # 选择所有竖直边进行圆角 result = result.edges("|Z").fillet(radius) elif feature["type"] == "hole": diameter = feature["diameter"] pos = feature["position"] result = ( result.faces(">Z").workplane() .center(pos[0], pos[1]) .hole(diameter) ) return result # 执行建模 model = build_from_spec(spec) # 导出 STEP cq.exporters.export(model, "output.step") print("STEP 文件已生成:output.step")

这段代码里有个细节值得展开说。box函数的centered参数我设成了(True, True, False),意思是 X 和 Y 方向居中,Z 方向不居中,这样长方体的底面就落在 Z=0 平面上。为什么要这样?因为后续打孔的时候,faces(">Z")选的是顶面,如果 Z 方向也居中的话,底面就在 Z=-10 的位置,虽然不影响打孔,但导出 STEP 之后导入其他软件,零件的基准面就不在原点上了,装配的时候还得手动挪。养成好习惯,从一开始就把坐标系约定好。

圆角操作也有讲究。edges("|Z")选的是所有平行于 Z 轴的边,对于长方体来说就是四条竖直边。如果你直接写edges().fillet(5),会把所有边都倒圆角,包括顶面和底面的边,结果就不是你想要的。CadQuery 的选择器语法很灵活,|Z表示平行于 Z 轴,>Z表示 Z 方向最大的面,#Z表示垂直于 Z 轴。这些选择器用熟了,建模效率会高很多。

3.2 URDF 生成与 CoppeliaSim 导入验证

URDF 这部分我单独拿出来讲,因为它的流程和 STEP 差别很大。假设用户输入是:“生成一个两轮差速机器人的 URDF,底盘是长 300 宽 200 高 100 的长方体,两个驱动轮直径 80 厚度 30,安装在底盘左右两侧中心位置,前后各一个万向轮。”

这个需求比单纯建模复杂得多,因为它涉及运动学结构。我的处理方式是分两步:先让 LLM 输出机器人的拓扑描述 JSON,再用模板引擎生成 URDF XML。

拓扑描述 JSON 大概长这样:

{ "robot_name": "diff_drive_bot", "links": [ {"name": "base_link", "shape": "box", "size": [0.3, 0.2, 0.1], "mass": 5.0}, {"name": "left_wheel", "shape": "cylinder", "radius": 0.04, "length": 0.03, "mass": 0.5}, {"name": "right_wheel", "shape": "cylinder", "radius": 0.04, "length": 0.03, "mass": 0.5}, {"name": "front_caster", "shape": "sphere", "radius": 0.02, "mass": 0.2}, {"name": "rear_caster", "shape": "sphere", "radius": 0.02, "mass": 0.2} ], "joints": [ {"name": "left_wheel_joint", "type": "continuous", "parent": "base_link", "child": "left_wheel", "axis": [0, 1, 0], "origin": [0, 0.115, -0.03]}, {"name": "right_wheel_joint", "type": "continuous", "parent": "base_link", "child": "right_wheel", "axis": [0, 1, 0], "origin": [0, -0.115, -0.03]}, {"name": "front_caster_joint", "type": "fixed", "parent": "base_link", "child": "front_caster", "origin": [0.12, 0, -0.05]}, {"name": "rear_caster_joint", "type": "fixed", "parent": "base_link", "child": "rear_caster", "origin": [-0.12, 0, -0.05]} ] }

注意单位,URDF 默认用米,所以 300 毫米要写成 0.3。这个单位转换如果搞错了,导入 CoppeliaSim 之后模型要么大得离谱,要么小得看不见。我建议在 JSON 里统一用毫米,生成 URDF 的时候再除以 1000,这样不容易出错。

轮子的安装位置计算也要留意。底盘宽 200 毫米,轮子厚度 30 毫米,如果轮子紧贴底盘侧面,轮子中心到底盘中心的 Y 方向距离就是 100 + 15 = 115 毫米,也就是 0.115 米。这个计算过程最好在代码里显式写出来,不要硬编码数字,方便后续调整参数。

生成 URDF 之后,导入 CoppeliaSim 验证是必不可少的一步。CoppeliaSim 对 URDF 的兼容性总体不错,但有几个坑我踩过。第一,如果 URDF 里引用的 STL 文件路径不对,CoppeliaSim 会静默忽略视觉几何,只显示碰撞体,看起来就是一堆线框。第二,关节的 axis 方向如果和 CoppeliaSim 的坐标系约定不一致,轮子转起来方向可能是反的。第三,惯性矩阵如果没填或者填得不合理,仿真的时候机器人会抖动甚至飞出去。

<!-- URDF 片段示例 --> <link name="base_link"> <visual> <geometry> <box size="0.3 0.2 0.1"/> </geometry> <origin xyz="0 0 0" rpy="0 0 0"/> </visual> <collision> <geometry> <box size="0.3 0.2 0.1"/> </geometry> </collision> <inertial> <mass value="5.0"/> <inertia ixx="0.0208" ixy="0" ixz="0" iyy="0.0375" iyz="0" izz="0.0542"/> </inertial> </link>

惯性矩阵的计算有公式可循。长方体绕中心轴的转动惯量:Ixx = m(h² + d²)/12,Iyy = m(w² + h²)/12,Izz = m(w² + d²)/12。圆柱体绕中心轴的转动惯量:Ixx = Iyy = m(3r² + h²)/12,Izz = mr²/2。这些公式我建议直接写成工具函数,输入质量和尺寸,输出惯性矩阵,避免每次手动算。

3.3 批量生成与参数化变体

text-to-cad 真正体现效率优势的场景,是批量生成参数化变体。比如你需要一系列不同尺寸的法兰盘,从 DN50 到 DN200,每个规格的螺栓孔数量和孔径都不一样。手动建模的话,改一个尺寸就要重新拉伸切除一遍,费时费力。用脚本生成,改几个参数就能跑出一整套。

我的做法是定义一个参数表,用循环批量生成。下面是一个法兰盘批量生成的例子:

import cadquery as cq # 法兰盘参数表:公称直径、外径、螺栓孔中心距、螺栓孔直径、螺栓孔数量、厚度 flange_specs = [ {"dn": 50, "od": 165, "pcd": 125, "hole_d": 18, "hole_n": 4, "thk": 20}, {"dn": 80, "od": 200, "pcd": 160, "hole_d": 18, "hole_n": 8, "thk": 22}, {"dn": 100, "od": 220, "pcd": 180, "hole_d": 18, "hole_n": 8, "thk": 24}, {"dn": 150, "od": 285, "pcd": 240, "hole_d": 22, "hole_n": 8, "thk": 26}, {"dn": 200, "od": 340, "pcd": 295, "hole_d": 22, "hole_n": 12, "thk": 28}, ] for spec in flange_specs: dn = spec["dn"] od = spec["od"] pcd = spec["pcd"] hole_d = spec["hole_d"] hole_n = spec["hole_n"] thk = spec["thk"] # 创建法兰盘主体 flange = ( cq.Workplane("XY") .circle(od / 2) .extrude(thk) ) # 中心通孔 flange = flange.faces(">Z").workplane().hole(dn) # 螺栓孔阵列 import math for i in range(hole_n): angle = 2 * math.pi * i / hole_n x = (pcd / 2) * math.cos(angle) y = (pcd / 2) * math.sin(angle) flange = ( flange.faces(">Z").workplane() .center(x, y) .hole(hole_d) ) # 导出 filename = f"flange_dn{dn}.step" cq.exporters.export(flange, filename) print(f"已生成:{filename}")

这段代码跑一遍,五个规格的法兰盘 STEP 文件就全出来了,前后不到十秒。如果手动建模,一个法兰盘至少五分钟,五个就是二十五分钟,还不算检查尺寸的时间。这就是脚本化建模的威力。

实操心得:批量生成的时候,文件名一定要包含关键参数,比如flange_dn50.step、flange_dn80.step。不要用output1.step、output2.step这种命名,过两天你自己都分不清哪个是哪个。另外,建议把参数表单独存成 CSV 或者 JSON 文件,代码里读文件而不是硬编码,这样改参数不用动代码,非程序员也能操作。

4. 常见问题与排查技巧实录

4.1 几何内核报错与模型修复

用 text-to-cad 生成模型,最常遇到的问题不是代码语法错误,而是几何内核在执行布尔运算或者圆角操作时直接抛异常。这类错误信息往往很晦涩,比如Standard_Failure、BRep_API: command not done,第一次看到完全不知道从哪里下手。

我总结了几种高频情况。第一种是圆角半径过大。比如长方体厚度只有 10 毫米,你让它倒一个半径 8 毫米的圆角,几何上根本做不到,内核就会报错。解决办法是加一个校验逻辑,圆角半径不能超过相邻最小边长的二分之一。这个校验可以在代码生成阶段做,也可以在建模函数里加 try-except 捕获异常后自动减小半径重试。

第二种是布尔运算的实体没有相交。比如你想在一个圆柱体上打孔,但孔的位置偏到了圆柱体外面,布尔减运算找不到相交区域,就会失败。这种情况通常是坐标计算错误导致的,排查的时候把每个特征的坐标打印出来,和预期值对比,很快就能定位。

第三种是曲面缝合失败。STEP 导出的时候,如果模型有微小的缝隙或者重叠面,缝合算法可能失败。这种问题在简单模型上很少见,但如果你从外部导入 STL 再转 STEP,就很容易遇到。解决办法是用cq.Workplane的clean()方法清理一下,或者用Solid.makeSolid()重新构造实体。

错误类型典型报错信息排查思路解决方案
圆角失败BRep_API: command not done检查圆角半径是否超过边长一半减小半径或跳过该特征
布尔运算失败Standard_Failure检查实体是否相交调整坐标或改用切割平面
缝合失败Sewing failed检查是否有微小缝隙使用 clean() 或重建实体
导出为空文件大小为 0检查建模结果是否为空打印实体体积验证
单位错误模型尺寸异常检查单位是否统一统一用毫米,导出时转换

4.2 LLM 输出不稳定的应对策略

用大模型生成建模指令,最大的痛点是不稳定。同一个输入,今天生成的 JSON 和明天生成的可能不一样,有时候字段名对了但值不对,有时候干脆漏掉某个特征。我试过几种应对策略,效果最好的是“约束解码 + 后校验”。

约束解码的意思是,在调用 LLM API 的时候指定response_format={"type": "json_object"},强制模型输出合法 JSON。这一步能过滤掉大部分格式错误。但光有 JSON 格式还不够,字段内容可能仍然不符合预期。所以还需要一个后校验层,用 Pydantic 定义数据模型,对 LLM 的输出做类型检查和范围校验。

from pydantic import BaseModel, Field, validator from typing import List, Literal class Feature(BaseModel): type: Literal["fillet", "hole", "chamfer"] radius: float = Field(gt=0, le=50) diameter: float = Field(gt=0, le=200) position: List[float] = Field(default=[0, 0], min_items=2, max_items=2) class PartSpec(BaseModel): shape_type: Literal["box", "cylinder", "sphere"] dimensions: dict features: List[Feature] = [] unit: Literal["mm", "m"] = "mm" @validator("dimensions") def check_dimensions(cls, v): for key, val in v.items(): if val <= 0 or val > 1000: raise ValueError(f"尺寸 {key}={val} 超出合理范围") return v # 校验 LLM 输出 try: spec = PartSpec(**json.loads(llm_output)) except Exception as e: print(f"LLM 输出校验失败:{e}") # 触发重试或者降级到默认参数

如果校验失败,我的做法是自动重试一次,并在提示词里加上失败原因,让模型修正。比如“上一次生成的圆角半径 80 毫米超过了长方体厚度 20 毫米的一半,请重新生成”。实测下来,加上这个反馈之后,第二次生成的成功率能到 90% 以上。

还有一个技巧是给 LLM 提供 few-shot 示例。在系统提示词里放两三个输入输出的例子,模型会模仿例子的格式和风格,稳定性明显提升。示例要覆盖常见的形状类型和特征组合,让模型有样学样。

4.3 CAD 软件导入 STEP 的兼容性处理

生成的 STEP 文件最终要导入 CAD 软件使用,但不同软件对 STEP 的解析行为有差异。我测试过 SolidWorks、中望 CAD、Fusion 360 和 FreeCAD,总结了一些经验。

SolidWorks 对 STEP AP214 的支持最好,导入之后直接是实体,不需要修复。但如果你导出的是 AP203,SolidWorks 可能会把它识别成曲面片体,需要手动缝合。中望 CAD 的兼容性也不错,但导入大模型的时候速度偏慢,建议先简化模型再导入。Fusion 360 是云端软件,导入 STEP 需要上传,文件大的话等待时间较长,但导入质量很高。FreeCAD 作为开源软件,兼容性中规中矩,偶尔会遇到面丢失的情况,用Part -> Check geometry工具可以检测和修复。

注意:如果你生成的模型要用于 3D 打印,导出 STEP 之后还需要转成 STL。STL 是网格格式,精度由弦高决定。弦高设得太小,文件巨大;设得太大,曲面看起来有棱角。我一般用 0.01 毫米的弦高,兼顾精度和文件大小。CadQuery 里可以用cq.exporters.export(model, "output.stl", tolerance=0.01)来指定。

5. 进阶扩展与个人实操体会

5.1 从单零件到装配体

单零件生成跑通之后,下一步自然是装配体。text-to-cad 如果只能生成单个零件,价值有限;能生成带配合关系的装配体,才真正有工程意义。我的思路是引入“装配描述 JSON”,定义零件之间的配合约束,比如同轴、贴合、偏移。

举个例子,一个简单的轴承座装配:底座、轴承、轴。底座上有一个孔,轴承外圈和孔配合,轴和轴承内圈配合。装配描述里要定义每个零件的位姿,以及配合面的对应关系。这部分目前 LLM 还不太擅长,因为涉及空间推理,容易搞错方向。我的做法是让 LLM 只生成零件,装配关系用规则引擎来处理,比如“孔轴配合”自动对齐轴线,“平面贴合”自动计算偏移量。

装配体导出 STEP 的时候,可以选择导出为单个实体或者保留零件层级。如果后续要在 CAD 软件里做运动仿真,建议保留层级,每个零件单独命名。CadQuery 的Assembly类支持这种操作,用add()方法添加零件,用constrain()方法添加约束,最后export()导出。

5.2 与机器人仿真工作流的衔接

URDF 生成之后,导入 CoppeliaSim 只是第一步。完整的仿真工作流还包括运动规划、碰撞检测、传感器模拟。text-to-cad 在这个链条里的定位是“快速原型工具”,帮你把机器人的结构模型快速搭出来,省去手动建模的时间。但运动学参数、控制器代码、传感器配置这些,还是得在仿真软件里手动调。

我个人的习惯是,用 text-to-cad 生成 URDF 之后,先在 CoppeliaSim 里做一次运动学验证,确认关节轴向和限位正确,然后再导入到 ROS 或者其他的规划框架里。如果发现轮子转向反了,不用重新生成 URDF,直接在 CoppeliaSim 里改关节属性就行,改完再导出修正后的 URDF。

5.3 我踩过的那些坑

最后分享几个我在这个项目里踩过的坑,希望能帮你省点时间。

第一个坑是 Python 环境混用。我一开始用系统自带的 Python 装 CadQuery,结果和 conda 环境里的 OCP 版本冲突,导入的时候直接段错误。后来统一用 conda 管理环境,再也没出过这个问题。记住,几何内核这种底层库,版本兼容性很敏感,不要混装。

第二个坑是单位不统一。LLM 生成的 JSON 里,有时候尺寸是毫米,有时候是米,我一开始没做校验,结果导出的模型要么是 0.1 毫米的微型零件,要么是 10 米高的巨型结构。后来在 Pydantic 模型里强制指定单位字段,并且在代码生成器里统一转换成毫米,问题才解决。

第三个坑是圆角顺序。在一个有孔的长方体上倒圆角,如果先倒圆角再打孔,孔边缘的圆角可能会被破坏;如果先打孔再倒圆角,圆角可能会延伸到孔壁上导致几何错误。正确的顺序通常是先打孔,再对特定的边倒圆角,并且用选择器精确指定要倒圆的边,不要用edges()全选。

第四个坑是 URDF 的惯性矩阵。我一开始偷懒,惯性矩阵全填零,结果导入 CoppeliaSim 之后机器人一启动就飞出去了。后来老老实实按公式算,质量、尺寸都填对,仿真才稳定下来。惯性矩阵不能省,这是物理仿真的基础。

这个项目后续还可以往几个方向扩展。一是支持更多的输出格式,比如 IGES、STL、3MF,覆盖更多的下游工具。二是引入反馈循环,用户看到生成的模型后可以用自然语言继续修改,比如“把孔往左移 10 毫米”,系统增量更新模型。三是和 3D 打印切片软件打通,生成模型后直接输出打印文件,形成从描述到制造的完整闭环。这些方向我还在摸索,有进展再和大家分享。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询