记录一个简单的小项目,供大家参考。
废话少说直接上图:
模块一:加载dat文件
function ecg_raw = loadECGData(path, datafile, samples2read)
% 加载并解码ECG二进制数据
% 输入:
% path - 数据文件路径
% datafile - 数据文件名
% samples2read - 读取样本数
% 输出:
% ecg_raw - 原始ECG信号
signald = fullfile(path, datafile);
fid2 = fopen(signald,'r');
A = fread(fid2, [3, samples2read], 'uint8')';
fclose(fid2);
% 数据解码
M2H = bitshift(A(:,2), -4);
M1H = bitand(A(:,2), 15);
M(:,1) = bitshift(M1H,8) + A(:,1);
M(:,2) = bitshift(M2H,8) + A(:,3);
% 信号截取与基线校正
ecg_raw = M(100:end-1000,1) - 1024;
end
模块二:ECG滤波
function ecg_filtered = filterECG(ecg_raw, Fs)
% ECG信号滤波处理
% 输入:
% ecg_raw - 原始ECG信号
% Fs - 采样频率
% 输出:
% ecg_filtered - 滤波后信号
% 基线漂移去除
hpFilt = designfilt('highpassiir', 'FilterOrder', 4, ...
'HalfPowerFrequency', 0.5, 'SampleRate', Fs, 'DesignMethod', 'butter');
ecg_base = filtfilt(hpFilt, ecg_raw);
% 带通滤波
bpFilt = designfilt('bandpassiir', 'FilterOrder', 4, ...
'HalfPowerFrequency1', 0.5, 'HalfPowerFrequency2', 40, 'SampleRate', Fs);
ecg_bp = filtfilt(bpFilt, ecg_base);
% 工频干扰消除
notch_freq = 50;
wo = notch_freq/(Fs/2);
bw = wo/35;
[num, den] = iirnotch(wo, bw);
ecg_filtered = filtfilt(num, den, ecg_bp);
end
模块三:R峰检测
function adjusted_r_peaks = detectRPeaks(ecg_signal, Fs)
% R峰检测算法
% 输入:
% ecg_signal - 滤波后ECG信号
% Fs - 采样频率
% 输出:
% adjusted_r_peaks - 校正后的R峰位置索引
% QRS波增强
bpFilt = designfilt('bandpassiir', 'FilterOrder', 2, ...
'HalfPowerFrequency1', 5, 'HalfPowerFrequency2', 15, 'SampleRate', Fs);
filtered = filtfilt(bpFilt, ecg_signal);
% 微分处理
diff_signal = diff([0; filtered]);
squared = diff_signal.^2;
% 移动平均
window_size = round(0.15 * Fs);
integrated = movmean(squared, window_size);
% 阈值检测
thr = 3 * mean(integrated);
min_interval = 0.2 * Fs;
peaks = find(integrated > thr);
r_peaks = peaks([true; diff(peaks) > min_interval]);
% 局部最大值精修
search_win = 40;
adjusted_r_peaks = zeros(size(r_peaks));
for i = 1:length(r_peaks)
start_idx = r_peaks(i);
end_idx = min(start_idx + search_win, length(ecg_signal));
[~, max_pos] = max(ecg_signal(start_idx:end_idx));
adjusted_r_peaks(i) = start_idx + max_pos - 1;
end
% 去重处理
[adjusted_r_peaks, ~] = unique(adjusted_r_peaks);
adjusted_r_peaks = sort(adjusted_r_peaks);
end
模块四:GUI界面
function create_gui()
% 参数配置
PATH = 'C:\ECG-GUI';
SAMPLES2READ = 660000;
Fs = 360;
% 创建主界面
fig = uifigure('Name', '心电分析系统', 'Position', [100 100 800 650],...
'Color', [0.96 0.96 0.96], 'Resize', 'on');
% 初始化数据存储
fig.UserData = struct('ecg_raw',[], 'ecg_filtered',[], 'Fs', Fs, 'avgInterval', NaN);
% =====================================================================
% 界面组件
% =====================================================================
% 顶部标题
uilabel(fig, 'Text','心电信号处理系统', 'FontSize',20,...
'Position',[200 600 400 40], 'FontColor',[0.2 0.2 0.6],...
'HorizontalAlignment','center');
% 按钮组
btnPanel = uipanel(fig, 'Position',[50 550 700 50],...
'BackgroundColor',[0.9 0.9 0.9], 'BorderType','none');
loadDataBtn = uibutton(btnPanel, 'Text','加载数据', 'Position',[80 10 100 30],...
'ButtonPushedFcn', @loadData);
filterBtn = uibutton(btnPanel, 'Text','信号滤波', 'Position',[210 10 100 30],...
'Tag','btnFilter', 'ButtonPushedFcn', @processFilter, 'Enable','off');
detectBtn = uibutton(btnPanel, 'Text','R峰检测', 'Position',[340 10 100 30],...
'Tag','btnDetect', 'ButtonPushedFcn', @showRPeaks, 'Enable','off');
heartRateBtn = uibutton(btnPanel, 'Text','计算心率', 'Position',[470 10 100 30],...
'ButtonPushedFcn', @calculateHeartRate, 'Enable','off');
% 波形显示区域
axRaw = axes(fig, 'Position',[0.08 0.45 0.85 0.26],...
'XTickLabel',[], 'FontSize',10, 'Tag','RawAxes');
title(axRaw, '原始信号 (点击"加载数据"选择文件)');
axFiltered = axes(fig, 'Position',[0.08 0.14 0.85 0.26],...
'FontSize',10, 'Tag','FilteredAxes');
xlabel(axFiltered, '时间 (s)');
title(axFiltered, '处理结果区');
% 状态显示栏
infoPanel = uipanel(fig, 'Position',[50 500 700 40],...
'BackgroundColor',[0.95 0.95 0.95]);
uilabel(infoPanel, 'Text','R波数量:', 'Position',[20 5 80 30],...
'FontColor',[0.2 0.2 0.6], 'FontSize',12,...
'HorizontalAlignment','right');
fig.UserData.txtCount = uilabel(infoPanel, 'Text','0',...
'Position',[110 5 100 30], 'FontSize',12);
uilabel(infoPanel, 'Text','平均间期:', 'Position',[220 5 80 30],...
'FontColor',[0.2 0.2 0.6], 'FontSize',12,...
'HorizontalAlignment','right');
fig.UserData.txtInterval = uilabel(infoPanel, 'Text','0 s',...
'Position',[310 5 120 30], 'FontSize',12);
uilabel(infoPanel, 'Text','平均心率:', 'Position',[440 5 80 30],...
'FontColor',[0.2 0.2 0.6], 'FontSize',12,...
'HorizontalAlignment','right');
fig.UserData.txtHeartRate = uilabel(infoPanel, 'Text','0 bpm',... % 添加心率显示
'Position',[530 5 120 30], 'FontSize',12);
% =====================================================================
% 回调函数
% =====================================================================
% 1. 数据加载
function loadData(~,~)
try
[file, path] = uigetfile(fullfile(PATH,'*.dat'), '选择ECG文件');
if isequal(file,0), return; end
% 读取数据
rawData = loadECGData(path, file, SAMPLES2READ);
fig.UserData.ecg_raw = rawData;
% 显示原始信号
t = (100:6100)/Fs;
cla(axRaw);
plot(axRaw, t, rawData(100:6100), 'b', 'LineWidth',0.8);
title(axRaw, sprintf('原始信号: %s', file));
xlabel(axRaw, '');
grid(axRaw, 'on');
axis(axRaw, 'tight');
% 启用后续功能
filterBtn.Enable = 'on';
catch ME
uialert(fig, sprintf('数据加载失败:\n%s', ME.message), '错误');
end
end
% 2. 信号滤波
function processFilter(~,~)
try
if isempty(fig.UserData.ecg_raw)
uialert(fig, '请先加载数据', '警告'); return;
end
% 执行滤波
filtered = filterECG(fig.UserData.ecg_raw, Fs);
fig.UserData.ecg_filtered = filtered;
% 显示处理结果
t = (100:6100)/Fs;
cla(axFiltered);
plot(axFiltered, t, filtered(100:6100),...
'Color',[0 0.5 0], 'LineWidth',1);
title(axFiltered, '滤波后信号');
grid(axFiltered, 'on');
axis(axFiltered, 'tight');
% 启用检测按钮
detectBtn.Enable = 'on';
% 启用计算心率按钮
heartRateBtn.Enable = 'on';
% 强制刷新界面
drawnow limitrate;
catch ME
uialert(fig, sprintf('滤波失败:\n%s', ME.message), '错误');
end
end
% 3. R峰检测
function showRPeaks(~,~)
try
if isempty(fig.UserData.ecg_filtered)
uialert(fig, '请先进行滤波处理', '警告'); return;
end
% 执行检测
peaks = detectRPeaks(fig.UserData.ecg_filtered, Fs);
validPeaks = peaks(peaks >= 100 & peaks <=6100);
% 更新显示
hold(axFiltered, 'on');
plot(axFiltered, validPeaks/Fs,...
fig.UserData.ecg_filtered(validPeaks),...
'rv', 'MarkerSize',8, 'MarkerFaceColor','r');
hold(axFiltered, 'off');
% 计算统计值
peakCount = numel(peaks);
if peakCount > 1
intervals = diff(peaks)/Fs;
avgInterval = mean(intervals);
else
avgInterval = NaN;
end
% 更新状态栏
fig.UserData.txtCount.Text = num2str(peakCount);
if ~isnan(avgInterval)
fig.UserData.txtInterval.Text =...
sprintf('%.3f s', avgInterval);
fig.UserData.avgInterval = avgInterval; % 保存平均间期
else
fig.UserData.txtInterval.Text = 'N/A';
end
catch ME
uialert(fig, sprintf('检测失败:\n%s', ME.message), '错误');
end
end
% 4. 计算心率
function calculateHeartRate(~,~)
try
if isnan(fig.UserData.avgInterval) || fig.UserData.avgInterval == 0
uialert(fig, '请先完成R峰检测', '警告');
return;
end
% 计算平均心率 (bpm)
heartRate = 60 / fig.UserData.avgInterval;
fig.UserData.txtHeartRate.Text = sprintf('%.2f bpm', heartRate);
catch ME
uialert(fig, sprintf('计算心率失败:\n%s', ME.message), '错误');
end
end
end
运行GUI界面这个程序即可。