MATLAB与zemax怎么联调?

发布时间:
2024-08-12 03:55
阅读量:
18

Zemax软件的本质是一个进行光线追迹的计算器,软件中分析(Analysis)、优化(Optimization)及公差(Tolerance)等功能,均是利用追迹后的光线数据所进行的后续的数据处理。但这些内置的数据处理功能,并不能完全满足对光学系统各种各样的分析需求。鉴于这一不足,我们希望获取到追迹后的全部光线数据,并根据需要自行编写程序,对光线数据进行更灵活的处理。相比Zemax内置的宏编程语言(ZPL),Matlab编程在数据处理上的功能更为全面,而Zemax中也提供了关于Matlab的API可供调用,因此将Zemax与Matlab进行交互,就能对光线数据执行任意的数学处理,最终完成对光学系统的分析和评估。

本次我们以一个公差分析的实例操作来说明整个Zemax与Matlab的交互流程。如下图所示,此处公差分析的需求是,对像面上的光线坐标按照一定间隔进行分组和计数,然后将统计结果绘制直方图及曲线,最后求出计数曲线的半高宽FWHM(大家可以猜猜这种分析方法是用在哪种仪器设计中)。由于Zemax没有相应的操作数来对数据的进行分组统计,就需要将光线数据导入到Matlab中数据集中,进而通过Matlab的函数执行数据处理(如histcounts函数可执行分组统计);另一方面,由于Matlab中处理后的结果不能写入到Zemax的评价函数中去,因而在调用Zemax公差分析的API时,无法将Matlab计算出的FWHM数值作为评价函数的数值,导致公差分析输出不了FWHM的结果(可参见该帖子optzmx.com/thread-1957-,Matlab与Zemax的交互暂时实现不了这个评价函数的互传,这是个比较大的bug,但C#和C++可以实现,这里为了数据处理上的方便和编程上的低门槛,还是采用Matlab来做),所以就需要先执行公差分析的API,分别保存出现了元件偏心/倾斜等偏差的各个镜头文件(.zmx),然后逐个加载各镜头文件并执行Matlab中的数据处理,将全部结果汇总后即为公差分析的最终结论。

综上所述,本次公差分析的完整流程为:

(1)建立Matlab与Zemax间的连接;

(2)在Matlab中加载光学系统的源文件,调用Zemax公差分析相关的API,分别保存灵敏度分析和Monte Carlo分析下,已产生表面/元件偏差的各镜头文件;

(3)依次加载上述各镜头文件,调用Zemax的API进行光线追迹,将追迹后的光线数据保存到Matlab的数据工作区中;

(4)利用MATLAB的函数命令进行数据处理,给出最终的公差分析结果。


以下是各部分的详细代码及解释:

一、 首先要建立Matlab与Zemax间的连接。Zemax提供了如下两种方式:(1)交互扩展,此时仅建立了Zemax与Matlab的连接,在连接成功后,Zemax中的任意操作(如设置光瞳-视场-波长、执行光线追迹、优化、公差等)既可直接在Zemax界面中设置,也可在Matlab中通过代码来实现(2)独立应用程序,此时不需要打开Zemax软件,只需在Matlab中执行程序代码,控制Zemax在后台执行相应操作。在Zemax的软件界面中,可找到交互扩展和独立应用程序的代码块。

Zemax提供了交互扩展和独立应用程序的代码块

两种方式没有实质区别,最终的分析结果都将通过Matlab来计算和呈现。本次选择独立应用程序的代码形式。打开独立应用程序的代码块,此时代码主体是一个函数function,代码主体格式不用改,找到 % Add your custom code here...的位置,从此处开始编写自己的代码块。

function [ r ] = Customized_tolerance( args ) if ~exist('args', 'var') args = []; end % Initialize the OpticStudio connection TheApplication = InitConnection(); if isempty(TheApplication) % failed to initialize a connection r = []; else try r = BeginApplication(TheApplication, args); CleanupConnection(TheApplication); catch err CleanupConnection(TheApplication); rethrow(err); end end end function [r] = BeginApplication(TheApplication, args) import ZOSAPI.*; TheSystem = TheApplication.PrimarySystem; % Add your custom code here... % 此处填写特定的代码 function app = InitConnection() import System.Reflection.*; % Find the installed version of OpticStudio. zemaxData = winqueryreg('HKEY_CURRENT_USER', 'Software\Zemax', 'ZemaxRoot'); NetHelper = strcat(zemaxData, '\ZOS-API\Libraries\ZOSAPI_NetHelper.dll'); % Note -- uncomment the following line to use a custom NetHelper path % NetHelper = 'C:\Users\FXGX\Documents\Zemax\ZOS-API\Libraries\ZOSAPI_NetHelper.dll'; % This is the path to OpticStudio NET.addAssembly(NetHelper); success = ZOSAPI_NetHelper.ZOSAPI_Initializer.Initialize(); % Note -- uncomment the following line to use a custom initialization path % success = ZOSAPI_NetHelper.ZOSAPI_Initializer.Initialize('C:\Program Files\OpticStudio\'); if success == 1 LogMessage(strcat('Found OpticStudio at: ', char(ZOSAPI_NetHelper.ZOSAPI_Initializer.GetZemaxDirectory()))); else app = []; return; end % Now load the ZOS-API assemblies NET.addAssembly(AssemblyName('ZOSAPI_Interfaces')); NET.addAssembly(AssemblyName('ZOSAPI')); % Create the initial connection class TheConnection = ZOSAPI.ZOSAPI_Connection(); % Attempt to create a Standalone connection % NOTE - if this fails with a message like 'Unable to load one or more of % the requested types', it is usually caused by try to connect to a 32-bit % version of OpticStudio from a 64-bit version of MATLAB (or vice-versa). % This is an issue with how MATLAB interfaces with .NET, and the only % current workaround is to use 32- or 64-bit versions of both applications. app = TheConnection.CreateNewApplication(); if isempty(app) HandleError('An unknown connection error occurred!'); end if ~app.IsValidLicenseForAPI HandleError('License check failed!'); app = []; end end function LogMessage(msg) disp(msg); end function HandleError(error) ME = MException('zosapi:HandleError', error); throw(ME); end function CleanupConnection(TheApplication) % Note - this will close down the connection. % If you want to keep the application open, you should skip this step % and store the instance somewhere instead. TheApplication.CloseApplication(); end

二、 加载源文件,调用公差分析API,保存各镜头文件。注意,这里将灵敏度分析和蒙特卡洛分析的镜头文件分别进行保存,其中灵敏度分析可以评估单个公差变量对评价函数造成的改变,而蒙特卡洛分析则综合评估各公差变量造成的整体影响。在保存灵敏度分析的镜头文件时,需在关注的公差变量的操作数下方插入一个SAVE操作数,这样在执行公差分析时会额外保存两个镜头文件(文件命名为TSAV_最大/最小_0003),对应着该公差变量取最大和最小值时的镜头数据;而在蒙特卡洛分析中,可设置保存的文件数,这里设为保存20个文件,分别命名为MC_T0001~MC_T0020。执行整段代码后,生成的全部镜头文件如下方列表所示。

% Add your custom code here... % 打开光学系统的源文件,执行公差分析,分别保存灵敏度分析和Monte Carlo分析的镜头文件,最后保存和关闭源文件。 Filepath = pwd; FileNameSeq = System.String.Concat(Filepath, '\Spectrometer.zmx'); TheSystem.LoadFile(char(FileNameSeq),false); % 公差的灵敏度分析可以评估单个变量对偏差的影响,通过Tolerance中的Save操作数 % 可以将灵敏度分析中生成的镜头文件进行保存。 TheTDE = TheSystem.TDE; Operand = TheTDE.AddOperand(); % 新建一个操作数列表的行 Operand.ChangeType(ZOSAPI.Editors.TDE.ToleranceOperandType.SAVE); % 添加某个操作数,此处为SAVE Operand.GetCellAt(2).IntegerValue = 3; % 将操作数中某列设置为相应的数值,具体可参见Zemax中ZOS-API帮助文档的实例3 % Run the Tolerancing analysis % 建立公差分析并保存Monte Carlo分析的各样本文件 tol = TheSystem.Tools.OpenTolerancing(); % Select Sensitivity mode tol.SetupMode = ZOSAPI.Tools.Tolerancing.SetupModes.Sensitivity; % Select Criterion and related settings tol.Criterion = ZOSAPI.Tools.Tolerancing.Criterions.RMSSpotRadius; tol.CriterionSampling = 3; tol.CriterionComp = ZOSAPI.Tools.Tolerancing.CriterionComps.None; tol.CriterionCycle = 2; tol.CriterionField = ZOSAPI.Tools.Tolerancing.CriterionFields.UserDefined; % Select the number or MC runs and files to save tol.NumberOfRuns = 20; tol.NumberToSave = 20; % Run the Tolerancing analysis tol.RunAndWaitForCompletion(); % TheSystem.Tools.CurrentTool=[]; tol.Close(); % 保存并关闭当前的zmx文件 TheSystem.Save(); TheSystem.Close(false);

执行公差分析代码块后保存的镜头文件列表

三、依次加载各镜头文件,调用Zemax的API进行光线追迹,将光线数据保存到Matlab数据区中。其中,光线追迹需要设置光线的光瞳、视场、波长等参数;其他注意项可参见代码中的注释;

% 加载已经保存的某个Monte Carlo文件中的某个文件 Filepath = pwd; FileNameSeq = System.String.Concat(Filepath, '\MC_T0001.zmx'); TheSystem.LoadFile(char(FileNameSeq),false); % 进行光线追迹并导出光线数据进行分辨率计算 % Set up Batch Ray Trace % 设置批量光线追迹的参数 raytrace = TheSystem.Tools.OpenBatchRayTrace(); nsur = TheSystem.LDE.NumberOfSurfaces; max_rays = 30; normUnPolData = raytrace.CreateNormUnpol((max_rays + 1) * (max_rays + 1),ZOSAPI.Tools.RayTrace.RaysType.Real,nsur); % define batch ray trace constants % 设置波长和视场的限制条件 hx = 0; max_wave = TheSystem.SystemData.Wavelengths.NumberOfWavelengths; hy_ary = [0, 0.707, 1]; % initialize x/y image plane arrays % 初始化视场、波长和光线数的高维矩阵,用于存放追迹后的光线坐标数据 x_ary = zeros(TheSystem.SystemData.Fields.NumberOfFields, max_wave,(max_rays+1) * (max_rays+1)); y_ary = zeros(TheSystem.SystemData.Fields.NumberOfFields, max_wave,(max_rays+1) * (max_rays+1)); % determine maximum field in Y only max_field = 0; for i=1:TheSystem.SystemData.Fields.NumberOfFields if TheSystem.SystemData.Fields.GetField(i).Y > max_field; max_field = TheSystem.SystemData.Fields.GetField(i).Y ; end; end % 按视场、波长和光瞳的位置逐条进行光线追迹 for field = 1:length(hy_ary) for wave = 1:max_wave % Adding Rays to Batch, varying normalised object height hy normUnPolData.ClearData(); waveNumber=wave; for i = 1:((max_rays + 1) * (max_rays + 1)) px = 0.22*(rand() * 2 - 1); py = 0.22*(rand() * 2 - 1); while (px^2 + py^2 > 1) % 若光瞳的坐标pxpy超出归一化,重新对py随机取值 py = rand() * 2 - 1; end normUnPolData.AddRay(waveNumber, hx, hy_ary(field), px, py, ZOSAPI.Tools.RayTrace.OPDMode.None); end % Run Batch Ray Trace raytrace.RunAndWaitForCompletion(); % Read batch raytrace and display results normUnPolData.StartReadingResults(); % ReadNextResult(success,raynumber,errcode,vigcode,x,y,z,L,M,N,L2,M2,N2,opd,intensity) % 其中x,y,z表示目标表面上的坐标;L,M,N表示光线经过目标面折射到其后介质中的方向余弦; % L2,M2,N2表示目标表面法线的方向余弦;OPD代表光程差; % Intensity 表示光线的相对透射强度,包括已定义的任何光瞳或表面切趾。 [success, rayNumber, errCode, vigCode, x, y, ~, ~, ~, ~, ~, ~, ~, ~, ~] = normUnPolData.ReadNextResult(); while success if ((errCode == 0 ) && (vigCode == 0)) x_ary(field, wave, rayNumber) = x; y_ary(field, wave, rayNumber) = y; end [success, rayNumber, errCode, vigCode, x, y, ~, ~, ~, ~, ~, ~, ~, ~, ~] = normUnPolData.ReadNextResult(); end end end

四、利用MATLAB的函数命令进行数据处理,给出最终的公差分析结果。注意,此时可以将光线数据另外备份到excel中,方便以后随时进行调取。这里对光线梳理的处理方法也不做具体展开,各位可根据需要编写合适的代码来处理数据。

% 将光线数据导出到excel中 Excelname = 'Raytrace.xlsx'; % squeeze函数将数组降维 raydata = [squeeze(x_ary(1,1,:)),squeeze(y_ary(1,1,:))]; writematrix(raydata,Excelname); % 采用histcounts函数可将光线坐标按照CCD每个像素点的范围进行分组统计 % 具体使用方法可参照官网资料 % https://ww2.mathworks.cn/help/matlab/ref/histcounts.html#d124e602688 % X为各光线的坐标;edges为CCD各像素的区间;N为分组后的计数值。 N = histcounts(X,edges);

END