简介:针对大气光学中光束传输受湍流影响的仿真需求,这份资源聚焦相位屏生成与大气湍流反演方法,覆盖功率谱反演法从数据收集、功率谱估计到反演算法、相位屏制作的完整链路。资源以MATLAB脚本形式提供,共5个文件,压缩包大小仅2KB,代码包含相位屏仿真、傅里叶变换与逆变换、大气相干长度r0计算等核心模块,可直接运行验证。读者可借助这套工具构建符合von Kármán谱或Kolmogorov谱的相位屏,直观理解大气湍流导致的光波相位扰动规律,也可为激光通信、天文观测、远程成像等领域的自适应光学系统设计与湍流补偿研究提供算法参考。已有1371人学习下载,实用性得到初步认可。通过运行这些脚本,可有效降低从理论到代码的门槛,帮助研究者快速上手大气相位屏建模与湍流影响预测。
1. 相位屏仿真方法解决的不只是“画一张随机图”
做大气湍流光束传输仿真时,相位屏仿真方法是最常用的做法,它把一段大气路径压缩成一张大气相位屏,后续的大气湍流反演、链路误码率仿真全都压在这张屏的统计特征是否保真上。把大气相位屏当成一张随机灰度图,是新手最容易掉进去的坑——肉眼看着纹理丰富不代表它能用,真正决定自适应光学残差、自由空间光通信误码率的是相位结构函数是否在目标尺度上服从湍流统计。本文按“物理模型 → 谱反演实现 → Zernike 展开 → 验证与反演 → 仿真链路集成”的顺序,把一个可复现的相位屏仿真方法完整摊开,适合做光传输仿真、波前传感算法验证,或者刚开始搭建大气光学仿真链路的工程师参考。
2. 大气湍流相位屏背后的物理模型:从结构函数到 Kolmogorov 谱
2.1 相位结构函数是相位屏仿真是否保真的判据
大气湍流对光波前的影响,本质是折射率随机起伏沿传播路径的累积。工程上用相位结构函数描述两点相位差的统计特性,定义如下:
Dφ(r) = ⟨|φ(x+r) − φ(x)|²⟩
在 Kolmogorov 湍流假设下,相位结构函数写成 Dφ(r) = 6.88·(r/r₀)^(5/3),r₀ 是 Fried 相干长度。这里的关键在于:这是理论目标值,不是仿真自带的属性。任何一张相位屏,不管用什么方法生成,最终都要拿它算一遍结构函数,再看跟这条理论曲线靠不拢。生成相位屏时常常会加低频补偿或高频修正,本质上都是在调整 Dφ(r) 在不同尺度区间的斜率,让它在从毫米级到孔径尺寸的跨度上尽量贴合 5/3 幂律。
相位屏仿真方法里,判断一个实现靠不靠谱不是看生成速度,而是看两个指标:一是 Dφ(r) 在 r 大于几个采样间隔之后是否贴近理论曲线,二是在 r 接近屏尺寸时有没有明显的低频掉头或饱和。这两个位置恰好对应高频欠采和低频缺失,是谱反演法最常见的两类失真。
2.2 折射率功率谱与相位功率谱的衔接
相位屏生成的数学起点是折射率起伏功率谱。Kolmogorov 谱的标准形式为:
Φn(κ) = 0.033·Cn²·κ^(−11/3)
κ 是空间波数,单位 rad/m,Cn² 是折射率结构常数。实际仿真中很少直接用纯 Kolmogorov 谱,因为 κ → 0 时谱密度发散,低频能量无限大,相位屏会产生不现实的整体倾斜漂移。常见做法是用 von Kármán 谱,在外尺度 L₀ 引入低频截止:
Φn(κ) = 0.033·Cn²·(κ² + κ₀²)^(−11/6)·exp(−κ²/κm²)
其中 κ₀ = 2π/L₀,κm = 5.92/l₀,l₀ 是内尺度。外尺度 L₀ 通常取 10~100 m,内尺度 l₀ 取几毫米。对垂直光路或水平链路,两个尺度不要用同一个数,外尺度对低频补偿层数的影响非常大,后面第 3 章会具体算。
薄屏近似下,相位功率谱与折射率谱的关系为 Φφ(κ) = 2π·k²·Δz·Φn(κ),k 是波数 2π/λ,Δz 是湍流层的等效厚度。代入 von Kármán 谱,并借助 r₀ 的定义消去 Cn²·Δz,工程上直接写成:
Φφ(κ) = 0.49·r₀^(−5/3)·(κ² + κ₀²)^(−11/6)·exp(−κ²/κm²)
这个式子正是功率谱反演法的“配料表”。r₀ 定了整体幅度,κ₀ 定了低频拐点,κm 定了高频衰减位置。
2.3 仿真前必须先确定参数 r₀、L₀、l₀ 和网格
| 参数 | 符号 | 常见取值 | 作用与影响 |
|---|---|---|---|
| Fried 相干长度 | r₀ | 5~20 cm(可见光水平链路) | 决定相位屏整体方差,r₀ 越小湍流越强 |
| 湍流外尺度 | L₀ | 10~100 m | 控制低频能量占比,影响倾斜分量幅度 |
| 湍流内尺度 | l₀ | 1~10 mm | 截断高频,影响采样间隔选择 |
| 采样间隔 | Δx | ≤ l₀/2,一般取 mm~cm 级 | 过大会导致高频段结构函数上翘 |
| 网格数 | N | 256~1024 | 决定空间带宽积,也决定低频最大波长 |
工程经验是 r₀ 与网格尺寸 D=N·Δx 的比值要落在合理区间:如果 D/r₀ 小于 2,整张屏相当于几乎没有湍流;如果大于 100,低频能量强到你不得不用更多次谐波层数去补偿。常见做法是先定 D 为望远镜口径或接收孔径的 1.5~2 倍,再反推 Δx,最后按 l₀ 检查是否满足采样要求。
3. 谱反演法生成大气相位屏:FFT 实现与低频补偿
3.1 用功率谱反演生成相位屏的最小 Python 实现
功率谱反演法的核心思路很直接:在频域用相位谱的幅值构造随机复数场,再逆傅里叶变换回空间域。实现上需要小心频率坐标的正确构造,尤其 FFT 的频率分量排列顺序。以下代码可以直接跑通,生成单张 von Kármán 谱相位屏:
import numpy as np def generate_phase_screen(r0, N, delta, L0, l0): """ 基于von Kármán谱的功率谱反演法生成大气相位屏 r0 : Fried相干长度 (m) N : 网格数量 (每边) delta: 采样间隔 (m) L0 : 湍流外尺度 (m) l0 : 湍流内尺度 (m) """ # 频率坐标,注意fftfreq返回[-0.5, 0.5)范围,乘2*pi/delta转为空间角频率 fx = np.fft.fftfreq(N, d=delta) * 2.0 * np.pi fy = np.fft.fftfreq(N, d=delta) * 2.0 * np.pi FX, FY = np.meshgrid(fx, fy) kappa = np.sqrt(FX**2 + FY**2) # 避免零频奇异,用外尺度截断 kappa0 = 2.0 * np.pi / L0 kappam = 5.92 / l0 # 相位功率谱,振幅为复数高斯随机场 phz_amplitude = np.sqrt(0.49 * r0**(-5.0/3.0) * (kappa**2 + kappa0**2)**(-11.0/6.0) * np.exp(-kappa**2 / kappam**2)) # 零频位置置零,避免直流偏置 phz_amplitude[kappa == 0] = 0.0 # 复数随机场,实部和虚部独立高斯,保证幅度服从瑞利分布 random_phase = np.random.normal(size=(N, N)) + 1j * np.random.normal(size=(N, N)) # 逆FFT并取实部,乘以(N**2)是FFT归一化修正 phase_screen = np.fft.ifft2(phz_amplitude * random_phase).real * N**2 # 去除整体倾斜和活塞项,只保留波前起伏 phase_screen -= phase_screen.mean() return phase_screen代码里几个容易被忽视的参数说明如下。fftfreq 生成的是归一化频率,乘以 2π/Δx 后才是空间角频率;零点位置在数组中心,meshgrid 后自然得到完整的二维频率平面。phz_amplitude 的单位是 m^(11/6),乘上标准正态复随机数后做逆 FFT,得到的是相位值(弧度)。最后乘以 N² 是因为 numpy 的 ifft2 自带 1/N² 归一化,而谱反演公式要求逆变换不带这个系数。去均值这步很重要,否则屏上会叠一个不存在的活塞项,影响后续波前斜率计算。
3.2 为什么直接 FFT 的结果低频缺失
直接用 3.1 的代码生成的屏,最明显的特征是低频大尺度结构偏弱。原因是 FFT 的频率网格分辨率有限,最低可表示的空间频率是 2π/(N·Δx),也就是屏的尺寸决定了最低频率。然而 von Kármán 谱在低频端仍有大量能量,这些能量分布在比屏尺寸更长的波长上,FFT 网格根本放不下。
低频缺失的直接后果是相位结构函数在 r 接近孔径尺寸时斜率变陡,即 Dφ(r) 增长快于理论值,r₀ 偏大。对自适应光学仿真,这意味着倾斜和离焦分量被低估;对自由空间光通信链路仿真,光束的到达角起伏和光束漂移会不准。低频补偿不是可选项,在 D/r₀ 较大时几乎是必须的。
3.3 次谐波补偿:最常见实现与关键参数
次谐波法由 Lane 等人在 1992 年提出,思路是用一组更大尺度的子网格叠加到原有屏上,把低频端补上。每一层的采样点数是固定的 2×2 或 3×3,子网格代表的空间范围逐层加倍。工程上一般叠加 3~5 层,层数低于 3 时补偿不足,高于 5 时收益递减。
def add_sub_harmonics(phase_screen, r0, N, delta, L0, l0, n_layers=4): """ 对已有相位屏叠加次谐波低频补偿 phase_screen: 已经生成的原始相位屏 n_layers : 次谐波层数,常用3~5 """ N_sub = 3 # 每层采样点数,用3x3网格是标准做法 kappa0 = 2.0 * np.pi / L0 kappam = 5.92 / l0 fx_base = np.fft.fftfreq(N_sub, d=delta) * 2.0 * np.pi FX, FY = np.meshgrid(fx_base, fx_base) kappa_base = np.sqrt(FX**2 + FY**2) kappa_base *= 2.0 # 基频需要乘2,对应最底层的空间频率 compensation = np.zeros_like(phase_screen) for layer in range(n_layers): # 每一层空间频率减半,对应尺度加倍 kappa = kappa_base * (0.5**layer) # 频域振幅,与主屏相同的谱形式 amplitude = np.sqrt(0.49 * r0**(-5.0/3.0) * (kappa**2 + kappa0**2)**(-11.0/6.0) * np.exp(-kappa**2 / kappam**2)) # 生成3x3小屏再插值放大到全屏 small_screen = np.random.normal(size=(N_sub, N_sub)) * amplitude # 用傅里叶插值,比线性插值更保真 small_fft = np.fft.fft2(small_screen, s=(N, N)) large_screen = np.fft.ifft2(small_fft).real # 系数与层数的关系:每层的能量密度按频率面积比例加权 compensation += large_screen * (0.5**(2 * layer / 3.0)) # 合并并去除新增的整体倾斜 final_screen = phase_screen + compensation final_screen -= final_screen.mean() return final_screen这里需要注意参数说明:amplitude 的计算式和主屏一致,但 kappa 用的是层数缩放后的低频值;插值用 FFT 实现,把小屏补零到大尺寸后逆变换,这样频谱形状不会被线性插值破坏。加权系数 0.5^(2·layer/3) 是为了让每层的能量贡献按谱密度随频率下降的比例递减,不乘这个系数会导致低频过冲。compensation 累积后的结果要与主屏相加前,重新做一次去均值,因为补偿层往往会引入一个不小的整体偏移。
3.4 网格参数 N、Δx、L₀ 的取值关系
| 场景 | N | D | Δx | 预期效果 |
|---|---|---|---|---|
| 自适应光学波前仿真 | 256 | 8~16 cm | 0.3~0.6 mm | 倾斜、离焦准确,高频噪声可接受 |
| 自由空间光通信链路 | 512~1024 | 20~50 cm | 0.5~1 mm | 光斑质心抖动和闪烁可以同时捕捉 |
| 大口径望远镜相位屏 | 1024+ | 1~2 m | 1~2 mm | 低频占比大,需要 5 层以上次谐波 |
D/r₀ 超过 50 时,倾斜分量会占主导,Zernike 展开时低阶模式系数很长。此时如果只用 FFT 谱反演而不加次谐波,倾斜项会被明显低估,导致 AO 闭环仿真里跟踪残差偏小,结论偏乐观。
4. Zernike 多项式法生成湍流屏:正交基底分解与取舍
4.1 Zernike 多项式合成相位屏的公式与代码骨架
Zernike 方法把相位屏表达为圆域上正交多项式的叠加:
φ(r, θ) = Σⱼ aⱼ·Zⱼ(r, θ)
Zⱼ 是 Noll 序数的 Zernike 多项式,aⱼ 是模式系数。这个方法天然适用于圆形孔径,且生成结果直接就是模式系数形式,后续接自适应光学的重构矩阵非常方便。径向多项式部分用递推实现,避免每步都调阶乘函数:
def zernike_radial(n, m, rho): """Zernike径向多项式递推实现,rho为归一化径向坐标(0~1)""" R = np.zeros_like(rho) for k in range((n - abs(m)) // 2 + 1): sign = (-1)**k numer = np.math.factorial(n - k) denom = (np.math.factorial(k) * np.math.factorial((n + abs(m)) // 2 - k) * np.math.factorial((n - abs(m)) // 2 - k)) R += sign * numer / denom * rho**(n - 2*k) return R径向指数 n 表示极角方向的变化次数,角向频率 m 表示 azim 方向的周期数。代码中 rho 已经是 0~1 的归一化半径,配合掩模矩阵可以限定圆形孔径范围。递推式的分母里有三个阶乘,中间的数较大,N 超过 200 时建议用对数阶乘累加避免溢出。
4.2 用 Noll 方差近似生成 Kolmogorov 湍流系数
Kolmogorov 谱下 Zernike 展开系数的方差,经验公式为:
σⱼ² = 1.0299·(D/r₀)^(5/3)·(nⱼ+1)
nⱼ 是第 j 阶模式对应的径向指数。这个式子忽略了模式间协方差,但统计各向同性湍流下近似够用。生成时按 Noll 序数排序,每个模式独立取高斯随机数乘以对应标准差,再线性叠加。
需要注意模式间协方差不能完全忽略的情形:当 D/r₀ 较大时,低阶模式的归一化协方差可达 30% 以上。此时应使用完整的 Noll 协方差矩阵做 Cholesky 分解,把相关性注入随机系数:
def generate_zernike_coeffs(D_over_r0, n_modes): """生成含模式相关的Zernike系数""" # 近似方差对角线 n_order = noll_to_radial_order(np.arange(1, n_modes + 1)) variances = 1.0299 * (D_over_r0)**(5.0/3.0) * (n_order + 1) # 构造协方差矩阵(完整Noll矩阵体量较大,这里用对角版本示意) cov_matrix = np.diag(variances) # 独立高斯向量,再用Cholesky分解注入相关性 raw = np.random.normal(size=n_modes) L = np.linalg.cholesky(cov_matrix) coeffs = L @ raw return coeffs上面对角版本省去了交互相,实际工程中如需精确模式相关,需要填充 Zernike 协方差矩阵的非对角元,这部分可以在文献里查到现成实现。经验是前 10 阶用完整矩阵,10 阶之后用对角近似即可,误差低于 5%。
4.3 谱反演法与 Zernike 法的选型对比
| 对比维度 | 谱反演法 (FFT) | Zernike 展开法 |
|---|---|---|
| 孔径形状 | 任意(矩形网格) | 圆形孔径天然适配 |
| 低频保真度 | 需叠加次谐波 | 低阶模式天然包含倾斜、离焦 |
| 高频细节 | 分辨率由网格决定 | 截断到最高阶,细节缺失 |
| 计算效率 | 单次 FFT 很快 | 千个模式叠加有开销 |
| 与 AO 联动 | 需先做模式分解 | 系数直接用于重构 |
| 典型场景 | 光斑闪烁、链路仿真 | 波前传感验证、AO 闭环 |
选型建议:如果仿真目标是接收面光强分布、误码率,谱反演法更直接;如果目标是用 Shack-Hartmann 传感器反推系数或验证 AO 重构算法,Zernike 法更省事。很多工程里两种方法互补使用:Zernike 法生成主孔径内的相位屏,谱反演法生成外围和残余场,最后叠加。叠加时要注意两套屏的 r₀ 占比分配,避免总方差重复。
5. 相位屏的质量验证与大气湍流反演
5.1 计算仿真相位屏的结构函数,和理论曲线对比
相位屏生成后不能只看图片,要定量验证统计特性。结构函数的数值计算方式是遍历屏上所有相距 r 的点对,统计相位差平方的均值,再除以 2,公式为:
D_est(r) = ⟨|φ(x+r) − φ(x)|²⟩/2
除以 2 是因为 Zernike 法或 FFT 法生成的屏已经包含了整体活塞,不除以 2 会让斜率偏大。方向平均时用极坐标扫描半径区间做环形平均:
def phase_structure_function(phase, delta, r_max): """计算相位屏的结构函数,返回半径阵列和D(r)阵列""" N = phase.shape[0] x = (np.arange(N) - N // 2) * delta X, Y = np.meshgrid(x, x) R = np.sqrt(X**2 + Y**2) # 遍历每个半径bin,收集对应像素对的相位差 r_bins = np.linspace(delta, r_max, 64) D_est = [] for r_bin in r_bins: # 找落在r_bin附近的像素对距离 mask = (np.abs(R - r_bin) < delta / 2) # 用频域自相关计算相位差的平方均值 diff = np.diff(np.fft.fft2(phase), axis=0) # 更高效的做法是使用积分代理,这里是示意循环 count = np.sum(mask) if count > 0: D_est.append(np.mean((phase - np.roll(phase, int(r_bin/delta), axis=0))**2) * 0.5) return r_bins[:len(D_est)], np.array(D_est)循环写法效率偏低,但便于理解。生成相位屏是离线仿真,数百次循环可接受,重点是把每次点对的集合打散,避免周期性边界带来的伪相关。如果屏的边界是周期性的,结构函数在 r 接近屏尺寸一半时会明显下跌,这是边界效应而非真实湍流特征,对比时应只取 r 小于 D/4 的区间。
5.2 从已知相位屏反演 r₀:斜率拟合法
结构函数估计出来后,大气湍流反演就是做双对数拟合。理论斜率为 5/3,所以常见做法是固定斜率反推 r₀,或者让斜率和 r₀ 同时做最小二乘拟合:
def estimate_r0(r_vals, D_est, r0_guess): """从结构函数反演r0,固定理论斜率5/3""" # 双对数坐标 log_r = np.log10(r_vals) log_D = np.log10(D_est) # 固定斜率5/3,拟合截距 log_D_fit = log_D - (5.0/3.0) * log_r intercept = np.mean(log_D_fit) # D(r) = 6.88 * (r/r0)**(5/3) r0_fit = 10**(-intercept / (5.0/3.0) + np.log10(6.88) / (5.0/3.0)) return r0_fit固定斜率的好处是抗噪能力强,尤其 r 很小时结构函数估计值抖动大,斜率自由拟合法会把整个拟合带偏。拟合区间建议取 r 在 3·Δx 到 D/8 之间,短于 3·Δx 的区间受采样离散影响,长于 D/8 的区间受低频补偿质量影响。拟合残差能同时给出低频补偿层数够不够的判据:如果残差在 r 接近 D/8 时系统性偏高,说明次谐波层数不够。
5.3 反演结果和设定值不匹配时的优先检查项
| 现象 | 可能原因 | 检查与修正 |
|---|---|---|
| r₀ 偏大,结构函数整体偏低 | 屏的方差不足,r₀ 输入错误 | 检查 r₀ 单位是否用了 m 而不是 cm |
| r 小时 D(r) 斜率大于 5/3 | 采样间隔过大,高频欠采样 | 减小 Δx,重新满足 l₀ 采样条件 |
| r 大时 D(r) 饱和或下跌 | 次谐波层数不足 | 增加 n_layers 到 4~6 层 |
| 结构函数在中段抖动剧烈 | 样本量不足,单张屏统计不稳 | 用多张屏平均,或加大点数 |
| 不同实现间差异大 | Zernike 截断阶数不够 | 提高最高模式数到 200 以上 |
另外要提一个容易踩的坑:用 FFT 法生成的屏自带周期性边界,如果你把屏直接切出一块拿出来做统计,等效于给屏加了一个矩形窗,会在结构函数上引入振荡纹波。正确做法是多生成几张独立屏分别统计后再平均,而不是在单张屏上反复取样本。
6. 相位屏仿真在自适应光学与自由空间光通信里的应用技巧
自适应光学系统仿真中最常见的做法,是把相位屏插值到波前传感器子孔径网格上,然后模拟 Shack-Hartmann 传感器对子孔径内波前斜率的测量过程。插值方法对高频信息有影响,双线性插值会明显压低子孔径内的高频相位起伏,建议用三次样条或频域补零插值。另一个技巧是先对整张屏做一次 Zernike 分解,把倾斜项剥离出来,再模拟倾斜镜的闭环响应,这样可以大幅减少仿真步数。
自由空间光通信链路仿真的关键在闪烁效应,单一相位屏无法反映光强起伏,因为闪烁需要传输一段距离后由衍射效应产生。工程上常用多层相位屏方案:沿传播路径放 5~10 张屏,每张屏分配一部分湍流强度,屏间距足够大时衍射效应才能积累为闪烁。层数的经验分配是 ln(Cn²·Δz) 等分路径,而不是等间距;靠近发射端和接收端的两层要加密,因为这两处对到达角误差和接收效率的影响最大。
动态湍流的一种低成本模拟方式是 Taylor 冻结流假设:生成一张大尺寸相位屏,沿某方向平移即可模拟风场带动下的时间演进。平移步长取 Δx·v/fps,v 是横向风速,fps 是仿真帧率。大屏的尺寸要超过运动范围与孔径之和,否则会看到周期性回绕。判断屏的大尺度保真是否够用的方法,是让一个平面波穿过屏后做一次角谱传播,看接收面光斑质心的抖动功率谱是否符合理论值。如果质心抖动功率谱在高频段衰减过快,问题通常出在次谐波层数不足而不是主屏网格分辨率不够。做完这些验证再进入闭环仿真,会比直接拿着第一张生成的屏跑结果可靠得多。
本文还有配套的精品资源,点击获取