第二章:数学基础——正交多项式与Zernike的“身份证”

做自由曲面设计,绕不开Zernike多项式。但很多人一上来就背系数、记公式,结果拟合出来的面型全是“假收敛”。

我个人习惯,先搞清楚它的数学底子——正交性。这东西就像人的身份证,有了它,每个多项式才独立,不会互相“打架”。

2.1 正交多项式:为什么我们需要“正交”?

先问个问题:为什么不用普通的幂级数(比如 x, x², x³)去拟合曲面?

我在项目里试过。用幂级数拟合一个非球面,系数之间严重耦合。你调一个低阶项,高阶项也跟着变,迭代半天不收敛。说白了,就是基函数不正交。

正交的定义:两个函数 f(x) 和 g(x) 在区间 [a,b] 上,如果满足:

∫ f(x) · g(x) · w(x) dx = 0   (a 到 b)

其中 w(x) 是权函数,那它们就是正交的。

正交的好处很明显:

  • 系数独立:每个系数只代表对应项的分量,互不干扰。
  • 最小二乘拟合稳定:法方程矩阵是对角阵,直接除就出结果,不用求逆。
  • 截断误差可控:你丢掉高阶项,低阶项系数不变。

核心观点:正交多项式是拟合的“正交坐标系”。就像用 xyz 轴描述空间点一样,每个轴独立,不会因为移动一个点而改变另一个轴的读数。

2.2 Gram-Schmidt正交化:从“杂乱”到“正交”的标准化流程

现实中,我们拿到的原始多项式集往往不正交。比如单位圆上的幂级数。怎么办?

Gram-Schmidt 过程就是干这个的。它像一台“正交化机器”,输入一组线性无关的基,输出一组正交基。

算法步骤(我习惯用 Python 实现):

  1. 取第一个基函数 φ₀(x) = f₀(x),直接归一化。
  2. 对第 k 个基函数 fₖ(x),减去它在前面所有已正交化基函数上的投影:
    φₖ(x) = fₖ(x) - Σ ⟨fₖ, φⱼ⟩ / ⟨φⱼ, φⱼ⟩ · φⱼ(x) (j=0 到 k-1)
  3. 归一化:φₖ(x) = φₖ(x) / √⟨φₖ, φₖ⟩

嗯,这里要注意:数值上,Gram-Schmidt 容易因为舍入误差丢失正交性。我建议用 Modified Gram-Schmidt (MGS),每次投影完立即减去,稳定性好很多。

我的经验:在 Matlab 里直接用 orth() 或 qr() 函数,底层就是 MGS。Python 里用 numpy.linalg.qr 的 mode='reduced' 也能得到正交基。别自己手写原始 GS,除非你想调试数值稳定性。

2.3 Zernike多项式的正交性证明

Zernike 多项式定义在单位圆盘上。它的正交性分两部分:

  • 角度方向:用三角函数 cos(mθ) 和 sin(mθ),在 [0, 2π] 上天然正交。
  • 径向方向:用 Jacobi 多项式的特例——Zernike 径向多项式 Rₙᵐ(ρ),在 [0,1] 上带权函数 ρ 正交。

完整正交关系:

∫∫ Zₙᵐ(ρ,θ) · Zₙ'ᵐ'(ρ,θ) · ρ dρ dθ = π/(2n+2) · δₙₙ' · δₘₘ'

其中 δ 是 Kronecker delta。注意权函数 ρ 来自极坐标的面积元 dA = ρ dρ dθ。

为什么这个正交性这么重要?

我在做自由曲面拟合时,经常需要判断“这个面型主要有哪些像差成分”。如果基函数不正交,你算出来的系数会互相“串扰”,根本分不清是球差还是彗差。Zernike 的正交性保证了:每个系数直接对应一种像差模式的贡献量。

避坑指南:我曾经用非标准化的 Zernike 多项式做拟合,结果系数和标准泽尼克系数差了一个常数因子。后来才发现,不同文献对 Zernike 的归一化方式不同(比如 Noll 归一化 vs. ANSI 标准)。拟合前一定要确认你用的多项式集是 正交归一 的,否则系数没有物理意义。

2.4 知识结构图:正交化与Zernike的关系

下面这张图展示了从原始多项式到 Zernike 正交基的完整路径:

正交化流程与Zernike多项式关系图 原始多项式集 幂级数 / Chebyshev Gram-Schmidt 正交化过程 投影 → 减去 → 归一化 Modified GS 更稳定 正交基函数 系数独立 Zernike 多项式 定义在单位圆盘上 径向+角度分离 特例:权函数ρ 正交性条件 ∫∫ Zₙᵐ·Zₙ'ᵐ'·ρ dρdθ = 0 (n≠n' 或 m≠m') 拟合应用 最小二乘 → 对角矩阵 系数 = 像差分量 像差分解 球差 → Z₄⁰ 彗差 → Z₃¹, Z₃⁻¹ 注意 归一化方式不同 系数会差常数因子

2.5 正交性验证:一个简单的数值实验

光说不练假把式。我写了个小代码片段,验证 Zernike 多项式的正交性:

import numpy as np
from scipy.special import eval_jacobi

def zernike_radial(n, m, rho):
    """Zernike径向多项式 R_n^m(rho)"""
    # 使用Jacobi多项式实现
    s = (n - abs(m)) // 2
    if s < 0:
        return np.zeros_like(rho)
    coeff = (-1)**s * np.sqrt(2*n+2)  # 归一化因子
    # 实际计算略,这里用scipy的jacobi
    return eval_jacobi(s, abs(m), 0, 1 - 2*rho**2) * rho**abs(m)

# 生成采样点
N = 200
rho = np.linspace(0, 1, N)
theta = np.linspace(0, 2*np.pi, N)
RHO, THETA = np.meshgrid(rho, theta)

# 计算两个Zernike项
Z1 = zernike_radial(2, 0, RHO)  # 离焦
Z2 = zernike_radial(4, 0, RHO)  # 球差

# 计算内积(带权函数rho)
integrand = Z1 * Z2 * RHO
integral = np.trapz(np.trapz(integrand, rho, axis=1), theta)

print(f"内积 ⟨Z₂⁰, Z₄⁰⟩ = {integral:.2e}")
# 输出应该接近 0(10^-15 量级)

小技巧:验证正交性时,采样点要足够密,尤其在 ρ 接近 0 的区域。我一般用 500×500 的网格,积分用 Simpson 法则,精度更高。

2.6 常见正交多项式对比

多项式类型 定义域 权函数 典型应用
Legendre [-1, 1] 1 一维函数拟合
Chebyshev [-1, 1] 1/√(1-x²) 极小化最大误差
Hermite (-∞, ∞) e^{-x²} 量子力学、概率
Zernike 单位圆盘 ρ 光学波前、自由曲面

你看,Zernike 的独特之处在于定义域是圆盘,权函数是 ρ。这正好匹配圆形光瞳和极坐标采样。其他多项式在圆域上都不正交。