如何在MATLAB中拟合指数曲线来抑制谐波振荡数据?

时间:2016-04-16 07:13:30

标签: matlab statistics signal-processing regression curve-fitting

我正在尝试将指数曲线拟合到包含阻尼谐波振荡的数据集。在正弦波振荡包含许多频率的意义上,数据有点复杂,如下所示:

enter image description here

我需要找到数据中的衰减率。我正在使用的方法here。它是如何工作的,它是将y值的对数高于稳态值,然后使用:

lsqlin(A,y1(:),-A,-y1(:),[],[],[],[],[],optimset('algorithm','active-set','display','off'))

适合它。

然而,这导致以下数据拟合: enter image description here

我尝试使用线性回归拟合,这显然不起作用,因为它取平均值。我还尝试过RANSAC认为峰值附近有更多数据。它的工作效果比线性回归好一些,但该方法存在缺陷,因为有时在错误的区域存在更多的点。

有没有人知道一种很好的方法来拟合这些数据的峰值?

目前,我正在考虑将500个数据点划分为10个不同的区域,并且在每个区域中找到最大值。最后,我应该使用上面提到的任何指数拟合方法得到50分。您如何看待这种方法?

2 个答案:

答案 0 :(得分:2)

以为我会向每个人提供可能有效的潜在解决方案的最新信息。如前所述,由于正弦频率的变化,数据很复杂,因此某些方法可能无法正常工作。根据所涉及的数据和频率,下面列出的方法可能很好。

首先,我假设数据的格式为:

y = average + b*e^-(c*x)

就我而言,平均值是290,所以我们有:

y = 290 + b*e^-(c*x)

话虽如此,让我们深入研究我尝试的不同方法:

findpeaks()方法

这是AlexanderBüse建议的方法。对于大多数数据来说,这是一种非常好的方法,但对于我的数据,由于存在多个正弦频率,因此它会出现错误的峰值。红色x显示峰值。

% Find Peaks Method
[max_num,max_ind] = findpeaks(y(ind));
plot(max_ind,max_num,'x','Color','r'); hold on;
x1 = max_ind;
y1 = log(max_num-290);
coeffs = polyfit(x1,y1,1)
b = exp(coeffs(2));
c = coeffs(1);

enter image description here

<强> RANSAC

如果你的大部分数据都达到峰值,RANSAC就会很好。你可以在我看来,由于多个频率,顶部附近存在更多的峰值。但是,我的数据存在的问题是并非所有数据集都是这样的。因此,它偶尔会奏效。

% RANSAC Method
ind = (y > avg);
x1 = x(ind);
y1 = log(y(ind) - avg);
iterNum = 300;
thDist = 0.5;
thInlrRatio = .1;
[t,r] = ransac([x1;y1'],iterNum,thDist,thInlrRatio);
k1 = -tan(t);
b1 = r/cos(t);
% plot(x1,k1*x1+b1,'r'); hold on;
b = exp(b1);
c = k1;

enter image description here

Lsqlin方法

此方法是使用here的方法。它使用Lsqlin来约束系统。但是,它似乎忽略了中间的数据。根据您的数据集,这可能非常适合原始帖子中的人员。

% Lsqlin Method
avg = 290;
ind = (y > avg);
x1 = x(ind);
y1 = log(y(ind) - avg);
A = [ones(numel(x1),1),x1(:)]*1.00;
coeffs = lsqlin(A,y1(:),-A,-y1(:),[],[],[],[],[],optimset('algorithm','active-set','display','off'));
b = exp(coeffs(2));
c = coeffs(1);

enter image description here

查找句点中的峰值

这是我在帖子中提到的方法,我在每个区域都获得了峰值。这种方法效果很好,从中我意识到我的数据实际上可能没有完美的指数拟合。我们看到它在开始时无法适应大峰。仅通过使用前150个数据点并忽略稳态数据点,我就能够做得更好。在这里,我每25个数据点找到一个峰值。

% Incremental Method 2 Unknowns
x1 = [];
y1 = [];
max_num=[];
max_ind=[];
incr = 25;
for i=1:floor(size(y,1)/incr)
    [max_num(end+1),max_ind(end+1)] = max(y(1+incr*(i-1):incr*i));
    max_ind(end) = max_ind(end) + incr*(i-1);
    if max_num(end) > avg
        x1(end+1) = max_ind(end);
        y1(end+1) = log(max_num(end)-290);
    end
end
plot(max_ind,max_num,'x','Color','r'); hold on;
coeffs = polyfit(x1,y1,1)
b = exp(coeffs(2));
c = coeffs(1);

使用所有500个数据点: Using all 500 data points

使用前150个数据点: enter image description here

使用b约束

查找期间的峰值

因为我希望它从第一个峰开始,所以我约束了b值。我知道系统是y=290+b*e^-c*x,我限制它b=y(1)-290。通过这样做,我只需要解决c c=(log(y-290)-logb)/x的问题。然后我可以取c的平均值或中位数。这种方法也很不错,它也不适合接近最终的价值,但由于变化很小,因此没有那么重要。

% Incremental Method 1 Unknown (b is constrained y(1)-290 = b)
b = y(1) - 290;
c = [];
max_num=[];
max_ind=[];
incr = 25;
for i=1:floor(size(y,1)/incr)
    [max_num(end+1),max_ind(end+1)] = max(y(1+incr*(i-1):incr*i));
    max_ind(end) = max_ind(end) + incr*(i-1);
    if max_num(end) > avg
        c(end+1) = (log(max_num(end)-290)-log(b))/max_ind(end);
    end
end
c = mean(c); % Or median(c) works just as good

这里我每25个数据点取一个峰值,然后取c的平均值 enter image description here

在这里,我每25个数据点取一个峰值,然后取c的中位数 enter image description here

这里我每10个数据点取一个峰值,然后取c的平均值 enter image description here

答案 1 :(得分:0)

如果主要目标是从拟合中提取阻尼参数,则可能需要考虑直接将阻尼正弦曲线拟合到数据中。这样的东西(用曲线拟合工具创建):

[xData, yData] = prepareCurveData( x, y );
ft = fittype( 'a + sin(b*x - c).*exp(d*x)', 'independent', 'x', 'dependent', 'y' );
opts = fitoptions( 'Method', 'NonlinearLeastSquares' );
opts.Display = 'Off';
opts.StartPoint = [1 0.285116122712545 0.805911873245316 0.63235924622541];
[fitresult, gof] = fit( xData, yData, ft, opts );
plot( fitresult, xData, yData );

特别是因为您的一些示例数据在感兴趣的区域(噪声之上)确实没有很多数据点。

但是,如果您确实需要直接拟合实验数据的最大值,则可以使用findpeaks函数仅选择最大值然后适合它们。您可能希望使用MinPeakProminence参数进行一些调整,以根据您的需要进行调整。