尧图精选

基于MATLAB的单缝双缝衍射仿真GUI:从傅里叶光学到交互式实验

🕒 发布时间:2026/9/16 13:01:54 📁 来源:尧图网络
简介面向MATLAB学习者与光学实验教学场景这款带GUI界面的单缝/双缝衍射仿真源码能直观展示光波经狭缝后的条纹分布并支持通过界面控件调节光源位置、缝宽、缝距等参数。资源包共5个m文件压缩后仅4KB包含主程序、衍射计算函数与界面交互脚本结构精简便于直接运行与二次开发。已有245人学习下载适合物理课程演示、光学原理自学或MATLAB GUI入门实践。源码涉及菲涅尔与夫琅禾费衍射计算、figure图形窗口构建、uicontrol控件布局及回调函数编写运行后可清晰观察中央亮斑、明暗相间条纹以及双缝干涉的相长与相消现象帮助将抽象的波动光学概念可视化也能为扩展多缝衍射、衍射光栅等更复杂光学模型提供基础。1. 基于MATLAB的单缝双缝衍射仿真为什么说GUI源码是光学实验的“积分器”大学物理实验里的单缝衍射最麻烦的不是调激光器而是每一次改动缝宽或缝距都要重新测一组数据在坐标纸上画出一条曲线的功夫足够等控件反应过来。用MATLAB把这套实验搬进GUI界面后滑块一拖衍射条纹的明暗变化、缺级位置、包络形状立刻呈现相当于把原来半小时的调节过程压缩成一次回调事件。这套源码对三类人最有用做课程设计的学生要交差讲光学课的老师要做动态演示还有做信号处理的工程师想快速验证夫琅禾费模型与傅里叶变换的关系。它真正解决的是把抽象的积分公式变成可以上手改参数的交互工具。2. 夫琅禾费衍射的数学复现从积分公式到Single.m代码2.1 为什么用夫琅禾费近似而不是菲涅尔积分单缝和双缝衍射都来自光波的叠加。严格说观察屏距离有限时应当用菲涅尔积分但这个积分在数值上不太容易收敛。当缝到屏的距离L远大于缝宽a和观察范围时夫琅禾费近似足够准确MATLAB计算也简单得多。常见做法是直接把缝上的每一点当作球面子波源计算它们到达屏上某点的光程差再用复数叠加出振幅。这样做的好处是物理图像清晰后续改成多缝光栅时只需要增加子波源数组。参数符号典型值作用缝宽a0.1 mm控制衍射包络宽度缝距d0.5 mm控制干涉条纹周期波长λ632.8 nm决定条纹疏密和缺级位置屏距L1 m影响条纹间距通常固定屏范围D0.02 m决定观察窗口大小这里必须强调单位。如果把0.1mm写进公式而没有乘以1e-3波长再取632.8nm算出的β值会大得离谱条纹会变成高频振荡。我一般在源码的最前面把毫米、纳米统一换算成米后面再不改单位。2.2 Single.m单缝衍射的数值积分实现单缝衍射的解析式是I(θ)I0·(sinβ/β)²其中βπa sinθ/λ。直接用这条公式画曲线很快但不利于理解“子波叠加”这一物理本质。源码里的Single.m如果只做解析式计算代码会非常短但那样就失去了扩展性。更稳妥的做法是保留原始积分形式对缝上各点发出的子波做循环叠加。% Single.m 单缝衍射强度计算核心 % a: 缝宽(m), lambda: 波长(m), L: 屏距(m), N: 屏上采样点 function I single_diffraction(a, lambda, L, N) x linspace(-0.01, 0.01, N); % 屏上坐标默认±1cm I zeros(size(x)); xi linspace(-a/2, a/2, 200); % 缝内等间隔取200个子波源 for i 1:N % 屏上第i个观察点到缝内每个子波源的光程差 phase 2 * pi / lambda * (sqrt(L^2 (x(i) - xi).^2) - L); % 等幅子波叠加忽略倾斜因子 I(i) abs(sum(exp(1j * phase)))^2; end I I / max(I); % 归一化方便与理论曲线对比 endxi是沿缝宽方向排列的子波源坐标phase计算每个子波到达观察点的相位延迟。这里没有用远场近似的sin(theta)形式而是直接算精确光程差这样即便L不够大结果也接近菲涅尔区域。1j是MATLAB的虚数单位exp(1j*phase)把相位转化为复振幅。200个子波源已经够用因为缝宽a通常远小于距离L相位变化是慢变的。如果N取600以下曲线会明显出现锯齿。2.3 Double.m双缝衍射的“干涉×衍射”拆解双缝衍射的条纹特征很容易被误解成“单纯干涉”。实际上屏幕上看到的强度分布是双缝干涉条纹乘上单缝衍射包络。源码里的Double.m最合理的写法是把两条缝的复振幅分别算出再求和。% Double.m 双缝衍射计算示例 % a: 缝宽(m), d: 缝间距(m), lambda: 波长(m), L: 屏距(m), N: 采样点 function I double_diffraction(a, d, lambda, L, N) x linspace(-0.01, 0.01, N); I zeros(size(x)); xi1 linspace(-d/2 - a/2, -d/2 a/2, 200); % 左缝 xi2 linspace( d/2 - a/2, d/2 a/2, 200); % 右缝 for i 1:N phase1 2 * pi / lambda * (sqrt(L^2 (x(i) - xi1).^2) - L); phase2 2 * pi / lambda * (sqrt(L^2 (x(i) - xi2).^2) - L); u sum(exp(1j * phase1)) sum(exp(1j * phase2)); I(i) abs(u)^2; end I I / max(I); endxi1和xi2分别表示两条缝内的子波坐标。把两部分复振幅相加再取模平方干涉和衍射效应就同时包含在结果里。初学的人容易只算干涉项忽略衍射包络导致所有干涉条纹亮度相等。这条代码还能直观展示缺级现象当d 3a时第3、6、9级干涉极大正好落在衍射暗纹位置屏幕上这些条纹会消失。运行源码时建议先设d3a观察缺级再把d调成非整数倍感受条纹亮度的连续变化。3. 从Runme.m启动到GUI控件uicontrol回调与数据传递3.1 手动搭建GUI入口figure、axes与uicontrolGUIDE生成的.fig文件虽然方便但会把大量界面布局代码藏起来不利于修改。源码里既然有Runme.m大概率是手动构建GUI。手动构建的核心是用figure创建窗口用axes放置坐标轴再用uicontrol添加滑块、按钮和文本框。% Runme.m 主程序入口手动构建GUI无GUIDE依赖 function Runme() fig figure(Position, [100 100 900 600], ... Name, 单缝/双缝衍射实验仿真); % 左侧绘图坐标轴 ax1 axes(Parent, fig, Position, [0.1 0.25 0.55 0.65]); title(ax1, 衍射条纹一维光强); xlabel(ax1, 屏幕坐标 x (m)); ylabel(ax1, 相对光强 I); % 右侧缝宽滑块 uicontrol(Style, text, Position, [530 500 80 20], ... String, 缝宽 (mm)); sliderA uicontrol(Style, slider, Position, [530 450 120 20], ... Min, 0.02, Max, 0.5, Value, 0.1, ... Callback, updatePlot); % 缝宽数值显示 editA uicontrol(Style, edit, Position, [660 450 60 20], ... String, 0.10, Callback, updateFromEdit); % 单缝/双缝切换 popupMode uicontrol(Style, popup, Position, [530 380 100 25], ... String, {单缝, 双缝}, Callback, updatePlot); endPosition中的四个数字分别代表控件左下角相对于figure的x坐标、y坐标、宽度和高度。要注意MATLAB的像素坐标系原点在左下角这与很多Windows程序的左上角原点不同。回调函数updatePlot会在滑块或下拉框状态改变时被MATLAB自动调用函数签名通常是两个参数(source, event)。这里用handles结构体保存所有控件句柄是另一种常见做法此处直接用了局部变量实际项目里建议把句柄存入guidata方便多个回调共享。3.2 回调函数的数据读取与单位换算回调函数的核心任务是读取控件当前值、触发计算、刷新坐标轴。直接贴代码% 统一的GUI更新回调函数 function updatePlot(~, ~) % 获取滑块当前值单位是mm a_mm get(sliderA, Value); % 转换为米 a a_mm * 1e-3; % 从popup读取当前模式1单缝2双缝 mode get(popupMode, Value); % 调用物理计算函数这里固定使用红色氦氖激光波长 lambda 632.8e-9; L 1.0; N 800; if mode 1 I single_diffraction(a, lambda, L, N); else d 0.5e-3; % 缝距固定为0.5mm也可用滑块控制 I double_diffraction(a, d, lambda, L, N); end % 更新坐标轴数据 x linspace(-0.01, 0.01, N); plot(ax1, x, I, b-, LineWidth, 1.2); xlim(ax1, [-0.01 0.01]); ylim(ax1, [0 1]); end这里每个get调用都会返回控件当前属性sliderA和popupMode是在主函数中通过guidata保存的句柄。如果直接运行回调却没有这些句柄MATLAB会报“未定义变量”。所以源码里必然有一段把句柄存入guidata的代码比如handles.sliderA sliderA; handles.popupMode popupMode; guidata(fig, handles);回调中读取滑块值后必须做单位换算。滑块Min和Max如果设为0.02和0.5单位是毫米而计算函数要求米这一步换算漏掉的话条纹宽度会差三个数量级。另外edit控件允许用户直接输入数字回调里要用str2double(get(editA,String))并且检查返回值是否为NaN。控件类型回调触发时机读取数据方式slider拖动滑块或点击滑槽get(handle,Value)edit输入后按回车或失去焦点str2double(get(handle,String))popup用户选择新项get(handle,Value) 返回选中索引表格中slider的Value总是double类型不需要解析字符串edit则一定要做字符串到double的转换否则用户输入非数字字符时str2double返回NaN绘图会中断。3.3 源码文件分工Single.m、Double.m与Runme.m的协作方式从文件命名看Single.m和Double.m偏向底层物理计算Runme.m是GUI入口Single_diffraction.m和Double_diffraction.m可能是完整仿真程序。实际运行时建议遵循一条分层规则Runme.m只负责创建UI具体计算交给Single或Double函数绘图则发生在回调内部。这种分层带来的好处是可以在命令行直接测试核心算法不依赖GUI。比如运行I double_diffraction(0.1e-3, 0.5e-3, 632.8e-9, 1.0, 800)就能在命令窗口得到强度向量再用plot(I)查看波形。调试时先验证算法正确再检查GUI回调问题定位会清晰很多。如果发现双击Runme.m后弹不出窗口优先检查路径是否包含所有.m文件以及当前文件夹是否被MATLAB添加到搜索路径。4. 衍射源码实战参数边界、量纲与常见坑4.1 量纲错位最隐蔽的bug来源我在给源码做代码审查时第一眼看的永远是单位。0.1 mm在MATLAB里写成0.1波长632.8 nm写成632.8e-9但缝宽常常被写成0.1e-3。如果某个算法内部默认“缝宽已经换算成米”而调用方传入了毫米值相位就会放大1000倍条纹变成密集的梳状波。检查方式很简单在回调里加一行disp(a)把换算后的缝宽打印到命令窗口看是不是1e-4而不是0.1。表常见单位转换对照原始值转换方法标准单位0.1 mm0.1 * 1e-31e-4 m632.8 nm632.8 * 1e-96.328e-7 m2 cm2 * 1e-20.02 m建议把lambda、L、a、d四个物理量集中放在一个初始化函数里并统一注释单位。这样即使项目换人接手也不会出现“这个a到底是毫米还是米”的疑问。4.2 采样点N的取舍从锯齿到流畅双缝干涉条纹间距Δx大约等于Lλ/d。取典型参数L1m、λ632.8nm、d0.5mmΔx≈1.27mm。屏幕范围-0.01m到0.01m宽度0.02m大约包含16个条纹周期。如果N200一个周期只有12个点勉强能看到正弦形状如果N50条纹会变成尖刺。源码里如果出现锯齿优先增大N到800或1200。但N过大也有问题。for循环逐点计算时N2000且每条缝采样200点总循环次数达到40万次GUI会明显卡顿。改进方法是向量化用矩阵运算替代for循环。% 向量化版双缝衍射计算推荐 x linspace(-0.01, 0.01, N); % N×1 列向量 xi1 linspace(-d/2 - a/2, -d/2 a/2, 200); % 1×200 行向量 xi2 linspace( d/2 - a/2, d/2 a/2, 200); ph1 2 * pi / lambda * (sqrt(L^2 (x - xi1).^2) - L); % N×200 ph2 2 * pi / lambda * (sqrt(L^2 (x - xi2).^2) - L); I sum(exp(1j * ph1), 2) sum(exp(1j * ph2), 2); I abs(I).^2;x是N×1xi1是1×200两者相减会触发隐式扩展生成N×200的矩阵。sum(...,2)按列求和得到每个观察点的复振幅。这段代码没有循环MATLAB内部用BLAS库加速N2000时依然流畅。如果你的MATLAB版本较老需要在x - xi1前加上repmat手动扩展否则会报维度不匹配错误。4.3 双缝缺级GUI里最值得展示的现象缺级是指干涉极大正好落在衍射暗纹上导致该级次条纹消失。条件为d/a为整数。例如当d0.5mm、a0.1mm时d/a5第5、10、15级干涉峰消失。源码的GUI如果只有一维强度曲线缺级看起来只是某个峰值高度异常。更直观的方法是绘制二维条纹图。% 用imagesc显示二维条纹带 I2d repmat(I, 50, 1); % 把一维强度向量复制成50行 figure; imagesc(x, 0:49, I2d); colormap(gray); xlabel(屏幕坐标 x (m)); ylabel(条纹纵向位置像素);repmat把强度向量复制50行形成宽度为1个像素、高度50像素的条纹带配合灰度颜色映射后视觉效果接近实验中的激光衍射图案。注意imagesc的第一个参数是x坐标向量第二个是y坐标第三个是二维矩阵。这里y坐标0:49只是用来撑起矩阵维度实际物理意义不强。如果要让条纹竖直显示可以保持x在水平方向。表典型d/a比值下的缺级表现d/a缺级级次现象说明22, 4, 6,...偶数级干涉峰全部消失33, 6, 9,...所有3的倍数级消失2.5无整数缺失条纹全部可见但强度不等如果GUI里有缝距滑块可以做成连续调节让使用者在拖动过程中观察缺级出现的临界状态。这个交互过程比静态插图更有教学价值。5. 用源码做验证实验反推物理公式与扩展到多缝光栅5.1 用滑块验证单缝中央亮斑宽度与缝宽成反比运行Runme.m后把缝宽从0.1mm调到0.2mm中央亮斑的宽度会明显变窄。理论上第一暗纹位置满足a·x/L ≈ λ即x≈Lλ/a。以λ632.8nm、L1m、a0.1mm为例第一暗纹位置约为6.3mm。在GUI坐标轴上移动鼠标观察光标位置可以粗略验证。想要更精确调用ginput手动点击第一暗纹所在位置% 在GUI的坐标轴上点击并输出坐标 [x_click, ~] ginput(1); disp([第一暗纹位置: , num2str(x_click*1000), mm]);ginput(1)等待用户在坐标轴内点击一次返回点击点的x和y坐标。把这个数值与理论计算的6.3mm对比偏差在±0.3mm内说明仿真参数设置正确。需要注意的是屏幕分辨率会影响点击精度建议先把坐标轴放大到只显示中央区域。5.2 把双缝源码改造成N缝光栅模型Double.m的核心是两条缝的复振幅叠加推广到N缝时只需要把缝中心坐标做成数组。透射光栅可以看作缝间距d固定的多个狭缝中心位置为(-N/2:N/2)*d。写一个通用函数% N缝衍射计算通用函数 function I grating_diffraction(centers, a, lambda, L, N) x linspace(-0.01, 0.01, N); I zeros(size(x)); for c centers xi linspace(c - a/2, c a/2, 200); ph 2 * pi / lambda * (sqrt(L^2 (x - xi).^2) - L); I I sum(exp(1j * ph), 2); end I abs(I).^2; I I / max(I); end调用时写入centers (-25:25) * 0.5e-3;就得到51缝光栅的衍射图样。与双缝相比光栅的干涉主极大更细两个主极大之间还有N-2个暗淡的次极大这正是光栅可以精确测量波长的原因。这种扩展说明源码里的物理模型具有通用性换一组参数就能从双缝实验跳转到光栅光谱实验。5.3 导出仿真数据到CSV方便二次绘图课程设计或论文里需要把仿真结果导入Origin作图。在GUI里增加一个“导出数据”按钮回调函数写入% 导出数据按钮回调 x linspace(-0.01, 0.01, length(I)); writematrix([x, I], diffraction_result.csv);writematrix从MATLAB R2019a开始支持旧版本可以用csvwrite。导出的CSV第一列是屏幕坐标第二列是归一化强度。用Excel或Python pandas直接读取就能脱离MATLAB完成后续的曲线标注。调试时如果发现仿真结果和论文图不一致把CSV导入Python重新画出往往比在MATLAB里反复改参数更容易定位问题。这条“GUI实时预览 CSV导出”的路径也是我在做光学仿真时最推荐的工作流。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联 返回资讯列表 →