PVC-O 400 级和 500 级差多少?把 32 页扫描版 ISO 标准做成可计算数据
一、先说清 PVC-O 是什么,"差一级" 差在哪
PVC-O 是双轴取向聚氯乙烯管材。原料还是 PVC,但成型时管坯在环向和轴向同时被拉伸,分子链沿这两个方向排列,同样壁厚下能承受更高的环向应力。
它的等级按 MRS 分级。MRS 是最小要求强度,定义条件固定:20℃、50 年、97.5% 置信下限。主流等级是 400 和 500,分别对应 40 MPa 和 50 MPa。
设计怎么用这个数:设计应力 σ = MRS / C,C 取 1.6。500 级算出 31.25 MPa,400 级算出 25.00 MPa。同一外径、同一壁厚,公称压力跟着 MRS 走。所以按 500 级设计出的壁厚,如果实际用的是 400 级材料,管壁承受的仍是 31.25 MPa,比 400 级自己的设计应力高 25%,能扛的压力只剩八成。
"差一级" 到底差多少压力、差多少年,不能靠说法,得把标准附录里的参考线拿到手,代进去算。
手上这份 ISO 16422-2:2024,32 页,扫描件,没有文本层。下面记的就是把它变成可计算数据的过程。真正的难点不是 OCR,是怎么知道 OCR 出来的数是对的。
二、扫描件不能靠 OCR 直接抄
附录 A 是一条多参数参考线公式:log₁₀t = A + B/T + C・log₁₀σ − D・log₁₀σ/T。t 单位小时,T 用开尔文,σ 是环向应力,MPa。
公式里每个符号都要精确。OCR 给出的文本里,log₁₀ 常被读成 logio,σ 读成 o,负号丢失,上下标错位。这类错误肉眼看着像真的,代进公式算出来的数却是错的。
策略定下来:OCR 只用来定位,不用来取数。它负责告诉我 "附录 A 在第 22 页、第 (A.1) 到 (A.5) 行有系数",具体数字回原页看。
三、渲染:220 DPI 起步
扫描件没有文本层,PyMuPDF 渲染成图再走 OCR。
ini
import fitz # PyMuPDF
doc = fitz.open("ISO 16422-2-2024.pdf")
page = doc[21] # PDF 第 22 页,对应印刷页 16
pix = page.get_pixmap(dpi=220)
pix.save("annexA_p22.png")
220 DPI 是这次的实际设置。正文能看清就够了,但公式下标这种小字号,图片糊一点就会丢笔画。拿到的扫描件质量一般,直接上 300 DPI,代价只是文件大一点。
四、OCR:只做定位,不做抄写
arduino
import pytesseract
from PIL import Image
img = Image.open("annexA_p22.png")
text = pytesseract.image_to_string(img, lang="eng")
# 只关心有没有出现这几行,不关心它们读成了什么
for line in text.splitlines():
if "(A." in line or "log" in line.lower():
print(line)
跑完能看到 (A.1) 到 (A.5) 的位置,确认附录结构:第 21 页是说明段,写适用温度区间、端帽类型、水 - 水试验;第 22 页是五条参考线的系数。
到这里,OCR 的任务就结束了。
五、校录:回原页逐字符对照
打开那张 220 DPI 的图,按行抄。抄的时候只做两件事:核对符号,核对数量级。
以 PVC-O 400 和 500 两个等级为例,其余三个等级按同样方式处理:
| 等级 | A | B | C | D |
|---|---|---|---|---|
| PVC-O 400 | −91.994 | 47 752.530 | 10.513 | 15 023.175 |
| PVC-O 500 | −94.470 | 49 653.145 | 10.789 | 15 114.450 |
四个系数里,A 是负数,最容易漏负号;B、D 是五位数、小数点后三位,抄错一位整条线就偏了。
六、自校验:用 MRS 的定义反过来验自己
抄完不能直接信。这里有个现成的验算点,就是第一节那个 MRS 定义:20℃、50 年、97.5% 置信下限。也就是说,把 20℃ 和 σ = MRS/10 代进去,五条参考线都应该算出 50 年左右。算不出来,就是抄错了。
css
import math
grades = {
"400": (-91.994, 47752.530, 10.513, 15023.175),
"500": (-94.470, 49653.145, 10.789, 15114.450),
}
T = 293.15 # 20℃ 对应的开尔文,注意不能用 20
for name, (A, B, C, D) in grades.items():
sigma = int(name) / 10 # MRS 400 → 40.0 MPa
log_t = A + B / T + C * math.log10(sigma) - D * math.log10(sigma) / T
hours = 10 ** log_t
print(name, round(hours, 1), "h ≈", round(hours / 8760, 1), "年")
两行输出都在 50 年上下:400 级算出 50.01 年,500 级算出 49.99 年。这个结果本身就是验收条件:如果某一级算出来是 30 年或者 200 年,先别怀疑标准,先怀疑自己抄错了符号或小数点。
七、落成结构化数据
校验通过后再谈存储。系数按等级拆成字段,后面算寿命、算比值都不用再翻 PDF。
css
{
"standard": "ISO 16422-2:2024",
"annex": "A",
"temperature_range": "20-60",
"grades": {
"400": { "A": -91.994, "B": 47752.530, "C": 10.513, "D": 15023.175 },
"500": { "A": -94.470, "B": 49653.145, "C": 10.789, "D": 15114.450 }
}
}
八、回到那个问题:400 和 500 到底差多少
数据齐了,代入同一个应力算。取 σ = 31.25 MPa,也就是按 500 级设计时管壁实际承受的应力:
css
sigma = 31.25
for name, (A, B, C, D) in grades.items():
log_t = A + B / T + C * math.log10(sigma) - D * math.log10(sigma) / T
print(name, 10 ** log_t)
两条参考线算出来的时间比是 1.11×10⁻⁴。
这个结果最有价值的信息不是 "多少年",而是三件事:
- 比值。同一应力下,400 级与 500 级的寿命比落在万分之一量级,不是少用几年的问题。
- 幂指数。n = C − D/T,20℃ 下约 −40.7,意思是应力每高 1%,寿命大约掉到三分之二;高 25%,就是四个数量级。
- 压力。同一外径、同一壁厚,等级从 500 降到 400,公称压力只剩八成,PN16 对应到 12.8 bar。
把第一节那笔账连起来:按 500 级设计、实际用 400 级材料,管壁应力超发 25%,压力打八折,寿命比掉到万分之一量级。这就是 "差一级" 的量化含义。
边界必须写清楚:20℃、40℃ 算出来的绝对年限是标准外推的结果,不是实测寿命,只有比值有意义;60℃ 那一组属于加速条件,超出产品正常使用范围,只能说明趋势。真实寿命还取决于回填质量、点载荷、划伤、水锤和接口。把 10¹⁰ 年这种数字当结论发出去,是误用。
九、踩过的五个坑
- 用 20 而不是 293.15。公式里 T 是开尔文,代摄氏温度进去,结果会差一个量级还多。
- 小时和年混用。t 的单位是小时,除以 8760 才是年,脚本里忘了换算,50 年会变成 43 万。
- 负号丢失。A 全是负数,OCR 和手抄都可能吃掉这个减号,算出来的寿命会莫名其妙变长。
- DPI 不够。220 DPI 对付清晰扫描够用,糊一点的页面必须提到 300,否则下标认不出来。
- 拿 OCR 文本当数据源。这是最危险的一条,OCR 的结果看着通顺,符号却是错的,而且错得很安静。