用Python模拟光的衍射:从惠更斯原理到夫琅禾费衍射的保姆级代码实现

潘铭允Jasmine

用Python模拟光的衍射:从惠更斯原理到夫琅禾费衍射的保姆级代码实现

光学衍射现象在自然界中无处不在,从阳光透过树叶形成的斑驳光影,到现代半导体制造中的光刻技术,衍射效应都扮演着关键角色。理解衍射不仅对物理学家至关重要,对工程师和程序员来说,能够用代码模拟这些现象同样具有巨大价值。本文将带你用Python从零构建完整的光学衍射模拟系统,通过可运行的代码直观展示从惠更斯原理到夫琅禾费衍射的完整过程。

1. 光学衍射的理论基础

1.1 惠更斯-菲涅尔原理

惠更斯原理认为波前的每个点都可以看作新的球面波源,而菲涅尔补充了相干叠加的概念。用数学表达,波前上点Q对观察点P的贡献为:

python复制def huygens_fresnel(Q, P, wavelength):
    r = np.linalg.norm(P - Q)
    return np.exp(1j * 2*np.pi * r / wavelength) / r

这个函数计算了单个次级波源的贡献,其中:

  • Q是波前上的点坐标
  • P是观察点坐标
  • wavelength是光的波长
  • 1j表示虚数单位

1.2 衍射的角谱理论

角谱理论将衍射视为频域中的滤波过程。传播过程可以表示为:

code复制Uₚ(fₓ, fᵧ) = U_q(fₓ, fᵧ) × exp(ikz√[1-(λfₓ)²-(λfᵧ)²])

Python实现这一过程的关键步骤:

python复制def angular_spectrum_propagation(U, wavelength, dx, z):
    # 计算频率坐标
    nx, ny = U.shape
    fx = np.fft.fftfreq(nx, dx)
    fy = np.fft.fftfreq(ny, dx)
    FX, FY = np.meshgrid(fx, fy)
    
    # 计算传递函数
    H = np.exp(1j * 2*np.pi * z * 
              np.sqrt(1/wavelength**2 - FX**2 - FY**2))
    
    # 频域传播
    U_fft = np.fft.fft2(U)
    U_prop = np.fft.ifft2(U_fft * H)
    return U_prop

2. 衍射模拟的Python实现

2.1 准备工作

首先建立我们的Python环境:

python复制import numpy as np
import matplotlib.pyplot as plt
from scipy.fft import fft2, ifft2, fftshift, ifftshift

# 参数设置
wavelength = 500e-9  # 500nm光波
pixel_size = 1e-6    # 1μm像素尺寸
grid_size = 2048      # 模拟区域大小

2.2 创建衍射孔径

定义一个圆形孔径函数:

python复制def create_circular_aperture(radius, size, pixel_size):
    x = np.arange(-size//2, size//2) * pixel_size
    y = np.arange(-size//2, size//2) * pixel_size
    X, Y = np.meshgrid(x, y)
    R = np.sqrt(X**2 + Y**2)
    aperture = np.zeros((size, size), dtype=complex)
    aperture[R <= radius] = 1 + 0j
    return aperture

2.3 菲涅尔衍射实现

基于卷积方法的菲涅尔衍射实现:

python复制def fresnel_diffraction(U_in, wavelength, z, dx):
    k = 2*np.pi / wavelength
    nx, ny = U_in.shape
    
    # 创建坐标网格
    x = np.arange(-nx//2, nx//2) * dx
    y = np.arange(-ny//2, ny//2) * dx
    X, Y = np.meshgrid(x, y)
    
    # 计算菲涅尔核
    kernel = np.exp(1j * k/(2*z) * (X**2 + Y**2))
    kernel *= np.exp(1j * k * z) / (1j * wavelength * z)
    
    # 计算衍射场
    U_out = ifft2(fft2(U_in) * fft2(kernel))
    return U_out

3. 夫琅禾费衍射的特殊情况

当观察距离足够大时,菲涅尔衍射过渡到夫琅禾费衍射。其核心公式简化为傅里叶变换:

python复制def fraunhofer_diffraction(U_in, wavelength, z, dx):
    k = 2*np.pi / wavelength
    nx, ny = U_in.shape
    
    # 预计算参数
    scale = wavelength * z / (nx * dx)
    
    # 计算衍射场
    U_out = fftshift(fft2(ifftshift(U_in)))
    U_out *= np.exp(1j * k * z) / (1j * wavelength * z)
    
    return U_out

这个实现的关键点:

  1. 使用FFT计算傅里叶变换
  2. 通过fftshiftifftshift调整频谱位置
  3. 添加了传播相位因子

4. 完整模拟流程与可视化

4.1 模拟单缝衍射

python复制# 创建单缝
slit_width = 10e-6  # 10微米缝宽
aperture = np.zeros((grid_size, grid_size), dtype=complex)
aperture[:, grid_size//2 - int(slit_width/pixel_size//2): 
           grid_size//2 + int(slit_width/pixel_size//2)] = 1

# 计算衍射
z = 0.1  # 观察距离0.1米
U_fraunhofer = fraunhofer_diffraction(aperture, wavelength, z, pixel_size)
intensity = np.abs(U_fraunhofer)**2

# 可视化
plt.figure(figsize=(10,6))
plt.imshow(intensity, cmap='hot', extent=[-1,1,-1,1])
plt.title('单缝夫琅禾费衍射图样')
plt.colorbar(label='光强')
plt.show()

4.2 不同衍射条件的比较

我们可以比较三种衍射情况:

衍射类型 适用条件 计算复杂度 Python实现特点
菲涅尔衍射 z³ ≫ (Δx)⁴/λ 中等 需要二次相位因子
夫琅禾费衍射 z ≫ (Δx)²/λ 直接傅里叶变换
严格衍射 任意距离 需要数值积分

4.3 复杂孔径的衍射

让我们尝试一个更复杂的六边形孔径:

python复制def create_hexagon_aperture(size, pixel_size, radius):
    from matplotlib.path import Path
    hexagon = Path.unit_regular_polygon(6)
    x = np.arange(-size//2, size//2) * pixel_size
    y = np.arange(-size//2, size//2) * pixel_size
    X, Y = np.meshgrid(x, y)
    coords = np.vstack((X.flatten(), Y.flatten())).T / radius
    mask = hexagon.contains_points(coords)
    return mask.reshape((size, size))

hex_aperture = create_hexagon_aperture(grid_size, pixel_size, 50e-6)
U_hex = fraunhofer_diffraction(hex_aperture, wavelength, 0.5, pixel_size)

5. 高级应用与优化技巧

5.1 计算效率优化

对于大型模拟,我们可以使用以下技巧加速计算:

python复制def optimized_fresnel(U_in, wavelength, z, dx):
    # 使用单精度浮点减少内存
    U_in = U_in.astype(np.complex64)
    
    # 预先计算并重用指数项
    k = 2*np.pi / wavelength
    phase = np.exp(1j * k * z) / (1j * wavelength * z)
    
    # 使用FFT预分配内存
    return phase * ifft2(fft2(U_in) * fft2(fresnel_kernel))

5.2 三维衍射模拟

通过叠加不同平面的结果,我们可以模拟三维衍射:

python复制def propagate_3d(U_in, wavelength, z_list, dx):
    results = []
    for z in z_list:
        U_z = fresnel_diffraction(U_in, wavelength, z, dx)
        results.append(np.abs(U_z)**2)
    return np.stack(results)

5.3 GPU加速

使用CuPy库可以将计算转移到GPU:

python复制import cupy as cp

def gpu_fresnel(U_in, wavelength, z, dx):
    # 将数据转移到GPU
    U_gpu = cp.asarray(U_in)
    
    # GPU上的FFT计算
    kernel_gpu = cp.exp(1j * cp.pi/(wavelength*z) * 
                       (cp.square(cp.arange(-N/2,N/2)*dx)[:,None] + 
                        cp.square(cp.arange(-N/2,N/2)*dx)[None,:]))
    
    U_out = cp.fft.ifft2(cp.fft.fft2(U_gpu) * cp.fft.fft2(kernel_gpu))
    return cp.asnumpy(U_out)  # 传回CPU

6. 实际应用案例

6.1 衍射极限分析

光学系统的分辨率受衍射极限限制。我们可以模拟两个点源的分辨情况:

python复制def two_point_resolution(separation, wavelength, na):
    # 创建两个点源
    points = np.zeros((grid_size, grid_size), dtype=complex)
    center = grid_size // 2
    points[center, center - int(separation/2/pixel_size)] = 1
    points[center, center + int(separation/2/pixel_size)] = 1
    
    # 模拟衍射受限成像
    u = fraunhofer_diffraction(points, wavelength, 1, pixel_size)
    intensity = np.abs(u)**2
    
    # 绘制强度剖面
    plt.plot(intensity[center])
    plt.title(f'两点分辨率测试(间隔={separation*1e6:.1f}μm)')
    plt.xlabel('位置(μm)')
    plt.ylabel('强度')

6.2 全息图生成

我们可以模拟简单的全息图生成过程:

python复制def generate_hologram(object_wave, reference_wave):
    # 干涉记录
    interference = object_wave + reference_wave
    hologram = np.abs(interference)**2
    
    # 量化模拟胶片响应
    hologram = np.clip(hologram / hologram.max(), 0, 1)
    return hologram

def reconstruct_hologram(hologram, reference_wave):
    # 全息图重建
    reconstructed = hologram * reference_wave
    return fraunhofer_diffraction(reconstructed, wavelength, z, dx)

7. 常见问题与调试技巧

在光学模拟中常会遇到一些问题,这里列出常见问题及解决方法:

  1. 频谱混叠

    • 现象:高频信息出现伪影
    • 解决:增加采样率或使用抗混叠滤波器
  2. 能量不守恒

    • 检查:总能量在传播中应保持恒定
    • 调试:验证傅里叶变换的缩放系数
  3. 相位跳变

    • 现象:相位图中出现不连续
    • 处理:使用np.angleunwrap函数

一个实用的调试函数:

python复制def check_energy(U1, U2, name1="前", name2="后"):
    E1 = np.sum(np.abs(U1)**2)
    E2 = np.sum(np.abs(U2)**2)
    print(f"能量检查 - {name1}: {E1:.3e}, {name2}: {E2:.3e}, 比率: {E2/E1:.3f}")
    if not np.isclose(E1, E2, rtol=0.1):
        print("警告:能量不守恒超过10%!")

8. 扩展与应用展望

现代光学模拟已经发展到可以处理各种复杂场景:

  • 部分相干光模拟:通过交叉谱密度函数
  • 矢量衍射理论:考虑偏振效应的严格解
  • 非线性光学效应:包括自聚焦和倍频

一个矢量衍射的简单示例:

python复制def vector_diffraction(E_field, wavelength, z):
    # 分解为x,y偏振分量
    Ex = E_field[..., 0]
    Ey = E_field[..., 1]
    
    # 分别传播
    Ex_prop = fresnel_diffraction(Ex, wavelength, z, pixel_size)
    Ey_prop = fresnel_diffraction(Ey, wavelength, z, pixel_size)
    
    return np.stack((Ex_prop, Ey_prop), axis=-1)

在实际项目中,我发现使用面向对象的方式组织代码能显著提高可维护性。例如创建一个DiffractionSimulator类,封装所有相关参数和方法,可以更方便地进行参数扫描和结果比较。对于需要高性能的场景,结合Numba的即时编译能进一步提升计算速度,特别是对于循环密集型的严格衍射计算。

内容推荐

Unity内存爆了?先别急着加内存,教你用Memory Profiler揪出AssetBundle加载的‘幽灵内存’
本文详细介绍了如何使用Unity的Memory Profiler工具诊断和解决AssetBundle加载导致的内存问题,包括内存泄漏、资源错乱等常见现象。通过实战案例和优化技巧,帮助开发者有效管理内存,避免游戏崩溃和性能下降,特别适合处理System out of memory等内存相关错误。
从ZIP文件到网络协议:深入浅出聊聊CRC-32校验码的前世今生与实战
本文深入探讨了CRC-32校验码的技术原理、历史发展及实战应用。从ZIP文件到网络协议,CRC-32作为数据完整性的关键保障,通过数学算法和工程优化实现了高效错误检测。文章详细解析了CRC-32的ISO-HDLC标准实现、查表法优化技巧,并提供了C语言实战代码示例,帮助开发者深入理解这一基础但至关重要的技术。
dslrBooth.Pro.7.49.3.1 深度汉化实战:解锁专业照相馆软件的全流程自动化照片处理
本文详细介绍了dslrBooth.Pro.7.49.3.1深度汉化版在专业照相馆软件中的应用,包括全流程自动化照片处理、汉化文件部署技巧及商业摄影自动化工作流搭建。通过实战案例和高级功能挖掘,帮助摄影师提升效率,实现照片自动美化处理,适用于婚礼跟拍、展会速拍等多种场景。
告别环境报错:Unity Robotics Hub示例依赖包(URDF-Importer, ROS-TCP-Connector)手动安装与版本匹配指南
本文详细介绍了Unity Robotics Hub示例中URDF-Importer和ROS-TCP-Connector依赖包的手动安装方法及版本匹配技巧,帮助开发者解决环境报错问题。通过版本兼容性分析和实战步骤,确保Unity与ROS系统稳定通信,提升机器人开发效率。
SciencePlots实战:一键生成符合顶级期刊标准的科研图表
本文详细介绍了SciencePlots库在科研绘图中的应用,帮助用户一键生成符合Nature、Science等顶级期刊标准的图表。通过Python和Matplotlib的结合,SciencePlots提供了丰富的样式预设和深度定制功能,大幅提升科研图表的专业性和效率。特别适合需要快速生成符合学术规范的科研人员和学生使用。
ABP框架实战:从配置到多租户的全面解析
本文全面解析ABP框架的配置系统与多租户实现,从基础配置到高级技巧,涵盖模块化配置、动态设置管理、数据隔离策略等核心内容。通过实战案例展示如何优化多租户系统性能,并分享调试与安全最佳实践,帮助开发者高效构建企业级SaaS应用。
别再纠结Pointwise还是Pairwise了:手把手教你根据业务场景选对LTR方法(附实战代码)
本文深度解析了学习排序(LTR)中的Pointwise、Pairwise和Listwise方法,帮助开发者根据业务场景选择最优方案。通过电商搜索、信息流推荐等实战案例,详细对比了各方法的优缺点,并提供了代码示例和性能数据,助力实现精准排序与高效推荐。
microchip dspic33 系列教程(4):UART配置与通信实战
本文详细介绍了Microchip dsPIC33系列芯片的UART配置与通信实战,涵盖硬件特性、寄存器配置、波特率计算、MCC图形化配置及通信稳定性优化。通过实例演示和调试技巧,帮助开发者快速掌握dsPIC33的UART通信技术,提升嵌入式系统开发效率。
除了TopMost,这5款免费窗口置顶工具哪个更适合你?实测对比来了
本文深度评测了5款免费窗口置顶工具,包括DeskPins、Actual Window Manager、PowerToys Always on Top、AutoHotkey脚本和WindowTop,从资源占用、操作便捷性、功能丰富度等维度进行对比,帮助Windows用户根据自身工作流选择最适合的窗口管理解决方案。
Simulink MinMax模块避坑指南:当uint8遇上int8,你的模型输出为啥总差1?
本文深入解析了Simulink MinMax模块在混合整数类型(uint8与int8)处理中的常见陷阱,揭示了模型输出差1的根本原因。通过详细的诊断流程、工程化解决方案和自定义安全模块的实现,帮助开发者避免数据类型转换错误,确保嵌入式代码的安全性和可靠性。
别再死记硬背了!SolidWorks二次开发,用好APIHelp这个“活字典”就够了
本文详细介绍了如何高效利用SolidWorks二次开发中的APIHelp工具,避免死记硬背API接口。通过分析APIHelp的在线与离线版本选择、界面布局、搜索技巧及API文档解读方法,帮助开发者快速掌握关键API的使用,提升开发效率。重点讲解了API版本迁移、参数理解和示例代码运用等实用技巧。
FFmpeg实战:5分钟搞定用Python脚本批量给视频加动态水印和片头
本文详细介绍了如何使用Python脚本结合FFmpeg工具批量给视频添加动态水印和片头,提升视频处理效率。通过多种实现方案和完整实战代码,帮助内容创作者快速构建自动化视频处理流水线,适用于短视频制作、品牌推广等场景。
ROS小车仿真进阶:如何让你的阿克曼模型在Gazebo里实现‘边跑边画’(SLAM+Move_Base联调实战)
本文详细介绍了如何在Gazebo仿真环境中实现ROS阿克曼小车的动态SLAM与导航联调,通过gmapping与move_base的协同工作,解决建图漂移、目标点失效等问题,使小车具备'边跑边画'的实时探索能力。文章包含架构设计、参数优化、问题诊断及实战案例,助力开发者提升机器人仿真技能。
别再只会用imwrite存图了!Matlab批量处理图片并保存的5个高效技巧(附代码)
本文介绍了Matlab中超越imwrite的5个高效图片批量处理技巧,包括自动化文件遍历、并行计算加速、动态生成输出文件名、高级保存选项与质量优化以及内存优化与异常处理。这些技巧能显著提升科研数据处理效率,特别适合处理大量显微镜图像等场景。
安卓逆向实战:手把手教你用Smali修改去除小说App广告(附百度/穿山甲SDK删除指南)
本文详细介绍了如何通过安卓逆向工程去除小说App中的广告,包括Smali代码修改和SDK文件删除两种方法。重点讲解了穿山甲和百度广告SDK的移除技术,从初始化阻断、动态库删除到广告回调拦截,提供了一套完整的广告屏蔽解决方案。适合安卓开发者和逆向工程爱好者学习实践。
目标检测新手必看:手把手教你用Python实现IoU计算(附YOLOv5实战代码)
本文详细介绍了目标检测中IoU(交并比)的概念、计算公式及其Python实现方法,特别结合YOLOv5框架提供实战代码。从基础原理到高级应用,涵盖坐标格式转换、批量计算及性能优化技巧,帮助初学者快速掌握这一核心评估指标。
PyTorch实战:构建LSTM AutoEncoder进行时间序列异常检测
本文详细介绍了如何使用PyTorch构建LSTM AutoEncoder模型进行时间序列异常检测。通过模拟服务器监控场景,展示了从数据预处理、模型架构设计到训练优化的完整流程,并提供了动态阈值设定和实时检测的实现方法。LSTM AutoEncoder能有效捕捉时间序列中的长期依赖关系,适用于金融、物联网等多个领域的异常检测需求。
macOS Monterey 与 Ubuntu 22.04 LTS 双系统实战:使用 rEFInd 打造无缝启动体验
本文详细介绍了在Mac设备上使用rEFInd引导管理器实现macOS Monterey与Ubuntu 22.04 LTS双系统的完整教程。从准备工作、磁盘分区到系统安装和rEFInd配置,逐步指导开发者打造无缝启动体验,特别针对T2安全芯片的兼容性问题提供了解决方案,并分享性能优化和双系统使用技巧。
你的文献综述AI提示词为什么总跑偏?避开这3个坑,让GPT真正理解你的学术需求
本文探讨了如何优化AI提示词以提升文献综述质量,指出角色定位模糊、指令结构松散和输出标准缺失是三大常见问题。通过模块化设计和精准指令,研究者可以让GPT真正理解学术需求,生成更专业的文献综述内容。文章提供了实战工具箱和模板,帮助学术工作者高效利用AI工具。
别再傻傻分不清!5分钟搞懂NPN和PNP三极管的电流流向与电压偏置(附实战电路图)
本文深入解析NPN和PNP三极管的电流方向与电压偏置差异,提供实战电路图和选型技巧。通过对比两种管型的工作原理、偏置条件和典型应用,帮助电子工程师快速掌握关键知识点,避免常见设计错误。
已经到底了哦
精选内容
热门内容
最新内容
从密度视角洞察异常:深入解析局部离群因子(LOF)算法原理与实践
本文深入解析局部离群因子(LOF)算法原理与实践,通过密度视角识别异常点。LOF算法利用相对密度而非绝对距离,有效解决传统方法在复杂场景中的局限性。文章详细拆解LOF核心四步,包括k距离、可达距离、局部可达密度和局部离群因子计算,并结合电商平台等实战案例展示其应用价值。同时,探讨了参数选型、重复点处理等工程技巧,以及流数据增量计算和深度学习混合应用等高级玩法。
别再手动画图了!用Python脚本玩转HFSS建模,效率提升10倍(附完整代码)
本文详细介绍了如何利用Python脚本实现HFSS自动化建模,大幅提升微波器件设计效率。通过参数化设计、批量操作和流程标准化,工程师可将建模时间缩短90%以上。文章包含完整代码示例,涵盖从基础几何创建到高级参数扫描的全流程,特别适合需要频繁迭代设计的HFSS用户。
PyCharm 与 GitLab 高效协作:从项目克隆到代码推送的完整工作流
本文详细介绍了PyCharm与GitLab高效协作的完整工作流,从环境配置、项目克隆到代码推送的全流程实践。重点讲解了SSH认证、分支管理、冲突解决等核心技巧,帮助开发者提升团队协作效率,实现无缝的代码版本控制与项目管理。
别再死记硬背if-else了!从‘最大数输出’这道题,聊聊C++里更优雅的写法(含algorithm头文件妙用)
本文探讨了C++中如何优雅地解决'最大数输出'问题,避免使用繁琐的if-else结构。通过介绍algorithm头文件中的max函数、三目运算符、循环结构以及现代C++特性,提供了五种更简洁高效的解决方案。这些技巧不仅适用于信息学奥赛(NOI)和OpenJudge等编程竞赛,也能提升日常开发中的代码质量。
别再硬着头皮画图了!用FlexSim快速搭建你的第一个自动化立库仿真模型(附避坑指南)
本文详细介绍了如何使用FlexSim快速搭建自动化立库仿真模型,特别针对AGV路径规划、货架布局等关键环节提供实用避坑指南。通过核心模块解析、参数优化技巧和动态验证方法,帮助读者从零开始掌握物流仿真技术,显著提升方案设计效率。
不止于SMB:在openSUSE上为Canon LBP2900配置LPD和命令行打印的几种姿势
本文详细介绍了在openSUSE Tumbleweed系统上为Canon LBP2900打印机配置LPD和命令行打印的多种方法,包括SMB共享、LPD协议和CUPS原生工具链。通过具体的命令模板和排错指南,帮助用户实现高效打印和自动化任务处理,特别适合需要批量处理和脚本集成的中级用户。
【Pluto SDR实战】从零搭建OFDM通信链路:MATLAB与SDR的协同设计
本文详细介绍了如何使用Pluto SDR和MATLAB从零搭建OFDM通信链路,涵盖OFDM技术原理、Pluto SDR配置、发射机与接收机实现,以及系统性能优化。通过实战案例,帮助读者深入理解数字通信系统设计,掌握SDR与MATLAB协同开发的核心技能。
告别手动删行!用Notepad++正则表达式5分钟搞定FEKO .ffe仿真数据清洗
本文介绍如何使用Notepad++正则表达式快速清洗FEKO .ffe仿真数据文件,解决手动删除注释行和空行的低效问题。通过详细的正则表达式替换步骤和进阶技巧,帮助用户5分钟内完成数据清洗,提升电磁仿真数据处理效率,特别适合ISAR成像等场景。
STM32H743驱动AD7616踩坑记:从HAL库到标准库,解决双SPI数据错位问题
本文详细记录了STM32H743驱动AD7616时遇到的双SPI数据错位问题及解决方案。通过从HAL库转向标准库的寄存器级操作,解决了ARM小端架构与SPI协议的数据打包冲突,并提供了性能优化建议和扩展应用案例,为嵌入式开发者提供了实用的调试经验。
华为防火墙GRE隧道穿越公网实战:eNSP模拟企业分支安全互联
本文详细介绍了华为防火墙GRE隧道在eNSP模拟环境中的实战配置,实现企业分支安全互联。通过GRE隧道技术,企业可以在公网上建立虚拟直连通道,结合IPSec加密确保数据安全传输。文章涵盖拓扑设计、基础网络配置、GRE隧道核心配置及安全策略控制,帮助读者掌握华为防火墙的部署与优化技巧。