泵与风机选型 V2.0(Read 读图验证法)
Overview
从厂商设备样本 PDF(扫描版或 OCR 版)中,按工况参数(风机:风量 m³/h + 全压 Pa;泵:流量 + 扬程)批量选出满足要求的设备型号,并完成校核、填表、输出报告。
V2.0 相对 V1.0 的关键升级:数据提取必须用 Read 工具直接读图验证,不能依赖 RapidOCR 或文本层 dump 的数字。这是 53 台风机批量选型实战反复踩坑后沉淀的结论。
核心教训(最重要,必读)
对扫描/OCR 版 PDF,任何自动文本识别(PyMuPDF 文本层、RapidOCR)都会产生数字错位、错行、甚至编造不存在的数值。实战中先后出现:
- 文本层把 SCF 页数据错位混进 HTFC 页,凭空生成 5 个假转速档(610/680/725/820/900)
- RapidOCR 把相邻转速行的数据错位(HTFC-28 690 行读成 730 行的数)
- 低分辨率读图(scale 2.5)系统性把全压读高 30~75 Pa
- 把「尺寸」列误当「重量」列(KFV 净重 6 kg 被读成 97 kg)
- 转速档名整体错位一档(HTFC-25 的 520 实为图上 450)
- 把不存在的风量值混入(HTFC-12 1300 档出现「8530」,实为 1200 档的 7173 误读)
- 电机极数误标(HTFC-20 720rpm 标成 6 极,实为 4 极)
结论:只有 Read 工具直接"看"高分辨率渲染图,才能可靠读出数字。
Workflow
Step 0:明确选型需求
- 设备类型、系列要求(用户可能指定优先系列)
- 每台设备的工况:风机 Q(m³/h)+P(Pa);泵 Q+扬程
- 选型系数规则(风量预留系数、全压要求)
- 特殊约束:单速/双速、防爆、噪声限值等
- 输出目标:Excel 参数表 / 报告
Step 1:定位章节页码
python scripts/extract_pdf_table.py --pdf "<样本.pdf>" --scan --keyword "SCF"
scan 结果只用于定位页码,绝不直接采信其数字。
Step 2:渲染高分辨率图 + Read 直接读图(V2.0 核心)
这是唯一可靠的数据提取方法。
2a. 渲染整页或裁剪区域
# render_page.py — 通用渲染脚本
import sys, fitz
pdf = sys.argv[1]; page = int(sys.argv[2]); scale = float(sys.argv[3]); out = sys.argv[4]
doc = fitz.open(pdf)
p = doc[page-1]
pix = p.get_pixmap(matrix=fitz.Matrix(scale, scale))
pix.save(out)
print(f"saved {out} size={pix.width}x{pix.height}")
python render_page.py "<样本.pdf>" 4 2.0 "p04.png"
- 整页读图:scale 2.0 足够(约 2450×1650 px,Read 能读)
- 表格密、数字小时:scale 4~6 裁剪目标机号区域再读
- 图片高度 >4000 px 会导致 Read 失败或 cache 命中异常,超大表必须分块裁剪(上下切半或按机号切块)
2b. 用 Read 工具读图,人工核对数字
用 Read 工具打开渲染出的 PNG,直接读表。核对要点:
- 每行(转速档)应有几个工况点,风量从大到小、全压从小到大(Q-P 曲线下降)
- 相邻转速行不要错位(高转速风量更大、全压更高)
- 机号标签与数据行对齐
- 电机型号、功率、噪声、重量逐列核对(重量列别和尺寸列搞混)
2c. 避免 Read cache 命中
Read 对相同内容图片会返回 "already loaded"。换新文件名重新渲染(如 v2_p04.png)即可绕过 cache。
Step 3:数据层(fan_data.py)
把核对后的数据集中写进 Python dict,便于选型算法消费:
HTFC_SERIES = {
9: [ # 机号 9
{'rpm': 1200, 'points': [(3150,283),(2785,312),(2465,341),(2030,369),(1770,395)],
'motor': 'YE2-80M1-4', 'power_kw': 0.55, 'noise_dba': 58, 'weight_kg': 84},
# ... 更多转速档
],
}
GDF_SERIES = {'GDF3.0-4': {'rpm':1380, 'points':[(2510,370),(2000,515),(1620,578)], ...}}
KFV_SERIES = {...}
SCF_SERIES = {...}
注意:小机号可能只有 1~3 个工况点,不是每档都 5 个。
Step 4:选型算法(select_fan.py)
选型分两层:正常选型 + 放宽模式。实战中用户会逐步收紧约束,最终规则如下。
正常选型(①~⑧ 步,全压完全满足):
- 类型优先级(用户指定,如 HTFC → GDF → KFV → SCF)
- 风量系数:标内
1.05~1.10;无合适时放宽到1.20(标号小优先) - 全压:
设计全压 ≤ P选 ≤ 1.5×设计全压(下限满足设计;上限富余不超过 50%) - 排序:标号小 → 风量贴中(1.075×Qd) → 全压小(节能)
放宽模式(⑨ 步,正常选型无解时)——核心约束(实战沉淀):
- 风量系数 ≥1.05 是硬下限:风量不允许放宽(不得小于 1.05 倍设计风量)
- 全压放宽比例 ≤40%(下限约束):即选型全压 ≥ 0.6 × 设计全压(全压不能太低)
- 全压富余比例 ≤50%(上限约束):即选型全压 ≤ 1.5 × 设计全压(全压不能太高,避免电机功率/噪声/成本过大)
- 风量可放大:若全压放宽会超 40%,放大风量找全压更高的工况点
- 目标函数:满足「风量 ≥1.05×Qd 且 0.6×Pd ≤ 全压 ≤ 1.5×Pd」的所有工况点中,全局跨系列选风量最接近设计风量的(风量系数最小)
> ⚠️ 关键陷阱:放宽模式必须全局跨系列比较风量,不能按系列优先级先找 HTFC。否则会因 HTFC 优先而把风量放大到 2 倍去选 HTFC-9(如 840 m³/h 需求被选成 1770 m³/h 的 HTFC-9),而忽略了 GDF 里风量 1.19 倍更贴近的解。
def select_one(q_d, p_d):
# ①~⑧:正常选型(标内 1.05~1.10 + 风量放宽 1.05~1.20,p_d≤全压≤1.5p_d,系列优先级)
# ⑨:放宽模式
p_floor = p_d * 0.6 # 全压下限:放宽 ≤40%
p_ceil = p_d * 1.5 # 全压上限:富余 ≤50%
q_lo_r = q_d * 1.05 # 风量下限 1.05(硬约束,不放宽)
# 全局收集 风量≥q_lo_r 且 p_floor≤全压≤p_ceil 的工况点(跨 HTFC/GDF/KFV/SCF)
# 排序:风量系数最小优先(最接近设计风量);同系数选全压更接近设计值
两个比例公式:
- 全压比例 = (P选 − P设计) / P设计 × 100%(范围 −40% ~ +50%;负值=放宽,正值=富余)
- 风量放宽比例 = (Q选 − Q设计) / Q设计 × 100%(正值,≥+5%)
Step 5:填表(fill_table.py)
用 openpyxl 读原表模板,从第 5 行起写入选型结果:
ws.cell(row=r, column=19, value=f'+{q_over_pct:.1f}%') # 风量放宽比例列(正值)
ws.cell(row=r, column=20, value=f'-{relax_pct:.1f}%') # 全压放宽比例列(负值)
- 保留原表头(第 1~4 行),只填数据行
- 备注列写「全压放宽 xx%」「风量放大至 xx 倍」「双速电机」等
- 最右两列(用户要求,均不写
0%,正负号表示"相对设计值的余量/不足"): - 风量放宽比例列(S 列,恒为正值,=(Q选−Q设)/Q设×100%,≥+5%)
- 全压比例列(T 列):全压满足(P选≥P设)时写正向余量
+xx%;全压放宽(P选<P设)时写负值-xx%。不写0%。 - 文件锁定处理:若原 Excel 被腾讯文档/Office 预览占用(
PermissionError: [Errno 13]),先复制到临时文件、用临时路径跑 fill、再shutil.copy2写回原文件。 - 重跑填表不覆盖字体色:fill_table 只写
cell.value不改cell.font,所以标红等格式会保留。若需重新标色,另行单独处理字体。
Step 6:需求变更后增量重跑
用户调整某几台设备工况后,不必全量重新读图,只需:
- 直接改
fan_data.py中对应DEMANDS元组(如(20, 'PF-WD-20', 11130→11310, 350, ...)) - 重跑
select_fan.py→fill_table.py - 校验硬约束:空选=0、风量<1.05=0、全压放宽>40%=0
- 注意:若用户重置了输出表为原始模板(列数可能从 20 缩回 18),fill_table 需重新补第 19/20 列,且留意第 4 列字段名("风机型号" vs "参考型号")
Step 7:版本对比与标色(常见后续需求)
用户常要求「对比两个版本的选型结果,标出有差异的设备」。做法:
import openpyxl
from openpyxl.styles import Font
# 按设备编号建映射:code -> (行号, 风量, 全压)
def load(ws):
d = {}
for r in range(5, ws.max_row+1):
code = ws.cell(row=r, column=2).value
if code is None: continue
d[code] = (r, ws.cell(row=r, column=7).value, ws.cell(row=r, column=8).value)
return d
# 风量或全压任一不同 → 标红(红色字体 FF0000)
red = Font(color='FF0000')
for code, (nr, nq, np_) in new_d.items():
if code not in old_d: continue
_, oq, op_ = old_d[code]
if nq != oq or np_ != op_:
for c in range(1, new_ws.max_column+1):
cell = new_ws.cell(row=nr, column=c)
if cell.value is not None:
cell.font = red
- 差异对比列 = 风量(第 7 列)+ 全压(第 8 列)
- 标色用字体红色(
Font(color='FF0000')),用户说"颜色改为红色"指字体色而非底色 - 若用户说"底色/黄色底色",则改
cell.fill = PatternFill(start_color='FFFF00', fill_type='solid')
关键经验(避免重复踩坑)
- Read 读图 > RapidOCR > 文本层:数据提取首选 Read 读图;OCR 只用于定位。
- 低分辨率读图会系统性偏差:scale 2.5 读数普遍偏高/错位,关键数据用 scale 4~6 裁剪读图。
- 图片尺寸限制:高 >4000px 的图 Read 会失败或 cache 异常,超大表分块。
- Read cache:相同内容返回 "already loaded",换文件名重渲染绕过。
- 重量列 ≠ 尺寸列:净重通常较小(几 kg~几百 kg),尺寸(长宽高 mm)容易混入。
- 转速档名会整体错位:核对时以图上机号标签 + 数据行对齐为准,不盲目信任代码里已填的 rpm 值。
- 相邻转速行数据会互换:核对时看风量范围是否随转速递增。
- 小机号工况点少:1~3 个工况点正常,别硬凑 5 个。
- Windows 路径:脚本写到工作区绝对路径,Python 用受管 venv(
C:/Users/ZJ/.workbuddy/binaries/python/envs/default/Scripts/python.exe)。 - 批量选型务必多次读图复核:用户可能逐台反馈错误,每次反馈都触发「重新读图核对整页」,而不是只改那一处。
- 放宽模式必须全局比风量,不能系列优先级:全压放宽阶段目标是「风量最接近设计值」,若按系列优先级先找 HTFC,会把小风量需求放大 2 倍选 HTFC-9,忽略 GDF 里风量 1.2 倍更贴近的解。
- Excel 被占用时用临时文件写回:目标文件被腾讯文档/Office 预览锁定会报
PermissionError: [Errno 13]。先shutil.copy2到临时路径跑 fill,再shutil.copy2写回原文件即可。 - 比例列不写 0%:用户明确要求"不要写 0"。风量列恒为正(≥+5%);全压列满足时写正向余量
+xx%,放宽时写负值-xx%,两列统一"正=余量、负=不足"语义,避免出现 0。 - 标色对比用字体色,重跑填表不覆盖字体:fill_table 只改
value不动font,所以先标红再重填表,红色会保留;对比"颜色"默认指字体红色FF0000,若用户说"底色/黄色底色"才用fill。 - 全压是双边约束(−40% ~ +50%):下限放宽 ≤40%(P选≥0.6×P设),上限富余 ≤50%(P选≤1.5×P设)。全压过高同样要避免——会导致电机功率浪费、噪声增大、成本上升(典型:某台全压富余达 154% 的选型,加此约束后需重选)。
Resources
scripts/
render_page.py— 通用渲染脚本(PyMuPDF 渲染指定页为 PNG)。依赖 PyMuPDF。extract_pdf_table.py— 双模式:scan(关键词定位页码)+ dump(坐标重组文本层,仅作总览)。依赖 PyMuPDF。ocr_region.py— RapidOCR 高精度识别(仅作辅助,不作为最终数据)。依赖 rapidocr-onnxruntime。
> 脚本可复用 V1.0(pump-fan-selection)skill 中同名脚本;本 skill 的脚本模板见上方 Step 2a。
references/
(本技能核心方法论已内联,无独立 reference 文件;工程校核方法复用 V1.0 的 selection-method.md)