☰
MATLAB光学仿真:F-P腔多光束干涉透射光强曲线复现实战
2026/10/18 17:17:48 网站建设 项目流程

简介:光学仿真中,多光束干涉是物理光学教学与研究的重要模型。针对不同反射率下透射光条纹强度分布随参数变化的典型问题,这份 Matlab 资源提供了可直接运行的仿真脚本与配套结果,面向光学、物理光学及光电信息科学与工程方向的学生、教师和科研人员,可用来复现教材图 4.3 的曲线,并进一步观察反射率对条纹对比度、间距及相位的影响。资源共 4 个文件,压缩包约 77KB,主要包含一个 m 格式仿真脚本、一张 jpg 干涉条纹结果图、一份 docx 强度分布分析说明和一份 txt 说明文件;脚本允许调整反射率等参数,基于多光束叠加计算透射光场,图像直观展示条纹形态,文档则解释条纹间距、对比度变化及其物理解读。目前已有 1596 人学习下载,小体积高聚焦的构成适合课程演示、课后验证或科研预研,既能快速还原教科书曲线,也能为多层薄膜、非均匀介质等更复杂干涉仿真提供扩展起点。

1. 光学仿真入门实战:用MATLAB复现多光束干涉透射光强曲线

做光学仿真的人,十有八九都会在某个深夜对着MATLAB窗口发愁:多光束干涉的透射光强曲线,到底该怎么从公式变成一张能放进报告里的图?我第一次跑通这个项目时最大的感受是,理论和代码之间的距离,往往就隔着一行向量的写法。这个MATLAB仿真脚本做的事情很聚焦:在F-P腔模型下,计算不同反射率R对应的透射光强分布,把物理光学里那张经典的图4.3复现出来,让条纹从宽平的包络一步步变成尖锐的梳齿。适合正在学物理光学、做干涉实验预处理,或者想练手MATLAB数值仿真的开发者。

2. 物理模型推导:F-P腔里的多光束叠加与Airy透射公式

2.1 F-P腔多光束干涉的物理图像

Fabry-Perot腔是产生多光束干涉最经典的装置,两块反射率相同的平行平面镜中间夹着一层介质,入射光进入腔体后会在两个内表面之间来回反射。每反射一次,就有一小部分光从出射面透射出去。于是出射方向上不再只有一束光,而是无数束振幅按几何级数衰减、相位依次递增的透射子波。这些子波在透镜焦平面上相干叠加,形成多光束干涉条纹。

多光束干涉和双光束干涉最大的区别在条纹锐度上。双光束干涉的强度分布是余弦形式的宽条纹,而多光束干涉在反射率足够高时,会出现非常尖锐的透射峰。原因在于参与叠加的光束数量发生了质变。反射率越低,腔内的光往返次数越少,有效参与叠加的光束越少,结果越接近双光束;反射率越高,光束在腔内往返次数越多,透射子波的有效数量越大,相干叠加时对相位偏离δ=2kπ的条件就越敏感。这个“敏感度”正是F-P腔能用来做高分辨率光谱分析的基础。

仿真里我们最关心的就是透射光强随相位差的变化关系。相位差δ来自相邻两束透射光之间的光程差,写成δ = 4π n d cosθ / λ,其中n是腔内介质折射率,d是腔长,θ是腔内折射角,λ是真空波长。正入射时θ=0,cosθ=1。当δ恰好等于2π的整数倍时,所有透射子波同相叠加,透射光强达到最大。

2.2 透射光强公式与Airy函数

推导透射光强有一个标准的复振幅叠加过程。设反射系数为r、透射系数为t,反射率R=|r|²,透射率T=|t|²。入射光振幅为E0,经过第一次透射后振幅为E0·t·t',后续每一束都比前一束多一次往返,即多乘一个反射率R的因子,同时多积累一个相位差δ。把所有透射复振幅按几何级数求和,取模平方后得到:

I_t/I_0 = T² / [(1-R)² + 4R·sin²(δ/2)]

这是Airy函数的标准形式。如果腔体没有吸收和散射损耗,能量守恒要求R+T=1,T=(1-R),代入后T²=(1-R)²,公式可以进一步化简为只含R的版本:

I_t/I_0 = 1 / [1 + F·sin²(δ/2)],其中F = 4R/(1-R)²

F叫锐度参数,也常写作F-P腔的“精细度系数”。注意这个F和后面要说的Finesse精细度不是同一个量,后者等于π√F/2,差了一个π/2的因子,读文献时很容易混淆。

看两个极端就能理解曲线的性格。R=0时F=0,分母恒为1,I_t/I_0恒等于1,说明没有反射就谈不上干涉,入射光全部透射,频谱是一条平直线。R越接近1,F增长得极快,比如R=0.9时F=360,R=0.98时F=9800。此时只要sin²(δ/2)偏离0一点点,分母就会被放大到很大,透射率急剧下降;只有在δ=2mπ处分母回到1,透射率保持峰值1。这就是“暗背景上顶着极亮尖峰”的物理来源。

2.3 仿真中相位差δ的两种定义方式

写代码之前必须先决定横轴怎么取值。第一种做法是把d、λ直接代入δ = 4πndcosθ/λ,得到的是一个极大数值。比如λ=632.8nm、n=1、d=1mm时,δ约等于2万弧度,画出来的横轴范围巨大,所有条纹挤在一起几乎看不见。第二种做法是把δ当作抽象自变量,直接取δ ∈ [0, 4π],画出两个自由光谱范围内的透射率分布。

教材插图几乎都采用第二种方式。等价的说法是:把腔长工作点锁定在某个整数级次附近,然后只看这个级次左右不超过一个周期的局部频谱。这样画出来的曲线保留了所有物理特征,同时横轴刻度直观,便于标注峰值位置。代码里我也采用这种做法,物理参数lambda、n、d保留在参数区用来做量级估算和换算,而不会让它们的乘积直接撑爆横轴。

3. MATLAB仿真实现:参数化脚本与向量化绘制

3.1 参数区与相位差数组的构建

整个仿真可以拆成三个部分:参数区、计算区、绘图区。下面三块代码拼起来就是完整的脚本。参数区负责定义相位差范围和要扫描的反射率集合,这部分决定了曲线覆盖哪些物理场景。

% par_scan.m 参数区 lambda = 632.8e-9; % 波长 632.8 nm n = 1.0; % 腔内介质折射率,空气取1 d = 1e-3; % 腔长 1 mm theta = 0; % 正入射,腔内折射角为0 % 单次往返相位差量级估算,仅用于理解物理尺度 % delta_single = 4*pi*n*d*cos(theta)/lambda; % 约 2e4 rad % 以抽象相位差为横轴,覆盖两个自由光谱范围 delta = linspace(0, 4*pi, 4000); % 要扫描的反射率,覆盖低反射到高反射 Rs = [0.04, 0.5, 0.9, 0.98];

参数说明:delta从0取到4π是因为相邻峰之间的相位差为2π,画两个周期可以让周期性特征一眼可见。4000个采样点是为了在高反射率下仍然能分辨峰的形态,后面避坑章节会详细说明为什么点太少会翻车。Rs数组里4个取值很有代表性:0.04模拟低反射率情形,0.5是中间状态,0.9进入高反射率区间,0.98接近真实镀膜镜面。如果你有实验数据,这里的Rs可以换成实测反射率。

3.2 多反射率曲线计算与向量化写法

计算区用循环遍历每个反射率,内部对delta数组做向量化运算。向量化是MATLAB仿真性能和代码可读性的分水岭,下面这个写法比逐点for循环快一个数量级,而且逻辑和公式直接对应。

% 预分配结果矩阵,行对应反射率,列对应相位差 I = zeros(length(Rs), length(delta)); for k = 1:length(Rs) R = Rs(k); F = 4 * R / (1 - R)^2; % Airy锐度系数 I(k, :) = 1 ./ (1 + F * sin(delta / 2).^2); % 归一化透射光强 end

逻辑说明:F = 4R/(1-R)²是从Airy公式整理出来的锐度系数,它把反射率映射成条纹尖锐程度。sin(delta/2).^2里,delta是1×4000的行向量,sin运算、平方运算都按元素进行,点运算符必须写全。1 ./ (1 + ...)同样是逐元素除法,如果写成1 /会变成矩阵除法,MATLAB会报维度错误或者算出完全错误的结果。预分配I矩阵避免循环中数组动态增长,这是一个很小的习惯,但数据量大时能明显感受到卡顿差异。

参数说明:公式里没有任何多余因子,因为透射光强已经被归一化到入射光强I_0。也就是说无论反射率多少,曲线的最大峰值都应该是1。这是后面检查代码是否写错的一个重要判据,如果max(I(k,:))不等于1,说明公式一定有问题。

3.3 绘图与图4.3样式还原

绘图区把四条曲线画在同一张图上,横轴除以π显示为0到4,正好对应教材插图的坐标习惯。

figure('Color', 'w', 'Position', [100 100 800 600]); hold on; colors = lines(4); % 生成4个区分度较高的颜色 for k = 1:length(Rs) plot(delta / pi, I(k, :), 'LineWidth', 1.6, ... 'Color', colors(k, :), ... 'DisplayName', sprintf('R = %.2f', Rs(k))); end xlabel('相位差 \delta / \pi'); ylabel('透射光强 I_t / I_0'); title('多光束干涉透射光强分布曲线'); xlim([0 4]); ylim([0 1.05]); legend('Location', 'northeast'); grid on;

逻辑说明:plot里传入delta/pi,把横轴从弧度映射到π的倍数,刻度显示0、1、2、3、4,与教材图4.3完全一致。ylim上界设置成1.05而不取1,是怕峰顶贴到图像边界影响观察,留出5%的余量是绘图小习惯。legend的DisplayName由sprintf生成,反射率数值直接出现在图例中,这样四条曲线分别对应哪个参数一目了然。运行后你会看到一条接近水平的线、一条圆润的周期曲线、两条越来越尖的梳齿状曲线,它们叠在一起就是图4.3的标准样式。

4. 仿真结果分析:精细度、对比度与条纹锐化规律

4.1 不同反射率曲线的演变规律

运行上面代码,先从视觉上总结四条曲线的变化。R=0.04时,曲线几乎是一条水平线,只在谷底有微弱的凹陷;R=0.5时波峰波谷清晰可见,但峰形圆润;R=0.9时峰开始变得尖锐,背景明显压低;R=0.98时曲线变成一排极细的梳齿,峰与峰之间几乎贴着横轴。

为了量化这些视觉印象,我整理了下面的参数表,全部由公式直接计算得出:

反射率 R谷值 I_min条纹对比度Finesse精细度半高宽 FWHM (rad)
0.040.8521.170.65无(谷值高于0.5)
0.500.1119.04.40.72
0.900.002836129.80.105
0.980.00019901155.50.020

对比度定义为I_max/I_min,I_min = 1/(1+F)。注意R=0.04那一行,谷值是0.852,也就是说透射光强最低也有85%,条纹对比度只有1.17,肉眼几乎无法分辨出干涉条纹。这说明低反射率下多光束干涉退化成双光束干涉的包络,甚至比双光束还要平。R=0.5时谷值降到0.111,对比度达到9倍,已经能明显看到峰谷结构。

4.2 精细度Finesse与自由光谱范围FSR

表里的Finesse精细度是我们最关心的性能指标,定义是Finesse = π√F / 2 = π√R / (1-R)。从0.04到0.98,Finesse从0.65暴涨到155.5,增长是指数级别的。它的物理含义可以理解为FSR与条纹半高宽的比值,也就是在一个自由光谱范围内能分辨多少个峰。Finesse越高,F-P腔的分辨能力越强。

在曲线上可以直观读出两个量。自由光谱范围FSR对应相邻两个峰之间的相位差,固定为2π,与反射率无关。半高宽FWHM则是峰值一半高度的宽度,随R增大急剧变窄。R=0.5时FWHM约0.72rad,R=0.9时缩到0.105rad,R=0.98时只有0.020rad——相当于一个周期内能放下约314个半高宽。如果换成频率轴,峰越窄就意味着能分辨的两条谱线频率间隔越小,这是F-P干涉仪作为光谱仪的核心指标。

4.3 峰值恒为1的物理直觉

有一个现象特别值得停下来想一想:无论反射率是0.04还是0.98,透射峰的峰值始终是1。初看很反直觉——反射率这么高,光都被反射回去了,透射峰怎么可能还是全部透射?关键在于δ=2mπ时,腔内所有透射子波严格同相叠加,透射方向发生完全相干相长,反射方向完全相消,能量守恒要求所有入射能量从透射端出来。

这个论断有严格的能量守恒做背书,同时也是代码自检的利器。在仿真脚本里加上assert(max(I(k,:)) > 0.9999 && max(I(k,:)) < 1.0001),如果断言失败,大概率是公式里的反射率或透射率关系写错了。我每次改参数或扩展功能后都会跑一遍这个断言,它能挡住很多低级错误。

5. 避坑指南:MATLAB光学仿真里的四个常见翻车现场

5.1 曲线更像余弦波:横轴范围和采样密度的问题

现象:第一次运行脚本,画出来的曲线怎么看都像余弦波,完全没有“梳齿”的感觉,R=0.9和R=0.98的曲线几乎重合。

原因:横轴范围设得太大或者采样点太少。比如delta取linspace(0, 200*pi, 1000),每个峰只分配到约10个点,峰值和谷值之间只有几个采样点,尖锐的峰形被欠采样抹平了。反射率越高这个问题越严重,0.98的峰半高宽只有0.02rad,如果相位差点间距大于0.1rad,连峰值都可能刚好落在采样点之间。

解决:先把横轴限制在[0, 4π],再用linspace(0, 4*pi, 4000)保证每个峰上有足够多的点。高反射率(R>0.95)建议把点数提高到8000。确认公式正确后,再根据实际需求扩到多周期或多自由光谱范围。

5.2 峰值大于1:反射率与透射率的关系写反

现象:R=0.9时曲线峰值跑到1.1甚至1.5,明显超出物理上最大的透射率1。

原因:这是公式里反射率和透射率关系处理出了问题。常见的错误是把透射率T单独放在分子上,写成I_t/I_0 = T / ((1-R)² + 4R·sin²(δ/2)),有些版本还会把T近似成1-R再放错位置。空气在没有吸收时T=1-R,代入后最终公式里R是唯一的参数,不会再有任何额外的透射因子。

解决:用原始形式T²/[(1-R)²+4R·sin²(δ/2)]一步步代入,最后化简成1/[1+F·sin²(δ/2)]。写代码时直接采用化简后的形式,从源头上杜绝T因子混入。并在代码后面加一行max(I(k,:))的检查,峰值必然落在0.9999到1.0001之间。

5.3 峰位整体平移:δ定义没有统一

现象:曲线形状完全正确,但峰不在δ=0、2π、4π的位置,而是整体平移了一段距离,比如跑到0.74π处。

原因:不同教材对δ的起点定义有差异。有的把δ定义为往返一次的光程差对应的相位差,有的定义为两束相邻透射光的相位差,还有的从第一束透射光和第三束透射光来定义。差一个π的整数倍甚至差一个系数2,都会导致曲线平移或者周期翻倍。

解决:写代码前先固定自己的定义,并把它写进注释。本文采用的δ是相邻两束透射光之间的相位差,所以峰出现在δ=2mπ。如果要和某本书上的插图对比,先确认那本书的δ定义,再决定是否需要把横轴平移或者缩放。

5.4 R接近1时出现NaN:除零边界没处理

现象:把R设成1或者非常接近1,比如R=1.0,脚本直接报NaN或者Inf,整条曲线画不出来。

原因:公式分母里有(1-R)²,R=1时分母为零,F变成无穷大,MATLAB返回Inf,后续计算产生NaN。实际镀膜镜面反射率永远达不到1,但代码不会替你判断这个边界。

解决:在参数区加一个保护:R = min(R, 0.999),或者用assert(all(R < 1))让脚本在非法参数时直接报错。我在之前坑里学到的是,参数区要有意识地设边界,而不是等MATLAB报错后再回去猜原因。

6. 二次开发:从静态曲线走向交互式F-P仿真面板

6.1 把R变成滑块实时刷新

静态脚本只能看预设的4条曲线,改成交互式后就能拖动滑块观察反射率连续变化对条纹的影响。用uifigure和uislider搭建一个轻量面板,核心回调函数只有十几行:

fig = uifigure('Name', '多光束干涉透射仿真'); g = uigridlayout(fig, [2 1]); ax = uiaxes(g); sld = uislider(g, 'Limits', [0.01 0.99], 'Value', 0.90); sld.ValueChangingFcn = @(sld, ~) redraw(ax, sld.Value); function redraw(ax, R) delta = linspace(0, 4*pi, 8000); F = 4 * R / (1 - R)^2; It = 1 ./ (1 + F * sin(delta / 2).^2); plot(ax, delta/pi, It, 'LineWidth', 1.6); ylim(ax, [0 1.05]); xlabel(ax, '相位差 \delta / \pi'); ylabel(ax, '透射光强 I_t / I_0'); title(ax, sprintf('R = %.3f Finesse = %.1f', R, pi*sqrt(R)/(1-R))); end

这个回调每触发一次就重算一整条曲线,8000个点的向量化运算耗时不到10毫秒,拖动时完全感觉不到卡顿。标题栏实时显示当前反射率对应的Finesse,把“调参”和“看指标”同步起来,适合当课堂演示工具。

6.2 把透射和反射曲线放在同一坐标系

无吸收时反射光强I_r = 1 - I_t,可以直接复用透射曲线:

Ir = F * sin(delta/2).^2 ./ (1 + F * sin(delta/2).^2); hold(ax, 'on'); plot(ax, delta/pi, It, delta/pi, Ir); legend(ax, {'透射', '反射'});

两条曲线会在每个峰处形成“透射峰对应反射谷”的镜像关系,这个可视化对理解能量守恒特别直观。反射率低于0.1时反射谷很浅,透射峰接近水平线;反射率接近1时反射谷几乎触底。如果加上吸收损耗alpha,需要把公式改成I_t+I_r < 1,并引入吸收率参数,这就离真实镀膜仿真非常近了。

这套脚本我其实是从一个更早的作业版本迭代过来的。那一次因为峰值超过1,我对着屏幕怀疑了半天人生,最后一步步拆开公式才发现是括号写错了位置。从那以后,我每次写光学仿真代码都会强制自己先写量纲注释、再写数值计算,最后把“峰值应该等于多少”这种物理判据直接写成断言放进代码里作为自检清单。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询