简介:面向物理光学、光电信息科学与工程等专业的学习者,提供一套基于Matlab的多光束干涉透射光条纹仿真工具包。资源围绕不同反射率下透射光强度分布的变化展开,对应物理光学教材中图4.3的典型曲线,适合课程学习、实验预习、教师课堂演示、课程设计或自学复现。压缩包共4个文件,仅77KB,包含完整Matlab仿真脚本、生成的条纹图像、Word分析文档及txt说明;脚本内置参数设置、干涉计算和绘图流程,用户可按需替换波长、入射角或反射率参数,快速比较不同条件下条纹宽度、对比度及半高全宽的变化。已有1596人浏览学习,配套图、码、文齐全,可直接运行并对照教材理解法布里-珀罗干涉仪或多光束叠加原理,也可作为进一步扩展多层膜干涉仿真的起点。
1. 光学仿真实操:多光束干涉透射光强度分布曲线,为什么反射率越高峰越尖
第一次在物理光学教材的图4.3上看到多光束干涉透射光强曲线时,多数人都会有个直观疑问:同样是干涉,为什么双光束的条纹是平滑余弦,而多光束在反射率接近1时变成了细锐的“针”?这个问题用MATLAB仿真最直接——写一个几十行的脚本,把不同反射率下的透射光强随相位差的变化画出来,就能亲眼看到条纹从宽到窄、从平缓到陡峭的整个过程。这篇笔记定位很明确:面向光学工程、光电专业学生和需要复现教材图形的工程师,给出完整可复现的MATLAB代码、参数选择逻辑和几个容易翻车的细节。做完之后,你不但能画出和书上图4.3一致的曲线,还能顺手验证精细度与自由光谱范围的关系。
2. 多光束干涉公式的物理底细:从平行板内反射到透射光强的Airy分布
2.1 Fabry-Perot腔内多束光叠加的等比级数模型
多光束干涉的经典场景是两片平行且部分反射的镜面构成的法布里-珀罗腔。一束光射入腔内后,会在两个镜面之间反复反射,每次透射出一部分光,最终在透射方向上形成多光束叠加。关键点在于:相邻两束透射光的相位差是恒定的,由光程差决定,等于δ = 4π n d cosθ / λ,其中n是腔内介质折射率,d是腔长,θ是腔内光线与镜面法线的夹角,λ是波长。这个相位差不随反射次数变化,所以所有透射光的复振幅构成一个公比为r^2 e^{iδ}的等比级数,其中r是镜面振幅反射系数。级数求和后取模平方,就得到标准透射强度公式:
I_t / I_0 = 1 / (1 + F sin^2(δ/2))
其中F = 4R / (1 - R)^2,R是强度反射率,F被称为精细度系数(coefficient of finesse)。在MATLAB里,我们不需要推导级数,只要直接把这个公式写成数组运算即可。实际仿真中,I_0通常取1,纵轴变成归一化透射率。
注意这里有个容易混淆的细节:sin^2(δ/2)的最大值是1,所以透射光强的最小值是1/(1+F),而最大值恒等于1。这意味着无论反射率多高,透射峰值永远都在1,不会因为反射增强而变小。曾有人误以为R大了透射光应该更弱,实际上在理想无吸收腔中,共振波长处透射率仍是100%,只是非共振波长被强烈反射,曲线变“挑剔”了。
2.2 反射率R如何改变透射光强分布
把F表达式代入,观察不同R时的曲线形状。当R = 0.04时,F = 4×0.04 / 0.96^2 ≈ 0.17,sin^2项的权重很小,透射率在1和1/(1+0.17)≈0.85之间波动,表现为类似双光束干涉的宽阔余弦条纹。当R = 0.9时,F = 4×0.9 / 0.1^2 = 360,此时只有当sin^2(δ/2)接近0时,分母才接近1,透射率才能达到峰值;稍微偏离δ = 2mπ,透射率迅速跌落。这就是反射率对透射条纹的“锐化”作用。
从数值仿真角度看,R越接近1,F值越大,曲线峰附近的变化越剧烈。这带来一个直接后果:画图时如果相位差采样点数不够,峰值处的数值可能没有被采到,画出来的曲线峰值会明显小于1。更严重的会出现“欠采样锯齿”,让一个本该光滑的尖峰变成折线。因此在脚本设计中,需要根据最大R值估算所需的最小采样点数,这一点会在第4章详细展开。
2.3 公式数值化:MATLAB中表示相位差的正交与量纲问题
MATLAB的sin函数默认输入单位是弧度。物理光学书里横轴常用相位差δ(弧度),有时会用δ/π作为单位,让干涉级次恰好落在整数位置。仿真时建议先统一成“δ/π”显示,这样曲线峰值位于δ/π = 0, 2, 4, …这些偶数点上,便于核查。代码中linspace(0, 8*pi, 2001)生成从0到8π的相位差数组,2001个点让每个π区间约250个点,对R=0.9的尖峰已经够用,但R=0.98时就需要更多点。
另一个量纲陷阱:公式里用的是sin^2(δ/2),不是sin(δ)^2,也不是sind。如果用角度制,必须先把δ转成度或者直接用rad;比较稳妥的写法是构造delta_rad,然后调用sin(delta_rad/2),再点平方。代码里点运算很重要,因为delta是数组,正弦后的数组再平方必须用.^,避免矩阵乘法报错。实际调试中遇到“错误使用 ^ 输入必须为标量”这类提示,十有八九是把.^写成了^。
3. 用MATLAB画透射光强曲线:一个可以直接跑的脚本与逐行拆解
3.1 完整脚本:不同反射率下的透射光强分布
这里给出一个完整可运行的MATLAB脚本,它对应物理光学书上图4.3的典型画法:横轴为δ/π,纵轴为归一化透射强度,多条曲线对应不同反射率。代码中特意保留注释和可调参数。
% 多光束干涉透射光强分布仿真 clear; clc; close all; % 反射率数组:覆盖低反射、中反射到高反射 R_list = [0.04, 0.1, 0.5, 0.9, 0.98]; % 相位差范围:0 ~ 8π,每个π区间分配251个点 delta = linspace(0, 8*pi, 2001); figure('Color','w', 'Position',[100 100 800 500]); hold on; grid on; for R = R_list % 精细度系数 F = 4*R / (1 - R)^2; % 透射光强归一化公式 I_t = 1 ./ (1 + F * sin(delta/2).^2); plot(delta/pi, I_t, 'LineWidth', 1.5, ... 'DisplayName', sprintf('R=%.2f', R)); end xlabel('相位差 \delta / \pi'); ylabel('透射光强 I_t / I_0'); ylim([0 1.08]); legend('Location','northeast'); title('不同反射率下多光束干涉透射光强分布');运行后你会得到5条曲线。R=0.04的曲线几乎是余弦形状,峰值宽而平缓;R=0.9和R=0.98的曲线则在δ/π = 0、2、4、6、8处出现极窄的尖峰,且底座几乎贴近零。这个图形趋势和教材图4.3完全一致。
3.2 逐行逻辑解释:从F到I_t的点运算
代码里最关键的是F = 4R / (1 - R)^2。注意这里R是标量,(1 - R)^2也是标量,所以用普通除法没问题。但如果你将来把R做成数组,就一定要改成F = 4R ./ (1 - R).^2。工程上我的习惯是能点运算的地方全用点运算,避免修改参数后触发维度错配。比如sin(delta/2).^2这里,delta是1×2001的行向量,sin(delta/2)也是同尺寸数组,.^2对每个元素平方,得到包含2001个值的数组。1 ./ (...)同样用点除,把标量1除以每个元素。
绘图部分,delta/pi把横轴转成π的倍数。这样x轴刻度默认在0、1、2、3处,而峰值出现在偶数刻度,看起来更清晰。使用DisplayName配合legend可以自动生成图例,比手动写'R=0.04'之类的文本更省事。这里强调一点:不要直接用legend({'R=0.04','R=0.1'...})这种硬编码方式,否则调整R_list后需要同步修改图例,容易漏改。
3.3 运行结果与教材图4.3的对照要点
运行脚本后,重点观察两点。第一,所有曲线的透射峰值都等于1,无论反射率多高,峰值位置都在δ/2 = mπ处,即δ = 2mπ。第二,R越大,曲线的半高全宽越窄。物理上,半宽度Δδ(以相位差计)近似为4/√F,而Finesse定义为2π/Δδ,因此精细度约为π√F/2。拿R=0.9来算,√F=19,精细度约为π×19/2≈29.8,这意味着一个自由光谱范围内能容纳约30个可分辨的峰,对应到图上就是峰值非常锋利。对照教材时,如果发现书上某些曲线峰值似乎低于1,那往往是印刷时纵轴归一化方式不同,或者镜面存在吸收损耗;我们的仿真假设无损,所以峰值恒为1。
4. 参数深入:反射率、相位扫描范围和采样点数如何决定曲线质量
4.1 反射率R从0.04到0.98:条纹锐化的临界点
反射率选取直接决定图形面貌。低反射区(R ≤ 0.1)的条纹接近余弦,适合展示多光束向双光束的过渡;中反射区(R ≈ 0.5)条纹已有明显变锐趋势;高反射区(R ≥ 0.9)则需要处理数值上的尖峰采样问题。实际做参数扫描时,我一般会取对数间隔,比如0.04、0.1、0.3、0.6、0.9、0.98,这样曲线在图上均匀分布,不会因为R=0.9和0.98靠得太近而挤压图例。如果你要复现教材某张具体图,先看书上给的R值,再决定是否加入额外中间值。
R接近1时,F值急剧增大。以R=0.98为例,F = 4×0.98/0.02^2 = 9800,这已经是R=0.9的27倍。此时曲线的半高宽Δδ ≈ 4/√9800 ≈ 0.04弧度,而单个π区间长度是π≈3.14,尖峰只占1/78的范围。如果采样点每个π区间只给100点,平均每隔0.0314弧度采一个点,运气不好峰值点会被漏掉,画出来就是一个略低于1的平顶峰。经验法则:一个π区间至少设置200点,对R=0.98建议500点。脚本里2001点覆盖8π,每个π区间250点,基本够用。
4.2 相位差扫描范围:为什么推荐2π的整数倍
仿真扫描区间选择0到2π理论上可以完整展示一个级次,但物理光学教材通常会把多个级次并列,好让读者理解“等间隔峰排列”。因此我推荐扫描至少4π或8π,让横轴出现2到4个峰。这样也能顺带验证相邻峰的间距是否严格等于2π(即自由光谱范围)。注意:如果用0到8π,横轴长度是4个自由光谱范围,R=0.98的曲线会出现4个同形状的峰,中间隔着一大片接近零的区域。如果你的目的是放大观察单个峰,可以直接把区间缩小,比如delta = linspace(3pi, 4pi, 1000),只画一个峰,这样能更清楚地看到FWHM的细节。
有一个常见误区:有人把δ直接从头到尾线性增加,却忘了δ正比于频率或波长变化,所以真正的实验扫描通常不是相位差线性,而是波长线性。不过在理论仿真中,直接以δ为自变量是最标准的,不需要考虑非线性问题。如果你要模拟实际光谱仪扫描,就需要把δ映射到λ(例如δ = 4πL/λ),这时横轴会变成波长,曲线形状在反射率很高时会出现非对称展宽——那是另一层话题了。
4.3 采样点数与峰值插值:避免“尖峰抹平”的采样量经验
欠采样是这类仿真中最常见的“玄学”问题。你以为代码没错,但画出来的R=0.98曲线峰值只有0.85,怎么调反射率都没用,最后发现是delta点数太少。具体原因在于:峰值在δ=2mπ处是一个孤立的极窄尖点,如果采样点中恰好没有δ等于2π的整数倍点,那么附近的最大采样值就会低于1。解决办法有三个方向:一是增加网格点数,把linspace的第三个参数提到5000甚至10000;二是显式把峰值点插入数组中;三是用fplot匿名函数绘图,让MATLAB自适应取样。
我个人推荐第三种,尤其在高反射率时。用fplot(@(d) 1./(1+9800sin(d/2).^2), [0 8pi]),MATLAB会在峰附近自动加密取样,画出的曲线不会缺角。但fplot返回的默认横轴是弧度,你要单独设置xticklabels才能显示为δ/π。如果你坚持用linspace,就记住这个经验:点数至少为最大反射率对应的FWHM宽度的十倍所覆盖的π区间数量的20倍。简单估算:对R=0.98,FWHM约0.04弧度,扫描8π约25弧度,你需要至少25/0.04 × 10 = 6250个点。所以2001点确实不够,想画细致就得加到8000以上。这也是为什么我在第3章的代码里只写到2001点——它适合R≤0.9的展示,不适合R=0.98的精细分析。
5. 避坑与常见问题:让仿真曲线稳定可复现的五个细节
5.1 现象:透射峰值始终小于1,最大值只有0.8左右
这个问题在R=0.98时尤其明显。原因是linspace采样点没有恰好落在δ=2mπ上,而尖峰宽度极窄,采样点落在峰两侧斜坡上,导致近似最大值低于真实峰值。解决方法是把delta序列中手动插入2mπ点:例如在生成delta后,用[0:2pi:8pi]补进数组,再排序去重。也可以用fplot自适应采样。排查时先用max(I_t)看返回值,如果接近1且相差小于0.001就能接受;如果差很多,就检查点数。
5.2 现象:R接近0.999时曲线变成一条直线或者峰值爆炸
当R非常接近1时,F = 4R/(1-R)^2在R=0.999时约4×0.999/1e-6 ≈ 4×10^6。理论上峰值还是1,但数值计算中,在远离峰值处,sin^2(δ/2)如果因浮点误差出现非零极小值,分母会变得极大,I_t变成极小值,接近0。而峰值处sin^2为0,分母为1,所以曲线会在极窄处跳变。如果网格点数不够,可能完全没采到峰值,整条曲线看上去就是一条接近于0的直线。解决方法是:不要用R=0.999这种极端值做展示,除非你确实要演示极限精细度;如果必须用,就把δ范围缩小到单个峰附近,且用高精度计算避免浮点截断。
5.3 现象:横轴标注的是弧度,和书上角度/相位差不一样
物理光学教材里图4.3有时横轴用“相位差δ”,但单位可能是π或2π,有时候直接用入射角。MATLAB默认的plot横轴只是坐标向量,并不带单位。解决方法是自己控制横轴语义:如果你画的是delta/pi,那xlabel就要写成“δ/π”;如果你直接画delta(弧度),就写“δ (rad)”。有些人忘记给横轴除以π,导致峰间距在数值上显示为6.28而不是2,看上去很奇怪。归根结底是你决定横轴是什么,只要标注和绘图一致就行。
5.4 现象:用sind(角度)画图,峰值位置和相位差对不上
MATLAB里sind期望输入度数,而δ是弧度。如果你用了sind(delta/2),那么只有当delta/2是整数时sin值为0,峰值位置出现在delta/2=180k度时,也就是δ=360k度≈6.28k弧度。这本身没问题,但y轴公式中的sin^2却变成了以度为单位,导致半高宽数值完全不同。更麻烦的是,如果你混用sin和sind,同一张图两条曲线宽度不可比。解决方法是全脚本统一用sin,且把delta明确为弧度。偶尔有人把2π误写为360,而MATLAB的pi就是π,不存在角度制,所以没必要用sind。
5.5 现象:导出的图放大后锯齿严重,文章里看不清峰值
这不算公式错误,而是出图规范问题。锯齿有两个来源:采样点不足,以及LineWidth太细。采样点问题按4.3节解决;线宽建议设置到2,且用“Alias”关掉?实际上在MATLAB figure里,锯齿主要来自屏幕显示,导出矢量图(例如pdf或eps)后锯齿会消失。我的做法是:print(gcf, '-dpdf', 'multi_beam.pdf'),再用Inkscape转成png时选择高分辨率。图表符合期刊投稿要求的关键点包括:图例字体不小于8pt,曲线颜色按反射率从浅到深排列,峰值纵坐标标到1.0,坐标轴范围比数据区略大,而不是紧紧贴边。
6. 进阶:从强度曲线提取FWHM与Finesse,用解析值交叉验证仿真
当你已经能画出漂亮的曲线后,下一步就是量化验证:测量的半高全宽是否和理论公式一致。这一步能有效排除坐标轴单位、公式系数等隐性问题。理论值很好算:对透射光强公式,峰值一半对应的条件为I_t = 1/2,也就是1 + F sin^2(δ/2) = 2,解得sin^2(δ/2) = 1/F。假设δ_0 = 2mπ是峰值位置,那么半高半宽Δδ/2满足sin((δ_0 + Δδ/2)/2) = sin(Δδ/4) ≈ Δδ/4 = 1/√F(小角近似),所以全宽Δδ ≈ 4/√F。由此Finesse = 2π/Δδ ≈ π√F/2。用这个解析值去对比仿真提取值,如果偏差超过5%,说明仿真中可能有欠采样或坐标轴换算错误。
提取FWHM我习惯用MATLAB自带findpeaks,然后半高交叉法。下面给一个单峰提取的例子:
% 选择单条高反射率曲线 R = 0.9; F = 4*R/(1-R)^2; delta = linspace(0, 4*pi, 8000); I_t = 1 ./ (1 + F*sin(delta/2).^2); % 只取第二个峰附近区域,避免峰值过多干扰 ind_center = find(abs(delta - 2*pi) < 0.01); center_val = max(I_t(ind_center)); half = center_val / 2; % 找左边界和右边界 left_idx = find(delta > 0 & delta < 2*pi & I_t < half, 1, 'last'); right_idx = find(delta > 2*pi & delta < 4*pi & I_t < half, 1, 'first'); fwhm = delta(right_idx) - delta(left_idx); finesse_exp = 2*pi / fwhm; finesse_theory = pi*sqrt(F)/2;解释一下这段代码的思路:先把8000个点的曲线算出来,然后锁定δ=2π处的峰。通过center_val找到峰值(理论上应为1),取半高阈值half。接着在左右两侧分别找到第一个低于half的点,这两个点对应的横坐标之差就是FWHM。这里使用了find加方向限制,避免了多个峰之间的干扰。对于R=0.9,理论Finesse≈29.8,仿真提取值通常落在29到31之间,误差主要来自线性插值而非物理偏差。如果你想把精度提高,可以在left_idx和right_idx之间用interp1做线性插值,精确求解I_t=half对应的δ值。工程上,插值后的偏差可以压到0.1%以下。
这个方法有一个隐藏价值:当你不确定某个复杂光学腔的实际精细度时,可以先按理想公式仿真,再拿实测曲线验算。我曾经在搭建可调谐滤波器时,用这个思路定位到镜片反射率低于设计值——因为提取出的Finesse只有预期的一半,反解等效R约为0.82,和厂家标称值差距不小,最后换货解决了问题。从那以后,每次仿真干涉曲线,我都会强制跑一遍FWHM交叉验证,哪怕只是写个教学Demo也不例外。这样做的习惯帮我避开了无数次“图看着对、数据却对不上”的尴尬。希望这篇笔记能帮你在光学仿真这条路上少走几个弯路。
本文还有配套的精品资源,点击获取