做干涉仿真做到第五篇其实该碰点“真正能说得上工程”的东西了。前面几篇我们用MATLAB复现过杨氏双缝、等夹角双光束干涉也聊过空间拍频和条纹周期但那些场景更多是在“看现象”。这一篇我想把重点放在迈克尔逊干涉仪上用复振幅叠加的思路把它完整仿真一遍等倾圆环、等厚直条纹、动镜扫描时光强变化、甚至光源非单色性导致的可见度衰减。这套模型一旦跑通后面你再去看傅里叶变换光谱、激光测距、光纤干涉这类应用底层逻辑会非常顺。迈克尔逊干涉仪的好处在于物理上只要一个分光棱镜和两面平面镜数学上却是双光束干涉里最典型的相位调制系统。我们不需要关注复杂的衍射传播所有的干涉信息都浓缩在光程差这一个量里。用它来做MATLAB仿真的进阶提阶正好把“复振幅叠加只把光强当成cos²函数来画”的思维升级成“相位差随空间变化”的完整建模。1. 从双缝到迈克尔逊这一篇到底在解决什么问题1.1 双光束干涉的通用模型到底是什么做干涉仿真最忌讳一上来就套公式画条纹。因为干涉本质上不是光强相加而是电场复振幅的叠加。你在MATLAB里画杨氏双缝时后台做的其实是两个球面波复振幅相加再取模的平方得到光强。到了迈克尔逊干涉仪物理装置变了但数学内核一点没变仍然是两束相干光在观察屏上复振幅相加。写成通用形式就是光束1在观察点P处的复振幅E1 A1 * exp(j * φ1)光束2在观察点P处的复振幅E2 A2 * exp(j * φ2)总光强I |E1 E2|²这里的φ1、φ2可以是常数也可以是随坐标变化的函数。之前我们算杨氏双缝时φ2 - φ1 k * d * sinθ其中d是缝间距。迈克尔逊干涉仪也是完全一样的逻辑只是把相位差的来源从“空间几何路径”换成了“两臂光程差”。只要想通这一点所有双光束干涉在代码实现上都可以复用同一套框架。1.2 为什么选迈克尔逊干涉仪当进阶案例我在前几篇里始终强调仿真不是把课本插图复制一遍而是通过改变参数回答“如果这样会怎样”。迈克尔逊干涉仪恰好是回答这类问题的最佳载体。第一它的相位差随动镜位移线性变化于是我们可以轻松模拟时间上的扫描过程观察中心光强随位移的余弦振荡。第二改变动镜倾角或观察视场能同时演示等倾和等厚两种条纹类型这在教学和工程中都是核心概念。第三光源非单色性、分光比不平衡、镜面倾斜度这些“非理想因素”都能在仿真里用一个参数或一个表达式直接加进去非常直观。很多做干涉仪实验的同学会遇到一个困惑调整光路时明明已经让两臂重合了屏上却总是不出条纹。看教材只讲光程差要小于相干长度但“为什么大了没有条纹”这个感觉靠文字很难建立。而你在MATLAB里把相干长度写成一个变量把光程差从0逐渐调到大于相干长度就会亲眼看到条纹对比度一点点消失。这种认知不是背公式能替代的这也是我坚持用仿真推演干涉原理的原因。2. 仿真背后的物理与数学从光程差到干涉条纹2.1 用复振幅而不是光强来叠加我见过不少初学仿真的同学这样写分别算两束光的光强I1和I2然后直接I I1 I2。这样得到的结果永远是均匀亮场因为光强相加不会产生干涉。正确做法一定是先让复振幅相加再取模平方。在MATLAB里复振幅的表示非常直接。比如一束沿传播方向的平面波可以写成E A * exp(1j * phase)其中phase是传播距离带来的相位延迟。两束光到达观察屏时如果光程不同就在各自的相位项里差出一个Δφ 2π * ΔL / λ。于是总光强可以展开成I A1² A2² 2A1A2 * cos(Δφ)这就是干涉项。注意最后一项是余弦函数只要Δφ随空间或时间变化光强就会呈周期性起伏也就是我们看到的条纹。用复数做的好处是当系统里有多个光束差分光比、多个相位延迟时直接累加复振幅比手写三角函数展开要简洁得多也更不容易错。所以我在所有干涉仿真里都坚持“先求相位再叠加复振幅最后取光强”这个顺序。2.2 等倾条纹和等厚条纹迈克尔逊的两种典型光程差迈克尔逊干涉仪有两种工作状态。第一种是动镜和参考镜严格垂直入射光稍微带有一定发散角或使用扩展光源。这时两束反射光可以看成是沿不同方向传播的平面波在观察屏上同一视场角θ处光程差为ΔL 2d * cosθ其中d是两臂的光程差等效对应的镜面位移。这里的θ是观察点相对光轴的角度。当固定d时θ不同ΔL就不同于是屏幕上出现同心圆环这类条纹叫等倾条纹。中心处θ0cosθ最大光程差最大越往外θ越大光程差越小圆环半径越密。第二种状态是动镜相对参考镜有个微小倾角α此时两臂反射光在空间上有个夹角等效于在两束光之间引入了随坐标线性变化的相位。近似处理时光程差可以写成ΔL ≈ 2d 2αx其中x是屏幕上的横向坐标。这时条纹是等间距的直条纹也就是等厚条纹。你移动动镜改变d所有条纹一起平移改变倾角α条纹间距会变化。这两个特点在实际调光路时一个对应“调距离”一个对应“调平行”是两种互相独立的操作。2.3 可见度与相干长度把“非理想光”请进仿真单色平面波只是理想模型。实际光源总有一定的谱宽比如LED谱宽几十纳米即使氦氖激光也有大约1GHz的线宽。谱宽带来的直接后果是不同波长的光各自产生一套干涉条纹叠加在一起后当光程差太大时亮暗条纹会互相抵消条纹对比度下降。描述条纹对比度用可见度V (Imax - Imin) / (Imax Imin)当光程差为0时可见度最高等于两束光振幅相等时的1当光程差远大于相干长度Lc时可见度趋于0。对于高斯线型的光源相干包络可以近似为γ(ΔL) exp(-(ΔL / Lc)²)把这一项乘到干涉项的余弦前面仿真立刻就从“理想数学曲线”变成了“接近实测的条纹”。我在文中会专门演示这个包络怎么加因为它正是理解傅里叶变换光谱仪的核心。3. MATLAB核心实现别急着写for循环3.1 搭建观察屏网格写干涉仿真的第一步永远是用meshgrid生成坐标网格。这里有一个常见争论要不要让网格总点数等于屏幕分辨率比如1600×1200我的建议是最开始的调试阶段用低分辨率比如300×300确认公式和参数没问题后再提高点数。否则一旦相位项写错高分辨率图像看起来全是密密麻麻的细条纹根本看不出问题。网格代码一般长这样clear; clc; N 400; % 网格点数 L 0.04; % 观察屏物理尺寸单位米 x linspace(-L/2, L/2, N); y linspace(-L/2, L/2, N); [X, Y] meshgrid(x, y); r sqrt(X.^2 Y.^2); % 极坐标半径等倾条纹用对于迈克尔逊等倾条纹观察屏上任意点对应的视场角为θ atan(r / f)其中f是一个等效焦距代表观察系统比如透镜的焦距。f越大同一r对应的θ越小条纹相对越稀疏。调试时先用小f让圆环在屏上显示得完整再逐步增加f看细节。3.2 等倾圆环和等厚直条纹的代码实现等倾圆环的代码逻辑是先根据坐标算出每个像素的视场角θ再算出该像素处两束光的光程差ΔL 2 * d * cos(θ)然后叠加复振幅求光强。我这里给一个可以直接跑的版本lambda 632.8e-9; % 氦氖激光波长 d 2e-6; % 两臂等效光程差对应位移 f 0.5; % 等效焦距控制圆环疏密 theta atan2(r, f); deltaL 2 * d * cos(theta); % 等倾条纹光程差 % 分光棱镜50:50两束反射光振幅都取0.5 E1 0.5 * exp(1j * 2 * pi * deltaL / lambda); E2 0.5; E E1 E2; I abs(E).^2; imagesc(x*1000, y*1000, I); axis image; colormap(gray); xlabel(x / mm); ylabel(y / mm); title([d , num2str(d)]);运行后你会看到一组同心圆环圆心位置是光程差最大处所以中心不一定是最亮。移动d圆环会一个接一个从中心冒出来或缩进去非常直观。这一点和杨氏双缝的条纹平移方式很不一样也是迈克尔逊干涉仪实际调光路时的重要判断依据。等厚直条纹更简单给光程差加一个随x线性变化的项即可alpha 5e-5; % 动镜微小倾角弧度 deltaL 2 * d 2 * alpha * X; E1 0.5 * exp(1j * 2 * pi * deltaL / lambda); E2 0.5; I abs(E1 E2).^2;注意这里的X必须用meshgrid之后的二维网格而不是向量x否则矩阵维度对不上。等厚条纹的间距近似为λ / (2 * alpha)所以alpha越小条纹越稀疏你不需要背公式只要在代码里改一下alpha的值立刻就能验证这个关系。3.3 动镜扫描在时间维度上做干涉静止的干涉图看懂了接下来就是迈克尔逊最有工程价值的部分让动镜连续移动记录某一点的光强变化。最常见的做法是记录轴上点中心点的光强随d的变化这时cosθ 1光强变化为I(d) 1 cos(2π * 2d / λ)如果两臂振幅不同则是I(d) A1² A2² 2A1A2 * cos(2π * 2d / λ)这个表达式就是后续傅里叶变换光谱的原始信号。用MATLAB实现时可以用一个循环扫描d也可以直接用向量化计算dscan linspace(0, 20e-6, 4096); deltaL 2 * dscan; Ic 0.5 0.5 * cos(2 * pi * deltaL / lambda); plot(dscan*1e6, Ic); xlabel(d / \mum); ylabel(中心光强);这里几千个点用向量化直接算完全不需要循环。你会看到余弦振荡周期为λ/2也就是动镜每移动半个波长中心点完成一个亮暗周期。很多教材把这个结论作为重点但只有亲手画出这条曲线你才对“半波长”有直观记忆。3.4 完整脚本应该怎么组织我建议把干涉仿真脚本分成三个块参数块、核心计算块、显示块。参数块集中放波长、位移、角度、分辨率方便反复改核心计算块只做相位和复振幅叠加显示块负责画图。这样当你想加非单色性、加噪、加倾角时只需要在参数块和核心计算块里动几行不会把脚本搅成一锅粥。另外如果你的机器内存紧张不要把N设到2000以上。因为meshgrid产生的X、Y每个都是2000×2000的double矩阵两个就是64MB再加上后续角度、相位、复振幅等临时变量内存会到几百MB。调试够了就赶紧clear掉不用的变量防止MATLAB越来越卡。4. 参数设置与仿真结果分析4.1 基本参数到底怎么选新手最容易纠结的事情是d取多大f取多大N取多大这里给一组我实际调试下来比较顺手的初始值参数推荐值调参说明lambda632.8e-9可见光波段条纹周期直观d2e-6到20e-6太小条纹太疏太密则可能欠采样f0.5到2控制等倾圆环在屏上的分布alpha1e-5到5e-5控制等厚条纹间距N400初始调试够用后期可加到800L0.02到0.04观察屏物理尺寸不能太小以等倾条纹为例相邻圆环之间的半径间隔大约为Δr ≈ f * λ / (2d)。当d 2μm、λ 632.8nm、f 0.5m时Δr 0.079m这个值比L 0.04m大所以你只能看到中心附近的一两个圆环。想要在屏上看到多个圆环要么增大d要么减小f。实际调试中我一般会先用大d快速看条纹分布再逐步减小d观察中心的变化效率会高很多。4.2 动镜位移d如何影响条纹形态这里有一个很容易混淆的点等倾圆环中增大d并不是让条纹整体放大或缩小而是让圆环从中心不断“冒出来”。因为中心处cosθ1光程差最大每当2d增加一个波长中心就多经历一次亮暗周期。而边缘处的光程差变化量小于2d变化量所以中心变化的“速度”比边缘快于是表现为圆环不断从中心抽出。等厚条纹则不同增大d会让整个条纹图样的相位整体平移也就是亮暗条纹一起向左或向右移动但条纹间距不变。如果动镜面有微小倾角增大d还会让条纹从视场的一边“扫”向另一边这就是实际干涉仪调平过程中你会在目镜里看到的那种移动现象。4.3 光源非单色性给干涉加一条可见度包络我在2.3节提到了相干长度Lc。现在把非单色性加进扫描信号看看会发生什么lambda 632.8e-9; dL 10e-6; % 假定的相干长度 dscan linspace(0, 100e-6, 8192); deltaL 2 * dscan; gamma exp(-(deltaL / dL).^2); % 高斯相干包络 Ic 0.5 0.5 * gamma .* cos(2 * pi * deltaL / lambda); plot(dscan*1e6, Ic); xlabel(d / \mum); ylim([-0.2, 1.2]);这里我选相干长度10μm可以看到动镜扫描到大约5μm后干涉条纹的幅度开始明显下降到20μm左右基本消失。这就是为什么白光干涉仪的动镜只能在极短范围内找到零光程差位置一旦偏离几个微米条纹就没了。如果你把这段Ic信号做FFT频谱峰值对应的频率会反推出波长这正是傅里叶变换光谱仪的原理。我在仿真时会把这段信号复制出来单独存变量然后跑一次fft确认峰值位置和设定波长一致。这个验证做完你对“干涉图-光谱”这对傅里叶变换关系会有质的理解。4.4 从等倾圆环到测量应用等倾圆环不只是好看。你可以把直条纹和圆环的间距当成“标尺”反过来求d或α。例如知道λ和f量出相邻圆环半径差Δr就能算出d。在MATLAB里直接显示图像并配合data cursor工具点两个相邻亮环的半径就能很快进行这种反推。我建议做一个小练习设定d 3μm并生成等倾条纹然后写几行代码自动寻找径向光强的峰值位置再由峰值间距反算d对比设定值。这个练习虽然简单却能帮你把“仿真只是画图”变成“仿真可以量化分析”后续做更复杂的干涉分析时你才会有意识地从图像中提取参数而不是仅仅看个视觉效果。5. 常见问题与调试经验5.1 条纹过密或欠采样很多人一上来就用高分辨率屏幕尺寸作为矩阵大小结果运行出来一片模糊什么条纹都看不见。这不是干涉图没生成而是条纹周期小于两个像素而被严重混叠。比如d很大时相邻圆环间距Δr很小如果网格空间采样间隔Δx大于Δr/2圆环就会变成摩尔纹甚至消失。解决办法有三个一是减小d或增大f从物理上让条纹变疏二是增加N提高采样三是先用解析公式估算条纹周期再决定N取多少。我个人的习惯是先让条纹周期在屏上大约占20到50个像素这样既看得清动画也跑得动还能保证一定精度。5.2 相位包裹和数值误差干涉公式里大量出现2π * deltaL / lambda当deltaL很大时相位值会非常大容易出现数值上的相位包裹现象。MATLAB计算过程中虽然不会报错但如果你在调试时用到角度变量要留意unwrap或mod的必要性。另外不要直接在光强表达式里写cos(k*d)然后和某个直流项相加忽略了1/2系数。否则得到的I可能超过1或者小于0显示出来要么全白要么全黑。一个稳当的做法是始终用复数叠加得到光强这样归一化比较自然也不会出现直流偏置算错的问题。5.3 动画卡顿和内存不足如果要把动镜扫描做成动画不要每次循环都重新调用imagesc创建新图形这会非常慢。正确做法是预先创建图形句柄然后在循环里更新CDatahImg imagesc(I); for k 1:numel(dscan) deltaL 2 * dscan(k); I 0.5 0.5 * exp(-(deltaL/dL).^2) .* cos(2*pi*deltaL/lambda); set(hImg, CData, I); drawnow; end这样刷新效率会高很多。若还想更快可以降低动画显示的帧数比如每5个d值更新一次人眼根本察觉不到差异。5.4 一条最容易忽略的工程经验最后分享一个我自己踩过几次的坑分光比不平衡。现实中的分光棱镜并不是完美的50:50两束光振幅不相等时干涉可见度会下降。这个在代码里体现为把E1和E2的振幅从0.5改成0.6和0.4再看条纹你会发现暗条纹不再到0而是留下一个常亮底。用可见度公式算一下正好能对上。仿真时我会故意加这个不平衡让学生在改进代码的过程中理解“为什么光学实验里要调整分光比”这个实际需求。从这个角度看MATLAB仿真光干涉的最终目的并不只是把课本图重画一遍而是把理想公式和现实误差之间的桥梁搭起来。等你什么时候能熟练地在仿真里“人为制造”条纹模糊、对比度下降、相位偏移再去看真实干涉仪的实验操作你会发现很多问题原来在代码世界里早就演练过了。