1. 项目概述:当AI遇上CAD,我们为何需要一个“中间人”?
最近几年,AI生成式设计的风刮得挺猛,从文本到图片,再到3D模型,大家似乎都在琢磨怎么让机器更懂“创造”。在工业设计和机械工程这个硬核领域,计算机辅助设计(CAD)是绝对的基石。但一个现实是,让AI直接去理解并生成复杂的、参数化的、可用于实际生产的CAD模型,难度堪比让一个刚学会造句的AI去写一部结构严谨的长篇小说。这里面的核心矛盾在于:主流的AI模型(比如各种大语言模型和扩散模型)擅长处理的是连续的、非结构化的数据(像文本、像素),而CAD模型本质上是一套由精确几何、严格约束和设计意图构成的结构化程序。
这就是“CADIR”这个项目试图破局的关键。CADIR,全称“A Cross-Backend Editable Intermediate Representation for Agentic CAD Generation”,翻译过来就是“一种面向智能体CAD生成的、跨后端的、可编辑的中间表示”。这个名字听起来有点学术,但拆开看就很有意思。它本质上想扮演一个“翻译官”或“中间人”的角色。想象一下,你(或者一个AI智能体)用自然语言说:“设计一个带圆角、有四个安装孔的法兰盘。” AI理解了这个意图,但它不能直接把这句话扔给SolidWorks或者FreeCAD,因为后者听不懂。AI需要先把这句话转换成一套机器能理解的、精确的“设计指令集”。CADIR就是这个指令集的标准化格式。
它的核心价值在于“跨后端”和“可编辑”。所谓“跨后端”,意味着用CADIR描述的设计,可以相对容易地转换到不同的CAD内核(比如开源的Open CASCADE Technology,也就是OCCT,或者商业的ACIS、Parasolid)上去执行,从而生成具体的几何模型。这就解决了AI模型与特定CAD软件强绑定的问题。“可编辑”则更为关键,它意味着这个中间表示不是一堆“死”的几何数据,而是保留了设计逻辑和参数,你可以事后去修改“圆角半径是5mm”为“8mm”,整个模型会智能地更新,而不是推倒重来。这对于需要通过多轮交互、逐步细化的“智能体”(Agentic)设计流程来说,是必不可少的特性。简单说,CADIR的目标是成为连接AI创意与CAD实践之间那条缺失的、双向可通的高速公路。
2. 核心设计思路:解构CAD生成的三重挑战
要理解CADIR的设计,我们得先看看当前AI生成CAD面临的几座大山。我自己在尝试用脚本或程序化方法做设计时,就深刻体会到这些痛点。
2.1 从“像素”到“程序”:几何表示的鸿沟
第一重挑战是表示鸿沟。现在很多AI生成3D模型的研究,输出的是网格(Mesh)或点云,就像用陶土捏出一个形状。这对于视觉展示、3D打印或许够用,但对于CAD来说远远不够。CAD模型需要的是边界表示(B-Rep):一个由面、边、顶点及其拓扑关系精确定义的实体。更重要的是,CAD是参数化的,一个圆柱体的高度“H”是一个变量,改变H,模型会随之变化。主流的AI生成范式很难直接输出这种带有变量和约束的结构化程序。
CADIR的思路不是让AI直接输出B-Rep,而是输出一个高级的、基于构造历史的操作序列。这类似于记录下设计师的操作步骤:“草图绘制一个圆 -> 拉伸成圆柱体 -> 在顶面打一个孔”。这个序列比原始的B-Rep数据更紧凑,也更容易被AI学习和生成,同时又保留了完整的编辑能力。
2.2 “一家之言”的困境:CAD内核的碎片化
第二重挑战是生态碎片化。CAD世界不是一个统一王国,而是由几个强大的“内核”诸侯割据。开源领域有OCCT(Open CASCADE Technology),商业软件有达索的CAA(基于ACIS或CATIA内核)、西门子的Parasolid(被Solid Edge、NX、SolidWorks使用)等。每个内核的API、数据结构、甚至某些几何算法都有差异。为一个内核写的生成代码,很难直接移植到另一个。
CADIR的“跨后端”特性正是为此而生。它定义了一套相对通用的、高层级的操作原语(Primitives)。例如,Extrude(sketch, distance)、Fillet(edge, radius)。AI只需要学习生成这套原语序列。然后,针对不同的后端(如OCCT、Parasolid),会有相应的“编译器”或“解释器”,将这些原语翻译成该内核的具体API调用。这就好比Java的“一次编写,到处运行”,CADIR希望实现“一次描述,多处生成”。
2.3 与AI智能体的对话:需要可编辑性与可反馈性
第三重挑战来自交互流程。我们想要的不是AI单次抛出一个无法修改的模型,而是能与AI进行多轮对话、逐步优化的设计伙伴。你说“法兰盘太厚了”,AI应该能理解并修改“拉伸”操作中的厚度参数,而不是重新生成一个全新的、可能结构都变了的模型。
这就要求中间表示必须是可编辑的、结构化的。CADIR将设计表示为一个可查询和修改的数据结构。每个操作步骤都是一个节点,节点之间有关联(比如打孔操作依赖于某个特定的平面)。AI智能体或用户可以定位到特定的参数进行修改,系统能自动处理依赖更新。这种能力是面向“智能体”(Agentic)的核心,它使得AI不仅能生成,还能理解、推理和修改自己的“作品”。
3. CADIR中间表示的关键技术拆解
那么,CADIR具体长什么样?它不可能重新发明轮子,必然是在现有CAD编程范式上做的抽象和标准化。根据项目标题和方向,我们可以推断其核心技术组成。
3.1 操作原语集:定义设计的“词汇表”
这是CADIR的基石。一套精心设计的、有限的操作原语,覆盖从草图到特征建模的主要操作。这套词汇表需要足够表达丰富,又要保持简洁,以降低AI学习难度。通常包括:
- 草图原语:
Point(x, y),Line(start, end),Circle(center, radius),Arc(center, radius, start_angle, end_angle),Constraint(geom1, geom2, type)(如同心、共线、垂直等)。 - 特征建模原语:
Extrude(sketch_profile, distance, direction),Revolve(sketch_profile, axis, angle),Loft(profiles),Sweep(profile, path)。 - 修饰与操作原语:
Fillet(edge, radius),Chamfer(edge, distance),Shell(body, faces_to_remove, thickness),Boolean(body1, body2, operation)(并集、差集、交集)。 - 参考几何原语:
Plane(reference, offset),Axis(point, direction),CoordinateSystem(origin, x_dir, y_dir)。
每个原语都是一个结构体或对象,包含了执行该操作所需的所有参数。这些参数部分可以是具体数值,部分可以是引用其他原语输出结果的“句柄”。例如,一个打孔(Hole)操作的原语,需要指定打孔的平面(引用一个Plane或实体表面)、定位尺寸(引用草图几何)、孔的直径和深度。
3.2 依赖关系图:记录设计的“逻辑链”
仅仅有操作序列是不够的。一个复杂的模型,操作之间有着严格的先后依赖关系。你不能在实体被创建之前就去给它倒圆角。因此,CADIR内部需要维护一个有向无环图(DAG)来表示这种依赖。
每个操作原语作为图中的一个节点。节点的输入是它所依赖的其他节点的输出(例如,一个拉伸特征依赖于一个草图节点)。这个依赖图至关重要:
- 参数化编辑:当用户修改某个早期节点(如草图尺寸)时,系统可以沿着依赖图,自动标记所有受影响的下游节点,并触发重新计算。
- 设计意图保留:依赖图本身就编码了设计师的构建逻辑。AI在生成时,也必须遵循合理的依赖关系,这本身也是对生成质量的一种约束。
- 可解释性:对于AI生成的模型,我们可以通过审视这个依赖图来理解AI的“思考过程”,增加了透明度和可信度。
3.3 几何与约束的抽象表示
如何表示草图几何和约束,是另一个核心。在底层CAD内核中,约束求解是一个复杂的数学过程。CADIR需要对其进行高层抽象。
- 几何:可能采用参数化方程或简单的定义式来表示基本图元,而不是直接存储内核中的几何对象句柄。例如,一个圆可以表示为
(center_x, center_y, radius)。 - 约束:定义一套标准的约束类型,如
Distance(p1, p2, d),Angle(line1, line2, a),Tangent(curve1, curve2),Coincident(point, curve)。这些约束被添加到草图原语中,形成一组方程组。CADIR本身可能不负责求解,而是将约束系统传递给后端内核的求解器去处理。
3.4 面向OCCT后端的适配层实现
既然网络热词中提到了OCCT,我们可以深入探讨一下CADIR在OCCT这个具体后端上的实现思路。OCCT是一个功能强大但API较为底层的开源几何内核。
适配层(后端编译器)的工作流程:
- 解析CADIR指令流:读取CADIR的JSON或自定义格式文件,重建出操作节点和依赖图。
- 映射到OCCT API:将每个CADIR原语翻译成一系列OCCT调用。
- 例如,
Extrude(sketch, distance)这个原语,适配层需要:- 调用OCCT的
BRepBuilderAPI_MakeFace等工具,根据CADIR中的草图几何定义,在OCCT中重建出闭合的线框(Wire)。 - 调用
BRepPrimAPI_MakePrism,以该线框为底面,沿指定方向拉伸指定距离,生成OCCT的TopoDS_Shape实体。
- 调用OCCT的
- 对于
Fillet(edge, radius),需要调用BRepFilletAPI_MakeFillet,并传入从CADIR中指定的边(在OCCT中需要通过名称或ID查找对应的TopoDS_Edge对象)和半径值。
- 例如,
- 维护对象映射表:这是实现可编辑性的关键。当OCCT生成了一个实体(
TopoDS_Shape)后,适配层需要将这个OCCT对象与CADIR中对应的操作节点ID关联起来,存储在一个映射表中。当后续操作(如布尔运算)需要引用这个实体时,或当用户修改参数需要重建时,可以通过这个映射表快速找到对应的OCCT对象。 - 处理参数更新:当CADIR中的某个参数被修改(如拉伸距离从10改为20),适配层需要:
- 根据依赖图,找到所有受影响的节点。
- 清除这些节点对应的旧OCCT几何结果(但保留映射关系等信息)。
- 按照新的参数,重新执行从该节点开始的所有下游操作序列,生成新的OCCT几何。
- 更新映射表。
注意:OCCT的建模过程是“立即执行”的,生成的是最终的几何结果,其本身不自动维护参数化历史。因此,CADIR适配层必须自己完整地维护这份“构造历史”和参数依赖,这是实现跨后端参数化编辑中最具挑战性的部分之一。
4. 构建一个简易概念验证:从想法到OCCT可视化
理论说得再多,不如动手试一下。我们来构思一个极度简化的概念验证,看看CADIR的思想如何落地。我们将用Python来模拟,因为它有丰富的生态和OCCT的封装库(如pythonocc-core)。
4.1 定义简易的CADIR数据结构
首先,我们定义自己的“微型CADIR”格式。我们用字典和列表来模拟。
# 定义一个操作节点的基类 class CADIRNode: def __init__(self, node_id, op_type): self.id = node_id self.type = op_type # 如 'Sketch', 'Extrude' self.inputs = [] # 依赖的父节点ID列表 self.params = {} # 操作参数字典 self.output_ref = None # 在后端生成的几何对象的引用(如OCCT的Shape) # 定义我们的微型CADIR文档 class MiniCADIR: def __init__(self): self.nodes = {} # node_id -> CADIRNode self.next_id = 1 def add_node(self, op_type, inputs, params): node = CADIRNode(self.next_id, op_type) node.inputs = inputs node.params = params self.nodes[self.next_id] = node self.next_id += 1 return node.id4.2 实现一个到OCCT的后端编译器
接下来,我们实现一个简单的编译器,将我们的MiniCADIR转换成OCCT的几何体。这里使用pythonocc-core。
from OCC.Core.gp import gp_Pnt, gp_Dir, gp_Ax2 from OCC.Core.Geom import Geom_Circle from OCC.Core.GeomAPI import GeomAPI_Interpolate from OCC.Core.BRepBuilderAPI import BRepBuilderAPI_MakeEdge, BRepBuilderAPI_MakeWire, BRepBuilderAPI_MakeFace from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakePrism from OCC.Core.TopoDS import TopoDS_Shape, TopoDS_Face from OCC.Core.BRepFilletAPI import BRepFilletAPI_MakeFillet from OCC.Core.TopExp import TopExp_Explorer from OCC.Core.TopAbs import TopAbs_EDGE from OCC.Core.TopTools import TopTools_ListOfShape class MiniCADIR_OCCT_Compiler: def __init__(self, cadir_doc): self.doc = cadir_doc self.shape_map = {} # 映射:node_id -> TopoDS_Shape def compile(self): """按节点ID顺序(假设即构建顺序)编译所有节点""" for node_id in sorted(self.doc.nodes.keys()): self._compile_node(node_id) # 返回最后一个节点的几何(通常是最终实体) return self.shape_map.get(node_id) def _compile_node(self, node_id): node = self.doc.nodes[node_id] if node.type == 'SketchCircle': # 编译一个圆形草图 center = node.params['center'] # 假设是 (x, y, z) radius = node.params['radius'] circle = Geom_Circle(gp_Ax2(gp_Pnt(*center), gp_Dir(0, 0, 1)), radius) edge = BRepBuilderAPI_MakeEdge(circle).Edge() wire = BRepBuilderAPI_MakeWire(edge).Wire() face = BRepBuilderAPI_MakeFace(wire).Face() self.shape_map[node_id] = face print(f"编译节点 {node_id}: 创建圆形面") elif node.type == 'Extrude': # 编译拉伸操作 target_id = node.inputs[0] # 假设只有一个输入,是草图面 target_face = self.shape_map[target_id] # 获取依赖的几何 distance = node.params['distance'] direction = node.params.get('direction', (0, 0, 1)) # 创建拉伸向量 from OCC.Core.gp import gp_Vec vec = gp_Vec(*direction).Normalized() * distance prism = BRepPrimAPI_MakePrism(target_face, vec) prism.Build() solid = prism.Shape() self.shape_map[node_id] = solid print(f"编译节点 {node_id}: 拉伸生成实体") elif node.type == 'Fillet': # 编译倒圆角操作 target_id = node.inputs[0] target_shape = self.shape_map[target_id] radius = node.params['radius'] fillet_builder = BRepFilletAPI_MakeFillet(target_shape) # 简化:对实体的所有边进行倒角(实际中应指定边) exp = TopExp_Explorer(target_shape, TopAbs_EDGE) while exp.More(): edge = TopoDS_Edge.Current(exp) fillet_builder.Add(radius, edge) exp.Next() fillet_builder.Build() if fillet_builder.IsDone(): filleted_shape = fillet_builder.Shape() self.shape_map[node_id] = filleted_shape print(f"编译节点 {node_id}: 倒圆角完成") else: print(f"编译节点 {node_id}: 倒圆角失败") self.shape_map[node_id] = target_shape4.3 编写设计脚本并生成可视化
现在,我们用定义好的“CADIR”来“编程”一个简单的带圆角的圆柱体,并调用编译器生成OCCT几何,最后用OCCT的Viewer显示出来。
# 主程序 if __name__ == "__main__": # 1. 创建CADIR文档 my_design = MiniCADIR() # 2. 添加设计操作序列(这就是AI可能生成的“指令”) sketch_id = my_design.add_node('SketchCircle', inputs=[], params={'center': (0, 0, 0), 'radius': 10.0}) extrude_id = my_design.add_node('Extrude', inputs=[sketch_id], params={'distance': 20.0, 'direction': (0, 0, 1)}) fillet_id = my_design.add_node('Fillet', inputs=[extrude_id], params={'radius': 2.0}) # 3. 编译到OCCT compiler = MiniCADIR_OCCT_Compiler(my_design) final_shape = compiler.compile() # 4. 使用OCCT的简单显示器进行可视化 from OCC.Display.SimpleGui import init_display display, start_display, add_menu, add_function_to_menu = init_display() display.DisplayShape(final_shape, update=True) print("设计生成完成!在3D窗口中查看带圆角的圆柱体。") start_display()这个例子非常简陋,省略了草图约束、复杂的特征识别、稳健的错误处理等。但它清晰地演示了CADIR的核心工作流:用一套标准的原语描述设计 -> 通过后端编译器翻译成特定内核(OCCT)的API -> 生成可交互的几何模型。AI的训练目标,就是学习如何从自然语言或设计意图生成这样一串结构化的add_node调用序列。
5. 面向AI的训练数据构建与生成策略
要让AI学会使用CADIR,高质量的训练数据是关键。这些数据应该是(设计意图描述,CADIR序列)的对子。
5.1 数据来源与自动化生成
- 开源模型库挖掘:从GrabCAD、Thingiverse等社区下载大量参数化CAD模型(如STEP文件附带特征树)。需要开发逆向工具,将特征树(如果存在)或通过几何分析推断出的构造步骤,转换成CADIR序列。这是一项艰巨但基础的工作。
- 程序化生成:编写脚本,随机生成符合工程常识的CADIR序列及其对应的描述文本。例如,随机生成“一个具有N个散热鳍片、中心带安装孔的基座”的参数和结构,同时用模板生成对应的描述:“一个带有{num_fins}个长方形散热鳍片的铝制基座,中心有一个直径为{hole_dia}的安装孔,整体厚度为{thickness}。” 这种方法可以大规模、低成本地创造数据。
- 众包与专家标注:邀请工程师或设计师,对现有模型进行描述,并手动或半自动地将其分解为CADIR步骤。这能获得更自然、更符合人类思维的语言描述。
5.2 AI模型选型与训练要点
考虑到CADIR序列是一种结构化的、序列化的数据,类似于编程代码,因此可以采用以下策略:
- 模型架构:类似GitHub Copilot,使用基于Transformer的解码器模型(如GPT系列)进行序列到序列的学习。输入是自然语言描述,输出是CADIR原语序列的token流。
- 关键训练技巧:
- 语法约束:在模型输出时,加入严格的语法约束,确保生成的token序列始终符合CADIR的语法规范(例如,
Extrude操作后面必须跟着一个草图ID和距离参数)。这可以大幅减少无意义的输出。 - 依赖关系注入:在训练时,不仅输入文本描述,还可以将当前已生成的部分依赖图作为上下文输入给模型,帮助它做出符合逻辑的下一步决策。
- 课程学习:从简单的特征(如单个拉伸、旋转)开始训练,逐步增加到复杂的布尔运算、阵列、放样等。
- 语法约束:在模型输出时,加入严格的语法约束,确保生成的token序列始终符合CADIR的语法规范(例如,
- 评估指标:不能只看语言相似度。需要将生成的CADIR序列编译成几何模型,并与目标几何进行对比。指标可以包括:
- 几何匹配度:体积重合率、豪斯多夫距离等。
- 编辑性评估:随机修改生成序列中的参数,检查重新编译后的模型是否发生合理变化,且不崩溃。
- 逻辑合理性:由人类专家评估生成的设计步骤是否合乎工程逻辑和制造约束。
6. 实际应用中的挑战与应对策略
在实际推进这样一个项目时,会遇到许多预料之中和预料之外的困难。
6.1 几何内核差异的深水区
“跨后端”听起来美好,但不同内核对几何操作的支持度和实现细节有差异。例如,OCCT和Parasolid对复杂倒圆角(变半径、面倒圆)的算法和稳定性可能不同。某个在OCCT上能成功编译的CADIR序列,在Parasolid后端可能会失败。
应对策略:
- 定义公共子集:CADIR最初可以只支持所有目标内核都稳定支持的操作原语。对于高级或内核特有的功能,可以作为“扩展原语”或“后端特定原语”来处理,但这会牺牲一部分可移植性。
- 容错与降级:编译器需要具备一定的智能。当某个操作在目标后端失败时,可以尝试降级方案(例如,用一系列固定半径的倒角来近似一个变半径倒角),或者至少给出清晰的错误报告,而不是整个崩溃。
- 测试套件:为每个操作原语针对每个后端编写详尽的测试用例,确保核心功能的跨平台一致性。
6.2 设计意图的模糊性与歧义
自然语言描述是模糊的。“一个坚固的支架”中,“坚固”如何量化?AI需要将其转化为具体的材料属性、厚度、加强筋布局等CADIR参数。这涉及到多模态理解和工程知识图谱。
应对策略:
- 多轮交互:不要指望AI一次猜对。CADIR的可编辑性支持多轮交互。AI可以先生成一个初步方案,用户反馈“这里太薄了”或“成本太高”,AI再针对性地修改对应参数。这比让AI一次性理解所有隐含意图更可行。
- 引入领域知识库:构建一个包含常见设计规则、材料库、标准件库、制造工艺约束的知识库。AI在生成CADIR时,可以查询这个知识库来细化参数。例如,听到“铝制法兰”,知识库可以提供典型的铝材强度、常用的厚度范围等信息。
- 提供选项:对于歧义点,AI可以生成几个不同参数化的备选方案(都表示为CADIR),让用户选择。
6.3 性能与复杂度管理
一个复杂装配体可能有成千上万个特征。完整的CADIR依赖图会非常庞大。编译、编辑更新、序列化/反序列化都可能成为性能瓶颈。
应对策略:
- 增量编译与缓存:当只修改一个局部参数时,编译器应只重新编译依赖图中受影响的部分子图,并复用未更改部分的缓存结果。
- 层次化表示:允许将一组特征打包成一个“子组件”,这个子组件对外可以简化为几个参数。这样既能简化高层设计,也能在需要时展开细节。这对于AI分层次生成设计很有帮助。
- 序列化优化:设计高效的二进制或紧凑的文本格式(如MessagePack)来存储CADIR流,减少IO开销。
7. 未来展望:CADIR会带来什么?
如果CADIR及其相关的AI生成技术走向成熟,它可能会深刻改变设计工作流:
- 自然语言成为新界面:设计师可以直接用语言描述概念,AI快速生成多个可供编辑的参数化雏形,极大降低从想法到初步模型的门槛和时间。
- 智能设计助手:CAD软件内置的AI助手,可以理解你正在设计的内容,根据上下文推荐下一个特征(“您是否需要在背面添加加强筋?”),甚至自动完成重复性、模式化的设计部分。
- 生成式设计民主化:传统的生成式设计(Generative Design)软件设置复杂、计算昂贵。结合CADIR,用户可能只需输入高层的功能和约束描述,AI就能探索出符合要求的、可编辑的参数化结构,而不仅仅是不可修改的有机形状。
- 设计知识沉淀与复用:企业内大量的历史设计模型,可以通过分析其CADIR表示,提炼出常用的设计模式、参数化模板和设计规则,形成可复用的智能设计资产库。
当然,这条路还很长。CADIR需要整个生态的共建:更强大的AI模型、更稳健的编译器、更丰富的训练数据、以及更友好的用户交互界面。但它的愿景是清晰的——让人更专注于创造性的设计意图,而将繁琐的、重复的建模操作交给智能系统去实现和优化。这或许就是“智能体CAD生成”为我们描绘的未来图景。作为从业者,关注并参与这类中间表示和工具链的早期构建,很可能是在未来把握设计智能化浪潮的关键一步。