MATLAB 数据处理(二)非线性拟合——洛伦兹拟合(Lorentz fit)

一、拟合示例

蓝色为原始值,红色为拟合值
左边为单峰洛伦兹拟合,右边为双峰洛伦兹拟合

在这里插入图片描述

二、单峰洛伦兹

2.1 洛伦兹线型函数表达式与物理含义

在这里插入图片描述

p1:谷值波长对应的纵坐标*p3
p2:中心波长,即谷值对应的横坐标
p3:(半高全宽/2^2
p4:纵向偏移

函数重要参数:

  • 谷值波长center
  • 半高全宽FWHM
  • 品质因子Q=center/FWHM

2.2 lsqcurvefit非线性拟合

  • 非线性拟合为已知输入向量X和输出向量Y,且已知输入输出函数关系为Y=F(p_start, X),系数向量p_start未知。

  • 设置目标函数为最小二乘表达式:min Σ(F(p_start, X)-Y)^2,多次迭代使目标函数最小

  • 因此,初始系数向量p_start十分重要,需要根据其物理意义赋初值

      [params,~] = lsqcurvefit(@F,p_start,X,Y);
    
  • params为返回的最优系数向量

2.3 代码实现

%% 输入参数
% X:wavelength(nm)
% Y:power(dbm)
% position:前一个最值在当前波形中的位置
% interval_num:左右的点数interval_num=interval_nm/sens;
% fit:是否要拟合
% fwhm:半高全宽 0.1
%% 输出参数
% x:新区间x,用于绘图
% y:新区间y,用于绘图
% locs:最值loc,用于绘图
% Ocenter:峰值波长
% new_position:下次进入的位置
% yFit:fit后的y,默认为y
% Fcenter:fit后的峰值波长,默认为Ocenter
% FWHM:半高全宽,默认为0
% Q:默认为0
function [x,y,locs,Ocenter,new_position,yFit,Fcenter,FWHM,Q] = matlab_single_fit(X,Y,position,interval_num,fit,fwhm)
   
   %1. 在整幅光谱上截取需要拟合的局部光谱    
    %% %====当前小区间====%
    x = X(position-interval_num:position+interval_num);%划当前波长对应的小区间
    y = Y(position-interval_num:position+interval_num); 
   %2. 求初值
    %% %====求当前波长小区间的最小值====%
    [~,minposition] = min(y);
    new_position = find(abs(X - x(minposition))<=1e-5);%返回当前值
    %% %====lorenz fit====%
    %====1.划当前最小值对应的小区间====%
    x = X(new_position-interval_num:new_position+interval_num);
    y = Y(new_position-interval_num:new_position+interval_num);  
    %====2.初始系数====%
    [pks(1),locs(1)] = min(y);%最大值一定是单峰或者双峰
    Ocenter = x(locs);
    %============%
    yFit=y;
    Fcenter=Ocenter;
    FWHM=0;
    Q=0;
    %==============%
    if(fit)
        p3 = (fwhm./2).^2;%赋初值
        c = 0;
        p2 = Ocenter;
        p1 = pks.*p3;           
        p_start = [p1 p2 p3 c];
        %====3.fit====%    
        [params,~] = lsqcurvefit(@single_lorentzfit,p_start,x,y);
        yFit = single_lorentzfit(params,x);       
        %====4.print====%
        Fcenter=params(2);      
        FWHM=2.*sqrt(params(3));
        Q=params(2)./FWHM;
    end
    fprintf("Ocenter %0.5f \n",Ocenter)
    fprintf("Fcenter %0.5f \n",Fcenter)
    fprintf("centerError1 %0.5f \n",Ocenter-Fcenter);
    fprintf("fwhm %0.5f \n",FWHM)
    fprintf("Q %0.5f \n",Q);
end
%% 单峰拟合
% 中心波长:p2
% fwhm:2sqrt(p3)
function F = single_lorentzfit(p,x)
    F = p(1)./((x-p(2)).^2 + p(3)) + p(4);
end 

三、双峰洛伦兹

3.1 洛伦兹线型函数表达式与物理含义

在这里插入图片描述
和上一个差不多

p1:谷值波长对应的纵坐标*p3
p2:中心波长,即谷值对应的横坐标
p3:(半高全宽/2^2
p4:纵向偏移
p5:谷值波长对应的纵坐标*p3
p6:中心波长,即谷值对应的横坐标
p7:(半高全宽/2^2

函数重要参数:

  • 谷值波长center1,center2
  • 半高全宽FWHM1,FWHM2
  • 品质因子Q=min(center1/FWHM1, center2/FWHM2)

3.2 代码实现

%% 输入参数
% X:wavelength(nm)
% Y:power(dbm)
% position:前一个最值在当前波形中的位置
% interval_num:左右的点数interval_num=interval_nm/sens;
% fit:是否要拟合
% fwhm:半高全宽 0.1
% power_diff:第二个极值与第一个极值峰的最大高度差
%% 输出参数
% x:新区间x,用于绘图
% y:新区间y,用于绘图
% locs:最值loc,用于绘图
% Odiff:Ocenter2-Ocenter1;传感值
% Ocenter1:峰值波长1
% Ocenter2:峰值波长2
% new_position:下次进入的位置
% yFit:fit后的y,默认为0
% Fdiff:fit后的传感值Fcenter2-Fcenter1;
% Fcenter1:fit后的峰值波长,默认为Ocenter1
% Fcenter2:fit后的峰值波长,默认为Ocenter2
% Q:默认为0
     
function [x,y,locs,Odiff,Ocenter1,Ocenter2,new_position,yFit,Fdiff,Fcenter1,Fcenter2,Q] = matlab_bimodal_fit(X,Y,position,interval_num,fit,fwhm,power_diff)
       
    %% %====当前小区间====%
    x = X(position-interval_num:position+interval_num);%划当前波长对应的小区间
    y = Y(position-interval_num:position+interval_num); 
    
    %% %====求当前波长小区间的最小值====%
    [~,minposition] = min(y);
    new_position = find(abs(X - x(minposition))<=1e-5);%返回当前值
    %% %====lorenz fit====%
    %====1.划当前最小值对应的小区间====%
    x = X(new_position-interval_num:new_position+interval_num);
    y = Y(new_position-interval_num:new_position+interval_num);  
    %====2.初始系数====%
    [pks(1),locs(1)] = min(y);%最大值一定是单峰或者双峰
    [pks_tmp,locs_tmp]=findpeaks(-y);%找到第二大的极值对应的locs
    pks_tmp = -pks_tmp;
    %找第二个极值
    for i=1:length(locs_tmp)
        if( pks_tmp(i)-pks(1)<power_diff && pks_tmp(i)-pks(1)>0)
            locs(2)=locs_tmp(i);
            pks(2)=pks_tmp(i);
            break;
        end
    end    
    if(length(locs)==2)
        Ocenter1 = x(locs(1));
        Ocenter2 = x(locs(2));
        Odiff = Ocenter2 - Ocenter1;
    else %如果没有第二个极值,赋值为0
        Ocenter1 = x(locs(1));
        Ocenter2 = Ocenter1;
        pks(2) = pks(1);
        Odiff = 0;
    end
    yFit = y;
    Fcenter1 = Ocenter1;
    Fcenter2 = Ocenter2;
    Fdiff = Odiff;
    Q=0;
    if(fit)    
        %====3.fit====%
        p3 = (fwhm./2).^2;%赋初值
        c = 0;
        p2(1) = Ocenter1;
        p2(2) = Ocenter2;
        p1 = pks.*p3;
        p_start = [p1(1) p2(1) p3 c p1(2) p2(2) p3];
        [params,~] = lsqcurvefit(@bimodal_lorentzfit,p_start,x,y);
        yFit = bimodal_lorentzfit(params,x);
        %====4.print====%
        Fcenter1=params(2);
        Fcenter2=params(6);
        Fdiff=Fcenter2-Fcenter1;
        FWHM1=2.*sqrt(params(3));
        FWHM2=2.*sqrt(params(7));
        Q1=Fcenter1./FWHM1;
        Q2=Fcenter2./FWHM2;
        Q=min(Q1,Q2);

    end
%     fprintf("Odiff %0.5f \n",Odiff)
%     fprintf("Fdiff %0.5f \n",Fdiff)
%     fprintf("diffError %0.5f \n",Odiff-Fdiff);
%     fprintf("Ocenter1 %0.5f \n",Ocenter1)
%     fprintf("Ocenter2 %0.5f \n",Ocenter2)
%     fprintf("Fcenter1 %0.5f \n",Fcenter1)
%     fprintf("Fcenter2 %0.5f \n",Fcenter2)
%     fprintf("Q %0.5f \n",Q);
end
%% 单峰拟合
% 中心波长:p2
% fwhm:2sqrt(p3)
function F = bimodal_lorentzfit(p,x)
    F = -(p(4) - p(1)./((x-p(2)).^2+p(3)) - p(5)./((x-p(6)).^2+p(7)));
end

四、测试

导入光谱,通过鼠标点击其中一个波谷;
自动拟合,根据需求选择是双峰还是单峰
在这里插入图片描述

close all; clear; clc;

%% 数据导入
structure = 'bimodal1';
data = readmatrix(structure);
X = data(:,1);
Y = data(:,2);
%%%%%%%%% 设置波长偏移范围 %%%%%%%%%%%%%
sens_point = 0.004;%0.2*0.02;
interval_num = 2 /sens_point;%局部光谱大小
fit = 1;%拟合
fwhm = 0.08;%肉眼看,其实可以写个求FWHM的函数,有需求的小伙伴自己写吧~
power_diff = 2;%第二个极值与第一个极值峰的最大高度差
plot(X,Y,'ButtonDownFcn', {@mouse_click, X, Y,interval_num,fit,fwhm,sens_point,power_diff });
function mouse_click(~,~,X,Y,interval_num,fit,fwhm,sens_point,power_diff)%eventData
    persistent count;
    persistent e;
    if isempty(count)
        count = 1;       
    else
        count = count + 1;
    end
    tic
    
    %% %====获取鼠标键值与position====%
    coords = get(gca,'CurrentPoint');%获取鼠标键值
    wave_point = coords(1,1) - mod(coords(1,1),sens_point);%获取鼠标值的波长
    position = find(abs(X - wave_point)<=1e-5);%获取当前波长在波形中的位置
    %single
%     [x,y,locs,Ocenter,new_position,yFit,Fcenter,FWHM,Q]=labview_single_fit(X,Y,position,interval_num,fit,fwhm);
%     e(count,1,:)=[(count-1);Ocenter;new_position;Fcenter;FWHM;Q];
    %bimodal
    [x,y,locs,Odiff,Ocenter1,Ocenter2,new_position,yFit,Fdiff,Fcenter1,Fcenter2,Q] = labview_bimodal_fit(X,Y,position,interval_num,fit,fwhm,power_diff);
    e(count,1,:)=[(count-1);Odiff;Ocenter1;Ocenter2;new_position;Fdiff;Fcenter1;Fcenter2;Odiff;Q]

    writematrix(e(:,1,:),'3.xls','Sheet',1);%count_click
    
    figure
    plot(x,y,'-*b',x,yFit,'-r','MarkerIndices',locs);
    
    toc
   
end

五、单峰&双模拟合

matlab_bimodal_single.m

close all; clear; clc;

%% 数据导入
structure = 'power1';
data = readmatrix(structure);
X = data(:,1);
Y = data(:,3);

%%%%%%%%% 设置波长偏移范围 %%%%%%%%%%%%%
sens_point = 0.005;%分辨率,光谱仪需要乘内部的插值数
interval_num = 3 /sens_point;%漂移范围
fwhm = 0.1;
power_diff = 1;
plot(X,Y,'ButtonDownFcn', {@mouse_click, fwhm, sens_point, interval_num, X, Y,power_diff,structure});


function mouse_click(~,~,fwhm,sens_point,interval_num,X,Y,power_diff,structure)%eventData
	
    tic
    %% %====获取鼠标键值,与当前小区间====%
    coords = get(gca,'CurrentPoint');%获取鼠标键值
    wave_point = coords(1,1) - mod(coords(1,1),sens_point);%获取鼠标值的波长
    position = find(abs(X - wave_point)<=1e-5);%获取当前波长在波形中的位置
    x = X(position-interval_num:position+interval_num);%划当前波长对应的小区间
    y = Y(position-interval_num:position+interval_num); 
    
    %% %====求当前波长小区间的最小值====%
    [~,minposition1] = min(y);
    position = find(abs(X - x(minposition1))<=1e-5);
    %% %====划当前最小值对应的小区间====%
    x = X(position-interval_num:position+interval_num);
    y = Y(position-interval_num:position+interval_num);  
    %% %====保存当前选取波长的数组数据====%
%     persistent count;
%     if isempty(count)
%         count = 0;
%     end
%     count = count+1;
%     data=[wave_now_x,wave_now_y];
%     writematrix(data,[structure,num2str(count),'.csv']);
    %% %====lorenzifit====%
    %====1.拟合斜线====%
%     cofficient_A = polyfit(wave_now_x,wave_now_y,1); %拟合斜线参数
%     y_linear_A = polyval(cofficient_A,wave_now_x); %拟合斜线作为背景
%     y_final_A = -y_linear_A + wave_now_y; %原始值-背景
y_final_A=y;
%     y_final_A(y_final_A<0) = 0; %小于0的数为0                          
    
    %====2.初始系数====%
    [pks(1),locs(1)] = min(y_final_A);%最大值一定是单峰或者双峰
    [pks_tmp,locs_tmp]=findpeaks(-y_final_A);%找到第二大的极值对应的locs
    pks_tmp = -pks_tmp;
    for i=1:length(locs_tmp)
        if( pks_tmp(i)-pks(1)<power_diff && pks_tmp(i)-pks(1)>0)
            locs(2)=locs_tmp(i);
            pks(2)=pks_tmp(i);
            break;
        end
    end

    fprintf("%0.5f pks\n",pks_tmp)
    p3 = (fwhm./2).^2;%赋初值
    c = 0;
    %====3.draw====%
    figure
    plot(x,y_final_A,'r-*','MarkerIndices',locs);
    switch length(locs)
        case 1 %single
            
            %====4.fit====%
            p2 = x(locs);
            p1 = pks.*p3;           
            p_start = [p1 p2 p3 c];
            [params,resnorm] = lsqcurvefit(@single_lorentzfit,p_start,x,y_final_A);
            yprime = single_lorentzfit(params,x);       
            %====5.print====%
            FWHM1 = 2*sqrt(params(3));
            fprintf("fwhm1 %0.5f \n",FWHM1)
            fprintf("center1 %0.5f \n",params(2))
            centerError1 = x(locs)-params(2);
            fprintf("centerError1 %0.5f \n",centerError1);
            Q=params(2)./(2.*sqrt(params(3)));
            fprintf("Q %0.5f \n",Q);
            fprintf("resnorm %0.5f \n",resnorm);
        case 2 %bimodal
            
            %====4.fit====%
            p2 = x(locs);
            p1 = pks.*p3;
            p_start = [p1(1) p2(1) p3 c p1(2) p2(2) p3];
            [params,resnorm] = lsqcurvefit(@bimodal_lorentzfit,p_start,x,y_final_A);
            yprime = bimodal_lorentzfit(params,x);
            %====5.print====%
            FWHM1 = 2*sqrt(params(3));
            fprintf("fwhm1 %0.5f \n",FWHM1);
            fprintf("center1 %0.5f \n",params(2));
            FWHM2 = 2*sqrt(params(7));
            fprintf("fwhm2 %0.5f \n",FWHM2);
            fprintf("center2 %0.5f \n",params(6));
            
            centerError1 = x(locs(1))-params(2);
            fprintf("centerError1 %0.5f \n",centerError1);           
            centerError2 = x(locs(2))-params(6);
            fprintf("centerError1 %0.5f \n",centerError2);
            
            diff_origin = x(locs(2))-x(locs(1));
            diff_fitting = params(6)-params(2);
            
            fprintf("diff_origin %0.5f \n",diff_origin);
            fprintf("diff_fitting %0.5f \n",diff_fitting);
            
            Q1=params(2)./(2.*sqrt(params(3)));
            Q2=params(6)./(2.*sqrt(params(7)));
            Q=min(Q1,Q2);
            fprintf("Q %0.5f \n",Q);
            fprintf("resnorm %0.5f \n",resnorm);
        otherwise
            disp("error")
            return;
    end

   figure
   plot(x,y_final_A,'-b',x,yprime,'-r');
%   R2=1-(sum((yprime-y_final_A).^2)/sum((y_final_A-mean(y_final_A)).^2));   %R2并不能很好的表征这种非线性拟合模型的可信度
   fprintf("R2 %0.5f \n",R2);
   toc
   
end
%% 单峰拟合
% 中心波长:p2
% fwhm:2sqrt(p3)
function F = single_lorentzfit(p,x)
    F = -p(1)./((x-p(2)).^2 + p(3)) + p(4);
end 
%% 双峰拟合
% 中心波长1:p2
% fwhm1:2sqrt(p3)
% 中心波长2:p6
% fwhm1:2sqrt(p7)
function F = bimodal_lorentzfit(p,x)
    F = -(p(4) - p(1)./((x-p(2)).^2+p(3)) - p(5)./((x-p(6)).^2+p(7)));
end
    
    
    
    
    
  • 15
    点赞
  • 85
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
Matlab中,可以使用lsqcurvefit函数进行光谱的洛伦兹拟合洛伦兹拟合是一种常用的非线性拟合方法,可以用于对光谱数据进行峰拟合。通过拟合洛伦兹线型函数来获得峰的位置、宽度和强度等参数。 首先,需要定义洛伦兹线型函数的表达式和物理含义。对于单峰洛伦兹拟合,可以使用以下的表达式: Lorentz = @(x, x0, gamma, A) A ./ (1 + ((x - x0)./gamma).^2) 其中,x为自变量,x0为峰的位置,gamma为峰的半宽度,A为峰的强度。 接下来,可以使用lsqcurvefit函数来进行非线性拟合。该函数可以根据给定的初始参数值和光谱数据,自动调整参数值以最小化残差,从而实现洛伦兹拟合。 最后,根据拟合结果可以得到峰的位置、宽度和强度等参数。可以将这些参数用于进一步的分析和处理。 具体的代码实现可以参考以下步骤: 1. 导入光谱数据,如使用xlsread函数读取数据。 2. 定义洛伦兹线型函数的表达式和物理含义。 3. 利用lsqcurvefit函数进行非线性拟合,获取拟合参数。 4. 根据拟合结果得到峰的位置、宽度和强度等参数。 5. 进行结果的可视化或进一步的分析。 请注意,以上代码是一个简化的示例,具体的实现可能需要根据你的数据和需求进行调整。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [利用光谱测量气体浓度——用Matlab实现Origin功能(扣基底、拟合积分计算)](https://blog.csdn.net/DIckisonDengsen/article/details/124074689)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [MATLAB 数据处理非线性拟合——洛伦兹拟合Lorentz fit)](https://blog.csdn.net/baidu_36202094/article/details/128206548)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小鱼爱学习,每天好心情

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值