案例1:无噪声的正弦波
如果你的正弦波没有任何噪声,你可以使用非常经典的信号处理技术:取第一阶导数并检测其是否等于零。
例如:
function signal = derivesignal( d )
signal = zeros(size(d));
for i=2:length(d)
if d(i-1) > 0 && d(i) <= 0
signal(i) = +1;
elseif d(i-1) < 0 && d(i) >= 0
signal(i) = -1;
end
end
end
使用您提供的示例数据:
dt = 1/8000;
t = (0:dt:(1-dt)/4)';
y = sin(2*pi*60*t);
y(1:1000) = y(1:1000) + 0.001*(1:1000)';
y(1001:2000) = y(1001:2000) - 0.002*(1:1000)';
d = [0; diff(y)];
signal = derivesignal(d);
figure(1); clf; set(gcf,'Position',[0 0 677 600])
subplot(4,1,1); hold on;
title('Data');
plot(t,y);
subplot(4,1,2); hold on;
title('First derivative');
area(d);
ylim([-0.05, 0.05]);
subplot(4,1,3); hold on;
title('Signal (-1 for trough, +1 for peak)');
plot(t,signal); ylim([-1.5 1.5]);
subplot(4,1,4); hold on;
title('Signals marked on data');
markers = abs(signal) > 0;
plot(t,y); scatter(t(markers),y(markers),30,'or','MarkerFaceColor','red');
这将产生以下结果:
![Example sinuisoid detection](https://istack.dev59.com/TF997.webp)
对于任何类型的正弦波,这种方法都非常有效,唯一的要求是输入信号不包含噪声。
情况2:带噪声的正弦波
一旦输入信号包含噪声,导数法就会失效。例如:
dt = 1/8000;
t = (0:dt:(1-dt)/4)';
y = sin(2*pi*60*t);
y(1:1000) = y(1:1000) + 0.001*(1:1000)';
y(1001:2000) = y(1001:2000) - 0.002*(1:1000)';
y = y + 0.2.*randn(2000,1);
下面的结果是由于“一阶差分放大了噪音”:
![Sinusoid with noise example](https://istack.dev59.com/gVDaQ.webp)
处理噪声的方法有很多种,最常用的方法是应用移动平均滤波器。移动平均滤波器的一个缺点是它们适应新信息的速度较慢,以至于信号可能会在出现后才被识别(移动平均滤波器具有滞后性)。
另一个非常典型的方法是使用Fourier分析来识别输入数据中的所有频率,忽略所有低振幅和高频率正弦波,并使用剩余的正弦波作为滤波器。剩余的正弦波将被(大部分)从噪声中清除,然后可以再次使用一阶差分来确定峰值和谷值(或对于单个正弦波,您知道峰值和谷值发生在相位的1/4和3/4π)。我建议您阅读任何一个信号处理理论书籍,以了解更多关于这种技术的信息。Matlab还有一些教育资料。
如果您想在硬件中使用此算法,我建议您也看看WFLC(加权傅里叶线性组合器)或使用例如1个振荡器或PLL(锁相环)来估计噪声波的相位,而不必进行完整的快速傅里叶变换。您可以在维基百科上找到数字锁相环的Matlab算法。
我会在这里建议稍微复杂一些的方法,以实时识别峰值和谷值:使用移动的最小二乘拟合将正弦波函数拟合到您的数据中,并使用Fourier分析的初始估计。
下面是我的函数:
function [result, peaks, troughs] = fitsine(y, t, eps)
f = fft(y);
l = length(y);
p2 = abs(f/l);
p1 = p2(1:ceil(l/2+1));
p1(2:end-1) = 2*p1(2:end-1);
freq = (1/mean(diff(t)))*(0:ceil(l/2))/l;
maxPeak = p1 == max(p1(2:end));
maxAmplitude = p1(maxPeak);
maxFrequency = freq(maxPeak);
p = [];
p(1) = mean(y);
p(2) = maxAmplitude;
p(3) = maxFrequency;
p(4) = 0;
p(5) = 0;
f = @(p) p(1) + p(2)*sin( p(3)*2*pi*t+p(4) ) + p(5)*t;
ferror = @(p) sum((f(p) - y).^2);
options = optimset('MaxFunEvals',50000,'MaxIter',50000,'TolFun',1e-25);
[param,fval,exitflag,output] = fminsearch(ferror,p,options);
result = f(param);
peaks = abs(sin(param(3)*2*pi*t+param(4)) - 1) < eps;
troughs = abs(sin(param(3)*2*pi*t+param(4)) + 1) < eps;
end
如您所见,我首先对数据执行
傅里叶变换,以找到振幅和频率的初始估计值。然后使用模型
a + b sin(ct + d) + et将正弦波拟合到数据上。 拟合值代表一个正弦波,我知道+1和-1分别是峰值和谷值。 因此,我可以将这些值识别为信号。
对于具有(缓慢变化)趋势和一般(白色)噪声的正弦波,这种方法非常有效:
dt = 1/8000;
t = (0:dt:(1-dt)/4)';
y = sin(2*pi*60*t);
y(1:1000) = y(1:1000) + 0.001*(1:1000)';
y(1001:2000) = y(1001:2000) - 0.002*(1:1000)';
y = y + 0.2.*randn(2000,1);
window = 250;
interval = 10;
result = nan(size(y));
signal = zeros(size(y));
for i = window+1:interval:length(y)
data = y(i-window:i);
period = t(i-window:i);
[output, peaks, troughs] = fitsine(data,period,0.01);
result(i-interval:i) = output(end-interval:end);
signal(i-interval:i) = peaks(end-interval:end) - troughs(end-interval:end);
end
figure(1); clf; set(gcf,'Position',[0 0 677 600])
subplot(4,1,1); hold on;
title('Data');
plot(t,y); xlim([0 max(t)]); ylim([-4 4]);
subplot(4,1,2); hold on;
title('Model fit');
plot(t,result,'-k'); xlim([0 max(t)]); ylim([-4 4]);
subplot(4,1,3); hold on;
title('Signal (-1 for trough, +1 for peak)');
plot(t,signal,'r','LineWidth',2); ylim([-1.5 1.5]);
subplot(4,1,4); hold on;
title('Signals marked on data');
markers = abs(signal) > 0;
plot(t,y,'-','Color',[0.1 0.1 0.1]);
scatter(t(markers),result(markers),30,'or','MarkerFaceColor','red');
xlim([0 max(t)]); ylim([-4 4]);
![将正弦波拟合到正弦信号上](https://istack.dev59.com/woFQt.webp)
这种方法的主要优点是:
- 您可以获得实际数据模型,因此您可以在未来发生信号之前预测信号!(例如,固定模型并通过输入未来时间段来计算结果)
- 您不需要每个时间段都估计模型(请参见代码中的参数
interval
)
缺点是您需要选择一个回溯window
,但您将使用任何用于实时检测的方法都会遇到此问题。
视频演示
Data
是输入数据,Model fit
是拟合到数据的正弦波(请参见代码),Signal
表示峰值和谷值,Signals marked on data
给出了算法的准确性印象。 注意:观察模型适应图中趋势的方式!
![将正弦波模型实时拟合到正弦信号上](https://istack.dev59.com/ObzDF.gif)
这应该能帮助您入门。还有很多关于信号检测理论的优秀书籍(只需谷歌这个词),它们将深入探讨这些类型的技术。祝好运!