3. 海底管道内检测数据解析与缺陷列表构建

内检测原始数据到结构化缺陷列表的解析链路

漏磁(MFL)或超声(UT)内检测的原始交付物通常包含三部分:里程/时钟触发的传感器通道数据、检测器姿态与速度记录、以及检测方给出的初步缺陷报告。要构建可用于完整性评估的缺陷列表,核心任务是把“通道信号—里程—时钟方位”三者对齐,再按阈值规则把信号异常段归并为独立缺陷,并换算成管径、壁厚归一化的尺寸参数。

本例以一段示例管道为对象:外径 D = 323.9 mm,公称壁厚 t = 12.7 mm,检测段长度 L = 8.0 km,检测器运行方向为从起点里程 0 m 向终点里程 8000 m。时钟方位约定为:以管道横截面俯视、12 点钟位于正上方,顺时针递增,0°与360°重合。缺陷深度以壁厚百分比表示,长度沿轴向、宽度沿环向。

原始数据 → 对齐 → 缺陷归并 → 结构化列表 原始数据源 • MFL/UT 通道信号 • 里程轮脉冲 • 姿态/速度记录 • 检测方初步报告 • 管径/壁厚/时钟基准 三向对齐 里程:脉冲→累计距离 时钟:传感器编号→角度 通道:环向位置映射 速度修正:滑移补偿 基准:起点里程=0 m 缺陷归并与量化 阈值:信号幅值>门限 相邻异常段合并规则 深度:%t 归一化 长/宽:轴向/环向尺寸 时钟方位:中心角 结构化缺陷列表 缺陷ID / 里程 / 时钟 深度%t / 长 / 宽 类型 / 置信度 分布统计:深度分档 时钟方位玫瑰图 时钟方位映射(俯视截面,顺时针) 12:00 (0°) 6:00 (180°) 9:00 3:00 D-03 (约 2:00) D-07 (约 8:00) D-11 (6:00) 缺陷深度分布统计(示例数据) 0–10%t 18 处 10–20%t 13 处 20–30%t 8 处 30–40%t 4 处 ≥40%t 2 处 合计 45 处;≥40%t 缺陷需优先进入剩余强度评估 关键换算:时钟角 θ = 传感器编号 × 360°/N;环向宽度 W = θ_span/360° × πD;轴向长度由里程差 Δs 给出。

关键解析规则与实现

原始通道数据到缺陷列表的转换,需要明确三条规则,否则同一份数据不同人解析会得到不同结果:

  • 里程对齐:里程轮脉冲数乘以轮周长得到累计距离,但检测器在弯管、积液段会发生滑移,需用姿态记录中的加速度/速度做滑移补偿。示例中取补偿系数 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 mmB31G 长度项
宽度环向尺寸约 40 mm缺陷形状修正
类型金属损失/凹坑/裂纹金属损失选择评估方法

分布特征判读关注三点:一是深度分档是否出现 ≥40%t 的缺陷,这类缺陷通常需要优先评估;二是时钟方位是否集中在某一区间(如 6 点钟底部积液区),提示腐蚀机理与积液相关;三是沿里程是否成簇出现,成簇往往对应弯管、低点或涂层破损段,应结合路由数据交叉核对。