微透镜阵列仿真与数据处理:Zemax+MATLAB助力光场相机和波前传感器
发布时间:2026/10/2 3:26:48来源:尧图网络
微透镜阵列这东西做光场相机和Shack-Hartmann波前传感器的人迟早都要正面硬刚。它看起来就是一片几十微米口径的小透镜按周期排开放在主镜头像面或者传感器前面但它的口径、焦距、阵列周期、填充因子这些参数直接决定了角度采样能力、波前检测精度和最终的成像质量。我前后在Zemax里折腾过不少微透镜阵列的建模也用MATLAB做过光线数据导出、子图像重排和光斑质心提取今天把这套从仿真到数据处理的完整链路拆开来讲。这篇内容适合正在做光场相机系统设计、波前传感器开发或者毕业论文要搭一套微透镜阵列仿真平台的人。我会尽量把思路讲透——不光是给参数、给步骤更重要的是讲清楚每一步为什么这么做以及哪些地方会踩坑。1. 先把微透镜阵列的功能吃透再谈建模很多新手拿到微透镜阵列就直接开建结果建出来的模型要么追迹报错要么和实物对不上。问题根源在于没弄清楚微透镜阵列在系统里到底扮演什么角色。1.1 微透镜阵列解决的核心问题微透镜阵列本质上是一组按规则周期排列的微型光学元件单个透镜口径通常在几十微米到几百微米之间。它解决的问题只有一个在传感器平面上同时获取光线的空间信息和角度信息。以光场相机为例主镜头把场景成像在一个平面上如果直接放普通传感器只能记录光的强度分布角度信息全部丢失。但在像面位置放一块微透镜阵列主镜头的出瞳会被分割成多个子孔径每个微透镜把对应子孔径的光线投影到后面的传感器上形成一个“宏像素”。宏像素内部的强度分布就包含了光线方向信息这就是Lytro等光场相机的基本原理。波前传感器尤其是Shack-Hartmann型则是把微透镜阵列放在被测波前处每个微透镜作为一个子孔径将局部波前聚焦到传感器上。当波前存在像差时子孔径焦斑会偏移偏移量与局部波前斜率成正比。通过测量所有子孔径光斑的质心位移就能重构整个波前。搞清楚这个功能定位很重要因为Zemax里的建模参数不是随便设的——微透镜的焦距决定了对角度或波前斜率的灵敏度口径和阵列周期决定了空间分辨率填充因子决定了光能利用率这些参数都会直接影响后续仿真结果。1.2 光场相机和波前传感器的关键参数差别虽然都用微透镜阵列但两类系统对参数的要求差别很大。光场相机这边核心匹配关系是主镜头F数等于微透镜F数。为什么因为微透镜阵列放在主镜头像面上主镜头出瞳被微透镜阵列切割后每个微透镜都应对准主镜头光瞳。如果两者F数不匹配会出现两个问题微透镜F数小于主镜头F数时子图像在传感器上溢出像素间串扰微透镜F数大于主镜头F数时子图像直径小于微透镜口径角度采样分辨率浪费。所以在Zemax里建模光场相机第一步就要把主镜头F数和微透镜F数对齐。举个实际例子如果主镜头F数是2.8微透镜口径是100微米那么微透镜焦距应设为280微米左右。波前传感器这边核心是光斑偏移量与波前斜率的关系Δx f·tan(θ)f是微透镜焦距θ是局部波前斜率。为了测量微弱波前畸变需要较长的微透镜焦距让偏移量在传感器上足够大但焦距长了可测的动态范围又会变小。Zemax仿真时需要在这两者之间权衡我一般会先用Zemax做一个单子孔径模型扫参看不同焦距和口径组合下的光斑偏移线性区间。再说一个容易被忽略的参数——填充因子。光场相机希望填充因子接近100%这样空间采样不丢信息所以通常用方形孔径微透镜波前传感器对填充因子要求宽松一些圆形孔径也可以但要注意边缘子孔径的光能利用率下降会导致质心提取精度变差。2. Zemax里微透镜阵列建模思路拆解Zemax建模微透镜阵列首先要回答一个问题用序列模式还是非序列模式这个选择决定了后面所有操作的复杂度。2.1 序列模式与非序列模式的选择逻辑序列模式适合光线有序传播的系统追迹速度快像差分析方便适合单透镜建模和系统优化。但微透镜阵列是成百上千个面型重复排列序列模式下每颗微透镜都要单独定义一组面工作量大而且阵列尺寸一大文件就爆炸。非序列模式支持真正的阵列对象NSC内的Array Object可以一次性生成完整的微透镜阵列支持光线分裂、散射和衍射效应更贴近真实的物理过程。缺点是追迹速度慢像差分析工具不如序列模式顺手。实际项目中我的做法是分情况选择只仿真单个微透镜的成像特性或焦斑分布用序列模式就够了仿真完整的光场相机或波前传感器系统尤其是要分析阵列边缘效应、子图像串扰时直接用非序列模式。2.2 三种主流Zemax建模方案对比目前我在Zemax里用过三种可行的方案各有适用场景。方案一非序列模式的Lenslet Array对象Zemax非序列模式内置了Lenslet Array对象可以直接定义子透镜数量、口径、曲率半径、排列方式方形或六角形等参数。这是最省事的方式适合快速搭建整体系统模型。但是要注意Lenslet Array对象用的是理想透镜近似如果微透镜面型不是标准球面或者需要加上非球面系数内置对象就不够用了。方案二序列模式多重结构模拟子透镜利用多重结构编辑器Multi-Configuration Editor定义多个结构每个结构对应微透镜阵列中的一个子透镜或一组子透镜通过改变每个结构的表面曲率、偏心等参数模拟阵列效果。优点是可以用Zemax强大的分析功能MTF、点列图、波前图缺点也很明显——阵列规模一大结构数量呈线性增长文件体积膨胀而且多重结构之间不可能完全模拟阵列的整体光场分布。方案三序列模式用户自定义面User Defined Surface, UDS通过Zemax用户自定义面接口用DLL或者ZPL宏描述微透镜阵列的面型函数把整个阵列作为一个特殊表面处理。这个方案灵活性最高可以精确描述任意面型包括非球面、自由曲面微透镜追迹速度也比非序列模式快。但需要自己写面型算法开发成本高适合有编程能力的光学工程师。2.3 我的选型建议和参数初始化方法如果是快速验证系统可行性我用方案一先把系统框架跑通确认微透镜数量、口径、焦距和传感器尺寸匹配。等到了要出结果、写论文或者出工程文档的阶段我会切到方案三或者方案二的混合模式。这里讲一下参数初始化逻辑。无论用哪种方案都必须先算清楚几个核心量假设微透镜阵列中单个子透镜口径为D_mla焦距为f_mla阵列周期为P。光场相机中需要满足P D_mla方形孔径满填充且f_mla D_mla × F_main其中F_main是主镜头F数。传感器上每个宏像素的像素数N_pix约等于主镜头光瞳直径与微透镜口径之比这个比值其实就是角度采样数。Zemax建模时我把这些参数做成长度方便在优化函数里约束主镜头出瞳与微透镜阵列的匹配。参数建议用Zemax的操作数来控制。在序列模式下可以通过EFFL有效焦距、OPLT、OPGT等操作数把主镜头F数和微透镜焦距约束到目标值在非序列模式下建议直接手动计算好再输入因为非序列模式下优化操作数不太好直接约束阵列内部参数。3. 光场相机和波前传感器的Zemax仿真侧重点同样的微透镜阵列在光场相机和波前传感器里的仿真流程差别很大。这里分开讲。3.1 光场相机仿真三层结构匹配是核心光场相机的系统结构是“主镜头 微透镜阵列 传感器”三层。Zemax建模时我的思路是第一步建立主镜头模型。主镜头可以是理想薄透镜用于原理验证也可以是实际镜头设计用于定量分析。如果用的是理想透镜设置为理想面型如Paraxial并记录其F数和出瞳位置。第二步在主镜头像面位置放置微透镜阵列。这里有一个关键点微透镜阵列并不是正好放在主镜头的几何像面上而是放在像面前方或后方一个特定的距离这个距离就是微透镜的焦距。也就是说微透镜阵列到传感器的距离等于微透镜焦距f_mla这样才能实现“主镜头出瞳被微透镜阵列成像到传感器上”。第三步设置传感器平面。在非序列模式下传感器表面设置为探测器对象尺寸要能覆盖整个阵列投影面积。我一般会先计算传感器总尺寸传感器总尺寸 微透镜数量 × 微透镜口径 边缘预留比如100×100的阵列口径100微米传感器就得至少10mm×10mm还要加上子图像可能溢出的余量。仿真时最重要的是设置合适的光线数量。光线太少子图像内部强度分布会呈颗粒状光线太多追迹时间又不可接受。我实测下来NSC模式下每条光线至少分到1000条左右才能在100微米级微透镜上得到接近衍射极限的光斑分布。还有一点光场相机仿真后处理很依赖子图像阵列的整齐排列。所以Zemax里的坐标轴选择很关键我习惯把微透镜阵列面设为全局坐标参考面探测器使用局部坐标匹配避免后期数据对不准。3.2 波前传感器仿真光斑偏移和质心的精度Shack-Hartmann波前传感器其实比光场相机简单一些因为不需要考虑主镜头孔径匹配问题只需要让入射波前经过微透镜阵列后在探测器上形成光斑阵列。Zemax仿真里我重点做的是两件事第一建立子孔径焦点偏移模型。把微透镜阵列定义为NSC中的Lenslet Array在阵列后面f_mla距离处放置探测器。入射波前通过在系统入瞳位置设置倾斜面或泽尼克相位面来模拟不同的波前畸变。第二扫描波前畸变量级记录每个子孔径焦斑的质心偏移。比如在入瞳面施加一个Zernike离焦项系数设为0.2波长到1波长分别在MATLAB里提取探测器图像的光斑位置拟合出波前斜率与实际施加系数之间的线性响应曲线。有一点要特别注意波前传感器的微透镜阵列往往直径比较小几十微米衍射效应不可忽略。Zemax的几何光线追迹在探测器上得到的光斑只是一个几何弥散圆不能反映真实的衍射光斑分布。这种情况下要在NSC中启用衍射计算方法或者用PODPhysical Optics Propagation传播工具。否则仿真出来的光斑尺寸偏小质心提取的噪声特性会和实际系统差很远。4. MATLAB和Zemax联动打通数据通路的关键Zemax建完模型只是第一步真正有工程价值的结果都要靠MATLAB来做——不管是光场相机的子图像重排还是波前传感器的质心提取和波前重建。所以这一步很关键。4.1 三种数据交互方式怎么选我用过三种方式连接MATLAB和Zemax各自适用场景不同。DDE方式老版本Zemax。Zemax 13之前的版本支持DDE接口MATLAB里可以用ddeinit(zemax, optical)建立通道通过执行Zemax命令来加载文件、追迹光线、获取数据。优点是方便缺点很明显DDE传输效率低而且新版本已经逐步移除了DDE支持。ZOS-API方式推荐。Zemax 13之后提供了ZOS-API通过.NET COM组件与MATLAB交互功能覆盖系统修改、光线追迹、数据获取、分析调用。这是目前最主流的接法。MATLAB里通过actxserver创建COM对象连接。文本文件中间交换。Zemax里通过脚本或手动操作导出光线数据到文本文件MATLAB再读取处理。这种方式最笨但最通用适合数据量不大、接口不好使的情况。现在的工程实践里我只推荐ZOS-API。用它可以做到MATLAB驱动Zemax批量修改参数、批量仿真、自动导出数据整个流程自动化程度高可复现性也好。4.2 ZOS-API直连Zemax的MATLAB示例下面给一个最小可用的MATLAB连接Zemax的示例框架% 连接Zemax ZOS-API需保证Zemax已打开并加载了光学系统 import ZOSAPI; TheConnection ZOSAPI.ZOSAPI_Connection(); TheApp TheConnection.ConnectAsEditor(0); % 0表示连接到当前打开的Zemax实例 TheSys TheApp.TheSystem; % 获取系统参数 fprintf(系统文件%s\n, TheSys.SystemFile); fprintf(单位%s\n, TheSys.SystemData.Units); % 使用光线追迹获取探测器数据需要根据具体分析对象调整 TheNCE TheSys.NCE; % 依次处理每个非序列对象... % 断开连接 clear TheSys; clear TheApp;这个框架能跑通的话后面批量仿真就可以做成循环。比如我要仿真不同微透镜焦距下的光斑偏移直接在MATLAB里修改Zemax对象参数重新追迹然后把探测器数据批量取回。需要说明的是ZOS-API调用时有一点容易踩坑MATLAB的版本和Zemax的版本需要匹配COM接口注册要正常。如果连接时报“未注册”之类的错误先检查.NET框架版本和Zemax安装是否完整顺便确认在MATLAB里执行了NET.addAssembly加载对应的DLL。4.3 MATLAB端核心处理算法光场重排和质心提取ZOS-API把Zemax的光线数据或探测器图像拿到MATLAB之后下一步就是做两件核心的事。光场相机这边要把原始的宏像素图像重排成光场参数化表示。假设Zemax探测器导出的原始图像是I_raw每个宏像素尺寸为M×M像素那么重排逻辑如下% 光场数据重排将原始平面重排为四维光场 % 宏像素数量为[nx, ny]每个宏像素M×M像素 LF zeros(nx, ny, M, M); for i 1:nx for j 1:ny LF(i, j, :, :) I_raw((i-1)*M1:i*M, (j-1)*M1:j*M); end end % LF(i,j,:,:)即是宏像素(i,j)内的角度采样可以用squeeze提取子孔径图像 sub_aperture_view squeeze(LF(i, j, :, :));这样重排之后固定角度坐标(M1,M2)抽取所有宏像素对应位置就能得到某个视角下的子孔径图像后面可以做数字对焦、深度估计等处理。波前传感器这边核心是质心提取。Zemax探测器给出的图像是一个二维灰度矩阵要找到每个子孔径内光斑的质心坐标。基础质心算法就是计算灰度加权平均位置function [cx, cy] centroid(img) total sum(img(:)); if total 0 cx 0; cy 0; return; end [rows, cols] size(img); [X, Y] meshgrid(1:cols, 1:rows); cx sum(sum(X .* img)) / total; cy sum(sum(Y .* img)) / total; end当然实际的质心提取要比这个复杂需要先做阈值分割去掉噪声、剔除边界还要考虑CMOS的固定模式噪声。更稳健的做法是先用一个高斯窗对每个子孔径图像加权再计算质心这样抗噪性能会好很多。实测下来同样的原始数据加权质心比重心法精度高大约1/4像素对波前重建结果影响非常明显。5. 实操避坑记录这几类问题我基本都遇到过仿真做得多了哪些环节容易翻车基本心里有数。这里挑几类最常见的问题分享出来希望能帮后来人少走弯路。5.1 Zemax光线追迹失败和收敛性问题微透镜阵列的特点是子透镜尺寸小、曲率半径短在序列模式下很容易出现光线全反射TIR或者追迹不到探测器的情况。尤其是曲率半径和口径比很高的时候边缘光线入射角超过临界角就会报错。解决办法有两类一是检查微透镜矢高。球面微透镜的矢高公式是s R - sqrt(R² - (D/2)²)。如果矢高过大比如超过10微米追迹时边缘光线的光程差计算容易出错。建议先用MATLAB或简单公式预先计算矢高确认在合理范围内再导入Zemax。二是检查入瞳直径和微透镜口径的匹配。入射光束孔径如果大于微透镜有效口径很大一部分光线会被阵列边缘遮挡甚至在非序列模式里直接绕过微透镜打到探测器上形成背景噪声。这种情况需要先设置孔径光阑确保入射光束恰好覆盖阵列的有效区域。5.2 数据交换中的坐标系陷阱这是最坑的问题之一。Zemax和MATLAB的坐标定义在方向上有差异尤其是探测器图像的坐标原点位置。Zemax探测器图像的原点在左下角MATLAB矩阵索引从左上角开始。如果直接从Zemax导出图像矩阵然后在MATLAB里做质心提取不做y轴翻转结果会整体偏移。我的习惯是在数据交换时统一约定。所有从Zemax导出的图像矩阵先做一次flipud上下翻转确保坐标体系与MATLAB一致。再用一个已知位置的光斑做标定验证坐标转换正确后再处理实际数据。另外非序列模式下Zemax探测器数据可以导出为文本格式里面附带像素位置信息。建议优先使用带坐标的数据不要只导灰度值这样MATLAB端处理时可以重建平面坐标。5.3 仿真耗时优化心得微透镜阵列仿真的最大痛点是速度。一个10×10的阵列每颗微透镜覆盖100×100像素如果每像素要追迹10条光线就是百万级光线量级NSC模式追迹时间可能长达几十分钟甚至数小时。我的优化经验有三条第一合理设置光线数量。先跑一个粗光线数比如每像素100条确认系统没有大问题后再加大到每像素1000条做最终精度仿真。不要一开始就满配。第二利用阵列对称性。如果是方形周期排列且入射光场具有对称性可以只仿真四分之一或二分之一的阵列区域然后在MATLAB里对称展开。这个方法我实测能把仿真时间压缩到原来的四分之一。第三在Zemax里适当调大“光线分裂”和“散射”的阈值抑制非必要的光线分裂计算。对于探测器图像如果不需要精确的光能分布可以直接关闭分裂只做纯几何追迹。5.4 探测器图像噪声与质心偏移的耦合最后一个常见坑Zemax仿真得到的理想光斑图样没有噪声但实际传感器必然有读出噪声和暗电流。如果拿理想仿真结果直接做质心提取精度很高但和实测差距很大。我的处理方式是在MATLAB里对Zemax得到的理想探测器图像主动加入高斯白噪声和背景偏置模拟真实的传感器响应。然后再做质心提取和波前重建。这样做出来的仿真灵敏度分析结果才更具参考价值。经验上加入约满阱1%的高斯噪声后质心提取精度会有明显下降这与实际相机的实测趋势一致。如果你的目的是设计指标分配建议提前在链路里加入噪声模型避免把仿真指标做得过于“理想”。这套Zemax MATLAB实现微透镜阵列仿真的流程核心要点可以归结为先搞清光场相机和波前传感器对微透镜阵列的不同参数需求再用Zemax选择合适的建模路径序列/非序列最后通过ZOS-API打通MATLAB数据处理链路。每一步都有严格的物理约束和工程权衡不是光有软件操作就能做好的。我个人在实际操作中最大的体会是微透镜阵列仿真里那些看起来“差不多就行”的参数——比如微透镜F数偏差、探测器像素尺寸、光斑质心的邻域选择——往往对最终精度的影响要以微米、亚像素来算。建议所有参数都先通过公式推算一遍再用Zemax穿插验证最后在MATLAB里做统计评估形成闭环。这套思路无论你是做光场相机还是波前传感器都适用。
网站建设高端定制企业官网