当电压检测值2出现数据低于1.95V时,判定为中频放大器故障。当电压检测值2出现数据高于2V时判定为中频放大器正常
当电压检测值3出现数据高于1.65V时,判定为低频放大器故障。当电压检测值3出现数据低于1.6V时,判定为低频放大器正常。如果判定为中频放大器故障,低频放大器就不能判定为故障,显示为低频放大器正常。如果没故障时界面分别显示绿色的中频放大器正常和低频放大器正常。如果发生故障,蜂鸣器响一声提示故障具体的故障点文字变化为红色。代码如下% voltageDataProcessor_optimized.m - 优化的电压数据实时处理与绘图程序
function voltageDataProcessor_optimized()
% 选择输入文件
[fileName, filePath] = uigetfile('*.dat', '选择DAT文件');
if fileName == 0
fprintf('未选择文件,程序退出。\n');
return;
end
% 完整文件路径
fullPath = fullfile(filePath, fileName);
% 创建图形窗口
fig = figure('Name', '电压数据实时处理与显示', ...
'Position', [100, 100, 1000, 600], ... % 调整窗口大小以适应3个子图
'CloseRequestFcn', @closeFigureCallback);
% 创建3个子图(垂直排列)
ax(1) = subplot(3, 1, 1); % 修改为3行1列布局
p1 = plot(ax(1), NaN, NaN, 'b');
title('电压检测值1');
xlabel('采样点');
ylabel('电压(V)');
grid on;
ax(2) = subplot(3, 1, 2);
p2 = plot(ax(2), NaN, NaN, 'g');
title('电压检测值2');
xlabel('采样点');
ylabel('电压(V)');
grid on;
ax(3) = subplot(3, 1, 3);
p3 = plot(ax(3), NaN, NaN, 'r');
title('电压检测值3');
xlabel('采样点');
ylabel('电压(V)');
grid on;
% 初始化数据存储变量(改为3组)
voltage1 = [];
voltage2 = [];
voltage3 = [];
% 文件读取状态
fileID = -1;
lastPos = 0;
% 滤波参数设置
filterOrder = 4; % 滤波器阶数
cutoffFreq = 0.1; % 截止频率(归一化)
% 创建低通滤波器
[b, a] = butter(filterOrder, cutoffFreq, 'low');
% 状态文本
statusText = uicontrol('Style', 'text', ...
'Position', [20, 20, 300, 20], ...
'String', '准备开始...', ...
'BackgroundColor', [0.9, 0.9, 0.9], ...
'HorizontalAlignment', 'left');
% 频率文本
freqText = uicontrol('Style', 'text', ...
'Position', [350, 20, 300, 20], ...
'String', '频率: 计算中...', ...
'BackgroundColor', [0.9, 0.9, 0.9], ...
'HorizontalAlignment', 'left');
% 故障显示文本 - 修改为两个独立的状态显示
midFaultText = uicontrol('Style', 'text', ...
'Position', [20, 70, 200, 20], ...
'String', '中频放大器: 正常', ...
'BackgroundColor', [0.9, 0.9, 0.9], ...
'ForegroundColor', [0, 0.6, 0], ... % 绿色字体表示正常
'HorizontalAlignment', 'left');
lowFaultText = uicontrol('Style', 'text', ...
'Position', [240, 70, 200, 20], ...
'String', '低频放大器: 正常', ...
'BackgroundColor', [0.9, 0.9, 0.9], ...
'ForegroundColor', [0, 0.6, 0], ... % 绿色字体表示正常
'HorizontalAlignment', 'left');
% 停止按钮
stopBtn = uicontrol('Style', 'pushbutton', ...
'Position', [700, 20, 100, 30], ...
'String', '停止监控', ...
'Callback', @stopMonitoring, ...
'BackgroundColor', [0.9, 0.4, 0.4]);
% 存储应用数据
appData = struct(...
'voltage1', voltage1, ...
'voltage2', voltage2, ...
'voltage3', voltage3, ...
'fileID', -1, ...
'lastPos', 0, ...
'fullPath', fullPath, ...
'running', true, ...
'p1', p1, ...
'p2', p2, ...
'p3', p3, ...
'ax', ax, ...
'statusText', statusText, ...
'freqText', freqText, ...
'midFaultText', midFaultText, ... % 中频放大器状态文本控件
'lowFaultText', lowFaultText, ... % 低频放大器状态文本控件
'b', b, ...
'a', a, ...
'maxDataPoints', 10000, ...
'maxDisplayPoints', 1000, ...
'midFaultStatus', false, ... % 中频放大器故障状态
'lowFaultStatus', false ... % 低频放大器故障状态
);
setappdata(fig, 'appData', appData);
% 打开文件
try
fileID = fopen(fullPath, 'r');
if fileID == -1
error('无法打开文件: %s', fullPath);
end
% 更新应用数据
appData.fileID = fileID;
setappdata(fig, 'appData', appData);
set(statusText, 'String', '文件已打开,开始监控...');
catch ME
set(statusText, 'String', ['错误: ' ME.message]);
return;
end
% 启动定时器
timerObj = timer(...
'ExecutionMode', 'fixedRate', ...
'Period', 0.5, ... % 更新间隔0.5秒
'TimerFcn', {@updatePlots, fig}, ...
'ErrorFcn', @timerErrorCallback, ...
'BusyMode', 'drop' ...
);
start(timerObj);
% 存储定时器对象
appData.timerObj = timerObj;
setappdata(fig, 'appData', appData);
% 关闭回调函数
function closeFigureCallback(~, ~)
appData = getappdata(fig, 'appData');
if isfield(appData, 'timerObj') && isvalid(appData.timerObj)
stop(appData.timerObj);
delete(appData.timerObj);
end
if isfield(appData, 'fileID') && appData.fileID > 2
fclose(appData.fileID);
end
delete(fig);
end
% 停止监控回调
function stopMonitoring(~, ~)
appData = getappdata(fig, 'appData');
appData.running = false;
setappdata(fig, 'appData', appData);
set(statusText, 'String', '监控已停止');
if isfield(appData, 'timerObj') && isvalid(appData.timerObj)
stop(appData.timerObj);
end
end
% 定时器错误回调
function timerErrorCallback(~, event)
set(appData.statusText, 'String', ['定时器错误: ' event.Data.message]);
end
end
% 更新图形函数
function updatePlots(~, ~, figHandle)
if ~ishandle(figHandle)
return;
end
appData = getappdata(figHandle, 'appData');
if ~appData.running
return;
end
try
% 获取当前文件大小
fseek(appData.fileID, 0, 'eof');
currentFileSize = ftell(appData.fileID);
% 检查文件是否有新内容
if currentFileSize > appData.lastPos
% 移动到上次结束位置
fseek(appData.fileID, appData.lastPos, 'bof');
% 读取新数据
newData = textscan(appData.fileID, '%s', 'Delimiter', '\n');
newLines = newData{1};
% 处理新数据
newVoltage1 = [];
newVoltage2 = [];
newVoltage3 = [];
for i = 1:length(newLines)
line = strtrim(newLines{i});
% 跳过空行
if isempty(line)
continue;
end
% 提取数字
[num, success] = extractNumber(line);
if success
% 根据行号分配到对应的电压数组(改为3组循环)
switch mod(i-1, 3) + 1
case 1
newVoltage1 = [newVoltage1; num];
case 2
newVoltage2 = [newVoltage2; num];
case 3
newVoltage3 = [newVoltage3; num];
end
end
end
% 更新数据
if ~isempty(newVoltage1)
appData.voltage1 = [appData.voltage1; newVoltage1];
appData.voltage2 = [appData.voltage2; newVoltage2];
appData.voltage3 = [appData.voltage3; newVoltage3];
% === 新增:限制数据缓冲区大小 ===
if length(appData.voltage1) > appData.maxDataPoints
keepPoints = appData.maxDataPoints;
appData.voltage1 = appData.voltage1(end-keepPoints+1:end);
appData.voltage2 = appData.voltage2(end-keepPoints+1:end);
appData.voltage3 = appData.voltage3(end-keepPoints+1:end);
end
% ==============================
% 更新图形(改为3组)
updatePlot(appData.p1, appData.voltage1, appData.maxDisplayPoints);
updatePlot(appData.p2, appData.voltage2, appData.maxDisplayPoints);
updatePlot(appData.p3, appData.voltage3, appData.maxDisplayPoints);
% 计算并显示通道3的频率
if length(appData.voltage3) > 100
fs = 100; % 假设采样频率为100Hz
frequency = calculateFrequency(appData.voltage3, fs);
set(appData.freqText, 'String', sprintf('频率: %.2f Hz', frequency));
title(appData.ax(3), sprintf('电压检测值3 (主要频率: %.2f Hz)', frequency));
end
% 应用滤波并更新滤波线(改为3组)
if length(appData.voltage1) >= 10
updateFilteredLine(appData.ax(1), appData.voltage1, appData.b, appData.a, appData.maxDisplayPoints);
updateFilteredLine(appData.ax(2), appData.voltage2, appData.b, appData.a, appData.maxDisplayPoints);
updateFilteredLine(appData.ax(3), appData.voltage3, appData.b, appData.a, appData.maxDisplayPoints);
end
% ====== 新增故障检测逻辑 ======
% 检测中频放大器状态
if ~isempty(appData.voltage2)
lastValue = appData.voltage2(end);
if lastValue < 1.6
% 检测到中频放大器故障
set(appData.midFaultText, 'String', '中频放大器: 故障', 'ForegroundColor', [1, 0, 0]); % 红色字体
% 如果之前是正常状态,则发出警报
if ~appData.midFaultStatus
% 发出蜂鸣声
playBeep();
% 更新状态
set(appData.statusText, 'String', '检测到中频放大器故障!');
end
% 设置中频放大器故障标志
appData.midFaultStatus = true;
elseif lastValue > 1.7
% 检测到中频放大器正常
set(appData.midFaultText, 'String', '中频放大器: 正常', 'ForegroundColor', [0, 0.6, 0]); % 绿色字体
% 清除中频放大器故障标志
appData.midFaultStatus = false;
% 更新全局状态(如果所有器件都正常)
if ~appData.lowFaultStatus
set(appData.statusText, 'String', '系统运行正常');
end
end
end
% 检测低频放大器状态
if ~isempty(appData.voltage3)
lastValue = appData.voltage3(end);
if lastValue > 1.65
% 检测到低频放大器故障
set(appData.lowFaultText, 'String', '低频放大器: 故障', 'ForegroundColor', [1, 0, 0]); % 红色字体
% 如果之前是正常状态,则发出警报
if ~appData.lowFaultStatus
% 发出蜂鸣声
playBeep();
% 更新状态
set(appData.statusText, 'String', '检测到低频放大器故障!');
end
% 设置低频放大器故障标志
appData.lowFaultStatus = true;
elseif lastValue < 1.6
% 检测到低频放大器正常
set(appData.lowFaultText, 'String', '低频放大器: 正常', 'ForegroundColor', [0, 0.6, 0]); % 绿色字体
% 清除低频放大器故障标志
appData.lowFaultStatus = false;
% 更新全局状态(如果所有器件都正常)
if ~appData.midFaultStatus
set(appData.statusText, 'String', '系统运行正常');
end
end
end
% ====== 故障检测结束 ======
% 更新数据
appData.voltage1 = [appData.voltage1; newVoltage1];
appData.voltage2 = [appData.voltage2; newVoltage2];
appData.voltage3 = [appData.voltage3; newVoltage3];
% 更新文件位置
appData.lastPos = currentFileSize;
setappdata(figHandle, 'appData', appData);
end
end
% 刷新图形
drawnow limitrate;
catch ME
set(appData.statusText, 'String', ['错误: ' ME.message]);
appData.running = false;
setappdata(figHandle, 'appData', appData);
end
end
% 蜂鸣声函数
function playBeep()
% 创建一个短暂的音频信号
fs = 8192; % 采样率
t = 0:1/fs:0.2; % 0.2秒
f = 1000; % 音调频率
beepSignal = sin(2*pi*f*t);
% 播放声音(使用soundsc自动调整幅度)
soundsc(beepSignal, fs);
end
% 数字提取函数
function [num, success] = extractNumber(line)
% 尝试多种方法提取数字
success = false;
num = NaN;
% 方法1: 使用正则表达式提取第一个数字
tokens = regexp(line, '[-+]?\d*\.?\d+', 'match');
if ~isempty(tokens)
num = str2double(tokens{1});
if ~isnan(num)
success = true;
return;
end
end
% 方法2: 使用sscanf提取
[num, count] = sscanf(line, '%f');
if count >= 1
num = num(1);
success = true;
return;
end
% 方法3: 尝试去掉非数字字符
cleanLine = regexprep(line, '[^0-9\.\-+]', '');
if ~isempty(cleanLine)
num = str2double(cleanLine);
if ~isnan(num)
success = true;
end
end
end
% 更新绘图函数 (修改后)
function updatePlot(plotHandle, data, maxDisplayPoints)
if isempty(data)
return;
end
totalPoints = length(data);
% 确定显示的起始点和结束点
if totalPoints > maxDisplayPoints
startIdx = totalPoints - maxDisplayPoints + 1;
endIdx = totalPoints;
dataToShow = data(startIdx:end);
else
startIdx = 1;
endIdx = totalPoints;
dataToShow = data;
end
% 更新图形数据
set(plotHandle, 'XData', startIdx:endIdx, 'YData', dataToShow);
% 更新坐标轴范围
ax = get(plotHandle, 'Parent');
xlim(ax, [startIdx, endIdx]); % 动态X轴范围
% 自动调整Y轴范围
minY = min(dataToShow);
maxY = max(dataToShow);
range = max(0.1, maxY - minY);
ylim(ax, [minY - 0.1*range, maxY + 0.1*range]);
end
% 更新滤波线函数 (修改后)
function updateFilteredLine(ax, data, b, a, maxDisplayPoints)
% 检查是否已存在滤波线
lines = findobj(ax, 'Type', 'line');
filteredLine = [];
% 查找滤波线(红色虚线)
for i = 1:length(lines)
if strcmp(get(lines(i), 'LineStyle'), '--') && ...
isequal(get(lines(i), 'Color'), [1, 0, 0])
filteredLine = lines(i);
break;
end
end
% 应用滤波
if length(data) > 10
filteredData = filtfilt(b, a, data);
else
filteredData = data;
end
totalPoints = length(data);
% 确定显示的起始点和结束点
if totalPoints > maxDisplayPoints
startIdx = totalPoints - maxDisplayPoints + 1;
endIdx = totalPoints;
dataToShow = filteredData(startIdx:end);
else
startIdx = 1;
endIdx = totalPoints;
dataToShow = filteredData;
end
if isempty(filteredLine)
% 创建新的滤波线
hold(ax, 'on');
plot(ax, startIdx:endIdx, dataToShow, 'r--', 'LineWidth', 1.5);
hold(ax, 'off');
% 添加图例
if isempty(legend(ax))
legend(ax, {'原始数据', '滤波后数据'}, 'Location', 'best');
end
else
% 更新现有滤波线
set(filteredLine, 'XData', startIdx:endIdx, 'YData', dataToShow);
end
end
% 频率计算函数(改进版)
function freq = calculateFrequency(data, fs)
% 确保数据长度足够
if length(data) < 100
freq = 0;
return;
end
% 去直流分量
data = data - mean(data);
% 使用自相关法检测周期
[acf, lags] = xcorr(data, 'coeff');
acf = acf(lags >= 0);
lags = lags(lags >= 0);
% 寻找主峰位置(跳过第一个点)
[peaks, locs] = findpeaks(acf(2:end));
if isempty(peaks)
freq = 0;
return;
end
% 找到最高峰
[~, idx] = max(peaks);
peakIdx = locs(idx) + 1; % 补偿跳过的第一个点
% 计算周期和频率
period = lags(peakIdx) / fs;
freq = 1 / period;
% 验证频率在合理范围内
if freq > fs/2 || freq < 0.1
% 如果自相关法失败,使用FFT作为备选
n = length(data);
fftResult = fft(data);
P2 = abs(fftResult/n);
P1 = P2(1:floor(n/2)+1);
P1(2:end-1) = 2*P1(2:end-1);
f = fs*(0:(n/2))/n;
[~, idx] = max(P1(2:end)); % 跳过DC分量
freq = f(idx+1);
end
end