图纸自动识别:从DWG解析到结构化数据提取的工程实践
每次拿到一套CAD图纸,我脑子里第一个冒出来的念头就是:这套图纸到底有多少信息是结构化的?
图层命名规范的设计院,图层里存着完整的设计数据;图层命名混乱的图纸,连哪根线是墙哪根线是管都要靠猜。
今天聊的是CAD图纸自动识别——不是把DWG转成图片,不是OCR识别,而是 从DWG文件的底层数据结构里,把图纸信息一层层挖出来。
一、CAD图纸识别的三个技术层级
很多人以为CAD识别就是"CAD转图片然后OCR识别",这是一个严重的误解。
CAD文件(DWG格式)本质上是结构化的矢量数据库,它存储了比图像多得多的信息。一个标准的DWG文件包含:
| 数据层 | 内容 | 可用性 |
| 图形数据 | 线条、弧线、多段线、填充的坐标信息 | 几何计算基础 |
| 图层结构 | 图层名称、线型、颜色、线宽 | 构件分类基础 |
| 图块数据 | Block定义、属性值(Attribute)、扩展数据(XDATA) | 规格信息提取 |
| 文字数据 | 单行文字、多行文字内容及位置 | 标注识别基础 |
| 扩展数据 | 设计院自定义的业务字段 | 高价值结构化数据 |
所谓CAD图纸识别,就是逐层解析这些数据,并结合AI视觉模型补充图形层面的信息。
技术实现上,分为三个层级:
CAD图纸识别三层架构 第一层:DWG文件结构解析 ├── 文件头解析(DWG版本、编码、对象表) ├── 实体遍历(Entity traverse,按Handle逐个读取) ├── 图层表读取(Layer table,获取所有图层及属性) ├── 图块表读取(Block table,获取所有图块定义) └── 文字表读取(Text style table,字体映射) 第二层:图形视觉识别 ├── 线条几何分类(墙体/管线/轮廓) ├── 空间关系推理(包含、相邻、平行判断) ├── 填充区域检测(房间边界、面积区域) └── 比例尺与单位解析 第三层:语义理解与结构化输出 ├── 构件语义分类(门窗/墙体/设备/管线) ├── 属性字段映射(将原始属性映射为标准字段) ├── 几何参数计算(面积/长度/高度) └── 数据校验与异常检测
二、第一层:DWG文件结构解析
2.1 从文件头开始
DWG文件不是透明的二进制格式,不同版本(DWG 2013/2018/2024)的文件结构差异很大。解析的第一步是识别版本,然后按版本对应的文件结构读取对象表。
AutoCAD的DWG文件结构大致如下:
DWG File Structure ├── File Header(文件头) │ ├── 版本标识(ACxxxx) │ ├── 编码信息 │ ├── 缩略图数据(可选) │ └── 预览图像偏移量 ├── Class Definitions(类定义表) ├── Object Map(对象索引表) ├── Handles Dictionary(句柄字典) └── Object Data(对象数据区) ├── Entities Section(实体区:图形对象) ├── Tables Section(表区:图层/线型/文字样式等) ├── Blocks Section(图块区:Block定义) └── Objects Section(其他对象)
解析过程中,最核心的是实体区(Entities Section)和图层表(Layer Table)。实体区存放了所有图形对象(线条、弧线、文字、填充等),图层表存放了每个图层的名称和属性。
# DWG实体解析示意(伪代码) def parse_entities(file): entities = [] # 读取实体区起始位置 entities_start = file.get_section_offset("ENTITIES") # 逐个读取实体对象 current_pos = entities_start while current_pos < entities_end: entity = read_entity_header(file, current_pos) if entity.type == "LINE": data = parse_line_entity(file, entity) elif entity.type == "LWPOLYLINE": data = parse_polyline_entity(file, entity) elif entity.type == "INSERT": data = parse_block_insert(file, entity) # 图块插入 elif entity.type == "MTEXT": data = parse_mtext_entity(file, entity) # 多行文字 elif entity.type == "HATCH": data = parse_hatch_entity(file, entity) # 填充区域 data.layer = entity.layer # 关联图层信息 data.handle = entity.handle # 唯一句柄 entities.append(data) current_pos = entity.next_offset return entities
2.2 图层表:信息的第一个过滤器
图层表(Layer Table / Layer Table Record)存储了所有图层的元数据。设计院在绘图时,会把不同类型的构件放到不同图层里——虽然命名规范参差不齐,但图层本身就是第一层分类依据。
# 图层表读取示意(伪代码) def parse_layer_table(file): layers = {} # 获取图层表在文件中的位置 layer_table_handle = file.header.layer_table_handle # 读取图层表对象 layer_table = file.resolve_handle(layer_table_handle) for record in layer_table.records: layer_name = record.name.decode("utf-8", errors="ignore") layer_color = record.color # ACI颜色索引 layer_linetype = record.linetype # 线型 layer_lineweight = record.lineweight # 线宽 layer_flags = record.flags # 图层状态标志 layers[layer_name] = { "name": layer_name, "color": layer_color, "linetype": layer_linetype, "lineweight": layer_lineweight, "is_off": bool(layer_flags & 0x01), # 是否隐藏 "is_frozen": bool(layer_flags & 0x02) # 是否冻结 } return layers
这一步的价值在于:即便设计院的图层命名不规范,也能通过图层颜色+线型+线宽的组合特征,对图层进行语义分类。
2.3 图块属性:图纸里藏着的结构化数据
很多设计院在绘图时,会用**属性图块(Attribute Block)**来标注构件规格。比如门窗图块里会包含:
-
门窗编号(如"M0921")
-
门窗类型(如"推拉门")
-
洞口尺寸(如"900×2100")
-
材质信息(如"铝合金")
这些信息在图块属性里存着,但肉眼打开CAD时往往只看到图块图形,看不到属性值——而属性值往往就是你要提取的数据。
# 图块属性提取示意(伪代码) def extract_block_attributes(file, block_ref): # block_ref 是图块插入点(INSERT entity) block_name = block_ref.block_name # 图块名称,如"WINDOW_铝合金推拉门" # 获取图块定义 block_def = file.get_block_definition(block_name) attributes = {} # 遍历图块内的属性定义(Attribute Definition) for attr_def in block_def.attribute_definitions: field_name = attr_def.tag # 属性标签,如"WIDTH"、"HEIGHT" prompt = attr_def.prompt # 属性提示,如"请输入宽度" # 找到图块引用中对应的属性值 attr_value = block_ref.get_attribute_value(field_name) if attr_value: attributes[field_name] = { "value": attr_value, "prompt": prompt, "position": attr_def.position, "style": attr_def.text_style } return { "block_name": block_name, "position": block_ref.position, "rotation": block_ref.rotation, "scale": block_ref.scale, "attributes": attributes }
这一步是CAD图纸识别里信息密度最高的一步——设计院通过属性图块存的规格数据,往往比图纸上的标注文字更准确、更结构化。
三、第二层:图形视觉识别
3.1 线条分类:从几何到语义
有些图纸里,图块用得不多,大量构件还是用普通线条画的。这种情况下,需要通过几何特征来判断线条是什么构件。
# 线条构件分类示意(伪代码) def classify_entity(entity, layers, context): layer_name = entity.layer geometry = analyze_geometry(entity) # 规则一:图层名语义映射 if is_architectural_layer(layer_name): return classify_by_architectural_rules(entity, geometry) # 规则二:几何特征判断 if is_closed_loop(entity) and has_parallel_pairs(entity): # 封闭图形+有平行边对 → 可能是门窗洞口 return classify_door_window(entity, geometry) if is_filled_region(entity) and is_large_area(entity): # 大面积填充 → 可能是房间或功能区域 return classify_room(entity, geometry) # 规则三:空间上下文推理 nearby_entities = context.get_nearby(entity, radius=100) for nearby in nearby_entities: if nearby.is_window() and entity.near(nearby): return "窗台下墙" return classify_by_geometry(geometry)
3.2 填充区域:房间边界的识别
建筑CAD图纸里,房间边界通常用填充(Hatch)来表示。识别填充区域的边界,可以提取房间面积、功能分区等关键数据。
填充识别有两个难点:
-
填充边界可能由多段线条组成,需要追踪每段线条的端点,构建完整的多边形
-
嵌套填充(填充里套着另一个填充)需要分层处理
# 房间填充区域提取(伪代码) def extract_room_boundaries(file): rooms = [] for hatch_entity in file.entities.filter(type="HATCH"): # 获取填充的边界路径 boundary_paths = hatch_entity.get_boundary_paths() # 每一层边界路径构成一个闭合多边形 for path in boundary_paths: if path.is_outer(): # 外边界 = 房间轮廓 vertices = trace_boundary(path) area = calculate_polygon_area(vertices) # 获取房间名称(通常在填充中心有文字标注) center = calculate_centroid(vertices) room_label = find_text_at(file, center, radius=50) rooms.append({ "boundary": vertices, "area": area, "label": room_label, "layer": hatch_entity.layer }) return rooms
四、第三层:语义理解与结构化输出
4.1 构件语义分类
几何识别解决的是"这根线条是什么形状",语义分类解决的是"这个形状代表什么构件"。
建筑图纸里的构件分类体系,参考《房屋建筑制图统一标准》GB/T 50001-2017:
| 构件类别 | 常见图层名关键词 | 几何特征 |
| 墙体 | WALL, Q, Q_*, 砌体 | 连续平行线对,间距60-400mm |
| 门窗 | DOOR, WINDOW, M, C, MC | 封闭矩形,内部门窗线 |
| 柱子 | COLUMN, Z, ZH | 矩形或圆形封闭图形 |
| 梁 | BEAM, L, GL | 与墙体平行或垂直 |
| 楼梯 | STAIR, LT | 踏步等距线+箭头 |
| 设备 | EQUIP, EQ, SB | 图块,规格在属性里 |
4.2 数据标准化与输出
提取的原始数据需要映射到标准字段,才能被下游系统使用。
# 构件数据标准化输出(伪代码) def normalize_to_standard_format(raw_data): output = [] for entity in raw_data: normalized = { "id": generate_uuid(), "category": map_to_standard_category(entity.type), "layer": entity.layer_original, "position": { "x": entity.position.x, "y": entity.position.y, "z": entity.position.z or 0 }, "geometry": { "type": entity.geometry_type, "area": entity.area if hasattr(entity, 'area') else None, "length": entity.length if hasattr(entity, 'length') else None, "dimensions": entity.dimensions }, "attributes": normalize_attributes(entity.attributes), "confidence": entity.classification_confidence, "source_file": entity.source_file } output.append(normalized) # 导出为结构化格式 return { "version": "1.0", "export_time": get_current_timestamp(), "total_entities": len(output), "categories": group_by_category(output), "data": output }
五、真实项目数据
项目背景:某机电安装工程,工程量清单编制阶段需要从设计院提供的暖通CAD图纸中提取设备型号、数量、安装位置,共约60张系统图和平面图。
处理结果:
| 指标 | 传统方式(人工读取) | BeesFPD CAD解析 |
| 60张图纸耗时 | 约8-10小时 | 约40分钟 |
| 数据格式 | 人工录入,格式不统一 | 标准化构件清单 |
| 错误率 | 约3-5% | 低于1% |
| 数据可用性 | 整理后才能使用 | 可直接导入算量软件 |
| 周期节省 | — | 约7小时/批次 |
六、技术边界说明
能做:
-
标准图层命名图纸的批量结构化解析
-
属性图块(Attribute Block)规格信息提取
-
填充区域(房间面积)自动计算
-
多图层、多比例图纸批量处理
-
数据导出为Excel、CSV、JSON格式
不能(目前阶段):
-
非标准图层命名图纸的完全自动分类(需要人工辅助映射)
-
复杂曲线构件(如渐变曲线、参数化构件)的精确提取
-
直接生成完整的BIM模型(这是Revit的工作,不是CAD解析的工作)
七、工具与规范参考
工具:元启数宇BeesFPD图纸解析系统
在线使用,无需安装:www.beesfpd.com
支持DWG 2013及以上版本,支持批量上传和结构化数据导出。
参考规范:
-
GB/T 50001-2017《房屋建筑制图统一标准》
-
GB/T 50104-2010《建筑制图标准》
-
GB/T 50106-2010《建筑给水排水制图标准》
-
JGJ/T 244-2011《房屋建筑修缮工程图纸数字化标准》
结语
CAD图纸识别这件事,真正的价值不在于"识别"本身,而在于把散落在DWG文件里的结构化数据提取出来——属性图块里的规格信息、图层里的设计意图、填充区域里的面积数据。
这些数据本来就在图纸里,只是以前要靠人眼去找、人手去录。AI做的是把这件事变成批量自动化的。
下次拿到一套CAD图纸,别急着打开AutoCAD,让解析引擎先跑一遍,看看能挖出多少你不知道的数据。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐



所有评论(0)