最近在GitHub上刷到一个很有意思的开源项目,星星数已经涨到480多颗,简单说就是给AI装一双会看图纸的眼睛——把Revit、IFC、DWG这类工程领域最常见的CAD/BIM文件,一键转成结构化的JSON数据。最香的是,整个过程不需要安装任何Autodesk的商业软件。也就是说,不用Windows下那一套庞大的Revit环境,不用正版授权,甚至你手头只有一台Linux服务器,也能把图纸中的墙体、门窗、楼层、构件信息、坐标关系全部提取出来,喂给大模型做分析。
这个思路我关注了很久,因为AI在文本和图像领域已经卷出天际了,但在建筑工程领域,绝大多数大模型还处于“睁眼瞎”状态。聊天、写文案、生成代码都很强,可你拿一张DWG总平面图给它,它基本看不出什么门道。核心原因不在模型本身,而是在于输入格式。Revit、IFC、DWG这些格式,本质上不是给自然语言模型消费的,它们是几何引擎、参数化建模软件和CAD平台之间交换数据的载体。想要让AI读懂图纸,就必须先有一个“翻译层”,把这些专业二进制格式转换成模型能理解的文本或结构化数据。
今天就把这个项目的来龙去脉拆开聊一聊,包括格式原理、技术选型、完整实操流程,还有我踩过的几个坑,希望能帮你少走弯路。不管你是BIM工程师、二次开发从业者,还是正在做建筑行业AI应用的产品经理,这篇文章应该都能给你一些参考。
1. 为什么AI总是“看不懂”图纸
先说一个很多人忽略的事实:建筑设计行业里的图纸格式,天然是给机器看的,而不是给人类或大模型看的。
1.1 三种格式的“性格差异”
RVT(Revit项目文件)是Autodesk专有的二进制格式,里面不仅包含三维几何体,还包含了族参数、构件类型、材质信息、阶段化数据、视图设置等一整套BIM逻辑。只要离开了Revit环境,想直接解析这个文件几乎是不可能的,因为它的数据组织方式和Revit的API深度绑定。
IFC则完全是另一个路子。它是buildingSMART组织维护的开放标准格式,专门用来描述建筑构件的语义信息。一个IFC文件里,墙就是IfcWall,门是IfcDoor,楼板是IfcSlab,它们之间的关系用IfcRelAggregates、IfcRelContainedInSpatialStructure这些关系实体来表达。因为它是公开的、文本化的(IFC-SPF格式本质上是ISO 10303-21标准的STEP物理文件),所以解析起来比Revit友好得多。
DWG则是AutoCAD的老牌格式,从1982年发展到现在,中间迭代了至少几十个版本。内部结构极其复杂,有各种版本差异,而且也是封闭的商业格式。和IFC不同,DWG通常只有几何数据,比如线、圆弧、多段线、填充、标注,基本不带建筑语义,所以后面做AI应用时需要额外做一层信息提取。
1.2 关键痛点:数据孤岛与格式壁垒
这几种格式之间的壁垒,说白了就是建筑行业的“数据孤岛”。设计院用Revit建模,施工单位拿到图纸后往往又要重新建模或手工翻图,运维阶段再面对一沓PDF和Excel更是头疼。整个行业花了大量人力在格式转换和数据整理上。
而AI时代的到来,让这个问题变得尤其尖锐。想要训练一个能自动审图的模型,你得先有大量标注好的图档数据;想做一个基于图纸的智能问答机器人,你得让大模型能读到构件属性和空间关系。光是数据清洗这一步,就能卡住90%的团队。这个开源项目走了一条很聪明的路线,不去硬啃Revit的二进制格式,而是先用开放标准IFC作为中间载体,再用各种开源库去解析DWG,最终统一输出成JSON或者文本,让大模型可以直接消费。
一句话总结:它的思路不是“替代Autodesk”,而是“绕开Autodesk”。你不需要装任何商业软件,只需要一个转换管道,把图纸变成AI能懂的语言。
1.3 传统做法到底贵在哪
很多人会问,用Revit本身不是能直接导出数据吗?确实,Revit可以通过Dynamo、Revit API导出构件明细表,也可以手动导出IFC。但这里有几个绕不过去的问题:
- 需要Windows环境加正版Revit授权,License费用不低。
- 批量处理时极其痛苦,几十个模型文件跑起来,Revit经常卡死或崩溃。
- API开发门槛高,你得熟悉C#和Revit的对象模型。
- 不适合作为服务端流水线的一部分,你不能在Docker容器里跑Revit。
所以,如果要做自动化、批量化的AI数据管道,传统的“先导CAD,再转换”的方式根本不适合。这也是这个开源项目存在的核心价值:它是一个轻量级的转换中间层,可以嵌入到你的Python数据处理流程里,跑在Linux服务器上,轻松实现批量处理。
2. 项目核心设计思路拆解
这个项目的整体架构,可以拆成三个层次:格式归一化层、语义提取层、AI适配层。
2.1 从Revit到JSON:先做格式归一再做语义提取
它的设计非常务实:不尝试用一个解析器通吃所有格式,而是分三步走。
第一步是格式归一化。无论是Revit、IFC还是DWG,最终都会转成一个统一的数据模型。Revit文件先导出成IFC,DWG先转换成DXF,然后由各自的解析器提取信息。这样做的好处是,后续的AI处理面对的是统一结构,不用为每种格式写不同的逻辑。
第二步是语义提取。从IFC里提取构件类型、空间结构、几何位置、连接关系;从DXF里提取图层、线型、文本标注和块引用。这个阶段产出的,就是一套基本完整的建筑信息数据集。
第三步是AI适配。把提取出来的结构化数据序列化成JSON、JSONL,或者缩略成纯文本形式,配合Prompt模板,就可以直接用于大模型的问答、摘要、审查等任务。
2.2 为什么选IfcOpenShell作为核心解析引擎
在IFC解析这个环节,项目使用了IfcOpenShell,这是开源社区里公认的IFC处理工具。它支持Python绑定,可以读取IFC实体属性、拆分构件、计算体积面积、甚至做几何拓扑分析。
我对这个库的评价是:它几乎把IFC的复杂度全部封装好了。你只需要写几行Python代码,就能拿到一面墙的厚度、高度、材质名称和它在第几层。它内部处理了STEP文件的解析、实体继承关系的展开、单位换算和坐标系变换,这些如果全部自己实现,没有几个月搞不定。
import ifcopenshell # 打开IFC文件 ifc_file = ifcopenshell.open("sample.ifc") # 提取所有墙构件 walls = ifc_file.by_type("IfcWall") for wall in walls: name = wall.Name global_id = wall.GlobalId # 获取属性集 pset = ifcopenshell.util.element.get_psets(wall) print(name, global_id, pset)这一段代码基本上就是项目的底层核心调用方式。可以看到,解析IFC的难度被降到了极低,非专业程序员也能上手。
2.3 DWG解析的关键:用ODA File Converter做“前锋”,用ezdxf做“后卫”
DWG就比IFC麻烦多了。因为DWG不是开放格式,纯开源的解析方案还不算成熟。项目里采用了一个很常见的组合拳:先用ODA File Converter把高版本DWG批量转成DXF,再用ezdxf这个Python库去读取DXF内容。
ODA File Converter是Open Design Alliance出的免费工具,支持DWG到DXF的双向转换,覆盖AutoCAD 2018到旧版的大部分格式。它没有图形界面自动化能力,但可以通过命令行调用,这在批量处理时非常有用。
ezdxf则是一个非常成熟的DXF处理库。它不只能读取,还能创建和修改DXF文件。对于建筑图纸,我们主要提取这几类内容:
- 图层信息,每个图层名称通常对应着“墙”、“门窗”、“标注”、“轴线”等语义。
- TEXT/MTEXT实体,这是图纸里的文字标注。
- INSERT实体,即块引用,通常代表门窗、家具、设备等。
- LINE/LWPOLYLINE/ARC实体,即基础几何轮廓。
import ezdxf # 打开DXF文件 doc = ezdxf.readfile("drawing.dxf") msp = doc.modelspace() # 遍历所有实体 for entity in msp: if entity.dxftype() == "TEXT": print("文本:", entity.dxf.text, "位置:", entity.dxf.insert) elif entity.dxftype() == "INSERT": print("块插入:", entity.dxf.name, "位置:", entity.dxf.insert) elif entity.dxftype() == "LWPOLYLINE": print("多段线,顶点数:", len(entity.get_points()))这个组合方案在实际运行中非常稳定。唯一需要注意的是,ODA File Converter不是完全开源的,不过对于绝大多数个人项目和小团队来说,免费授权足够用了。
2.4 Revit文件解析的“曲线救国”路线
至于Revit的RVT文件,项目并没有直接去解析,而是走“先导出IFC再解析”的路线。这个设计很聪明,因为RVT是封闭的,想直接读取geometrical data,要么用Revit API,要么逆向工程,成本极高。而BIM软件本身支持的IFC导出,已经能把绝大部分构件语义和几何信息带出来了。
这里我补充一个实操经验:用Revit导出IFC时,一定要在导出设置里勾选“Split Walls and Columns by Level”、“Export 2D Elements”这些选项,否则导出的IFC模型在楼层划分和二维图元上会丢失信息。另外,Revit导出IFC有两种格式,一种是IFC2x3 Coordination View,另一种是IFC4 Reference View。如果是给AI做分析用,建议优先用IFC4,因为它的属性表达能力更强,构件类型也更细。
3. 实操:从零跑通“图纸转AI可读数据”
这部分我拿一个实际案例来演示完整流程。假设我们有一个三层办公楼的Revit模型,已经用Revit导出了IFC文件,同时还有一张地下室平面图的DWG,现在要把它们全部转成JSON,然后交给大模型回答问题。
3.1 环境准备
需要准备的环境很简单,一台Linux或者Windows电脑就行,不需要安装任何Autodesk产品。核心依赖是Python 3.9以上,加上几个库。
pip install ifcopenshell ezdxf shapely还要下载ODA File Converter,安装后确认命令行可用。在Windows上,它通常位于“C:\Program Files\ODA\ODAFileConverter\ODAFileConverter.exe”,在Linux上则需要用wine运行,或者下载Linux原生版。
3.2 IFC文件的信息提取
首先处理IFC文件。我的目标是提取出每一层楼的房间面积、墙体体积、门窗数量,以及这些构件的属性信息。
import ifcopenshell import ifcopenshell.util.element as element import json # 打开IFC文件 ifc_file = ifcopenshell.open("office_building.ifc") # 获取所有楼层 storeys = ifc_file.by_type("IfcBuildingStorey") result = {"storeys": []} for storey in storeys: storey_data = { "name": storey.Name, "elevation": storey.Elevation if hasattr(storey, "Elevation") else None, "walls": [], "doors": [], "windows": [], "spaces": [] } # 获取楼层中包含的构件 contains_elements = ifcopenshell.util.element.get_contained_elements(storey) for element_type in contains_elements: # 墙 if element_type.is_a("IfcWall"): wall_psets = element.get_psets(element_type) storey_data["walls"].append({ "name": element_type.Name, "height": wall_psets.get("Pset_WallCommon", {}).get("Height"), "area": wall_psets.get("Qto_WallBaseQuantities", {}).get("NetSideArea"), "volume": wall_psets.get("Qto_WallBaseQuantities", {}).get("NetVolume") }) # 门 elif element_type.is_a("IfcDoor"): storey_data["doors"].append({"name": element_type.Name}) # 窗 elif element_type.is_a("IfcWindow"): storey_data["windows"].append({"name": element_type.Name}) # 获取房间空间 spaces = ifcopenshell.util.element.get_contained_elements(storey, ifc_type="IfcSpace") for space in spaces: space_psets = element.get_psets(space) storey_data["spaces"].append({ "name": space.Name, "area": space_psets.get("Qto_SpaceBaseQuantities", {}).get("NetFloorArea"), "volume": space_psets.get("Qto_SpaceBaseQuantities", {}).get("NetVolume") }) result["storeys"].append(storey_data) # 导出JSON with open("ifc_output.json", "w") as f: json.dump(result, f, ensure_ascii=False, indent=2) print("IFC解析完成,共", len(result["storeys"]), "个楼层")这一点我需要说一下,如果你直接用官方API硬啃IFC文件,可能得研究半天IfcRelContainedInSpatialStructure的关系链,而用ifcopenshell.util.element.get_contained_elements()这个方法,一下子就把楼层和构件的包含关系拿出来了。这就是站在前人的肩膀上。
3.3 DWG文件的批量转码与提取
接下来处理DWG格式的地下室平面图。我这里有两张DWG,都是AutoCAD 2018格式,直接用ezdxf读取会报“DWG is not supported, use DXF”之类的错误。所以先交给ODA File Converter转成DXF。
命令行方式如下:
# 先把所有DWG转成DXF ODAFileConverter "input_dir" "output_dir" "ACAD2018" "DXF" "0" "1" "*.dwg"这个命令的含义是:把input_dir下的所有DWG文件,转成AutoCAD 2018版本的DXF文件,输出到output_dir。参数0代表输出文件类型为DXF,1覆盖同名文件。
转换完成后,再用ezdxf读取DXF。我写了一个提取图层、文字标注和块引用的脚本。
import ezdxf import json doc = ezdxf.readfile("basement_plan.dxf") msp = doc.modelspace() layers = set() texts = [] blocks = [] for entity in msp: layers.add(entity.dxf.layer) if entity.dxftype() == "TEXT": texts.append({ "text": entity.dxf.text, "position": list(entity.dxf.insert) }) elif entity.dxftype() == "MTEXT": texts.append({ "text": entity.text(), "position": list(entity.dxf.insert) }) elif entity.dxftype() == "INSERT": blocks.append({ "name": entity.dxf.name, "position": list(entity.dxf.insert) }) result = { "layers": list(layers), "texts": texts, "blocks": blocks } with open("dwg_output.json", "w") as f: json.dump(result, f, ensure_ascii=False, indent=2) print("DWG解析完成,共", len(layers), "个图层,", len(texts), "条文字标注,", len(blocks), "个块引用")这个脚本跑完后,我拿到的JSON里能看到“轴网”、“墙体”、“门”、“窗”、“标高”这些图层名,以及“C-1”、“M-1”、“FM甲-1”这类门窗编号,还有“-4.200”、“±0.000”这样的标高标注。这些信息对于AI理解图纸内容非常有价值。
3.4 合并数据并喂给大模型
IFC和DWG都解析完之后,下一步就是把两种数据合并成一个总JSON,然后根据场景组装Prompt,调用大模型API进行问答。
我最常用的做法,是把JSON转成一段格式化的纯文本,然后用“角色设定+任务指令+文本数据+问题”的结构构造Prompt。
import json import openai # 读取解析后的IFC和DWG数据 with open("ifc_output.json") as f: ifc_data = json.load(f) with open("dwg_output.json") as f: dwg_data = json.load(f) # 组装文本描述 building_desc = "这是一栋三层办公楼。\\n" for storey in ifc_data["storeys"]: building_desc += f"楼层{storey['name']},标高{storey['elevation']}米:\\n" building_desc += f"- 包含墙构件{len(storey['walls'])}个,门{len(storey['doors'])}个,窗{len(storey['windows'])}个\\n" for space in storey["spaces"]: building_desc += f"- 房间{space['name']},面积{space['area']}平方米,体积{space['volume']}立方米\\n" building_desc += "地下室平面图信息:\\n" building_desc += f"- 图层:{', '.join(dwg_data['layers'])}\\n" building_desc += f"- 门编号:{', '.join([b['name'] for b in dwg_data['blocks'] if '门' in b['name']])}\\n" # 调用大模型 client = openai.OpenAI(api_key="your-api-key") response = client.chat.completions.create( model="gpt-4o-mini", messages=[ {"role": "system", "content": "你是资深建筑设计师,请根据图纸数据回答用户问题。"}, {"role": "user", "content": f"图纸数据如下:\\n{building_desc}\\n\\n问题:这栋楼一共有多少扇门?每层面积最大的房间是哪个?"} ] ) print(response.choices[0].message.content)这个流程跑通之后,你就会发现大模型对图纸的理解能力立刻上了一个台阶。它能根据你提供的数据回答“总共有多少门”、“三层哪个房间面积最大”、“地下室有多少种门编号”这类问题。虽然数据源仍然是结构化的,但对使用者来说,体验已经是自然语言对话了。
3.5 进阶:用向量检索做超大图纸的问答
遇到超大项目时,一次把整个模型塞进Prompt会超出上下文窗口,或者导致成本飙升。我用的方案是先把解析出的构件信息做向量化,存入向量数据库,再配合RAG(检索增强生成)回答。
每个构件或房间作为一个文档,例如“三层会议室的净面积是85平米”、“首层大门采用的是FM甲-1甲级防火门”。然后用Embedding模型转成向量,存到Chroma或者Qdrant里。查询的时候,先检索相关的构件信息,再扔给大模型归纳。
这个进阶方案我在几个真实项目里验证过,效果比一次性全量注入好很多,特别是在处理十万级以上构件量的模型时,速度、成本、准确率都能兼顾。
4. 拿到数据之后,能干什么实在事
把图纸转成AI可读数据只是第一步,真正有想象力的地方在下游应用。
4.1 AI辅助审图与合规检查
审图是建筑设计流程里非常耗费人力的环节。传统审图需要设计师和审查人员一页一页翻图纸,对照规范找错误。现在有了结构化数据,完全可以做一个自动合规检查工具。
比如,你可以定义一系列规则:“楼梯梯段净宽不小于1.2米”、“走廊净高不小于2.4米”、“防火门等级不低于乙级”,然后用脚本批量检查IFC数据里的构件属性,不符合的自动标红。你也可以把规范文本和图纸数据一起喂给大模型,让它做自由文本的合规审查,效率比纯人工高很多。
我试过用这个思路处理一个中型项目的结构图,把墙厚、混凝土标号、配筋信息提取出来,然后跟结构规范做比对,半小时内找出了三处疑似不满足规范的地方,设计师复核后确认两处确实是设计疏忽。
4.2 工程量自动统计与造价估算
从IFC里能拿到墙的体积、梁的截面尺寸、门的数量、窗的面积,这些都是工程量清单的基础数据。虽然目前传统的工程量计算软件已经很成熟,但它的流程是封闭的,生成的数据难以直接供AI分析。
而有了一个可编程的“数据中台”,你可以随时用Python脚本统计任意维度的工程量,然后对接造价库做快速估价。比如三层总共需要多少立方米的C30混凝土,有多少平方米的断桥铝合金窗,这些数据可以直接汇总成表格,供商务标编制和成本管理使用。
4.3 基于图纸的智能搜索与问答
很多设计院和施工企业都有大量的历史图纸,这些图纸散落在各个工程师的电脑里,查找起来极其痛苦。利用这个转换工具,你可以把所有历史图纸解析成统一的文档库,然后做一个企业内部的“图纸搜索引擎”或者“问答机器人”。
建筑师只需要问“之前那个医院项目里,门诊楼的抗震等级是多少?”系统就会从对应图纸的数据中检索出答案。这个应用对老图纸特别有价值,因为很多老项目的原始信息只存在于DWG文件里,施工人员去现场之前想查点资料,还得翻半天CAD才能打开看。
5. 常见问题与排查技巧实录
实操过程中绝对会遇到各种坑,这里整理几个最常见的问题和我的排查方法。
5.1 ODA File Converter转换失败
这个工具对高版本DWG的支持有时候会出现问题,特别是一些用天正CAD(基于AutoCAD二次开发)画的图纸。天正图元写入的是自定义对象,ODA转换之后容易出现“代理实体”或者直接丢失信息。
遇到这类图纸,我一般是先用天正CAD自身导出T3格式,再走ODA转换。或者直接在AutoCAD里用EXPORTTOAUTOCAD命令把天正对象分解成普通实体。转换后先用ezdxf快速扫一遍,看看图层和文字是否完整,再做后续解析。
5.2 Revit导出IFC时构件丢失
这个我遇到过好几次,典型场景是Revit模型里的“自适应族”或者“基于面的族”,导出IFC时会变成一堆没有语义的“IfcBuildingElementProxy”,属性全丢。解决办法是,在Revit中导出IFC之前,把族类别改成对应的系统族类型,比如把幕墙嵌板族改成IfcCurtainWall。另外,导出设置里要勾选“Export Revit property sets”和“Export IFC common property sets”,这两个选项非常关键,不勾的话导出文件里几乎没有属性信息。
5.3 JSON文件过大导致模型上下文溢出
处理大体量项目时,生成的JSON可能达到几十兆,直接把文本塞进Prompt会超出上下文窗口。我的经验是,根据问题类型做有针对性地裁剪。如果是问门窗数量,那只需要提取构件类型和数量字段,不需要几何坐标;如果是问空间关系,则保留空间结构树和连接关系。另一个方案是用RAG,前面说过了,这是最可扩展的路径。
5.4 DWG坐标单位不统一
很多DWG文件没有遵守“毫米和米”的规范,有的用毫米,有的用英寸,还有CAD制图习惯直接用“1绘图单位=1米”的。解析出来的坐标如果不做单位换算,喂给AI就会产生严重误导。我的做法是在解析脚本里统一做分层处理,根据图纸里的标高标注和尺寸标注推算单位比例,然后统一换算成毫米存储。这个步骤没有通用解法,只能针对具体图纸做一些规则化处理。
5.5 格式解析问题速查表
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| IFC解析出来没有属性信息 | Revit导出未勾选属性选项 | 重新导出,勾选属性集选项 |
| DWG转DXF后缺少文字 | 天正自定义对象未分解 | 先导出T3格式,再转换 |
| DXF图层名乱码 | 编码问题 | 设置dxf.readfile参数encoding="gbk" |
| 模型单位混乱 | 不同专业图纸混用单位 | 统一转换为毫米,按比例尺换算 |
| 构件类型为Proxy | 族类别未正确映射 | 修改信息,使用Revit中导出设置调整族类型 |
| 大模型回答不准 | 数据裁剪过狠或格式不友好 | 调整Prompt,增加上下文语境说明 |
最后再分享一个小技巧
项目用了一段时间之后,我觉得最有价值的一个“隐藏功能”是把转换出来的JSON当作微调数据集的生成器。你可以用大模型对同一批JSON数据生成不同角度的问题和答案,人工验一遍之后,就得到了一批高质量的行业问答数据,拿来做模型微调或者评估集都很好用。
我自己目前就在用一个更大的本地测试集,解析了大概一百多个住宅楼的IFC模型,生成了三千多道建筑规范相关的问题,用它们评估不同大模型在建筑领域的表现。实测下来,数据质量直接影响模型表现,而这个项目等于帮我搞定了一整套数据管线。
如果你也在做建筑AI相关的事情,这个“图纸数据桥”的思路值得借鉴。别一上来就盯着Revit二次开发,先把数据翻成一个通用格式,后面很多事情都会顺畅很多。