
简介本资源面向光学工程、激光物理及计算成像方向的本科生、研究生与科研人员提供基于Gerchberg-SaxtonGS迭代算法的光束整形仿真方案解决高斯光束向高阶高斯光、一阶空心高斯光及贝塞尔高斯光三类特殊结构光场的相位调控与转换问题。压缩包共10个文件含3个核心Matlab函数GS.m主算法、frt.m/Fresnel变换、ifrt.m逆变换、1个预存相位数据.mat文件、5张关键结果图含强度分布与相位分布对比及1幅空心光束bmp示意图总大小4.07MB结构紧凑、模块职责明确便于理解算法流程与结果验证。已有138人学习下载所有代码经Matlab 2019b实测可直接运行附带清晰效果图无需调试即可复现光束演化过程特别适合光学仿真入门者掌握GS算法原理、相位恢复思想及结构光生成方法。1. 项目概述从高斯光束到复杂光场的GS算法实现最近在整理光学仿真相关的代码库翻到了一个老项目是关于用Matlab实现GSGerchberg-Saxton算法将基础的高斯光束转换成几种更复杂、更有趣的光场分布。这个项目在光学设计、激光加工、光镊以及全息术等领域其实挺有实用价值的。很多人可能对高斯光很熟悉就是那种中心最亮、向边缘平滑衰减的光斑它是很多激光器的天然输出模式。但在实际应用中我们常常需要非标准的光场比如能量分布更均匀的“平顶光”、中心有个暗斑的“空心光”或者传播距离超长的“无衍射光”。这个项目里的高阶高斯光、一阶空心高斯光和贝塞尔高斯光就是这类特殊光场的典型代表。这个项目的核心就是用Matlab实现GS相位恢复算法来计算出能将输入的高斯光场变换成我们想要的这些目标光场所需要加载的相位板或者说全息图。GS算法是一种迭代傅里叶变换算法它不需要复杂的透镜系统纯粹在计算机上通过数字模拟就能求解出实现特定光场变换所需的相位信息。这对于设计衍射光学元件DOE或者空间光调制器SLM上加载的全息图是至关重要的一步。我手头这个源码包对应编号2166期就是一个完整的、可运行的Matlab实现包含了从基本原理到最终可视化的全过程。接下来我会结合代码详细拆解GS算法的每一步并展示如何用它来生成三种目标光场。无论你是光学工程的学生还是从事激光微加工、光学成像的研究人员这套思路和代码都能为你提供一个清晰的起点。我们不仅会看代码怎么写更会深入讨论为什么这么写以及在实操中可能遇到的坑和对应的解决技巧。2. GS相位恢复算法的原理与Matlab实现骨架在动手写代码之前我们必须先搞清楚GS算法到底在干什么。简单来说它解决的是这样一个“相位恢复”问题我知道一个输入光场的振幅比如高斯分布也知道在某个平面比如焦平面上我希望得到的光场的振幅比如空心环分布但我不知道为了实现这个变换需要在输入光场上加载什么样的相位分布。GS算法通过迭代在已知输入和输出振幅的约束下来回折腾最终“猜”出这个丢失的相位信息。它的迭代流程非常经典可以概括为以下四个步骤构成一个循环初始化从输入平面开始。我们已知输入光场的振幅A_in比如高斯分布但相位未知。通常我们给一个初始猜测的相位比如全零平面波前或者随机相位。正向传播将当前输入平面的复振幅振幅A_in乘以当前相位exp(1i*phase)进行某种变换通常是傅里叶变换模拟光通过透镜在焦平面上的衍射得到输出平面的复振幅估计。施加输出面约束这是算法的关键。在输出平面我们丢弃计算得到的振幅但保留其相位。然后用我们期望的目标光场的振幅A_target比如空心环替换掉计算出的振幅形成新的输出平面复振幅。反向传播将上一步得到的新复振幅进行逆变换逆傅里叶变换回到输入平面。在输入平面我们丢弃计算出的振幅但保留其相位。然后用我们已知的输入振幅A_in替换掉计算出的振幅从而更新了输入平面的相位估计。迭代将更新后的输入平面复振幅A_in乘以新相位作为下一次迭代的起点重复步骤2到4直到满足停止条件比如迭代次数达到设定值或者前后两次迭代的结果差异小于某个阈值。在Matlab里实现这个循环骨架代码非常清晰。我们首先需要定义一些基本参数% 基本参数设置 N 512; % 采样点数决定了计算网格的分辨率通常是2的幂次方以利用FFT效率 lambda 632.8e-9; % 波长单位米这里用的是常见的He-Ne激光波长 k 2*pi/lambda; % 波数 L 0.01; % 计算窗口的物理尺寸单位米例如10mm dx L/N; % 采样间隔 x linspace(-L/2, L/2-dx, N); % 生成一维坐标轴 [X, Y] meshgrid(x, x); % 生成二维网格坐标接下来是生成输入光场这里就是基础的高斯光束。高斯光束的振幅分布由束腰半径w0决定% 生成输入高斯光场 w0 L/10; % 束腰半径设为窗口尺寸的1/10确保光场在窗口内衰减到足够小 A_in exp(-(X.^2 Y.^2) / w0^2); % 高斯振幅分布峰值归一化为1 % 注意这里A_in是实数矩阵代表振幅。初始相位可以设为零。 initial_phase zeros(N, N); % 初始相位猜测 U_in A_in .* exp(1i * initial_phase); % 初始的输入复振幅然后我们需要定义目标光场的振幅A_target。对于不同的目标高阶高斯、空心高斯、贝塞尔高斯A_target的计算公式不同这将是下一节的重点。假设我们已经有了A_targetGS迭代循环的核心代码如下% GS算法迭代参数 num_iter 200; % 迭代次数通常50-200次就能得到不错的结果 U U_in; % 初始化迭代变量 for iter 1:num_iter % 步骤1: 正向传播 (傅里叶变换到频域/输出面) U_f fft2(U); % 步骤2: 施加输出面约束 % 计算当前输出面的振幅和相位 amp_f abs(U_f); phase_f angle(U_f); % 用目标振幅替换计算出的振幅保留相位 U_f_new A_target .* exp(1i * phase_f); % 步骤3: 反向传播 (逆傅里叶变换回输入面) U_b ifft2(U_f_new); % 步骤4: 施加输入面约束 % 计算当前输入面的振幅和相位 amp_b abs(U_b); phase_b angle(U_b); % 用已知的输入振幅替换计算出的振幅保留相位 U A_in .* exp(1i * phase_b); % 可选每若干次迭代显示一次当前输出面振幅观察收敛过程 if mod(iter, 50) 0 figure(1); imagesc(x*1e3, x*1e3, abs(U_f).^2); % 显示强度 axis image; colormap hot; colorbar; xlabel(x (mm)); ylabel(y (mm)); title([迭代次数: , num2str(iter)]); drawnow; end end % 迭代结束后最终的相位分布就是我们需要的结果 final_phase angle(U); % 这是加载在输入光场上以实现变换的相位板分布注意上面的代码是一个原理性演示。在实际应用中为了模拟光通过透镜在焦平面上的衍射正向传播通常不是简单的fft2而是乘以一个二次相位因子模拟透镜相位调制后再进行傅里叶变换。更精确的模型会考虑菲涅尔衍射或角谱传播。但GS算法的核心思想——在输入面和输出面交替施加振幅约束——是不变的。源码包中的实现会更完整包含了这些传播模型的细节。3. 三种目标光场的数学模型与Matlab生成GS算法的魔力在于只要你能用数学公式描述出你想要的光场强度分布振幅的平方它就能尝试帮你找出实现它的相位。这一节我们深入看看项目涉及的三种目标光场高阶高斯光、一阶空心高斯光和贝塞尔高斯光。理解它们的数学定义是正确生成A_target矩阵的关键。3.1 高阶高斯光束 (Higher-Order Gaussian Beam)我们平时说的“高斯光”通常指基模高斯光束TEM00模。高阶高斯光束对应的是横模比如TEM10, TEM01, TEM11等。它们可以用厄米-高斯函数在直角坐标系或拉盖尔-高斯函数在柱坐标系来描述。在这个项目中很可能指的是拉盖尔-高斯光束因为它更容易产生旋转对称的复杂图案。拉盖尔-高斯光束的场分布由两个模数决定径向指数p和角向指数l。其振幅分布可以表示为A_target(r, φ) ∝ (r/w)^|l| * L_p^{|l|}(2r^2/w^2) * exp(-r^2/w^2) * exp(i*l*φ)其中r和φ是极坐标。w是光束在计算平面的束宽。L_p^{|l|}是广义拉盖尔多项式。exp(i*l*φ)项给出了螺旋相位当l≠0时光束具有轨道角动量中心是相位奇点因此强度为零形成空心光束。但这里“高阶高斯光”可能特指l0而p0的情况此时光束中心可能不是暗斑而是有多个亮环。在Matlab中生成p2, l0的拉盖尔-高斯光束振幅的示例代码如下% 参数 p 2; % 径向指数 l 0; % 角向指数 w_target L/8; % 目标光束的束宽 % 计算极坐标 [Phi, R] cart2pol(X, Y); % Matlab的cart2pol输出顺序是[theta, rho] % 计算广义拉盖尔多项式项 (这里需要laguerreL函数在符号数学工具箱或自定义) % 注意Matlab的laguerreL函数参数顺序是(n, k, x)其中np, k|l| Lp laguerreL(p, abs(l), 2*(R/w_target).^2); % 构建振幅分布 (忽略归一化常数和螺旋相位因子因为我们只关心振幅) A_target (R/w_target).^abs(l) .* Lp .* exp(-(R/w_target).^2); % 由于GS算法只关心振幅所以exp(i*l*Phi)项在计算A_target时不需要。 % 但请注意如果l不为零目标光场本身是带有螺旋相位的这会影响最终相位板的设计。 % 在GS算法中我们通常将A_target设为实数振幅算法会自己找出包含这个螺旋相位的总相位解。实操心得Matlab的laguerreL函数计算速度可能较慢尤其是对于大矩阵。如果追求效率可以预先计算好多项式系数或者使用递推关系自定义函数。另外当p和l较大时振幅分布可能非常复杂包含多个亮环和暗环这对GS算法的收敛性是一个考验可能需要增加迭代次数。3.2 一阶空心高斯光束 (First-Order Hollow Gaussian Beam)空心高斯光束是拉盖尔-高斯光束的一个特例通常指角向指数l ≠ 0的情况。最常见的是l1的一阶空心高斯光束它有一个完美的中心暗斑周围是一个亮环。这种光束在光学镊子中用于捕获低折射率粒子在激光加工中用于产生环形焦斑。其振幅分布可以直接从上面的拉盖尔-高斯公式取p0, l1得到A_target(r, φ) ∝ (r/w) * exp(-r^2/w^2)可以看到当r0时振幅为零因此中心是暗的。Matlab生成代码非常简单l 1; p 0; w_target L/8; [~, R] cart2pol(X, Y); % 对于p0拉盖尔多项式L_0^1(x)1 A_target (R/w_target) .* exp(-(R/w_target).^2);这个分布比高阶高斯简单GS算法通常能很快收敛出一个质量很好的相位图。3.3 贝塞尔高斯光束 (Bessel-Gaussian Beam)贝塞尔高斯光束是一种近似无衍射光束它在传播一段距离内横向光强分布几乎不变。理想的贝塞尔光束需要无限大的能量而贝塞尔高斯光束是其物理可实现的一种近似由一个高斯包络调制一个贝塞尔函数核心构成。其振幅分布通常表示为A_target(r) ∝ J_n(α * r) * exp(-r^2 / w^2)其中J_n是第n阶第一类贝塞尔函数。α是一个与光束横向尺度相关的参数决定了贝塞尔函数零点的位置。α越大中心亮斑和周围环纹越密集。w是高斯包络的束宽。当n0时是零阶贝塞尔高斯光束中心是一个亮斑周围是一系列同心亮环。当n1时是一阶贝塞尔高斯光束中心是暗斑类似空心光束但环纹结构更丰富。Matlab生成零阶贝塞尔高斯光束振幅的代码如下n 0; % 贝塞尔函数的阶数 alpha 100; % 尺度参数需要根据窗口大小L调整使得贝塞尔函数的几个周期能显示在窗口内 w_target L/4; % 高斯包络的束宽通常比贝塞尔核心的尺度大 [~, R] cart2pol(X, Y); % 计算贝塞尔函数项注意避免R0时可能出现的计算问题对于n0J_n(0)0 A_target besselj(n, alpha * R) .* exp(-(R/w_target).^2); % besselj(n, x) 是Matlab内置的第一类贝塞尔函数踩坑记录参数alpha的选择非常关键。如果alpha太小贝塞尔函数变化缓慢看起来就像一个普通的高斯光。如果alpha太大贝塞尔函数的振荡非常剧烈在有限的采样点N下会出现严重的混叠失真GS算法可能无法收敛到一个合理的解。一个经验法则是确保alpha * L/2即窗口边缘对应的自变量在5到20之间这样既能观察到多个环纹又不会超出采样能力。需要多次尝试调整。4. GS算法实现中的关键细节与性能优化有了目标振幅A_target直接套用第二节的迭代骨架就能运行。但要想得到高质量、可实用的相位图并且让程序跑得又快又稳有几个关键细节必须处理好。这些细节在提供的源码包中很可能已经包含但理解它们背后的原因同样重要。4.1 传播模型的选取从傅里叶变换到角谱传播在第二节的骨架代码中我们用了fft2和ifft2来模拟正向和反向传播。这实际上假设了输入平面和输出平面是透镜的前后焦面关系即傅里叶变换关系。这是一种特殊情况也是最常用、计算最快的一种模型。然而更一般的情况是相位板输入面和观察面输出面之间是自由空间传播。这时我们需要用衍射理论来建模。常用的有两种数值方法菲涅尔衍射Fresnel Diffraction适用于中等距离的传播。可以用卷积或单次傅里叶变换实现。角谱传播Angular Spectrum Propagation这是一种更精确的方法理论上适用于任何距离只要采样满足奈奎斯特准则。它直接在频域进行滤波。角谱传播的Matlab实现步骤为% 假设光场复振幅为 U传播距离为 z [Ny, Nx] size(U); dfx 1/(Nx*dx); % 频域采样间隔 dfy 1/(Ny*dx); fx (-Nx/2:Nx/2-1) * dfx; fy (-Ny/2:Ny/2-1) * dfy; [FX, FY] meshgrid(fx, fy); % 计算传递函数 H k 2*pi/lambda; H exp(1i * k * z * sqrt(1 - (lambda*FX).^2 - (lambda*FY).^2)); % 处理 evanescent waves (空间频率过高根号内为负数的区域) H(real(sqrt(1 - (lambda*FX).^2 - (lambda*FY).^2)) 0) 0; % 进行角谱传播 U_freq fft2(U); U_prop_freq U_freq .* fftshift(H); % 注意传递函数需要fftshift对齐频域中心 U_out ifft2(U_prop_freq);在GS算法中正向和反向传播都需要使用这个角谱传播函数。它的计算量比简单的FFT大但更通用。源码中可能会根据具体需求选择传播模型。4.2 相位解包裹与动态范围限制GS算法迭代收敛后我们得到的是包裹相位angle函数的结果范围在[-π, π]之间。如果我们要将这个相位图制作成实际的衍射光学元件DOE或者加载到空间光调制器SLM上就需要考虑相位的物理实现。对于DOE通常是表面浮雕结构相位调制量φ与浮雕深度d的关系是φ 2π (n-1) d / λ。相位从0到2π对应一个深度周期。因此包裹相位0-2π是可直接使用的。对于SLM它通过电压控制液晶分子取向来调制相位通常也有一个0-2π的调制范围。我们需要将Matlab计算出的包裹相位-π到π线性映射到SLM的驱动灰度值如0-255。这里有一个重要的坑GS算法有时会收敛到一个相位变化非常剧烈的解相邻像素的相位差可能超过π。当我们将这个包裹相位映射到0-2π时这些地方会出现2π的跳变形成“相位涡旋”或陡峭边缘。在制作或显示时这些特征可能无法被完美再现受限于加工分辨率或SLM像素尺寸导致衍射效率下降或产生杂散光。解决方法是加入“相位平滑”约束。一种简单的方法是在迭代过程中对每次更新后的相位进行低通滤波例如高斯滤波抑制高频的剧烈变化。但这样可能会影响最终光场的保真度。另一种更优雅的方法是使用“加权GS算法”或“混合输入输出算法”在迭代中引入反馈来控制相位的平滑度。源码中可能包含了类似的技巧。4.3 收敛性判断与迭代策略基本的GS算法可能收敛缓慢甚至陷入停滞或振荡。为了提高效率和效果可以采用以下策略自适应步长在更新相位时不是完全用新相位替换旧相位而是采用一个松弛因子βphase_new phase_old β * (phase_calculated - phase_old)其中β在0到1之间。β较小时更新保守收敛稳定但慢β较大时更新激进可能振荡。可以设计一个随迭代次数变化的β。多分辨率优化这是一个非常有效的技巧。先在低分辨率例如N128下运行GS算法得到一个粗糙的相位解。然后将这个低分辨率相位图通过插值放大作为高分辨率例如N512计算的初始猜测。这能大大加快收敛速度并帮助算法跳出局部极小值。收敛判断除了固定迭代次数还可以计算一个误差度量如输出面振幅与目标振幅的均方根误差RMSEerror sqrt(sum(sum((abs(U_f) - A_target).^2)) / (N*N));当error低于某个阈值或者连续几次迭代不再显著下降时就可以停止迭代。4.4 计算效率优化对于512x512甚至更大的网格迭代几百次计算量不小。优化方法包括使用fft2和ifft2这已经是Matlab里最快的傅里叶变换了。将循环向量化避免在循环内对每个像素进行操作。使用单精度如果精度要求不是极高可以使用single类型数据减少内存占用和计算时间。预计算传播函数对于固定的传播距离z角谱传播的传递函数H可以预先计算好避免每次迭代都重复计算。5. 结果可视化、分析与实际应用考量算法跑完了相位图也拿到了但这只是第一步。我们需要评估这个相位图的质量并思考如何把它用在实际的光学系统中。5.1 结果可视化不止看相位最终相位图这是我们的主要“产品”。用imagesc显示final_phase观察其结构。一个设计良好的相位图其条纹应该是相对平滑、连续的。突然的、锯齿状的跳变可能预示着问题。figure; imagesc(x*1e3, x*1e3, final_phase); axis image; colormap hsv; colorbar; % hsv色图对显示相位很直观 xlabel(x (mm)); ylabel(y (mm)); title(计算得到的相位分布 (包裹于[-π, π]));模拟重建光场将计算得到的相位图final_phase加载到输入高斯光上然后进行一次正向传播看看得到的光强分布是否与我们的A_target一致。这是最直接的验证。U_sim A_in .* exp(1i * final_phase); % 使用与GS迭代中相同的传播方式如FFT计算输出面 U_out_sim fft2(U_sim); I_out_sim abs(U_out_sim).^2; figure; subplot(1,2,1); imagesc(x*1e3, x*1e3, A_target.^2); axis image; colormap hot; title(目标光强); subplot(1,2,2); imagesc(x*1e3, x*1e3, I_out_sim); axis image; colormap hot; title(模拟重建光强); % 可以计算两者的相关系数或RMSE来定量评估衍射效率这是一个关键指标。它定义为重建光场中落在目标区域例如对于空心光就是那个亮环内的能量与输入总能量的比值。GS算法通常追求高保真度但未必是最高衍射效率。有时需要在保真度和效率之间权衡。% 以空心高斯光为例定义一个环形区域掩膜 mask (R ring_inner_radius) (R ring_outer_radius); energy_target_region sum(sum(I_out_sim .* mask)); energy_total_input sum(sum(A_in.^2)); diffraction_efficiency energy_target_region / energy_total_input; fprintf(衍射效率: %.2f%%\n, diffraction_efficiency*100);5.2 从相位图到实际元件SLM加载与DOE加工对于空间光调制器SLM每个像素对应一个灰度值控制相位延迟。我们需要将final_phase范围-π到π线性映射到0到最大灰度值如255。phase_wrapped mod(final_phase, 2*pi); % 确保在0-2π之间 slm_grayscale uint8(phase_wrapped / (2*pi) * 255); imwrite(slm_grayscale, phase_pattern.bmp); % 保存为SLM可加载的图片重要提示SLM通常有“相位-灰度”响应曲线不一定是线性的。在实际使用前必须对SLM进行定标建立灰度值与实际相位调制量的对应关系并在生成灰度图时进行校正。对于衍射光学元件加工DOE通常是表面浮雕结构。相位分布φ(x,y)直接对应着刻蚀深度d(x,y) λ * φ(x,y) / (2π (n-1))其中n是DOE材料的折射率。加工文件如GDSII需要包含这个深度信息转化成的几何图形。这里涉及到更多的工艺约束如最小特征尺寸、台阶数多阶衍射元件等需要在GS算法设计阶段就加以考虑这属于“迭代傅里叶变换算法”的进阶课题了。5.3 常见问题与调试技巧问题一重建光场中心总有一个亮斑对于空心光。可能原因GS算法陷入了局部最优解或者初始相位猜测太差。尝试使用随机相位作为初始猜测或者采用“多分辨率优化”策略。也可以在目标振幅A_target的中心强制设一个零值区域。问题二重建光场环纹不对称或有杂散光。可能原因采样点数N不够导致混叠或者计算窗口L太小光场能量在边界处被截断。尝试增大N和L。另外检查传播模型是否准确特别是使用角谱传播时传递函数H中对于倏逝波的处理是否正确。问题三算法不收敛误差曲线上下震荡。解决方法引入松弛因子β并随着迭代减小它。或者尝试更先进的算法变种如“混合输入输出算法”它在施加输入面约束时更灵活有助于跳出局部极小值。问题四计算速度太慢。优化首先确保使用了fft2/ifft2。其次如果目标光场是圆对称的可以考虑将问题简化到一维径向来处理能极大提升速度。但对于非对称或复杂光场此方法不适用。这个Matlab源码项目提供了一个强大的工具箱将GS算法从理论公式变成了可运行、可修改的代码。通过调整目标光场的定义、算法参数和传播模型你可以用它来设计实现各种各样光场变换的相位元件。在实际科研或工程中这往往是走向实验的第一步也是最关键的一步。理解代码背后的每一个选择才能在你自己的项目中游刃有余。本文还有配套的精品资源点击获取