本次复现的论文Vital Sign Detection of FMCW Radar Based on Improved Adaptive Parameter Variational Mode Decomposition来自沈阳航空航天大学,和论文复现7中的文章很相似,该研究由国家自然科学基金项目(61671310)等多个项目资助1。

我们使用的数据库、软件等与论文复现7一致。
****开始****
首先,这篇文章并未引用优化算法对参数进行优化,而是采用网格搜索,也就是预设参数范围、步长,逐个测试验证,这样的好处是速度快,但处理复杂情况,它的局限性会突显。此外,其优化目标由两项组成,第一项是常见的能量损失率,第二项是筛选出呼吸、心跳能量集中的频带(模式判别),因此产生了两个阈值,这两个阈值设置不到会导致分解失效。
预处理过程中,该研究认为去除直流分量是重要的,我们实现一个简洁的圆拟合去直流偏移方法,利用线性最小二乘快速求解圆心,并将其作为直流分量减去:
Matlab
function sig_corrected = removeDCOffsetByCircleFit(complex_signal)
% 输入: complex_signal 行向量
% 输出: 去除直流偏移后的复信号
x = real(complex_signal);
y = imag(complex_signal);
% 最小二乘圆拟合 (Taubin 法)
n = length(x);
% 构建矩阵 A = [x, y, ones]
A = [x(:), y(:), ones(n,1)];
b = -(x(:).^2 + y(:).^2);
% 求解 A * [a; b; c] = b 使得 (x+a)^2+(y+b)^2 = R^2
sol = A \ b;
a = sol(1); % x 中心偏移
b = sol(2); % y 中心偏移
% 直流偏移量
DC_I = -a;
DC_Q = -b;
% 减去直流
sig_corrected = complex_signal - (DC_I + 1j*DC_Q);
end
优化过程:
Matlab
function [bestK, bestAlpha, lossMat] = iapvmd_optimize_params(signal, fs, K_range, alpha_coarse, fine_step, thresh_r, thresh_h)
% IAPVMD 参数优化(能量损失率 + 模式判别)
% 呼吸频带 0.05-0.7 Hz,心音频带 0.8-3 Hz
% 输入:
% signal : 行向量
% fs : 采样率 (Hz)
% K_range : 例如 2:7
% alpha_coarse : 粗搜索的 alpha 向量,例如 100:100:6000
% fine_step : 精搜索步长(默认 10)
% thresh_r, thresh_h : 能量占比阈值(默认 0.9)
% 输出:
% bestK, bestAlpha : 最优参数
% lossMat : 粗搜索损失矩阵
if nargin < 6 || isempty(thresh_r), thresh_r = 0.9; end
if nargin < 7 || isempty(thresh_h), thresh_h = 0.9; end
if nargin < 5 || isempty(fine_step), fine_step = 10; end
signal = signal(:).';
N = length(signal);
E_total = sum(abs(fft(signal)).^2);
K_range = K_range(K_range >= 2);
if isempty(K_range)
error('K_range 必须包含 >=2 的值');
end
band_resp = [0.05, 0.7];
band_heart = [0.8, 3.0];
fprintf('=== IAPVMD 粗搜索阶段(步长 = %.1f) ===\n', mean(diff(alpha_coarse)));
lossMat = zeros(length(K_range), length(alpha_coarse));
bestLoss = inf;
bestK_coarse = K_range(1);
bestAlpha_coarse = alpha_coarse(1);
for iK = 1:length(K_range)
K = K_range(iK);
for iA = 1:length(alpha_coarse)
alpha = alpha_coarse(iA);
loss = computeLossWithDiscrimination(signal, fs, K, alpha, E_total, band_resp, band_heart, thresh_r, thresh_h);
lossMat(iK,iA) = loss;
if loss < bestLoss
bestLoss = loss;
bestK_coarse = K;
bestAlpha_coarse = alpha;
end
fprintf(' K=%d, α=%.1f → 有效=%d, 损失=%.6f %s\n', K, alpha, loss<1, loss, ...
ternary(loss==bestLoss && loss<bestLoss,'*',''));
end
end
fprintf('粗搜索最优: K=%d, α=%.1f, 损失=%.6f\n', bestK_coarse, bestAlpha_coarse, bestLoss);
% 精搜索
fprintf('\n=== IAPVMD 精搜索阶段(步长 = %.1f) ===\n', fine_step);
alpha_low = max(alpha_coarse(1), bestAlpha_coarse - 200);
alpha_high = min(alpha_coarse(end), bestAlpha_coarse + 200);
alpha_range_fine = alpha_low : fine_step : alpha_high;
if ~any(abs(alpha_range_fine - bestAlpha_coarse) < 1e-6)
alpha_range_fine = sort([alpha_range_fine, bestAlpha_coarse]);
end
bestLoss_fine = inf;
bestAlpha_fine = bestAlpha_coarse;
for iA = 1:length(alpha_range_fine)
alpha = alpha_range_fine(iA);
loss = computeLossWithDiscrimination(signal, fs, bestK_coarse, alpha, E_total, band_resp, band_heart, thresh_r, thresh_h);
if loss < bestLoss_fine
bestLoss_fine = loss;
bestAlpha_fine = alpha;
end
fprintf(' K=%d, α=%.1f → 有效=%d, 损失=%.6f %s\n', bestK_coarse, alpha, loss<1, loss, ...
ternary(loss==bestLoss_fine && loss<bestLoss_fine,'*',''));
end
fprintf('精搜索最优: K=%d, α=%.1f, 损失=%.6f\n', bestK_coarse, bestAlpha_fine, bestLoss_fine);
bestK = bestK_coarse;
bestAlpha = bestAlpha_fine;
end
function loss = computeLossWithDiscrimination(signal, fs, K, alpha, E_total, band_resp, band_heart, thresh_r, thresh_h)
try
[u, ~, omega] = vmd(signal, alpha, 0, K, false, 1, 1e-7);
L = size(u,1);
if L == 0, loss = 1; return; end
N = length(signal);
f = (0:N-1) * (fs / N);
omega_Hz = omega * fs;
valid = false;
E_imf_sum = 0;
for k = 1:L
Pxx = abs(fft(u(k,:))).^2;
totalE = sum(Pxx);
if totalE == 0, continue; end
E_imf_sum = E_imf_sum + totalE;
idx_r = (f >= band_resp(1)) & (f <= band_resp(2));
idx_h = (f >= band_heart(1)) & (f <= band_heart(2));
ratio_r = sum(Pxx(idx_r)) / totalE;
ratio_h = sum(Pxx(idx_h)) / totalE;
if ratio_r > thresh_r || ratio_h > thresh_h
valid = true;
end
end
if ~valid
loss = 1;
else
loss = abs(E_imf_sum - E_total) / (E_total + eps);
end
catch
loss = 1;
end
end
function s = ternary(cond, t, f)
if cond, s = t; else, s = f; end
end
重构信号有一个筛选机制,呼吸信号用基频+谐波重构,心跳是用心跳频段的IMF去除呼吸谐波IMF后重构。但我觉得,如果只是估计呼吸率,那谐波可以直接滤除,而后续的如果呼吸谐波和心跳基波接近,那么就会降低心率检测的效果。
Matlab
function [respSignal, heartSignal, respRate_Hz, heartRate_Hz] = iapvmd_reconstruct(signal, fs, K, alpha, delta_r, delta_h, print_ratios)
% 基于信号分组法的 IAPVMD 重构(改进频带:呼吸0.05-0.7Hz,心跳0.8-3Hz)
% 输入:
% signal : 行向量
% fs : 采样率 (Hz)
% K, alpha : VMD 参数
% delta_r : 呼吸频带能量占比阈值(默认 0.9)
% delta_h : 心音频带能量占比阈值(默认 0.9)
% print_ratios: 是否打印每个IMF的能量占比(默认 false)
% 输出:
% respSignal, heartSignal : 重构信号(行向量)
% respRate_Hz, heartRate_Hz : 估计频率 (Hz)
if nargin < 5 || isempty(delta_r), delta_r = 0.9; end
if nargin < 6 || isempty(delta_h), delta_h = 0.9; end
if nargin < 7 || isempty(print_ratios), print_ratios = false; end
signal = signal(:).';
N = length(signal);
if N < 10
respSignal = signal; heartSignal = signal;
respRate_Hz = NaN; heartRate_Hz = NaN;
return;
end
if K < 2
warning('K 必须 >= 2,自动设为 2');
K = 2;
end
tau = 0.01;
[u, ~, omega] = vmd(signal, alpha, tau, K, false, 1, 1e-7);
L = size(u,1);
if L == 0
respSignal = signal; heartSignal = signal;
respRate_Hz = NaN; heartRate_Hz = NaN;
return;
end
% 频率轴和模态中心频率(归一化后乘以 fs)
f = (0:N-1) * (fs / N);
omega_Hz = omega * fs;
% 呼吸和心音频带
band_resp = [0.05, 0.7];
band_heart = [0.8, 3.0];
% 计算每个模态在呼吸/心音频带的能量占比
Er_ratio = zeros(1, L);
Eh_ratio = zeros(1, L);
for k = 1:L
Pxx = abs(fft(u(k,:))).^2;
totalE = sum(Pxx);
if totalE == 0, continue; end
idx_r = (f >= band_resp(1)) & (f <= band_resp(2));
idx_h = (f >= band_heart(1)) & (f <= band_heart(2));
Er_ratio(k) = sum(Pxx(idx_r)) / totalE;
Eh_ratio(k) = sum(Pxx(idx_h)) / totalE;
end
% 打印能量占比(如果开启)
if print_ratios
fprintf('\n%-6s %-12s %-20s %-20s\n', 'IMF#', '中心频率(Hz)', '呼吸频带能量占比', '心音频带能量占比');
for k = 1:L
fprintf('%-6d %-12.3f %-20.4f %-20.4f\n', k, omega_Hz(k), Er_ratio(k), Eh_ratio(k));
end
fprintf('阈值设置: 呼吸占比 > %.2f, 心跳占比 > %.2f\n', delta_r, delta_h);
end
% 呼吸集合 L_set,心跳集合 H_set
L_set = find(Er_ratio > delta_r);
H_set = find(Eh_ratio > delta_h);
% ----------呼吸谐波集合 G 的构建 ----------
% 遍历所有呼吸模态,对每个呼吸模态考虑 (v+2) 倍谐波,v=0,1,2
G_set = [];
if ~isempty(L_set)
for g = L_set
base_freq = omega_Hz(g);
for v = 0:2
harmonic = (v+2) * base_freq;
% 寻找最接近谐波频率的模态索引
[~, idx] = min(abs(omega_Hz - harmonic));
% 如果该模态属于心跳集合且尚未加入 G_set,则加入
if ismember(idx, H_set) && ~ismember(idx, G_set)
G_set(end+1) = idx;
end
end
end
G_set = unique(G_set); % 去重
end
% ------------------------------------------------
% 重构呼吸信号 (L ∪ G)
if ~isempty(union(L_set, G_set))
respSignal = sum(u(union(L_set, G_set), :), 1);
else
respSignal = zeros(1, N);
end
% 重构心跳信号 (H \ G)
heart_set = setdiff(H_set, G_set);
if ~isempty(heart_set)
heartSignal = sum(u(heart_set, :), 1);
else
heartSignal = zeros(1, N);
end
% 频率估计(使用相应频带)
respRate_Hz = getDominantFreq(respSignal, fs, band_resp);
heartRate_Hz = getDominantFreq(heartSignal, fs, band_heart);
end
function freq = getDominantFreq(signal, fs, band)
signal = signal(:).';
if isempty(signal) || length(signal) < 5
freq = NaN; return;
end
try
[Pxx, f] = periodogram(signal, [], 4096, fs);
catch
N = length(signal);
Y = fft(signal);
Pxx = abs(Y(1:floor(N/2)+1)).^2 / N;
f = fs * (0:floor(N/2)) / N;
end
idx = (f >= band(1)) & (f <= band(2));
if any(idx)
[~, maxIdx] = max(Pxx(idx));
f_band = f(idx);
freq = f_band(maxIdx);
else
freq = NaN;
end
end
模式判别"(Mode Discrimination)是本文的一个创新点,在算法的参数选择阶段 ,被用作一个分解有效性 的硬性约束(筛选器),即必须有一个IMF几乎都是呼吸频段、一个IMF几乎都是是心跳的频段,否则分解不成功,但我认为,这个要求可能过于苛刻,尤其是本文仅使用网格搜索参数的情况,在信号重构阶段 :它真正作为分类与归属的约束,根据能量占比决定每个IMF分配到呼吸信号还是心跳信号,并处理谐波干扰。
1L. Qu, C. Liu, T. Yang and Y. Sun, "Vital Sign Detection of FMCW Radar Based on Improved Adaptive Parameter Variational Mode Decomposition," in IEEE Sensors Journal, vol. 23, no. 20, pp. 25048-25060, 15 Oct.15, 2023, doi: 10.1109/JSEN.2023.3312513.
****结束****