3. 海底管道内检测数据解析与缺陷列表构建
内检测原始数据到结构化缺陷列表的解析链路
漏磁(MFL)或超声(UT)内检测的原始交付物通常包含三部分:里程/时钟触发的传感器通道数据、检测器姿态与速度记录、以及检测方给出的初步缺陷报告。要构建可用于完整性评估的缺陷列表,核心任务是把“通道信号—里程—时钟方位”三者对齐,再按阈值规则把信号异常段归并为独立缺陷,并换算成管径、壁厚归一化的尺寸参数。
本例以一段示例管道为对象:外径 D = 323.9 mm,公称壁厚 t = 12.7 mm,检测段长度 L = 8.0 km,检测器运行方向为从起点里程 0 m 向终点里程 8000 m。时钟方位约定为:以管道横截面俯视、12 点钟位于正上方,顺时针递增,0°与360°重合。缺陷深度以壁厚百分比表示,长度沿轴向、宽度沿环向。
关键解析规则与实现
原始通道数据到缺陷列表的转换,需要明确三条规则,否则同一份数据不同人解析会得到不同结果:
- 里程对齐:里程轮脉冲数乘以轮周长得到累计距离,但检测器在弯管、积液段会发生滑移,需用姿态记录中的加速度/速度做滑移补偿。示例中取补偿系数
k_s = 1.004,即修正里程s = k_s × n_pulse × C_wheel。 - 时钟映射:若检测器有 N 个环向传感器均布,第 i 个传感器对应时钟角
θ_i = i × 360°/N。缺陷的时钟方位取异常段覆盖传感器的角度加权中心。 - 缺陷归并:相邻通道、相邻里程采样点若信号幅值均超过门限,且间隔小于归并距离(示例取
3 × 采样间距),则视为同一缺陷;否则拆分为独立缺陷。
下面给出一个可直接运行的 Python 解析示例。输入为简化的缺陷候选段记录(实际工程中由信号处理模块输出),输出为结构化缺陷列表与分布统计。
import math
from collections import Counter
D = 323.9 # 外径 mm
t = 12.7 # 壁厚 mm
N_SENSOR = 128 # 环向传感器数
MERGE_GAP = 3 # 归并间隔(采样点数)
# 候选段:dict(里程m, 传感器起, 传感器止, 深度%t, 轴向长度mm)
raw = [
{"s": 512.4, "i0": 20, "i1": 23, "depth": 12.5, "len": 45},
{"s": 512.9, "i0": 21, "i1": 24, "depth": 13.1, "len": 40},
{"s": 513.5, "i0": 22, "i1": 25, "depth": 12.8, "len": 38},
{"s": 1204.0, "i0": 60, "i1": 62, "depth": 28.4, "len": 60},
{"s": 2310.7, "i0": 100, "i1": 104, "depth": 41.2, "len": 85},
{"s": 2311.2, "i0": 101, "i1": 105, "depth": 40.6, "len": 80},
{"s": 4800.3, "i0": 8, "i1": 10, "depth": 8.9, "len": 30},
]
def clock_angle(i0, i1):
mid = (i0 + i1) / 2.0
return (mid * 360.0 / N_SENSOR) % 360.0
def circ_width(i0, i1):
span = (i1 - i0 + 1) * 360.0 / N_SENSOR
return span / 360.0 * math.pi * D
# 按里程排序后归并
raw.sort(key=lambda r: r["s"])
defects, cur = [], None
for r in raw:
if cur and (r["s"] - cur["s_last"]) <= MERGE_GAP * 0.5:
cur["s_last"] = r["s"]
cur["depth"] = max(cur["depth"], r["depth"])
cur["len"] = max(cur["len"], r["len"])
cur["i0"] = min(cur["i0"], r["i0"])
cur["i1"] = max(cur["i1"], r["i1"])
else:
if cur: defects.append(cur)
cur = dict(r, s_last=r["s"])
if cur: defects.append(cur)
rows = []
for k, d in enumerate(defects, 1):
rows.append({
"id": f"D-{k:02d}",
"里程m": round(d["s"], 1),
"时钟": round(clock_angle(d["i0"], d["i1"]), 1),
"深度%t": round(d["depth"], 1),
"长度mm": d["len"],
"宽度mm": round(circ_width(d["i0"], d["i1"]), 1),
})
for r in rows:
print(r)
bins = Counter()
for r in rows:
d = r["深度%t"]
if d < 10: bins["0-10"] += 1
elif d < 20: bins["10-20"] += 1
elif d < 30: bins["20-30"] += 1
elif d < 40: bins["30-40"] += 1
else: bins["≥40"] += 1
print("深度分档:", dict(bins))
示例数据经上述规则归并后,512 m 附近的三条候选段合并为一个缺陷(深度取最大值 13.1%t),2310 m 附近的两条合并为深度 41.2%t 的缺陷,其余各自独立,最终得到 4 个缺陷。深度分档统计为:0–10%t 1 处、10–20%t 1 处、≥40%t 1 处、20–30%t 1 处。该结果与上方 SVG 中的分布示意属于不同示例数据集,实际使用时以解析输出为准。
缺陷列表字段与分布特征判读
结构化缺陷列表至少应包含以下字段,才能直接作为后续剩余强度评估的输入:
| 字段 | 含义 | 示例值 | 用途 |
|---|---|---|---|
| 缺陷ID | 唯一标识 | D-03 | 追溯与引用 |
| 里程 | 沿管道轴向位置 | 2310.7 m | 定位、开挖验证 |
| 时钟方位 | 环向中心角 | 287.6° | 判断是否位于焊缝、支撑处 |
| 深度 | 占壁厚百分比 | 41.2%t | 剩余强度计算核心参数 |
| 长度 | 轴向尺寸 | 85 mm | B31G 长度项 |
| 宽度 | 环向尺寸 | 约 40 mm | 缺陷形状修正 |
| 类型 | 金属损失/凹坑/裂纹 | 金属损失 | 选择评估方法 |
分布特征判读关注三点:一是深度分档是否出现 ≥40%t 的缺陷,这类缺陷通常需要优先评估;二是时钟方位是否集中在某一区间(如 6 点钟底部积液区),提示腐蚀机理与积液相关;三是沿里程是否成簇出现,成簇往往对应弯管、低点或涂层破损段,应结合路由数据交叉核对。