2. Shack-Hartmann波前传感器数据模拟与重构

任务说明

本案例实现Shack-Hartmann波前传感器(SHWFS)的完整数据模拟与波前重构闭环:生成已知像差(离焦+像散)的波前,模拟微透镜阵列对波前的采样,计算每个子孔径光斑质心偏移,最后用Zernike模式法重构波前并与原始像差对比验证。

实现原理与代码

SHWFS通过微透镜阵列将波前分割为子孔径,各子孔径聚焦光斑位置反映局部波前斜率。设子孔径尺寸为d,焦距为f,波前斜率为(∂W/∂x, ∂W/∂y),则质心偏移为(Δx, Δy) = (f/k)·(∂W/∂x, ∂W/∂y),其中k为波数。本案例用Zernike多项式前4项(平移、倾斜x、倾斜y、离焦)加像散项模拟真实波前,通过数值差分获得斜率,再以模式法重构。

import numpy as np

# ========== 参数设置 ==========
N = 32               # 子孔径网格 N×N
subap_size = 0.3e-3  # 子孔径边长 0.3mm
f = 5e-3             # 微透镜焦距 5mm
wavelength = 635e-9  # 波长 635nm
k = 2*np.pi/wavelength

# ========== 1. 生成真实波前(Zernike模式叠加) ==========
def zernike_polynomials(x, y, max_order=6):
    """返回前6项Zernike多项式值 (Noll序号1-6)"""
    r2 = x**2 + y**2
    Z = np.zeros((6, x.shape[0], x.shape[1]))
    Z[0] = np.ones_like(x)                    # 平移
    Z[1] = 2*x                                # 倾斜x
    Z[2] = 2*y                                # 倾斜y
    Z[3] = np.sqrt(3)*(2*r2 - 1)              # 离焦
    Z[4] = 2*np.sqrt(6)*x*y                   # 像散45°
    Z[5] = np.sqrt(6)*(x**2 - y**2)           # 像散0°
    return Z

# 归一化坐标(单位圆内)
coords = np.linspace(-1, 1, 200)
X, Y = np.meshgrid(coords, coords)
mask = X**2 + Y**2 <= 1.0
Z = zernike_polynomials(X, Y)

# 真实系数:离焦0.8μm,像散0.5μm,倾斜少量
true_coeff = np.array([0, 0.1e-6, -0.05e-6, 0.8e-6, 0.5e-6, 0.2e-6])
W_true = np.zeros_like(X)
for i in range(6):
    W_true += true_coeff[i] * Z[i]
W_true[~mask] = np.nan

# ========== 2. 模拟子孔径斜率采样 ==========
# 子孔径中心坐标(归一化)
sub_coords = np.linspace(-0.9, 0.9, N)
subX, subY = np.meshgrid(sub_coords, sub_coords)
valid = subX**2 + subY**2 <= 0.81  # 圆形孔径内有效子孔径

# 在真实波面上数值差分求斜率(单位:rad)
# 映射到200×200网格
def map_to_grid(val):
    idx = np.clip(((val + 1)/2 * 199).astype(int), 0, 199)
    return idx

ix = map_to_grid(subX)
iy = map_to_grid(subY)
# 数值梯度(单位网格间距)
dy, dx = np.gradient(W_true)
dx[~mask] = 0
dy[~mask] = 0
# 子孔径处斜率(rad/m)
slope_x = dx[iy, ix] / (2/199)  # 网格间距对应归一化坐标
slope_y = dy[iy, ix] / (2/199)

# 质心偏移(像素单位,假设相机像素尺寸=subap_size/10)
pixel_size = subap_size / 10
centroid_x = f * slope_x / k / pixel_size
centroid_y = f * slope_y / k / pixel_size
centroid_x[~valid] = np.nan
centroid_y[~valid] = np.nan

# ========== 3. Zernike模式法重构 ==========
# 构建子孔径处各模式斜率矩阵
def zernike_slopes(x, y, max_order=6):
    """计算子孔径中心处各Zernike模式的斜率"""
    Z = zernike_polynomials(x, y, max_order)
    slopes = []
    for i in range(max_order):
        # 数值差分
        dzdy, dzdx = np.gradient(Z[i])
        # 归一化坐标梯度
        dzdx = dzdx / (2/199)
        dzdy = dzdy / (2/199)
        # 插值到子孔径位置
        sx = dzdx[iy, ix]
        sy = dzdy[iy, ix]
        slopes.append([sx[valid], sy[valid]])
    return np.hstack(slopes).T  # (2*M, 6)

M = valid.sum()
A = np.zeros((2*M, 6))
# 直接计算各模式在子孔径处的斜率
for i in range(6):
    Z_i = Z[i]
    dzdy, dzdx = np.gradient(Z_i)
    dzdx = dzdx / (2/199)
    dzdy = dzdy / (2/199)
    A[:M, i] = dzdx[iy[valid], ix[valid]]
    A[M:, i] = dzdy[iy[valid], ix[valid]]

# 测量斜率向量
b = np.concatenate([slope_x[valid], slope_y[valid]])

# 最小二乘求解
coeff_recon, _, _, _ = np.linalg.lstsq(A, b, rcond=None)

# ========== 4. 重构波前与误差评估 ==========
W_recon = np.zeros_like(X)
for i in range(6):
    W_recon += coeff_recon[i] * Z[i]
W_recon[~mask] = np.nan

# 去除平移项后比较
W_true_centered = W_true - np.nanmean(W_true)
W_recon_centered = W_recon - np.nanmean(W_recon)
residual = W_true_centered - W_recon_centered
rmse = np.sqrt(np.nanmean(residual**2))
pv_true = np.nanmax(W_true_centered) - np.nanmin(W_true_centered)

print(f"有效子孔径数: {M}")
print(f"真实系数: {true_coeff[3:]}")
print(f"重构系数: {coeff_recon[3:]}")
print(f"波前RMSE: {rmse*1e6:.3f} μm")
print(f"波前PV值: {pv_true*1e6:.3f} μm")
print(f"RMSE/PV: {rmse/pv_true*100:.2f}%")

落地与验证

运行方式:安装NumPy后直接运行脚本。代码自包含全部参数与数据生成逻辑。

验证方法:观察输出中重构系数与真实系数是否接近(离焦0.8μm、像散0.5μm),RMSE/PV比值应小于5%。若系数偏差大,检查子孔径采样密度N是否足够(可调至64验证收敛性)。

预期输出示例:有效子孔径数约700~800,重构离焦系数约0.78~0.82μm,像散系数约0.48~0.52μm,RMSE/PV约2%~4%。实际数值取决于子孔径布局与差分精度,但应在上述范围内。

SHWFS数据模拟与重构流程 畸变波前 W(x,y) 离焦 + 像散 + 倾斜 微透镜阵列 (N×N) 每个子孔径聚焦光斑 Δx = f·(∂W/∂x)/k Δy = f·(∂W/∂y)/k 质心偏移 ∝ 局部波前斜率 Zernike模式法重构 A·c = b → c = A⁺·b 斜率矩阵A | 测量斜率b | 系数c 验证对比 重构波前 vs 真实波前 RMSE / PV < 5% 判定通过

关键实现说明

  • 数据模拟:在200×200网格上生成连续波前,通过数值梯度模拟子孔径斜率采样,避免解析求导的复杂性,更接近实际传感器工作方式。
  • 模式法重构:构建各Zernike模式在子孔径处的斜率矩阵A,用最小二乘求解系数。矩阵A的列对应模式,行对应x/y斜率测量值。
  • 验证闭环:重构系数与真实系数直接对比,同时计算波前RMSE/PV比值作为整体精度指标,两者共同判定重构质量。

判定标准:重构系数与真实系数偏差小于10%,且RMSE/PV比值小于5%时,认为重构算法实现正确。若偏差大,优先检查子孔径采样密度与数值差分步长是否匹配。