☰
涡旋光束全息与拓扑荷模拟:MATLAB相位生成与角谱传播实战
2026/10/2 3:47:42 网站建设 项目流程

简介:本资源为涡旋光束全息与拓扑荷模拟的MATLAB程序,面向光学工程、物理及光通信方向的学习者与研究人员,用于模拟涡旋光束与全息光栅的相互作用过程。程序基于贝塞尔函数构建涡旋相位分布,结合傅里叶光学方法,通过fft2与ifft2实现衍射传播计算,可分析不同拓扑荷下LG光束经全息光栅后的一阶衍射特性,并借助MATLAB可视化直观展示光束模式与能量分布。压缩包内共1个文件,为.m源码文件,整体约1KB,结构精简,便于直接运行与二次修改。目前已有1938人学习下载,适合希望深入理解涡旋相位、拓扑荷调控及全息衍射机理的读者参考,也可为光学信息处理与量子光学相关实验设计提供模拟思路。

1. 涡旋光束全息与拓扑荷模拟:从相位图到可复现的 MATLAB 工程

涡旋光束(Vortex Beam)最迷人的地方在于它携带轨道角动量(OAM),而轨道角动量的大小由拓扑荷 ℓ 决定。很多做光通信、光镊、超分辨成像的同行第一次接触这个方向,都是被一张螺旋相位图吸引进来的——一圈渐变灰度,绕中心转 2πℓ。但真正落到工程上,问题立刻变成:拓扑荷怎么在 MATLAB 里量化生成?全息图怎么编码才能被 SLM(空间光调制器)正确加载?为什么仿真出来的光强分布和文献对不上?这篇笔记就围绕「涡旋光束全息与拓扑荷模拟程序」这个标题,把相位生成、全息编码、传播仿真、参数标定这几件事拆开讲清楚。适合刚上手 OAM 仿真的研究生、需要快速搭一套验证平台的工程师,以及想把 MATLAB 计算全息流程固化下来的从业者。全程只依赖 MATLAB 基础工具箱,不涉及任何需要额外授权的模块。

2. 拓扑荷与螺旋相位:先把物理量算对再谈全息

2.1 拓扑荷 ℓ 到底在程序里代表什么

拓扑荷 ℓ 是一个整数(也可以是分数,但工程上绝大多数场景用整数),它描述的是相位绕光轴一周累积的 2π 倍数。数学上涡旋光束的相位项写作 exp(iℓθ),其中 θ = atan2(y, x) 是方位角。这里第一个容易翻车的地方就是 atan2 的参数顺序:MATLAB 里是atan2(Y, X),不是atan2(X, Y)。我见过不止一个同学把 X、Y 写反,结果相位图整体旋转了 90°,还以为是 SLM 装歪了。

在程序里,拓扑荷直接决定三件事:相位梯度的陡峭程度、中心相位奇点的阶数、以及远场光强环的直径。ℓ 越大,相位变化越快,对 SLM 的相位调制精度要求越高。一般 SLM 的相位量化是 8 bit(256 级),当 ℓ 超过 10 以后,单个像素周期内的相位跳变可能超过 2π/256,就会出现相位包裹误差,这是后面避坑章节要重点讲的。

2.2 生成螺旋相位的可复现代码

下面这段是我平时搭仿真平台时最先跑通的最小闭环,网格分辨率、物理尺寸、拓扑荷都是显式参数,方便后面替换。

% vortex_phase.m % 生成拓扑荷为 ell 的螺旋相位分布 clear; clc; N = 512; % 网格点数,建议 2 的幂,便于 FFT L = 10e-3; % 物理边长 10 mm ell = 3; % 拓扑荷,整数 lambda = 632.8e-9; % 氦氖激光波长 x = linspace(-L/2, L/2, N); [X, Y] = meshgrid(x, x); [Theta, ~] = cart2pol(X, Y); % Theta 为方位角,范围 [-pi, pi] Phase = exp(1i * ell * Theta); % 复振幅,模为 1 % 可视化相位(取角) figure; imagesc(angle(Phase)); axis image off; colormap gray; title(['螺旋相位, \ell = ', num2str(ell)]); colorbar;

逻辑说明:cart2pol返回的第一个输出就是方位角 θ,范围是 [-π, π],这比手写atan2更不容易出错。Phase是复振幅,模为 1,只保留相位信息。参数说明:N决定采样密度,512 是速度和精度的平衡点;L是仿真区域的实际物理尺寸,它和N一起决定像素间距dx = L/N,这个量在后面做角谱传播时必须用到;ell就是拓扑荷,改成正负号可以切换螺旋方向,改成非整数会得到分数涡旋,但中心会出现径向相位切口,需要额外处理。

2.3 相位包裹与 unwrap 的边界

angle(Phase)得到的是包裹在 [-π, π] 的相位,这是正常的,因为 SLM 本身也只能调制 0 到 2π。但如果你要做相位梯度分析或者计算相位奇异点位置,就需要 unwrap。MATLAB 的unwrap默认沿第一维展开,对二维相位图要分两次调用:

PhaseWrapped = angle(Phase); PhaseUnwrap = unwrap(unwrap(PhaseWrapped, [], 2), [], 1);

注意:unwrap 在相位奇点附近会失效,因为奇点处相位本身没有定义。所以不要拿 unwrap 后的结果去判断 ℓ 的符号,判断符号应该看环绕一周的相位累积方向,用sum(diff(angle(Phase(1,:))))这类环绕积分更可靠。

3. 计算全息编码:把复振幅塞进纯相位 SLM

3.1 为什么不能直接加载复振幅

绝大多数 SLM 是纯相位调制器件,只能改变相位,不能改变振幅。而涡旋光束如果还叠加了透镜聚焦相位或者轴棱锥相位,整体就是一个复振幅场。直接取angle()会丢掉振幅信息,导致重建光场出现额外衍射环。工程上有三条常见路线:一是只做纯相位涡旋,忽略振幅整形;二是用迭代算法(GS、GSW)把振幅信息编码进相位;三是用离轴全息,把目标场搬到载频上,用空间滤波分离一级衍射。我一般先用第一条快速验证拓扑荷,再用第三条做正式全息图。

3.2 叠加透镜相位的全息图生成

下面这段在螺旋相位上叠加一个聚焦透镜相位,生成可以直接加载到 SLM 的全息图。

% hologram_lens.m % 螺旋相位 + 透镜相位,生成纯相位全息图 f = 300e-3; % 透镜焦距 300 mm k = 2*pi/lambda; % 波数 LensPhase = exp(-1i * k * (X.^2 + Y.^2) / (2*f)); TotalField = Phase .* LensPhase; Hologram = angle(TotalField); % 取相位,范围 [-pi, pi] Hologram = mod(Hologram, 2*pi); % 映射到 [0, 2pi] Hologram8 = uint8(Hologram / (2*pi) * 255); % 8 bit 量化 figure; imagesc(Hologram8); axis image off; colormap gray; title('加载到 SLM 的 8 bit 全息图');

逻辑说明:LensPhase是薄透镜的二次相位因子,符号取决于透镜是汇聚还是发散,这里用负号表示汇聚。TotalField是复振幅乘积,angle取出总相位,mod把负相位翻转到 [0, 2π],最后量化成 8 bit。参数说明:f是焦距,它决定远场焦斑的位置,如果你的光路里 SLM 后面还有透镜,这个 f 要改成等效焦距;量化级数 255 对应 SLM 的 8 bit 相位深度,如果你的 SLM 是 10 bit 或 12 bit,把 255 换成 1023 或 4095,否则会白白浪费器件精度。

3.3 全息图加载前的三个检查项

在把全息图送进 SLM 之前,我习惯做三个检查:第一,看直方图,确认相位值没有大量堆积在 0 或 255,否则说明相位范围没铺满;第二,看中心区域,涡旋的奇点应该是一个明显的暗核,如果中心是亮的,说明 ℓ 可能为 0 或者相位被破坏;第三,用imwrite存成无损 PNG 或 BMP,不要存 JPG,JPG 的块效应会在远场引入高频噪声。这三步花不了两分钟,但能省掉后面几小时的排查。

4. 传播仿真:角谱法验证拓扑荷是否真的存在

4.1 角谱法的适用边界

验证全息图是否正确,最直接的办法是仿真光场从 SLM 面传播到探测面的过程。常见方法有菲涅尔衍射积分、角谱法(Angular Spectrum Method, ASM)和傅里叶变换法。角谱法的优势是传播距离可以很短,不受菲涅尔近似限制,适合 SLM 后面几毫米到几百毫米的场景。代价是它需要做两次 FFT,计算量比菲涅尔大,但在 512×512 或 1024×1024 网格下,现代笔记本跑一次也就几百毫秒。

4.2 角谱传播的完整实现

% asm_propagate.m % 角谱法传播,输入复振幅 U0,传播距离 z z = 0.3; % 传播距离 0.3 m dx = L / N; % 像素间距 fx = (-N/2:N/2-1) / (N*dx); [FX, FY] = meshgrid(fx, fx); H = exp(1i * k * z * sqrt(1 - (lambda*FX).^2 - (lambda*FY).^2)); H(abs(lambda*FX) > 1 | abs(lambda*FY) > 1) = 0; % 消除倏逝波 U0 = TotalField; % 出射面复振幅 Uz = ifft2(ifftshift(fftshift(fft2(U0)) .* H)); I = abs(Uz).^2; figure; imagesc(x*1e3, x*1e3, I); axis image; colormap hot; xlabel('x (mm)'); ylabel('y (mm)'); title(['z = ', num2str(z*1e3), ' mm 处光强']);

逻辑说明:H是角谱传递函数,sqrt里的项在空间频率超过 1/λ 时变成虚数,对应倏逝波,物理上会快速衰减,所以直接置零。fftshift和ifftshift的配合是为了把零频移到中心再做乘法,这是 MATLAB 里做傅里叶光学最容易写错的地方。参数说明:z是传播距离,改这个值可以扫描焦深;dx由L/N决定,它必须和fx的定义匹配,否则频率坐标会错位;U0这里直接用TotalField,如果你要模拟 SLM 的量化误差,应该把Hologram8反量化回相位再转成复振幅。

4.3 从光强图反推拓扑荷

传播后的光强图如果是一个中心暗、外围亮的环,说明涡旋存在。环的直径和拓扑荷近似满足 D ≈ 2λf|ℓ|/(πw),其中 w 是入射高斯光束的束腰。更严格的验证是干涉法:让涡旋光和平面波干涉,会出现 ℓ 条叉状条纹,叉的数量就是拓扑荷。在 MATLAB 里可以这样快速生成干涉图:

% 与平面波干涉 Ref = exp(1i * k * X * sin(5*pi/180)); % 5 度倾斜平面波 Interf = abs(Uz + Ref).^2; figure; imagesc(Interf); axis image off; colormap gray; title('涡旋-平面波干涉图,数叉数即拓扑荷');

倾斜角不要太大,5° 左右叉状条纹最清晰;太大条纹会密到数不清,太小条纹间距过宽,叉的特征不明显。

5. 避坑与排查:拓扑荷仿真里最常见的五个翻车点

5.1 现象:光强环中心是亮的,看不到暗核

原因:最常见的是atan2参数写反,或者用了atan(Y./X)而不是atan2。atan的值域只有 [-π/2, π/2],无法覆盖整个圆周,相位在第二、三象限会断裂,涡旋结构被破坏。另一个可能是拓扑荷设成了 0,或者网格中心没有落在奇点上——当 N 为偶数时,中心在 N/2+1 处,linspace生成的坐标恰好包含 0;但如果用0:N-1生成坐标,中心就偏了。

解决:统一用cart2pol或atan2(Y, X);检查ell是否非零;用x = linspace(-L/2, L/2, N)保证零点在网格中心。

5.2 现象:远场光强环出现断裂或不对称

原因:相位量化级数不够。当 ℓ 较大时,一个像素周期内的相位变化超过量化步长,8 bit 量化会产生明显的相位台阶,远场出现高阶衍射。另一个原因是全息图存成了 JPG,压缩伪影引入了额外相位噪声。

解决:把量化级数提高到 10 bit 或 12 bit(如果 SLM 支持);存图用 PNG 或 BMP;如果必须用 8 bit,把 ℓ 控制在 8 以内,或者用迭代算法优化相位分布。

5.3 现象:角谱传播结果出现棋盘格或高频条纹

原因:fftshift和ifftshift用错顺序,或者频率坐标fx的定义和fft2的输出顺序不匹配。MATLAB 的fft2零频在左上角,而fx用(-N/2:N/2-1)生成的是零频在中心的坐标,两者必须通过 shift 对齐。

解决:记住口诀「先 fftshift 再乘 H,再 ifftshift 再 ifft2」。如果还是不对,单独用一个高斯光束做传播测试,高斯光束传播后应该还是高斯,形状不变,只是宽度变化,用这个来验证传播代码是否正确。

5.4 现象:干涉图叉数比设定的 ℓ 多或少

原因:如果涡旋光和参考光的倾斜角过大,高阶衍射级会混入,叉数看起来变多。如果倾斜角过小,叉状条纹太宽,可能把相邻的叉合并,看起来变少。另一个可能是传播距离不对,涡旋光没有完全展开,相位结构不完整。

解决:倾斜角控制在 3° 到 8° 之间;传播距离选在焦斑附近,先用光强图确认环已经形成再做干涉;如果还是不对,检查参考光是否真的是平面波,exp(1i*k*X*sin(theta))里 X 的单位是米,theta 是弧度,单位错了条纹间距就全错。

5.5 现象:换一台电脑跑,结果不一样

原因:MATLAB 版本差异导致cart2pol或fft2的数值行为有微小变化,或者随机数种子没固定(如果用了 GS 迭代)。更隐蔽的是linspace在端点处理上的版本差异,虽然概率很低,但在高精度仿真里会放大。

解决:固定随机种子rng(0);把关键参数写成脚本开头的常量,不要散落在各处;如果结果对版本敏感,用ver命令记录 MATLAB 版本,并在代码注释里标明验证过的版本范围。

6. 进阶技巧:用分数拓扑荷和 OAM 复用做一次完整验证

分数拓扑荷是很多人想碰又容易踩坑的方向。当 ℓ 不是整数时,相位绕一周累积不是 2π 的整数倍,相位图会出现一条径向切口(branch cut),切口两侧相位跳变 2π。在 MATLAB 里生成分数涡旋只需要把ell改成非整数,但传播后的光强环会沿切口方向裂开,这是正常现象,不是代码错了。如果你要做 OAM 复用仿真,可以把多个不同 ℓ 的涡旋场叠加:

% OAM 复用:两个拓扑荷叠加 ellList = [2, -3]; weights = [1, 0.8]; U_mux = zeros(N); for idx = 1:length(ellList) U_mux = U_mux + weights(idx) * exp(1i * ellList(idx) * Theta); end U_mux = U_mux .* LensPhase; H_mux = mod(angle(U_mux), 2*pi);

叠加后的全息图会同时包含两个涡旋的相位特征,传播后在焦面附近会形成两个分离的环,环的直径分别对应各自的 |ℓ|。验证方法是分别用 ℓ=2 和 ℓ=-3 的匹配滤波相位去相关,相关峰的位置就是对应 OAM 模式的焦点。这里的关键参数是权重weights,它决定两个模式的能量分配,如果权重差太多,弱模式的相关峰会被强模式的旁瓣淹没。我一般让权重比不超过 3:1,否则弱模式在仿真里几乎看不见。

还有一个实用技巧:把拓扑荷扫描做成循环,一次性生成 ℓ 从 -10 到 10 的全息图序列,存成 PNG 序列,然后用 SLM 的播放功能做动态切换。这样可以在不重新编译代码的情况下,快速测试不同拓扑荷的传输特性。扫描时注意每张图的文件名要带符号,ell_-3.png和ell_3.png不能混,Windows 文件名不区分大小写但 MATLAB 的imread区分,混了会读错文件。

最后说一个我自己的习惯:每次改完拓扑荷或者传播距离,先跑一遍 512×512 的快速版本,确认光强环形状对了,再切到 1024×1024 做正式出图。直接上高分辨率,一旦参数错了,等 FFT 跑完才发现,时间全浪费在等待上。这个习惯帮我省下的时间,比任何优化技巧都多。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询