
前阵子我接手了一个超透镜聚焦项目目标波长 1550 nm口径 30 μm焦距 60 μm。一开始我天真地想用 CST 一锅端直接把整个透镜建出来参数扫描、优化、后处理全在 CST 里完成。结果开工第一天就被现实教育了——透镜区域内上千个纳米柱每个柱子的宽度都可能不一样全参数扫描根本跑不动光建模就能把我劝退。后来我把流程拆成 CST 与 Matlab 联合仿真CST 负责单元级电磁仿真和最终全波验证Matlab 负责相位计算、查找表匹配和布局生成。这个思路做下来效率翻了不止一倍而且每一步都看得见摸得着出了问题也知道回哪一步去查。这篇文章不绕弯子把我这个案例的完整流程、参数设置和踩过的坑一次讲清楚。1. 联合仿真前先想清楚CST 和 Matlab 各自该干什么很多人在接触超透镜设计时有个误区以为所有的活都能在 CST 里干完。实际上超透镜设计链里最耗时的不是“算目标相位”而是“扫描单元结构”。CST 擅长做精确的电磁场仿真Matlab 擅长做数组运算、矩阵处理和工艺约束下的优化匹配。两者各自有不可替代的位置强行让其中一个干另一个的活只会把自己耗死。1.1 超透镜设计里最耗时的环节不是“算相位”而是“扫单元”超透镜的单元结构是亚波长尺度的介质柱或孔洞阵列。每个单元的透射相位会随柱宽、柱长、柱高、周期等参数变化但相位和结构参数之间没有简单解析式只能靠全波电磁仿真一个个算出来。要做一面能聚焦的透镜至少需要几百个单元组合的透射数据才能保证后续查找表匹配时有足够分辨率。这里有个很现实的问题CST 里建一个单元模型不难但要用它去扫几百组尺寸再人工记录每一组结果的相位和透射率那就完全不现实了。我的做法是让 CST 单次只仿一个“单元胞”在周期性边界条件下算出一个 S21 结果然后把大量参数组合的扫描任务交给批处理。Matlab 这边则负责读数据、排相位、算目标、做匹配。两边分工之后单元扫描和透镜布局设计可以并行推进整个周期压缩了一大截。1.2 为什么要拆成“单元扫描 系统设计 全波验证”三段如果直接建整个透镜的全模型柱子的数量从几百到几万不等网格数量会爆炸式增长。尤其当单元尺寸只有几百纳米、和波长接近时CST 必须对每个柱子的边缘做局部网格加密否则相位提取会严重偏差。全模型一跑就是数小时甚至数天而参数一改就得重来试错成本高到难以接受。分段式设计的好处在于单元扫描阶段只仿真一个周期单元计算量小可以批量并行产出的是“结构参数 → 透射相位/透射率”的查找表系统设计阶段在 Matlab 里完成把目标相位分布和查找表做匹配产出的是每个坐标点对应的单元尺寸全波验证阶段再把这些单元按坐标拼成完整透镜只做一次到两次数值验证用来确认聚焦效果。这样每一段都可以独立检查。单元数据不对先修单元匹配逻辑不对先查 Matlab 脚本全模型效率低再考虑是相位误差还是网格问题。而不是像一开始那样整个模型糊在一起出了问题连从哪入手排查都不知道。1.3 两条技术路线文件交换 vs 脚本接口CST 与 Matlab 的联仿路线主要有两条选择依据取决于你电脑上装了什么样的软件环境以及你要做的是“一次性设计”还是“反复迭代优化”。第一条是文件交换也是我最推荐起步时的方案。CST 把每个单元仿真的频率、相位、幅度导出成 txt 或 csv 文件Matlab 批量读取后处理。最后再生成一个坐标与尺寸表用宏脚本在 CST 里重建完整透镜。这样做的好处是两边完全解耦不依赖软件版本的 API 兼容性出了问题也好定位。第二条是脚本接口。CST 的仿真工程可以通过 Windows 下的自动化接口被 Matlab 直接调用在 Matlab 里写代码去创建工程、改参数、跑仿真、取结果。这种方案能真正做到闭环优化比如根据实测的效率偏差自动修改某几个单元的尺寸再重新仿真。但代价是配置麻烦不同版本之间的对象模型可能不一致而且一旦 CST 那边的求解器设置变了Matlab 里的调用代码也要跟着改。文件交换和脚本接口不是互斥的。我的实际习惯是设计主流程走文件交换后续的小范围优化可以临时用脚本接口只改局部几个参数不把整个优化链路都押在 API 上。下面这个案例的主流程全部按文件交换路线来讲。路线优点缺点适合场景文件交换稳定、易排查、不依赖版本不能实时闭环迭代单元扫描、布局生成、全波验证脚本接口自动闭环、支持在线修改环境配置复杂、版本兼容性风险局部参数微调、批量优化2. 案例指标与单元结构选择先把目标定死做仿真项目最忌讳边做边改指标。我在这个案例里第一步就把所有核心参数锁定了后面每个环节都围绕这套指标展开。这里把设计指标的推导过程和单元选型的逻辑说清楚大家换波长、换口径时可以直接套用相同的思路。2.1 设计指标拆解波长、NA、口径与焦距互相牵制我的案例指标是设计波长 λ 1550 nm透镜口径 D 30 μm焦距 f 60 μm。这几个参数不是随便凑的它们共同决定了透镜的数值孔径 NA。NA 的计算公式是NA ≈ D / (2f) 30 / (2 × 60) 0.25NA 会影响焦点的理论大小。根据衍射极限艾里斑半径约等于r_airy 0.61 × λ / NA 0.61 × 1550e-9 / 0.25 ≈ 3.78 μm这意味着我在后期验证聚焦效果时焦点中心能量集中区域的半径大概在 3.8 μm 左右稍大一点到 4~5 μm 去积分都是合理的。还有个重要关系是单元数量。透镜直径为 30 μm单元周期我预定为 P 600 nm那么在直径方向上大概能排下 50 个单元总面积约为N_total ≈ π × (15 μm)^2 / (0.6 μm)^2 ≈ 1963 个单元这个数量级既不会让全波验证变得不可承受又能比较真实地反映超透镜的聚焦行为。如果一开始就上 100 μm 口径单元数突破两万全波仿真对内存和时间的消耗会陡增。建议新手先用小口径把流程跑通再逐步放大。2.2 单元结构选型矩形介质柱最匹配线偏振超透镜单元有很多种形式圆孔、方孔、十字形、矩形柱、圆柱、纳米鳍等。我选择的是矩形介质柱也就是在基底上刻出一根根矩形的柱子柱子的高度固定两个水平方向的尺寸 Wx 和 Wy 独立变化用来分别调制两个正交线偏振分量的相位。选择矩形柱而不是圆柱理由很直接圆柱只有一个半径参数同一个半径只能同时决定 x 偏振和 y 偏振的相位自由度不够矩形柱两个边长分别独立变化在 x 偏振入射下Wy 的影响远小于 Wx这样就能获得两组相对独立的相位响应。对于只关心单一偏振聚焦的场景矩形柱已经足够。材料方面我用的非晶硅。1550 nm 波段非晶硅的吸收不算大折射率又足够高能在较小的柱高范围内实现 0~2π 的相位覆盖。柱高 H 设定为 800 nm这个高度是通过粗扫确定的如果 H 太矮有效折射率变化范围不足相位覆盖不到完整的 360 度如果太高加工深宽比不友好仿真里也会拉长收敛时间。2.3 扫描范围怎么定相位覆盖、工艺极限和步进粒度单元扫描范围一定要覆盖 0~2π 的相位变化区间而且最好留出余量否则后续查找表匹配时有些目标相位找不到对应单元尺寸就会出现大面积匹配误差。我的矩形柱尺寸设定为 Wx、Wy 从 60 nm 到 300 nm步长 20 nm两个方向独立变化总组合数为13 × 13 169 个组合为什么从 60 nm 起步而不是更小这主要受限于我手头项目的可制造性评估——60 nm 以下的关键尺寸在工艺上容易出问题。上限 300 nm 接近单元周期 600 nm 的一半再大柱子之间的耦合会变得不可控相位响应也不规律。步长 20 nm 是一个折中值步长更小比如 5 nm能拿到更平滑的相位变化但单元组合数会暴增到 49 × 49 2401 个仿真时间实在顶不住。参数数值说明单元周期 P600 nm亚波长避免高阶衍射柱高 H800 nm保证相位覆盖 0~2πWx / Wy 范围60~300 nm受工艺和耦合约束步长20 nm平衡精度与仿真代价单元组合数169两个维度 13×133. CST 单元扫描与相位数据库的建立过程单元扫描是整个联合仿真链路的地基。这里每一步设置都会直接影响相位数据库的可靠性。我详细拆一下我的做法尤其是那些容易忽略但影响巨大的细节。3.1 单元边界条件与端口设置细节在 CST 里仿真超透镜单元核心是用 Floquet 周期边界来模拟“无限大的周期阵列”。实际透镜中每个单元周围有大量相同周期的邻居单元之间的电磁耦合不能忽略所以绝对不能用普通的开放边界来仿真单个孤立柱子。我建的模型结构很简单一个 600 nm × 600 nm × 衬底厚度的单元盒中心放一根 800 nm 高的矩形柱材料设为非晶硅。XY 平面方向的边界条件设为 Unit Cell周期边界Z 方向设置两个波端口一个作为入射面一个作为透射输出面。端口离结构表面的距离值得注意。端口太近会把表面附近的倏逝模式误差算进 S 参数里太远又会增加网格数量。我一般把波端口和柱体表面留出至少半个波长的空气间隔也就是约 800 nm 以上的距离。在 1550 nm 单频点仿真时这段距离内网格数量并不多但对相位准确度很关键。3.2 批量扫描与导出命名规范169 个组合不可能手动一个一个改尺寸。我的做法是在 CST 里定义全局参数 Wx 和 Wy然后用参数扫描或宏脚本批量运行。为了脚本读取方便每个仿真结果文件名直接带上参数值sweep_wx_100_wy_180.txt导出文件内容通常包含频率点、S11 幅度、S11 相位、S21 幅度、S21 相位这几列。我建议在 CST 后处理模板里直接输出线性幅度和相位角度避免后续再做 dB 转换时弄错正负号。批量跑单元仿真时我用的是频域求解器只算 1550 nm 附近窄带单频点求解速度快。每个单元十几秒到几十秒169 个组合并行开 4 个任务实际不到一个小时就跑完了。如果用宽带时域求解器单个单元时间也不长但网格自适应收敛会让整体时间成倍增加没必要。3.3 相位数据从 S 参数到可用查找表的处理步骤CST 导出的 S21 相位默认范围是 -180° 到 180°这一个一个组合的相位值放在一起时会有明显的周期跳变。比如某些柱宽变化时相位跨过 ±180° 边界会从 179° 直接跳到 -179°看起来像剧烈振荡。这种跳变如果不处理Matlab 匹配时会算出差 358° 的“假误差”导致选错单元尺寸。我的处理方法是把 169 个组合的相位按 Wx 递增、Wy 递增的网格重排成矩阵然后逐行做 unwrap。具体操作在 Matlab 里就是% sweep_phase 是 13x13 矩阵对应 Wx 行、Wy 列 phase_2d_unwrap unwrap(sweep_phase * pi / 180) * 180 / pi;unwrap 只能沿着某一维展开所以我会先沿 Wx 方向展再沿 Wy 方向展两次都做一遍。处理完的相位矩阵仍然有一个整体常数偏移量这个偏移对最终聚焦没有影响因为超透镜关注的是单元之间的相对相位差。为了统一我选一个基准单元通常取扫描范围中心的组合把所有相位都减去这个基准单元的相位值phase_db phase_2d_unwrap - phase_2d_unwrap(7, 7); phase_db mod(phase_db, 360); % 归一化到 0-360°这样就得到了一张可用的查找表输入目标相位输出 Wx、Wy 组合。除了相位我也会把每个组合的 S21 线性幅度单独存一列后续做权重匹配时要用。提示CST 端口的参考面位置会影响 S21 相位的绝对数值但在我的处理流程里所有单元都是同一套端口设置相位做差之后的相对值不受参考面影响。只要不中途挪动端口位置这个细节就不用反复担心。4. Matlab 侧相位分布计算与单元映射单元数据库建好之后重头戏就转移到 Matlab 这边。这里要做三件事计算透镜表面的目标相位分布、把每个位置的目标相位和查找表做匹配、最后把匹配结果导出成 CST 可以读取的布局文件。每个环节都不复杂但连起来容易出错尤其是坐标离散化和相位周期归一化这两个坑。4.1 目标相位公式与离散网格生成超透镜的聚焦原理是通过在平面上引入一个球面波前的相位分布把平面波入射转换成聚焦球面波。目标相位的计算公式是φ(x, y) -k0 × (sqrt(x² y² f²) - f)其中 k0 2π / λ。这个公式的意义是透镜中心到焦点的光程最短透镜边缘到焦点的光程最长所以边缘需要额外的相位延迟来补偿光程差。实际计算时我只关心 2π 内的相位余数也就是 mod(φ_target, 2π)。网格生成要注意一个细节透镜表面必须按照单元周期 P 来离散。我在 Matlab 中生成坐标数组时以透镜中心为原点在口径 30 μm 的圆形区域内按 600 nm 间隔布置网格点lambda 1550e-9; k0 2 * pi / lambda; f 60e-6; P 600e-9; x (-25:25) * P; % 半径 15 um约 50 个点 y (-25:25) * P; [X, Y] meshgrid(x, y); R sqrt(X.^2 Y.^2); mask R 15e-6; % 圆形口径掩模 phi_target mod(-k0 * (sqrt(R.^2 f^2) - f), 2 * pi);坐标原点是透镜中心这一点必须和 CST 全模型里的全局坐标对齐否则最后布局生成后所有柱子的位置都会整体偏移虽然不会影响聚焦效果但会给后来的效率积分带来坐标系转换的麻烦。4.2 查找表匹配最近邻选择与透射率加权目标相位算完后每个网格点都要在查找表里找一个最接近的单元组合。最简单的办法是最近邻匹配也就是对每个网格点的相位 phi_target遍历查找表里所有 169 个组合找 |phase_db - phi_target| 最小的那个 Wx、Wy。但实际做的时候我强烈建议把透射率也加进匹配函数。原因是相位接近不等于这个单元透射效率高某些柱宽组合虽然相位刚好合适但透射率可能只有 60%用多了整块透镜效率会被拉低。改进的匹配目标函数可以是score |Δphase| w × (1 - |S21|)其中 Δphase 是相位差|S21| 是透射幅度w 是权重系数。我案例里把 w 设在 0.2 左右相当于相位差 20 度时允许透射率和最大值差 20 个百分点的单元被选中。这样找到的不是“相位最准”的组合而是“又准又透”的组合。% 简短示意查找表 phi_db 是 169x1对应每个 Wx/Wy 组合 % abs_s21 是对应的透射幅度 for i 1:size(X, 1) for j 1:size(X, 2) if mask(i, j) score abs(phi_db - phi_target(i, j)) 0.2 * (1 - abs_s21); [~, idx] min(score); Wx_map(i, j) Wx_all(idx); Wy_map(i, j) Wy_all(idx); end end end如果查找表匹配结束后发现有一些网格点找不到相位误差足够小的组合比如最小误差都超过 30 度那基本可以判断单元扫描范围没覆盖完整相位区间。这种情况不要继续硬跑回 CST 把单元扫描范围扩大或调整柱高后再来一轮。4.3 布局导出与 CST 再建模的桥接方式匹配完成后的 Wx_map、Wy_map 就是整个透镜的“设计图纸”。接下来要把它变成 CST 能用的模型。我有两种方式方式一是导出纯坐标表格每行包含 x、y、Wx、Wy然后用 CST 的宏脚本读取表格文件在建模区域循环创建矩形柱。这个方式灵活适合单元数量在两三千以内的模型。方式二是直接生成一个 GDS 版图文件再在 CST 里导入。GDS 方式在工艺对接时更好用但涉及版图层次定义前期准备工作更多。我案例里用的是方式一。Matlab 导出一个 layout.csv 文件x,y,wx,wy -1.5e-05,-1.5e-05,1.2e-07,2.2e-07 -1.44e-05,-1.5e-05,1.4e-07,1.8e-07 ...然后在 CST 里通过 VBA/Lua 宏脚本逐行读取创建矩形柱。脚本基本逻辑是遍历每一行把 (x, y) 作为柱子中心的水平坐标把 Wx、Wy 作为矩形截面的边长柱高 H 固定为 800 nm材料设为非晶硅。生成脚本的模板如下-- 伪代码示意 for each_row in layout do create_block(x, y, wx, wy, H) end这种“Matlab 出表、CST 执行脚本”的模式减少了很多手动操作。我第一次手动建了 200 个柱子就放弃了脚本化之后几千个柱子也就是几十秒的事。5. 全波验证从设计到焦点的最后一公里单元扫描和布局生成都完成后整个流程的成败就看全波验证能不能得到干净的聚焦光斑。这一步也是最容易出现“效率虚低”或“焦点找不到”的地方很多问题不是在设计环节而是在验证设置上。5.1 全模型网格与监视器设置全模型建模时脚本已经生成了所有柱子但 CST 默认网格不会对每个柱子边缘做足够细分。用于单元扫描的局部加密策略和全模型不同全模型网格不能太细否则内存直接爆掉但又必须在柱子边界处保留足够的采样。我的做法是使用局部网格属性对矩形柱区域单独设置最大网格步长。单元周期 600 nm柱子宽度 60~300 nm我把空气区域的最大网格设为 150 nm柱子内部及边缘最大网格设为 50 nm 左右。这样网格总数在可控范围内同时能保证相位精度的基础要求。监视器设置非常关键。我要看焦点平面所以加了一个电场监视器监视面设置在 z 60 μm 处也就是理论焦距位置。另外在 z 方向加一条一维场探针线从 z 40 μm 一直延伸到 z 80 μm这样可以扫描出沿光轴方向的场强峰值位置避免焦点不在理论焦距处时还不知道去哪找。5.2 焦点位置与聚焦光斑的判断方法仿真结束后第一步不是算效率而是看焦点位置。由于单元匹配存在离散误差焦点的实际位置通常不会分毫不差地在 z 60 μm 处。小幅偏移几微米是正常的。我拿到 z 方向场强分布后找到峰值位置 z_peak再回到那个平面去看横向场分布判断光斑形状是不是接近圆形。超透镜的焦斑应该是近似艾里斑中间一个亮核周围有较弱的旁瓣。如果横向场分布出现明显的多峰或者拉长形状说明相位分布有问题优先检查查找表匹配是否正确、布局导出时坐标是否有偏移。这里有个容易踩的坑CST 后处理里的电场强度场值是复数幅度取 abs(E) 或 abs(E)^2 要看目标是什么。强度分布应该用场的平方幅度也就是 abs(E)^2才能对应光强。5.3 聚焦效率计算定义、公式和修正聚焦效率是超透镜的核心指标不同文献定义略有差别我这里的定义是焦点平面中焦点区域内的功率积分除以入射到透镜表面的总功率。在 CST 后处理里入射总功率可以直接从入射端口提取。焦点区域功率需要对 z z_peak 平面的坡印廷矢量 z 分量做面积分。积分区域的半径我取 4.5 μm略大于理论艾里斑半径 3.8 μm把旁瓣能量也包含一部分保证效率值不虚高也不低估。我第一版全模型跑出来的效率只有 52%这个数字当时看着很气馁。后来排查发现主要原因有两个一是某些高 NA 边缘区域的单元相位匹配误差偏大二是部分低透射率单元被无脑选中。按透射率加权的匹配函数修正后效率提高到 69%。对于单层介质超透镜来说这个效率已经算不错的水平。6. 踩坑记录翻阅日志时最值得留意的三个问题整个流程跑了三轮前两轮多多少少都有问题。下面这三个问题是我在实际操作中遇到的也是同行比较容易中招的地方单独拎出来说。6.1 相位参考面不一致导致整张查找表偏移第一次做单元扫描时我在中途调整过一次端口距离想着空气间隔远一点更保险。结果 Matlab 匹配时发现部分单元组合的相位相对关系完全对不上查找表里同样尺寸的柱子在不同批次导出的相位值差了几十度。找了一圈原因才意识到端口参考面变了之后每个单元多了或少了额外一段空气的传播相位。这个问题在原理上不难理解CST 的 S21 相位包含了从端口到端口的完整传播路径改端口位置等于给所有单元加了同一个常数偏移。如果所有单元都同批次导出这个常数偏移不影响相对相位一旦分批次且设置变化整体查找表就废了。解决办法很简单单元扫描全过程中端口位置、底部边界位置、激励幅度全部保持一致不要中途改动。我把这些设置写进了扫描模板每次新项目直接沿用才彻底避免这个问题。6.2 扫描步长太粗相位台阶跳得比 π 还大第二次重跑时我把步长激进地调整为 30 nm想着反正单元少一半方便快速验证。结果匹配出来的透镜效率掉到了 47%一看相位差直方图大量网格点的相位误差在 25° 到 35° 之间。20 nm 步长时相邻柱宽的相位变化平均约为 10°~15°30 nm 步长后这个差值翻倍尤其在 Wx 接近扫描边缘时相位响应曲线斜率很陡一步能跳 30° 以上。这种误差累积在透镜边缘区域特别致命因为边缘本身就需要很大的相位梯度再用误差大的单元去拼聚焦效果必然恶化。我自己总结的经验单元扫描步长不要超过相位曲线最陡处变化量的 1/2。如果不确定就用小区间粗扫定位梯度最大的柱宽范围再在那个区间加密。这个教训让我后来再做类似结构时都是先扫一维看相位曲线斜率再决定二维扫描步长。6.3 效率低却没查反射的乌龙有一轮仿真的效率始终卡在 40% 左右我把相位误差、网格密度、积分半径都查了一遍全都没有明显问题。后来无意间看了眼 S11才发现某些单元的反射率接近 30%。这些单元的透射率不高但相位匹配得分太好选择时把透射权重压得过低导致它们被大面积选中。超透镜的效率瓶颈不只在相位反射损耗同样是实打实的能量流失。单元的相位响应和透射幅度之间没有必然联系一个相位完美的单元完全可能因为内部共振导致强反射所以查找表里一定要同时记录 S21 幅度和 S11 幅度。匹配时加上透射率权重是第一步进阶做法是对反射高的单元组合直接一票否决宁可相位差几度也不让能量反射掉。最后再分享一个小习惯我在导出每个单元结果时文件名严格按 Wx_Wy 编号Matlab 读取时用正则把参数从文件名里解析出来。这样处理的好处是万一查找表里的某个数据点异常我可以直接定位到是哪个柱子尺寸组合出了问题回到 CST 里单看那一组结果几分钟就能确认是仿真本身异常还是后处理换算错误。这个习惯在几次排查异常导出的过程中帮我省了大量时间算是这套联合仿真流程里性价比最高的一步。