
我们一起聊聊基于布谷鸟搜索优化的多阶段胎儿心电信号增强提取算法(MATLAB)
完整算法流程图
图片
详细算法流程步骤
数据加载与预处理
读取EDF格式的多通道胎儿ECG数据(直接胎儿心电+4个腹部导联)
应用50Hz陷波滤波器消除工频干扰
1-100Hz带通滤波去除基线漂移和高频噪声
布谷鸟搜索优化初始化
设置鸟巢数量(25)、最大迭代次数(25)、弃巢概率(0.25)
定义搜索空间:滤波器阶数(80-300)、学习率(0.01-0.99)
使用Lévy飞行分布生成初始解
适应度评估
对每个参数组合运行NLMS自适应滤波
小波提取胎儿ECG成分
计算基于小波的SNR:fitness = 0.9×SNR - 0.1×(阶数/最大阶数)
莱维飞行更新
按Lévy飞行模式生成新解
边界约束确保参数有效性
贪婪选择保留更优解
劣解替换
淘汰适应度最差的25%解
随机生成新解或基于当前最优解扰动生成
最优参数应用
全数据应用优化后的NLMS滤波器
执行多阶段胎儿ECG提取: a. 小波去噪 b. 1-35Hz胎儿心电带通滤波 c. 非线性能量算子增强QRS波 d. 移动平均平滑
性能评估
计算小波域SNR
对比基线参数性能
可视化三阶段信号对比
算法应用领域
应用领域 | 具体场景 |
围产期监护 | 非侵入式胎儿心脏功能实时监测 |
胎儿心律失常诊断 | 早产儿心动过速/心动过缓早期预警 |
多胎妊娠监护 | 分离多个胎儿心电信号 |
产前药物评估 | 监测药物对胎儿心脏活动的影响 |
胎儿缺氧检测 | 心电变异分析预测宫内窘迫 |
生物医学研究 | 胎儿自主神经系统发育研究 |
移动健康设备 | 便携式胎儿监护仪信号处理核心 |
信号分析应用流程
处理阶段 | 技术实现 | 创新优势 |
多源数据融合 | 联合直接胎儿ECG+4个腹部导联构建参考系统 | 利用空间信息增强信号分离能力 |
动态噪声抑制 | 自适应陷波+带通滤波组合 | 针对性消除50Hz工频干扰和肌电噪声 |
元启发式优化 | 布谷鸟搜索全局优化NLMS参数 | 避免局部最优,平衡收敛速度与精度 |
混合适应度函数 | SNR(90%) + 复杂度(10%)加权评估 | 确保临床可用性与实时性平衡 |
多尺度特征提取 | 5层db4小波分解+系数阈值处理 | 分离母亲心电与胎儿心电频带特征 |
非线性能量增强 | Teager能量算子:ψ[n]=x²[n]-x[n-1]x[n+1] | 突出胎儿QRS波群,抑制P/T波干扰 |
智能后处理 | 自适应窗长平滑(25ms) + 振幅归一化 | 保持波形形态的同时减少伪迹 |
抗噪评估 | 小波域相关系数SNR:20log₁₀⎹ρ⎹/√(1-ρ²) | 克服传统时域SNR对幅度敏感缺陷 |
与深度学习结合方式
融合方向 | 技术方案 | 预期效益 |
智能参数预测 | LSTM学习最优滤波器参数映射关系,替代布谷鸟搜索 | 计算效率提升10倍,适合实时监护 |
端到端提取 | 1D-CNN直接处理原始腹部信号→胎儿ECG输出 | 避免传统信号处理级联误差累积 |
生成对抗增强 | GAN合成带噪声的胎儿心电数据,扩充训练样本 | 解决临床标注数据稀缺问题 |
多任务学习 | 联合训练:胎儿ECG提取 + QRS检测 + 心律失常分类 | 构建一体化胎儿心脏评估系统 |
注意力机制 | Transformer聚焦胎儿QRS关键时段,抑制母亲心电干扰 | 提升复杂噪声环境下的鲁棒性 |
迁移学习 | 成人ECG预训练模型微调适应胎儿心电特征 | 解决胎儿数据不足问题 |
可解释性分析 | 梯度类激活图(Grad-CAM)可视化网络关注区域 | 满足临床诊断的可解释性需求 |
边缘计算部署 | 知识蒸馏压缩模型,适配嵌入式胎儿监护设备 | 实现院外实时监护 |
clear
close all
% Load data
edfFile = 'r10.edf';
info = edfinfo(edfFile);
data1 = edfread(edfFile, 'SelectedSignals', 'Direct_1');
d = cell2mat(table2cell(data1));
data2 = edfread(edfFile, 'SelectedSignals', 'Abdomen_1');
B2 = cell2mat(table2cell(data2));
data3 = edfread(edfFile, 'SelectedSignals', 'Abdomen_2');
B3 = cell2mat(table2cell(data3));
data4 = edfread(edfFile, 'SelectedSignals', 'Abdomen_3');
B4 = cell2mat(table2cell(data4));
data5 = edfread(edfFile, 'SelectedSignals', 'Abdomen_4');
B5 = cell2mat(table2cell(data5));
% Setup parameters
Fs = 1000;
Ts = 1/Fs;
Tm = (0:length(d)-1) / Fs;
% First-stage signal pre-processing for noise reduction
% Apply bandpass filter to eliminate baseline wander and high-frequency noise
b_notch = designfilt('bandstopiir', 'FilterOrder', 2, ...
'HalfPowerFrequency1', 48, 'HalfPowerFrequency2', 52, ...
'DesignMethod', 'butter', 'SampleRate', Fs);
b_bp = designfilt('bandpassiir', 'FilterOrder', 4, ...
'HalfPowerFrequency1', 1, 'HalfPowerFrequency2', 100, ...
'DesignMethod', 'butter', 'SampleRate', Fs);
% Apply filters to abdominal signals
B2_filtered = filtfilt(b_notch, B2);
B2_filtered = filtfilt(b_bp, B2_filtered);
B3_filtered = filtfilt(b_notch, B3);
B3_filtered = filtfilt(b_bp, B3_filtered);
B4_filtered = filtfilt(b_notch, B4);
B4_filtered = filtfilt(b_bp, B4_filtered);
B5_filtered = filtfilt(b_notch, B5);
B5_filtered = filtfilt(b_bp, B5_filtered);
% Apply filters to direct signal (reference)
d_filtered = filtfilt(b_notch, d);
d_filtered = filtfilt(b_bp, d_filtered);
% Differential approach using multiple abdominal channels
% Creates a better reference signal for extracting fetal ECG
noisy_sig = B2_filtered;
% Setup Cuckoo Search Algorithm for optimizing LMS parameters
% Cuckoo Search parameters
n_nests = 25; % Number of nests (population size)
max_iterations = 25; % Maximum number of iterations
pa = 0.25; % Probability of abandoning worse nests
% Search space boundaries
min_order = 80; % Minimum filter order
max_order = 300; % Maximum filter order (reduced for efficiency)
min_mu = 0.01; % Min learning rate
max_mu = 0.99; % Max learning rate
% Initialize nests [order, step_size]
nests = zeros(n_nests, 2);
fitness = zeros(n_nests, 1);
% Generate initial nests with Lévy flight distribution
for i = 1:n_nests
nests(i, :) = [randi([min_order, max_order]), rand * (max_mu - min_mu) + min_mu];
end
% Select shorter segment for optimization to speed up process
start_time = 0;
end_time = 10;
start_idx = find(Tm >= start_time, 1);
end_idx = find(Tm >= end_time, 1);
d_opt = d_filtered(start_idx:end_idx);
noisy_opt = noisy_sig(start_idx:end_idx);
% Track best solution
best_fitness = -inf;
best_solution = [0, 0];
fitness_history = zeros(max_iterations, 1);
% Define Lévy flight function
function s = levy_flight(beta)
% Mantegna's algorithm for Lévy flights
sigma_u = (gamma(1+beta)*sin(pi*beta/2)/(gamma((1+beta)/2)*beta*2^((beta-1)/2)))^(1/beta);
u = randn(1) * sigma_u;
v = randn(1);
s = u/abs(v)^(1/beta);
end
% Cuckoo Search main loop
for iter = 1:max_iterations
% Evaluate fitness for each nest
for i = 1:n_nests
order = round(nests(i, 1));
mu = nests(i, 2);
% Run LMS filter with these parameters
lms = dsp.LMSFilter(order + 1, 'StepSize', mu, 'Method', 'Normalized LMS', 'WeightsOutputPort', true);
[y, e, w] = step(lms, noisy_opt, d_opt);
% Use wavelet-based SNR computation to better evaluate signal quality
[~, fetal_signal] = extract_fetal_wavelet(e);
curr_snr = wSNR(d_opt, fetal_signal);
% Balance SNR with computational complexity
% Higher weight on SNR (0.9) vs order reduction (0.1)
fitness(i) = 0.9 * curr_snr - 0.1 * (order / max_order);
end
% Find current best nest
[current_best_fitness, best_idx] = max(fitness);
% Update best solution if improved
if current_best_fitness > best_fitness
best_fitness = current_best_fitness;
best_solution = nests(best_idx, :);
end
% Keep track of progress
fitness_history(iter) = best_fitness;
fprintf('Iteration %d: Best Order = %d, Step Size = %.4f, Fitness = %.4f\n', ...
iter, round(best_solution(1)), best_solution(2), best_fitness);
% Get a new cuckoo by Lévy flight
for i = 1:n_nests
% Generate step size using Lévy flight
beta = 1.5; % Parameter for Lévy distribution (typically between 1 and 2)
step_size_order = levy_flight(beta) * 20; % Scale for order parameter
step_size_mu = levy_flight(beta) * 0.1; % Scale for step size parameter
% Select a random nest
j = randi(n_nests);
% Generate new nest using Lévy flight
new_order = round(nests(i, 1) + step_size_order * (nests(i, 1) - nests(j, 1)));
new_mu = nests(i, 2) + step_size_mu * (nests(i, 2) - nests(j, 2));
% Ensure bounds
new_order = max(min_order, min(max_order, new_order));
new_mu = max(min_mu, min(max_mu, new_mu));
% Evaluate new solution
lms = dsp.LMSFilter(new_order + 1, 'StepSize', new_mu, 'Method', 'Normalized LMS', 'WeightsOutputPort', true);
[y, e, w] = step(lms, noisy_opt, d_opt);
[~, fetal_signal] = extract_fetal_wavelet(e);
new_snr = wSNR(d_opt, fetal_signal);
new_fitness = 0.9 * new_snr - 0.1 * (new_order / max_order);
% Replace if better
if new_fitness > fitness(i)
nests(i, :) = [new_order, new_mu];
fitness(i) = new_fitness;
end
end
% Abandon worst nests and build new ones
[sorted_fitness, idx] = sort(fitness, 'descend');
for i = floor(n_nests * (1-pa))+1:n_nests
% Replace worst nests with new random solutions
% But biased towards the best solution (exploitation)
if rand < 0.5
% Generate completely new nest
nests(idx(i), :) = [randi([min_order, max_order]), rand * (max_mu - min_mu) + min_mu];
else
% Generate solution biased towards best solution
alpha = 0.3 * rand; % Small random factor
nests(idx(i), :) = best_solution + alpha * (rand(1, 2) .* 2 - 1) .* [30, 0.1];
% Ensure bounds
nests(idx(i), 1) = max(min_order, min(max_order, round(nests(idx(i), 1))));
nests(idx(i), 2) = max(min_mu, min(max_mu, nests(idx(i), 2)));
end
end
end
% Apply optimal parameters to full dataset
optimal_order = round(best_solution(1));
optimal_mu = best_solution(2);
fprintf('\nOptimal Filter Order: %d\n', optimal_order);
fprintf('Optimal Step Size: %.4f\n', optimal_mu);
% Process full signal with optimized parameters
lms_optimal = dsp.LMSFilter(optimal_order + 1, 'StepSize', optimal_mu, 'Method', 'Normalized LMS', 'WeightsOutputPort', true);
[y_optimal, e_optimal, w_optimal] = step(lms_optimal, noisy_sig, d_filtered);
% Apply advanced post-processing for better signal quality
[fECG_filtered_optimal, fetal_ecg_final] = advanced_fecg_extraction(e_optimal, Fs);
% Calculate SNR with optimized parameters
Tm_full = (0:length(y_optimal)-1) / Fs;
start_time = 0;
end_time = 30;
start_idx = find(Tm_full >= start_time, 1);
end_idx = find(Tm_full >= end_time, 1);
selected_signal_opt = y_optimal(start_idx:end_idx);
selected_original = noisy_sig(start_idx:end_idx);
selected_tm = Tm_full(start_idx:end_idx);
selected_fecg = fetal_ecg_final(start_idx:end_idx);
% Calculate SNR using wavelet-based method
SNR_optimal = wSNR(d_filtered(start_idx:end_idx), selected_fecg);
fprintf('SNR with Optimized Parameters: %.4f\n', SNR_optimal);
% Compare with a baseline LMS filter (fixed parameters)
baseline_order = 200;
baseline_mu = 0.2;
lms_baseline = dsp.LMSFilter(baseline_order + 1, 'StepSize', baseline_mu, 'Method', 'Normalized LMS', 'WeightsOutputPort', true);
[y_baseline, e_baseline, w_baseline] = step(lms_baseline, noisy_sig, d_filtered);
[~, fetal_ecg_baseline] = advanced_fecg_extraction(e_baseline, Fs);
SNR_baseline = wSNR(d_filtered(start_idx:end_idx), fetal_ecg_baseline(start_idx:end_idx));
fprintf('SNR with Baseline Parameters: %.4f\n', SNR_baseline);
fprintf('Improvement: %.4f dB\n', SNR_optimal - SNR_baseline);
% Plot results
figure;
subplot(3,1,1);
plot(selected_tm, selected_original);
title('Original Abdominal ECG Signal');
xlabel('Time (s)');
ylabel('Amplitude');
ylim([-250 400]);
xlim([0 6]);
subplot(3,1,2);
plot(selected_tm, -e_optimal(start_idx:end_idx));
title(['After LMS Filtering (Order: ' num2str(optimal_order) ', μ: ' num2str(optimal_mu, '%.4f') ')']);
xlabel('Time (s)');
ylabel('Amplitude');
ylim([-250 400]);
xlim([0 6]);
subplot(3,1,3);
plot(selected_tm, selected_fecg);
title(['Final Fetal ECG Signal (SNR: ' num2str(SNR_optimal, '%.2f') ' dB)']);
xlabel('Time (s)');
ylabel('Amplitude');
ylim([-250 400]);
xlim([0 6]);
% Required helper functions
function [denoised_signal, fetal_signal] = extract_fetal_wavelet(signal)
% Wavelet-based extraction of fetal ECG
% Decompose signal using wavelet transform
[c, l] = wavedec(signal, 5, 'db4');
% Threshold coefficients to remove noise
thr = median(abs(c))/0.6745 * sqrt(2*log(length(signal)));
c_t = wthresh(c, 's', thr/2.5); % Soft thresholding with reduced threshold for better fetal preservation
% Reconstruct signal
denoised_signal = waverec(c_t, l, 'db4');
% Extract fetal components (detail coefficients in certain bands)
d3 = wrcoef('d', c_t, l, 'db4', 3);
d4 = wrcoef('d', c_t, l, 'db4', 4);
d5 = wrcoef('d', c_t, l, 'db4', 5);
% Combine relevant detail coefficients for fetal signal
fetal_signal = d3 + d4 + d5;
end
function snr_val = wSNR(reference, extracted)
% Wavelet-based SNR calculation for better evaluation of fetal ECG
% Apply wavelet denoising to both signals
[~, ref_fetal] = extract_fetal_wavelet(reference);
[~, ext_fetal] = extract_fetal_wavelet(extracted);
% Normalize both signals
ref_fetal = ref_fetal / std(ref_fetal);
ext_fetal = ext_fetal / std(ext_fetal);
% Calculate correlation-based SNR
r = corrcoef(ref_fetal, ext_fetal);
correlation = r(1,2);
snr_val = 20 * log10(abs(correlation) / sqrt(1 - correlation^2));
% Ensure reasonable SNR range
snr_val = max(0, min(snr_val, 30)); % Cap between 0-30 dB
end
function [denoised_signal, enhanced_fetal] = advanced_fecg_extraction(signal, fs)
% Multi-stage fetal ECG extraction
% Stage 1: Wavelet denoising
[denoised_signal, fetal_wavelet] = extract_fetal_wavelet(signal);
% Stage 2: Bandpass filtering to isolate fetal frequency range (typically 1-35 Hz)
bp_fetal = designfilt('bandpassiir', 'FilterOrder', 4, ...
'HalfPowerFrequency1', 1, 'HalfPowerFrequency2', 35, ...
'DesignMethod', 'butter', 'SampleRate', fs);
fetal_filtered = filtfilt(bp_fetal, denoised_signal);
% Stage 3: Non-linear energy operator to enhance QRS complexes
y = fetal_filtered;
psi = y(2:end-1).^2 - y(1:end-2).*y(3:end);
psi = [0; psi; 0]; % Pad to maintain original length
% Stage 4: Moving average smoothing
window_size = round(fs * 0.025); % 25ms window
smoothed = movmean(psi, window_size);
% Stage 5: Combine enhanced signal with wavelet output
enhanced_fetal = fetal_wavelet + 0.6 * smoothed;
% Normalize output
enhanced_fetal = enhanced_fetal / max(abs(enhanced_fetal)) * max(abs(fetal_wavelet));
end
本文转载自高斯的手稿
