Skip to content

Dongjin-Du/Udwave

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

udwave

Ultrasonic guided wave damage detection toolkit

A Python library for detecting structural damage in pipes using ultrasonic guided wave pitch-catch measurements. Implements the methodology of:

Du, D., Karve, P.M., Rubin, S., Mahadevan, S., and Lawrence, S. Selective leaching detection in metal pipes using ultrasonic guided wave testing. (2024)


Overview

Given repeated ultrasonic measurements from a reference (undamaged) pipe and a test (damaged) pipe, udwave:

  1. Computes six damage indicators (DIs) for every pairwise combination of reference and test signals
  2. Selects the best DI using the KDE-based distribution overlap metric
  3. Finds the optimal detection threshold by minimising the weighted Error Influence Index (EII)
  4. Estimates the probability of damage for any new unknown-status pipe

User-defined DIs can be plugged in alongside the built-in ones.


Damage indicators

Name Type Description
EMI Single-sensor Energy Magnitude Indicator — normalised Fourier amplitude difference
EDR Single-sensor Energy Deviation Ratio — CWT energy ratio
IDC Single-sensor Instantaneous Deviation Coefficient — CWT cross-correlation
SDC Single-sensor Signal Difference Coefficient — phase-aligned Pearson correlation
CSDI Dual-sensor Coherence-Based Signal Deviation Index — Welch coherence RMSD
MIEM Dual-sensor Matrix-based Instantaneous Energy Method — IDE cross-matrix deviation

Single-sensor DIs require one signal per measurement (N, L). Dual-sensor DIs require two simultaneous signals per measurement (N, L, 2), where axis 2 holds the two sensor channels.


Installation

git clone https://github.com/Dongjin-Du/udwave.git
cd udwave
pip install -r requirements.txt
pip install -e .

Quick start

Single-sensor

import numpy as np
from udwave import select_best_di, optimise_threshold, detect

# ref, damaged, unknown : (N, L) ndarrays
result = select_best_di(ref, damaged)
opt    = optimise_threshold(result, w1=0.5, w2=0.5)
report = detect(ref, unknown,
                di_name   = result['best'],
                threshold = opt['best_T'])

print(report['pipe_status'])        # 'Damaged' or 'Undamaged'
print(report['pipe_probability'])   # e.g. 0.82

Dual-sensor

# ref, damaged, unknown : (N, L, 2) ndarrays
# axis 2 = [sensor_front, sensor_back]
t = np.arange(L) * (1 / sampling_frequency)

result = select_best_di(ref, damaged, t=t, fs=sampling_frequency)
opt    = optimise_threshold(result, w1=0.75, w2=0.25)
report = detect(ref, unknown,
                di_name   = result['best'],
                threshold = opt['best_T'],
                t=t, fs=sampling_frequency)

Custom DI

def my_di(ref, test):
    # ref : (N, L),  test : (M, L)
    # must return (N, M) ndarray
    ...

result = select_best_di(ref, damaged, custom_dis={'MyDI': my_di})

Plots

from udwave.plotting import plot_distribution, plot_eii, plot_detection

best = result['best']
plot_distribution(result['di_ref'][best], [result['di_test'][best]], best)
plot_eii(opt)
plot_detection(report)

Project structure

udwave/
├── src/
│   ├── __init__.py            ← public API
│   ├── damage_indicators.py   ← EMI, EDR, IDC, SDC, CSDI, MIEM
│   ├── select_di.py           ← DI selection via overlap
│   ├── optimise_threshold.py  ← EII-based threshold optimisation
│   ├── detect.py              ← damage probability estimation
│   ├── signal_utils.py        ← CSV loading, pulse extraction
│   └── plotting.py            ← figures
├── tests/
│   └── test_udwave.py
├── examples/
│   ├── example_single_sensor.py
│   ├── example_dual_sensor.py
│   └── example_custom_di.py
├── docs/
│   └── api_reference.md
├── requirements.txt
├── setup.py
└── LICENSE

Running tests

pip install pytest
pytest tests/ -v

CSV data format

Each CSV file represents one measurement session:

Column Content
0 Trigger / reference channel (for pulse detection)
1 Sensor signal

No header row. Use udwave.signal_utils to load:

from udwave.signal_utils import load_folder, signal_prepare, stack_sensors

# single-sensor
recordings = load_folder('path/to/csvs', start=410, end=500)
data = signal_prepare(recordings, s_length=90)   # (N, 90)

# dual-sensor
s1 = load_folder('path/to/front', start=410, end=500)
s2 = load_folder('path/to/back',  start=410, end=500)
data = stack_sensors(s1, s2, s_length=90)         # (N, 90, 2)

EII weights

w1 controls the penalty for false positives (Type I error) and w2 for missed detections (Type II error). Adjust based on the consequences in your application:

# equal importance
opt = optimise_threshold(result, w1=0.5, w2=0.5)

# false positives are 3x more costly than missed detections
opt = optimise_threshold(result, w1=0.75, w2=0.25)

# missed detections are 3x more costly (safety-critical)
opt = optimise_threshold(result, w1=0.25, w2=0.75)

License

MIT — see LICENSE.

About

Ultrasonic guided wave damage detection toolkit — DI selection, threshold optimisation, and damage probability estimation for pipe structural health monitoring.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages