简介面向光学波前探测与重构场景的MATLAB算法资源包针对哈特曼波前传感器采集的斜率数据提供高阶有限差分与迭代最小二乘积分相结合的波前重构实现。该算法可修正大气湍流、光学元件面形误差等引起的波前畸变适用于自适应光学、激光通信及光学检测等高精度领域。压缩包共3个文件包括2个MAT格式的梯度数据文件和1个M格式主程序整体大小3.84MB可直接运行复现完整重构流程。已有647人学习下载适合从事波前传感研究、光学仿真或相关课程设计的学生与工程师使用。通过该资源可掌握从读取斜率数据、有限差分曲率估计到迭代优化波前形状的完整建模思路并基于提供的实测/仿真数据快速验证算法效果同时代码便于修改扩展可辅助理解最小二乘积分在光学重构中的收敛过程。1. 基于有限差分的高阶迭代最小二乘波前重构斜率数据到相位图的直通管道从Shack-Hartmann波前传感器里拿到的从来不是相位图而是子孔径上的斜率阵列。想把斜率重新积成相位传统做法是拟合Zernike模态但湍流强、高频成分多的时候模态截断会让重构结果发虚。基于有限差分的高阶迭代最小二乘波前重构算法走的是另一条路把斜率与相位的关系直接写成差分方程组用迭代最小二乘去求解相当于在网格上做一次带正则化的数值积分。这套思路适合做自适应光学、激光传输和干涉检测的人尤其是需要从斜率数据快速恢复波前、又不想被模态束缚的场景。新手能从零搭起一套可复现的Python流程熟手也能在这里找到自己踩过的坑。2. 斜率数据怎么变成相位图有限差分把积分问题写成可解的线性方程2.1 重构问题的数学形式测量方程与未知量波前相位φ在N×N网格上采样之后x方向相邻两点之间的斜率近似等于相位差除以间距y方向同理。把所有这些差分方程堆起来就得到一个线性系统s Aφ n其中s是传感器读出的斜率向量φ是我们想恢复的相位列向量n是测量噪声A是由有限差分模板生成的稀疏矩阵。波前重构的本质就是从斜率测量值求解这个线性系统也就是把离散的梯度场重新积回相位场这就是标题里“积分”这个词的数学含义。这里存在两个反直觉点。第一A的方程个数通常是未知数个数的近两倍因为二维网格上每个点同时提供x和y两个方向的斜率信息系统是超定的。第二A必然有一个零空间任意常数加到φ上差分结果完全不变所以活塞项先天不可测。这两个点决定了后面所有算法选择的走向也是为什么不能简单地用一次矩阵求逆收工。实际工程里斜率数据不可能干净。坏子孔径、暗电流、标定残差都会叠进s因此重构算法必须在“拟合测量值”和“抑制噪声放大”之间做权衡。有限差分把问题离散成一个标准的线性最小二乘问题之后这种权衡就变成可控的正则化参数选择而不是靠调整模态数这类比较间接的手段。2.2 三种差分几何模型Hudgin、Fried与Southwell的选择有限差分模型里常听到的三种名字是Hudgin、Fried和Southwell区别在于斜率定义的位置和相位网格的对齐方式。它们对应的差分模板不同系统矩阵的稀疏结构和边界处理方式也完全不同。模型斜率定义位置相位定义位置一句话特点Hudgin相邻两相位点连线的中点网格格点上最贴近Shack-Hartmann子孔径边缘采样的物理过程Fried相邻两子孔径中心之间子孔径中心与大气相干长度、角度测量搭配自然Southwell相位点本身同一套网格节点方程对称性好实现直观输出相位图不用二次插值我实际项目里默认从Southwell起步因为斜率向量和相位向量天然对齐重构出来的相位图直接就是传感器网格上的面型分布省去一次空间配准。更重要的是Southwell模型的系统矩阵在二维网格上有非常整齐的稀疏结构适合用稀疏矩阵或算子方式做高速迭代。Hudgin模型在物理上更贴近Shack-Hartmann的真实采样过程如果你的标定数据来自子孔径边缘的差分测量换到Hudgin往往能再压掉一点残差。Fried模型则多见于大气参数测量和斜率互相关计算的场合日常波前重构用得相对少。提示换差分模型不只是换一组系数边界行的构造方式和噪声的传递特性都会变。决定用哪个模型之前先用已知相位做一组仿真对比别直接靠感觉。2.3 显式矩阵与算子式两种路径构建系统矩阵A有两条路。第一种是显式构造稀疏矩阵适合网格规模在256×256以内、需要快速调试算法逻辑的场景。第二种是算子式构造把A和A的转置写成两个函数不实际存储矩阵适合512以上网格或内存受限的实时系统。用Kronecker积构造Southwell模型的差分矩阵很紧凑代码量和出错概率都比逐行填要小得多import numpy as np from scipy.sparse import kron, eye, diags, vstack N 64 # 网格边长相位未知数为 N*N d 2.0 / (N - 1) # 网格间距模拟口径归一化到半径 1 # 一维差分矩阵每行只有 [-1, 1]作用在相邻两个网格点之间 D1 diags([-1.0, 1.0], [0, 1], shape(N - 1, N)) / d # x 方向斜率 沿行方向差分y 方向斜率 沿列方向差分 Dx kron(eye(N), D1).tocsr() Dy kron(D1, eye(N)).tocsr() # 测量矩阵 Ax 斜率方程在上y 斜率方程在下 A vstack([Dx, Dy]).tocsr() # 测试一次矩阵向量乘s 长度为 2*N*(N-1) phi_test np.random.randn(N * N) slope_test A phi_test print(slope_test.shape)这段代码里D1是长度为N-1的一维差分算子Kronecker积把它沿着二维网格的行方向或列方向复制。kron(eye(N), D1)得到x方向差分等价于对每一行独立做一维差分kron(D1, eye(N))得到y方向差分等价于对每一列独立做一维差分。vstack把两组方程拼成完整的测量矩阵方程总数为2N(N-1)未知量为N²。参数说明d的取值直接影响A的数值尺度如果d设得不对后面迭代步长和正则化系数的经验值全部要跟着变。口径归一化到半径1后64网格的d约为0.03175如果你用像素间距做单位那么d1对应的谱半径和步长经验值要重新标定。另一个细节是D1用diags构造时偏移量[0,1]表示主对角线和右上一对角线shape(N-1,N)保证矩阵是瘦长的不会在网格边界多出未知量。显式矩阵的好处是能直接调用scipy.sparse.linalg里的现成求解器坏处是迭代过程中A r这种运算会频繁发生稀疏矩阵的索引开销在某些机器上会变成瓶颈。网格到512时我也不太想存那个接近52万行的矩阵虽然它占的内存其实不大但每一次向量乘都要走CSR索引批量仿真时很拖速度。这时可以改写成算子形式A的作用等价于对二维相位数组分别做行差分和列差分然后拼接A的转置等价于把两组斜率分别做“反向差分”再相加本质上就是梯度算子和散度算子。这个思路在第五章的避坑部分会给出一个可用的LinearOperator实现它也是把网格推到1024以上还能保持实时性的关键。3. 迭代最小二乘为什么要“高阶”病态、正则化与收敛性3.1 直接求逆为什么容易翻车如果能直接求伪逆从斜率到相位就是一发矩阵乘的事。但这个方案在真实数据上很容易翻车根源在于A^T A的条件数随网格规模急剧膨胀。以Southwell模型为例A^T A对应离散Laplacian算子的某种变体它的特征值从低频到高频跨度通常达到10^5甚至10^6量级。低频特征值对应离焦、像散这类大尺度像差数值大恢复容易高频特征值对应微细结构数值很小任何一个微小的斜率噪声除以小特征值之后都会被放大成剧烈的相位抖动。这还不算完。A本身有零空间至少包含常数相位如果不加任何约束直接求伪逆求解器会把这一自由度分配给任意数值结果就是重构相位整体带上一个随机偏置。更复杂的情况是斜率的测量误差在不同子孔径上并非完全独立标定残差会形成空间相关结构让噪声在某些方向上格外突出。直接法把这些误差一视同仁地纳入求解没有任何手段去压制。迭代法在这类问题上的优势不是“算得快”而是天然具备正则化能力。迭代过程可以从平坦的初始相位开始先恢复低频大尺度像差再逐步补充高频细节。如果适可而止比如在残差下降到噪声水平附近就停住高频噪声还没来得及放大这就是所谓的“提前停止”隐式正则化。而显式加上Tikhonov正则化项之后迭代法还能进一步控制高频增益这是直接求伪逆做不到的。3.2 Landweber迭代与提前停止Landweber迭代是最简单的一阶迭代法格式非常直白x_new x ω·Aᵀ(s − A x)每步先算当前残差r s − A x然后把残差通过Aᵀ映射回相位空间乘以步长ω叠加到当前解上。从优化视角看这就是用最速下降法求解min‖Aφ−s‖²。Aᵀ在这里的作用等价于把斜率残差“积分”回相位所以整个迭代过程可以理解成反复做“求斜率残差—积分修正—再求残差”的闭环。步长ω直接决定算法稳不稳定。理论上ω必须小于2/λ_max其中λ_max是AᵀA的最大特征值超过这个值残差会发散。实际操作中我不会手算谱半径而是用幂法快速估计一下def power_iteration(A, num_iter30): 估算 A.T A 的最大特征值用于确定 Landweber 步长 v np.random.randn(A.shape[1]) v v / np.linalg.norm(v) for _ in range(num_iter): v A.T (A v) norm_v np.linalg.norm(v) if norm_v 1e-12: break v v / norm_v # 最大特征值近似为 ||A v|| / ||v|| lam np.linalg.norm(A v) return lam ** 2 lam_max power_iteration(A) omega 1.8 / lam_max # 加一点安全系数但别太保守幂法的原理很简单反复用AᵀA作用于一个随机向量向量会逐渐偏向最大特征值对应的特征方向向量范数的增长速率就是特征值的近似。参数选择上迭代次数30次足以得到一个量级正确的估计安全系数取1.8而不是2.0是为了留出数值误差空间。如果ω取得过于保守比如只有理论值的三分之一Landweber不会发散但收敛速度会肉眼可见地变慢这是新手最容易忽视的“不报错但性能差”情况。Landweber的最大缺点是线性收敛等残差降到噪声水平需要几百甚至上千次迭代。但它的实现极其简单每一步只涉及一次A乘和一次Aᵀ乘非常容易移植到GPU或者DSP上。而且在实时闭环系统里上一帧的重构结果可以作为下一帧的初始值迭代次数可以压得很低这种“热启动”特性是直接法不具备的。3.3 高阶迭代CGLS与LSQR如果只是在做离线分析和仿真Landweber的收敛速度有点让人着急。共轭梯度法应用于最小二乘问题也就是通常说的CGLS是收敛效率高一个数量级的选择。它不再沿最速下降方向走而是维护一组在AᵀA意义下共轭的搜索方向每一步的方向都经过修正避免了一阶方法那种反复震荡的走法。下面是一个可以直接复制使用的CGLS实现def cgls(A, b, x0None, maxiter50, tol1e-8): 带提前停止的 CGLS 迭代求解最小二乘 min ||A x - b||^2 n A.shape[1] x np.zeros(n) if x0 is None else x0.copy() r b - A x # 原始残差 s A.T r # 残差投影到相位空间 p s.copy() # 搜索方向初始化为 s gamma s s residual_norms [] for _ in range(maxiter): q A p # 搜索方向经过 A 映射 alpha gamma / (q q) x x alpha * p r r - alpha * q s_new A.T r gamma_new s_new s_new # 提前停止残差投影范数降到阈值以下就收工 if np.sqrt(gamma_new) tol * np.sqrt(max(gamma, 1e-12)): break beta gamma_new / gamma p s_new beta * p s s_new gamma gamma_new residual_norms.append(np.sqrt(gamma_new)) return x, residual_norms这段里alpha是沿当前方向的最优步长通过对一维二次函数求极值得到。beta是Polak-Ribière形式的共轭修正系数保证新方向与之前的方向在AᵀA度量下共轭。提前停止条件用的是Aᵀr的范数而不是r的范数因为最小二乘问题的最优性条件就是Aᵀr等于零用这个判据更直接。x0可以传入上一帧的重构结果在多帧连续测量的场景下两三步迭代就能逼近收敛。如果不想自己维护迭代细节scipy.sparse.linalg.lsqr是LSQR算法的官方实现比手写CGLS更稳。它基于Lanczos双对角化过程对病态问题有更好的数值行为还支持damp参数直接做Tikhonov正则化from scipy.sparse.linalg import lsqr x_lsqr lsqr(A, b, damp1e-3, atol1e-6, btol1e-6, iter_lim200) phi_lsqr x_lsqr[0].reshape(N, N)damp参数的含义是给问题加上一个二范数惩罚项相当于min ‖Aφ−s‖² damp²·‖φ‖²。damp值越大解越平滑取值在1e-4到1e-2之间通常是比较合理的起点具体要看A的归一化方式。iter_lim是迭代上限LSQR本身会自动在某个精度停机设置这个上限只是防止极端病态情况下空转。3.4 “高阶”到底指高阶迭代还是高阶差分这个标题里的“高阶迭代”我理解的主要是迭代算法层面的高阶从一阶Landweber升级到CGLS/LSQR这一类共轭方向法。不过也有另一种解释把一阶差分模板换成四阶五点模板让差分本身更精确地逼近真实梯度。两种做法并不冲突甚至常常配合使用。高阶差分模板的优势在于减少差分近似误差在斜率测量本身很干净的情况下重构精度会明显提升。代价是A矩阵每行从2个非零元变成4个或更多稀疏结构变复杂AᵀA的条件数进一步恶化所以必须搭配更稳健的高阶迭代求解器。我的建议是先用一阶差分加LSQR跑通全流程确认误差瓶颈确实来自差分近似精度之后再升级差分模板不要一上来就追求高阶。4. 从零跑通重构流程仿真斜率生成、迭代求解与误差评估4.1 造一个带噪的测量场调试重构算法最怕直接上真实数据因为你分不清是算法错了还是标定错了。先造一个已知相位按Southwell模型采样成斜率加上可控噪声这样每一步都有对照。import numpy as np N 64 x np.linspace(-1.0, 1.0, N) X, Y np.meshgrid(x, x) # 演示相位离焦 像散 高阶球差RMS 归一化到约 1 rad phi_true (0.8 * (2*X**2 2*Y**2 - 1) 0.3 * (X**2 - Y**2) 0.1 * (3*(X**2 Y**2)**2 - 6*(X**2 Y**2) 1)) phi_true - phi_true.mean() phi_true phi_true / np.sqrt(np.mean(phi_true**2)) # 按 Southwell 模型差分得到斜率 d 2.0 / (N - 1) sx np.diff(phi_true, axis1) / d # 形状 (N, N-1)沿行方向 sy np.diff(phi_true, axis0) / d # 形状 (N-1, N)沿列方向 # 展平成测量向量并加高斯噪声 s_clean np.concatenate([sx.ravel(), sy.ravel()]) rng np.random.default_rng(42) sigma 0.02 # 斜率噪声标准差单位与相位差/间距一致 s_noisy s_clean rng.normal(0.0, sigma, sizes_clean.shape)这里用多项式叠加生成相位胜在简单直接。RMS归一化到1 rad是刻意为之方便后面评估误差时直接按比例换算。噪声sigma取0.02意味着信噪比约50倍在仿真里属于中等偏好一点的条件能同时看出算法性能和噪声放大现象。如果sigma取到0.1重构结果会明显变糙适合压力测试调试阶段从0.02起步比较合适。需要注意np.diff的axis选择这和第二章矩阵构造必须严格对应。sx沿axis1差分对应A矩阵中Dx块的行为sy沿axis0差分对应Dy块。如果这里写反了后面重构出来会得到一个镜像翻转的面型这也是第五章要展开的头号坑。4.2 构建差分矩阵并快速验证直接复用第二章的矩阵构造代码封装成函数可以让实验流程干净很多from scipy.sparse import kron, eye, diags, vstack def build_southwell_matrix(N, d): 构造 Southwell 模型的测量矩阵 A 和二阶差分正则化矩阵 L D1 diags([-1.0, 1.0], [0, 1], shape(N - 1, N)) / d Dx kron(eye(N), D1).tocsr() Dy kron(D1, eye(N)).tocsr() A vstack([Dx, Dy]).tocsr() # 二阶差分算子用于 Tikhonov 正则化 D2 diags([1.0, -2.0, 1.0], [-1, 0, 1], shape(N - 2, N)) / d**2 L vstack([kron(eye(N), D2), kron(D2, eye(N))]).tocsr() return A, L返回的L是二阶差分算子加到正则化项里能惩罚面型的局部曲率避免重构结果出现不必要的锯齿。用N16先把流程跑通是个好习惯矩阵小了迭代很快报错也容易定位。确认无误后再切到64或128。4.3 Landweber与CGLS求解对比有了A和带噪斜率先跑一版最朴素的Landweber看看收敛轨迹长什么样def landweber(A, b, x0, omega, maxiter2000): Landweber 迭代返回重构相位和残差历史 x x0.copy() r b - A x res [] for _ in range(maxiter): x x omega * (A.T r) r b - A x res.append(np.linalg.norm(r)) if res[-1] 1e-6 * np.linalg.norm(b): break return x, res参数上x0初始化为全零就可以。omega用第三章的幂法估计值然后乘以1.8的安全系数。2000次迭代上限对64网格通常够用但如果你发现到达提前停止条件时已经接近上限就说明omega偏小或者网格太大。再跑CGLS做对比# 用前面实现的 cgls 函数 phi_cgls, res_cgls cgls(A, s_noisy, maxiter200, tol1e-8)我跑这个配置时Landweber大约需要一千多次迭代才能把残差压到噪声水平CGLS通常三四十次就收敛到类似精度。差异的原因在于Landweber每步只利用残差的一阶信息而CGLS通过共轭方向不断消除低阶模态的影响搜索空间的维度增长快得多。如果要把Tikhonov正则化直接加进来可以构造增广系统lam 1e-3 A_aug vstack([A, np.sqrt(lam) * L]).tocsr() b_aug np.concatenate([s_noisy, np.zeros(L.shape[0])]) phi_reg, _ cgls(A_aug, b_aug, maxiter200, tol1e-8)增广的做法相当于把正则化项当作额外的“虚拟测量方程”要求二阶差分结果尽量接近零。lambda越大约束越强。常见做法是用L曲线找拐点但工程上我一般先在1e-4到1e-2之间扫几档观察重构残差和边界形态选一个折中值。4.4 评估重构质量不要只看全场RMS重构做完怎么判断好坏最直接的做法是计算重构相位与真实相位的RMS差但要注意两个坑。第一活塞项先天不可测直接算RMS会把一个系统性偏置算进去第二整体倾斜主要由斜率噪声主导和算法本身关系不大。所以评估时要先做平面拟合把活塞和倾斜去掉再看残差phi_rec phi_cgls.reshape(N, N) # 用最小二乘拟合残差中的活塞、x 倾斜、y 倾斜 A_plane np.stack([np.ones_like(X), X, Y], axis-1).reshape(-1, 3) coef, _, _, _ np.linalg.lstsq(A_plane, (phi_true - phi_rec).ravel(), rcondNone) residual (phi_true - phi_rec).reshape(N, N) - (coef[0] coef[1] * X coef[2] * Y) rms_res np.sqrt(np.mean(residual**2)) strehl_approx np.exp(-rms_res**2) # Marechal 近似适用于小残差 print(f去倾斜残差 RMS: {rms_res:.4f} rad) print(fStrehl 近似: {strehl_approx:.4f})这里np.exp(-rms²)是Marechal近似适用于RMS小于约1 rad的情况给出的是系统Strehl比的一个上限估计。残差RMS在0.1 rad量级时Strehl约99%属于很理想的情况RMS到0.3 rad时Strehl掉到91%左右已经能感到成像质量变化。这个评估流程也适用于没有真实相位的场景真机上可以把干涉仪测量当作基准用同样的流程对比。我的习惯是每次改变算法参数后都同时记录残差RMS、去除倾斜后的残差RMS和对应的Strehl估计三组数字只看其一容易误判。5. 波前重构容易翻车的五个地方现象、原因与排查5.1 斜率方向反了导致重构相位镜像翻转现象仿真里原始相位是正离焦重构出来变成负离焦或者面型呈镜像对称地翻转怎么看都不对。原因传感器的x/y轴定义和算法里np.diff的axis约定不一致。最常见的翻车点是把沿行的差分当作x方向但传感器硬件输出的行方向恰好对应的是y轴。解决不要急着调参数先做一个方向标定实验。把真实相位设成phi_true X也就是一个沿x方向的线性斜坡生成对应斜率并重构。如果输出相位沿x方向增长且符号正确那坐标系就没问题。如果是沿y方向增长检查A矩阵中Dx和Dy块的拼接顺序。这个测试一分钟就能跑完能省掉后面至少半天的排查时间。5.2 边界出现一圈“围墙”现象重构相位内部形态合理但四周边缘隆起或塌陷形成像围墙一样的环状伪迹。原因边界上的网格点缺少一侧的邻居差分方程数和内部点不一致残差在边界累积。更隐蔽的情况是正则化用得不够高频噪声在边界处被放大因为边界处没有足够的约束来平滑这些波动。解决先加Tikhonov正则化把lambda从1e-5往上调观察边界伪迹是否缩小。如果明显回落就说明是噪声放大问题找一个lambda让边界和内部残差平衡即可。如果加了正则化边界还是有系统性偏差那大概率是差分模型在边界上构造错误检查Dx和Dy的行数是否分别恰好是N(N-1)以及边界行是否意外多算或少算了一组方程。5.3 迭代残差不降反升现象Landweber的残差曲线前十几步正常下降之后缓慢回升或者一开始就剧烈震荡直接发散。原因Landweber步长ω超出稳定上限通常是2/λ_max。另一个常见原因是给A做了增广或改了正则化后没有重新估计谱半径还沿用旧步长。CGLS也不保证绝对安全如果A矩阵在迭代中被意外转成稠密或A和b的形状不匹配同样会出现残差异常。解决每次修改测量矩阵后都重新跑一遍power_iteration估算ω。如果项目中经常改网格大小把ω估计逻辑封装成独立函数在每次重构开始时自动调用。CGLS异常时先检查A是否保持稀疏格式然后在相同输入上跑一次scipy的lsqr交叉验证如果lsqr正常而手写CGLS异常那就是实现细节的问题对照参数更新顺序逐一排查。5.4 网格一大内存直接爆掉现象N128时一切正常切到N512后程序OOM或者迭代速度突然慢了十几倍。原因最常见的根因是一不小心触发了A.T A的计算把稀疏矩阵乘成了稠密矩阵。A的行数约52万、列数约26万AᵀA稠密化之后是26万×26万的浮点矩阵内存需求超过500GB必挂。另一个隐蔽原因是矩阵构造阶段用了lil_matrix且忘记转换后续运算被隐式转成低效格式。解决永远不要显式构造AᵀA。用算子式封装A和Aᵀ的行为再传给LinearOperator让scipy求解器直接使用这样迭代过程中A永远不会物化为稠密矩阵。下面这个类可以直接替换前面所有用A的地方from scipy.sparse.linalg import LinearOperator class GradientOperator(LinearOperator): 基于差分算子的 A 与 A^T不存储矩阵本体 def __init__(self, N, d): self.N N self.d d n_slopes 2 * N * (N - 1) super().__init__(dtypenp.float64, shape(n_slopes, N * N)) def _matvec(self, x): phi x.reshape(self.N, self.N) sx np.diff(phi, axis1) / self.d sy np.diff(phi, axis0) / self.d return np.concatenate([sx.ravel(), sy.ravel()]) def _rmatvec(self, y): ny self.N - 1 sx y[:self.N * ny].reshape(self.N, ny) sy y[self.N * ny:].reshape(ny, self.N) # 负散度运算相当于把斜率残差映射回相位空间 px -np.diff(np.pad(sx, ((0, 0), (1, 0))), axis1) / self.d py -np.diff(np.pad(sy, ((1, 0), (0, 0))), axis0) / self.d return (px py).ravel() A_op GradientOperator(N, d)这个实现的内存占用和网格规模基本无关只在每次matvec时生成两个临时斜率数组。rmatvec里用np.pad在边界补零配合np.diff实现转置运算正确性可以用小规模矩阵验证生成随机向量v检查A_op.rmatvec(A_op.matvec(v))的结果是否与显式A的对应结果一致。5.5 仿真全绿、真机全红现象仿真数据上重构误差漂亮得能发论文同一套代码切换真实Shack-Hartmann数据后重构波前剧烈抖动甚至出现斑点状伪迹。原因真实斜率数据里有坏点子孔径、响应非均匀和饱和干扰这些在仿真里都没建模。更隐蔽的是传感器标定矩阵和算法里假设的均匀间距模型并不完全一致个别子孔径的增益偏差会让对应斜率长期偏高或偏低。解决第一步是数据清洗对每条斜率的幅值做统计超过中位数3倍标准差的视为坏点剔除后重构。第二步把清洗变成迭代过程先正常重构一次计算残差把残差大的斜率行权重调低再做加权最小二乘。加权可以在不改变算法框架的前提下实现只需把A的对应行乘以权重、b的对应项也乘以权重然后重新跑CGLS。如果抖动仍然严重最后一步是回头做传感器标定找到增益不一致的子孔径而不是继续在算法层硬扛。6. 让重构更快一步预条件、热启动与实测前的验证套路6.1 预条件让特征值别再两极分化CGLS收敛速度再快也逃不过病态问题拖后腿。AᵀA的特征值分布跨度大迭代前期大量精力花在消除低频模态上。加一个简单的对角预条件就能明显改善。对于Southwell模型AᵀA的对角元近似反映每个网格点被多少条斜率方程覆盖内部点大约是4边界点少一些。预条件矩阵M取这个对角元然后把迭代格式改成x x ω·M⁻¹·Aᵀr。对角预条件的计算代价几乎为零对收敛速度的提升却非常可观尤其是网格大时。6.2 多分辨率热启动另一种实用技巧是先在小网格上重构一个粗略相位作为大网格迭代的初始值。具体做法是把64网格的测量斜率降采样到16网格用LSQR快速得到一个低频面型再通过双线性插值上采样到64网格作为完整问题的x0。这样CGLS往往只需十几步就能收敛因为初始解已经包含了大部分真实面型的低频成分。这个技巧在实时闭环系统里价值很高因为相邻帧之间的波前变化通常很小直接拿上一帧的重构结果做热启动比每次从零开始快得多。6.3 实测前的三件套验证我把这套验证流程当作上真机前的固定动作。第一Monte Carlo仿真固定相位和网格扫描噪声sigma从0.005到0.1每个档位跑30次以上画出重构误差曲线确认误差随噪声线性增长。如果某个档位误差突跳说明正则化强度在该信噪比下失去约束。第二残差白噪声检验重构完成后把残差做二维FFT观察频谱是否平坦。如果低频残留明显说明有像差混叠如果高频有尖峰说明模型或正则化有问题。第三已知面型验证用变形镜产生一个已知形状重构后与干涉仪结果对比误差必须小于系统指标的十分之一。这三步走完再上闭环就基本不会遇到原则性问题。我第一次做这类重构时跳过标定直接把算法丢给真实斜率数据结果方向错误、边界伪迹、坏点干扰混在一起排查了两个星期才意识到坐标系根本没对齐。后来养成的习惯就是先仿真固定算法再用简单斜坡面型标定坐标最后才碰真实数据。这个顺序省下的时间远比预想的多。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?