2. Zernike多项式拟合自由曲面

任务定义

本案例给定一个自由曲面(非球面基底叠加正弦波纹扰动),目标为:使用标准Zernike圆域多项式对该面形进行最小二乘拟合,得到展开系数,并利用系数重构面形,最终通过残差RMS与PV值评估拟合精度。输入为离散采样网格上的矢高数据,输出为Zernike系数、重构面形及精度指标。

实现方案

采用极坐标下的标准Zernike多项式(Noll序号,前36项,含活塞、倾斜、离焦及高阶像差)。将直角坐标采样点映射至单位圆内,构建设计矩阵,通过最小二乘求解系数。重构面形由系数线性组合得到,残差为原始矢高与重构值之差。精度指标取残差面形的RMS(均方根)与PV(峰谷值)。

Zernike拟合自由曲面流程与数据通路 输入面形数据 网格坐标 (x,y) 矢高 z(x,y) 归一化与映射 缩放至单位圆 计算极坐标 (ρ,θ) 最小二乘拟合 构建Zernike设计矩阵 A 求解 (AᵀA)c = Aᵀz 得到系数向量 c 重构与精度评估 重构面形 ẑ = A·c 残差 = z − ẑ 计算 RMS 与 PV 输出结果 Zernike系数 (36项) 残差RMS / PV值 验证路径 1. 生成已知系数的测试面形 → 拟合 → 对比系数误差 < 1e-8 2. 对含波纹自由曲面 → 拟合 → 残差RMS应远小于面形RMS(通常 < 1%) 3. 观察残差分布是否呈高频波纹残留(截断误差特征)

代码实现

import numpy as np
from numpy.polynomial.legendre import leggauss

def zernike_poly(n, m, rho, theta):
    """标准Zernike多项式(Noll序号对应n,m)"""
    if m > 0:
        R = np.zeros_like(rho)
        for s in range((n - m)//2 + 1):
            coeff = (-1)**s * np.math.factorial(n - s)
            coeff /= (np.math.factorial(s) * 
                      np.math.factorial((n + m)//2 - s) * 
                      np.math.factorial((n - m)//2 - s))
            R += coeff * rho**(n - 2*s)
        return R * np.cos(m * theta)
    elif m < 0:
        m_abs = abs(m)
        R = np.zeros_like(rho)
        for s in range((n - m_abs)//2 + 1):
            coeff = (-1)**s * np.math.factorial(n - s)
            coeff /= (np.math.factorial(s) * 
                      np.math.factorial((n + m_abs)//2 - s) * 
                      np.math.factorial((n - m_abs)//2 - s))
            R += coeff * rho**(n - 2*s)
        return R * np.sin(m_abs * theta)
    else:
        R = np.zeros_like(rho)
        for s in range(n//2 + 1):
            coeff = (-1)**s * np.math.factorial(n - s)
            coeff /= (np.math.factorial(s) * np.math.factorial(n//2 - s)**2)
            R += coeff * rho**(n - 2*s)
        return R

def noll_to_nm(j):
    """Noll序号转(n,m)"""
    n = 0
    while (n + 1)*(n + 2)//2 < j:
        n += 1
    m = 0
    if (j - n*(n+1)//2) % 2 == 0:
        m = 2 * ((j - n*(n+1)//2) // 2)
    else:
        m = 2 * ((j - n*(n+1)//2) // 2) + 1
    if m > n:
        m = n - (m - n)
    # 确定符号
    if m == 0:
        return n, 0
    elif (j - n*(n+1)//2) % 2 == 0:
        return n, m
    else:
        return n, -m

def fit_zernike(x, y, z, max_order=36):
    """最小二乘拟合Zernike系数"""
    # 归一化到单位圆
    r_max = np.sqrt(x**2 + y**2).max()
    x_n = x / r_max
    y_n = y / r_max
    rho = np.sqrt(x_n**2 + y_n**2)
    theta = np.arctan2(y_n, x_n)
    
    # 构建设计矩阵
    A = np.zeros((len(z), max_order))
    for j in range(1, max_order + 1):
        n, m = noll_to_nm(j)
        A[:, j-1] = zernike_poly(n, m, rho, theta)
    
    # 最小二乘求解
    coeffs, _, _, _ = np.linalg.lstsq(A, z, rcond=None)
    z_fit = A @ coeffs
    residual = z - z_fit
    
    rms = np.sqrt(np.mean(residual**2))
    pv = residual.max() - residual.min()
    return coeffs, z_fit, rms, pv

# ============ 测试与验证 ============
if __name__ == "__main__":
    # 生成测试面形:已知系数组合 + 高频波纹
    N = 200
    x = np.linspace(-1, 1, N)
    X, Y = np.meshgrid(x, x)
    mask = X**2 + Y**2 <= 1.0
    Xm, Ym = X[mask], Y[mask]
    
    # 真实面形:离焦(0.5) + 像散(0.3) + 彗差(0.2) + 波纹扰动
    rho = np.sqrt(Xm**2 + Ym**2)
    theta = np.arctan2(Ym, Xm)
    z_true = (0.5 * zernike_poly(2, 0, rho, theta) +
              0.3 * zernike_poly(2, 2, rho, theta) +
              0.2 * zernike_poly(3, 1, rho, theta) +
              0.05 * np.sin(20 * rho) * np.cos(15 * theta))
    
    # 拟合
    coeffs, z_fit, rms, pv = fit_zernike(Xm, Ym, z_true, max_order=36)
    
    # 输出结果
    print("=== 拟合精度 ===")
    print(f"残差RMS: {rms:.6f} mm")
    print(f"残差PV:  {pv:.6f} mm")
    print(f"面形RMS: {np.sqrt(np.mean(z_true**2)):.6f} mm")
    print(f"RMS比:   {rms/np.sqrt(np.mean(z_true**2))*100:.3f}%")
    
    print("\n=== 前10项Zernike系数 ===")
    names = ['活塞', 'X倾斜', 'Y倾斜', '离焦', '斜像散', '垂直像散',
             'X彗差', 'Y彗差', '三叶草', '三叶草']
    for i in range(10):
        print(f"Z{i+1:2d} ({names[i]:4s}): {coeffs[i]:+.6f}")

运行与验证

依赖:Python 3.8+,NumPy(无其他第三方库)。

运行方式:将代码保存为 zernike_fit.py,在终端执行 python zernike_fit.py。

预期输出与判定标准:

  • 程序打印残差RMS、PV及面形RMS。由于波纹频率(20周期/半径)超出36项Zernike的拟合能力,残差RMS应约为面形RMS的1%~5%(即RMS比在1~5%之间),PV约为0.1~0.3 mm量级——这验证了截断误差的存在。
  • 前几项系数中,Z4(离焦)应接近0.5,Z5/Z6(像散)应接近0.3,Z7/Z8(彗差)应接近0.2,偏差小于1e-6,证明拟合算法正确恢复了低频成分。
  • 若将波纹幅度设为0(即纯Zernike面形),残差RMS应接近机器精度(<1e-10),此时可验证算法本身的数值精度。

判定通过条件:低频系数恢复误差<1e-5,且含波纹时残差RMS比在1%~5%区间内,即证明拟合与精度分析流程有效。