ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

M型Czerny-Turner光谱仪设计:用非对称光路压制彗差的光学仿真实践

M型Czerny-Turner光谱仪设计:用非对称光路压制彗差的光学仿真实践 简介这份资源围绕M型Czerny-Turner光谱仪的结构优化展开涵盖理论推导、参数计算与仿真验证全流程适合具备光学仪器与物理基础、从事光学系统设计与研发的科研人员和技术工程师。资源通过Python代码演示反二次衍射条件的计算并解释关键步骤同时给出使用Zemax OpticStudio进行光学元件配置、光线追迹与像质评价的详细流程还提供计算分辨率、球差及其他潜在像差的Python函数并介绍优化算法与跨平台数据分析方法。压缩包为单个docx文档大小29KB内嵌可运行的Python代码及详细注释组织紧凑方便对照实践。目前已有308人学习下载。对于希望深入理解光谱仪工作原理、改善分辨率与像质的开发者这份资料提供了从理论推导到仿真验证的完整参考。1. M型Czerny-Turner光谱仪一个把彗差压下去的光路改动如果你搭过一套Czerny-Turner光谱仪一定遇到过这样的情况中心波长处的分辨率看着还行往两端偏谱线就变胖、拖尾怎么调狭缝高度都没用。这不是装配问题也不是探测器没对好焦而是光栅两侧那两个球面镜造成的彗差在作怪。M型Czerny-Turner光谱仪正是为压制这种彗差出现的结构把准直镜和聚焦镜从对称布置改成非对称的M形光路让两边球面镜产生的彗差在像面附近互相抵消。这篇文章从像差来源讲起给出结构参数的设计依据接着用Python从零写光线追迹和优化循环再落到Zemax里建模比对验证最后整理几个能帮你少加班的踩坑记录。适合正在做光谱仪光机设计、或者读研需要完成光谱系统仿真的朋友带着公式和代码走一遍比只看原理图管用得多。2. 像差从哪来球差、彗差、像散在C-T结构里的分布2.1 Czerny-Turner光路为什么会有彗差先把C-T结构的底账翻出来。光路是入射狭缝 → 准直镜球面镜→ 平面光栅 → 聚焦镜球面镜→ 像面探测器。准直镜把狭缝的光变成平行光光栅分光聚焦镜把不同波长的平行光汇聚到像面不同位置。这套结构最大的优点是只用球面镜成本低、无色差因此从单色仪到便携式光谱仪到处都是它的影子。代价是什么球面镜离轴使用。狭缝不在准直镜的光轴上而是偏在一边光线以某个离轴角打在球面镜上。离轴球面镜会产生一系列像差球差、彗差、像散、场曲。其中影响光谱仪分辨率最主要的矛盾是彗差——子午面内不同孔径高度的光线经过球面镜后主光线和上下边缘光线的汇聚点不重合形成一条拖着尾巴的彗星状弥散斑。彗差导致的谱线展宽在短波和长波两端表现得尤其明显。那像散呢谱线在子午方向沿光谱展开方向的展宽主要受彗差控制弧矢方向沿狭缝长度方向的光线聚焦点离得像面更远所以狭缝像会变模糊。很多设计只盯分辨率忽略了像散结果测出来光谱响应变得很钝。M型结构优化的目标是把彗差和像散一起压到可接受范围而不是单一指标。2.2 结构参数对像差的影响焦距、离轴角、光栅的三角关系设计C-T结构需要拍板的参数有几个准直镜焦距 f1、聚焦镜焦距 f2、准直镜离轴角 α、聚焦镜离轴角 β、光栅刻线密度 G、入射臂长和出射臂长以及狭缝宽度。它们之间的约束关系可以这样理解。光栅方程决定光谱展开范围 d(sinθi sinθd) mλ 其中 d 是光栅常数θi 是入射角θd 是衍射角m 是衍射级次。对于M型C-T准直镜和聚焦镜的离轴角不再相等而是让两边球面镜的彗差在中继像面处方向相反、大小接近从而抵消。这里有两个经验关系可以先用彗差的大小近似正比于离轴角的平方、反比于焦距的平方。也就是说想减小彗差要么减小离轴角要么加长焦距。但离轴角太小会导致光路结构干涉——光栅和两个反射镜会互相挡光系统没法排布。所以设计上通常的做法是固定一个离轴角范围比如8°到15°再把两个镜子的焦距配比做优化。2.3 从对称C-T到M型非对称配置抵消彗差的原理常规C-T是对称式准直镜和聚焦镜焦距相同、离轴角相同光栅放在中间光线像Z字形走。这种结构的优点是加工方便、装配简单但从像差补偿的角度看几乎没有利用球面镜之间的相互作用来消彗差。M型的核心改动是两个球面镜不再对称放置而是通过调整离轴角和焦距让准直镜产生的彗差和聚焦镜产生的彗差在像面上方向相反、大小相同。这样系统总彗差趋近于零而球差和像散仍然可以通过孔径光阑位置和镜面曲率半径做二次优化。另一个M型的好处是降低了对镜面加工精度的敏感度。对称结构中两镜像差叠加任何一片镜子的面形误差都会直接反映在分辨率上。非对称配置相当于给系统留了补偿余量。下面进入实操环节用Python把这套追迹算出来。3. 用Python做光线追迹与结构优化从零写追迹代码3.1 准备环境与坐标系设定写这段代码不需要额外的光学库就用numpy做向量计算。环境准备是常规操作装一个Python 3.8以上版本配上numpy和matplotlib在vscode里把python解释器选好跑起来就行。如果是从零开始学Python先看一遍python入门的基础语法重点是numpy数组操作和函数定义这个追迹脚本用到的语法不超过这些。坐标系怎么定很关键我采用的约定是光轴方向为Z轴正方向光谱展开方向为X方向子午面内Y方向为狭缝长度方向弧矢方向。所有镜面都只绕Y轴旋转这样追迹可以限制在子午面附近计算量小也方便可视化。3.2 光线追迹核心代码球面镜反射与光栅衍射写一个追迹函数。输入是光线起点和方向向量依次经过准直镜、光栅、聚焦镜输出光线在像面上的落点坐标。import numpy as np def reflect_spherical(ray_origin, ray_dir, mirror_vertex, radius, tilt_angle): 球面镜反射追迹 参数: ray_origin: 光线起点 (3,) ray_dir: 光线方向单位向量 (3,) mirror_vertex: 镜面顶点坐标 (3,) radius: 球面曲率半径正值表示凹面朝向入射光 tilt_angle: 镜面绕Y轴倾斜角单位弧度 返回: 反射后的光线起点和方向 # 镜面绕Y轴旋转矩阵 c, s np.cos(tilt_angle), np.sin(tilt_angle) rot_matrix np.array([[c, 0, s], [0, 1, 0], [-s, 0, c]]) # 将光线变换到镜面局部坐标系 local_origin rot_matrix (ray_origin - mirror_vertex) local_dir rot_matrix ray_dir # 球面方程: z radius - sqrt(radius^2 - x^2 - y^2) # 用解析法求交点避免数值迭代 a local_dir[0]**2 local_dir[1]**2 local_dir[2]**2 b 2 * (local_dir[0] * local_origin[0] local_dir[1] * local_origin[1] - (local_dir[2] * (radius - local_origin[2]))) c_const (local_origin[0]**2 local_origin[1]**2 (local_origin[2] - radius)**2 - radius**2) discriminant b**2 - 4 * a * c_const if discriminant 0: return None # 光线没有打到镜面上 t (-b - np.sqrt(discriminant)) / (2 * a) hit_point_local local_origin t * local_dir # 球面法线方向 normal_local np.array([-hit_point_local[0] / radius, -hit_point_local[1] / radius, 1 - hit_point_local[2] / radius]) normal_local normal_local / np.linalg.norm(normal_local) # 反射定律: r d - 2(d·n)n reflected_local local_dir - 2 * (local_dir normal_local) * normal_local # 变换回全局坐标系 rot_inv rot_matrix.T return mirror_vertex rot_inv hit_point_local, rot_inv reflected_local这个函数把球面镜的求交转化成了一元二次方程光线和球面求交在光学追迹里是最基础的操作。注意镜面倾斜角的符号正方向定义为让入射光线往X正方向偏转的方向。这里的曲率半径符号约定是凹面镜取正值凸面镜取负值如果你后面的Zemax设置里符号反过来追迹结果符号也会反务必统一。光栅的衍射追迹单独写它本质上是相位关系的应用。def diffract_grating(ray_origin, ray_dir, grating_normal, line_density, wavelength, diff_order1): 平面光栅衍射 参数: ray_dir: 入射方向准直镜反射后的方向 grating_normal: 光栅面法线方向 line_density: 刻线密度 (lines/mm) wavelength: 波长 (nm) diff_order: 衍射级次常用1级 返回: 衍射光线方向 d_mm 1.0 / line_density # 光栅常数单位 mm wl_mm wavelength * 1e-6 # nm 转 mm # 投影法则把入射方向分解为垂直于刻线的分量和平行于刻线的分量 # 光栅只改变垂直于刻线方向的分量 t grating_normal / np.linalg.norm(grating_normal) # 假设刻线沿Y方向则垂直刻线方向为X-Z平面内 k np.array([1.0, 0.0, 0.0]) # 子午面方向 k k - (k t) * t # 投影到光栅面 sin_i ray_dir k # 衍射方程: sinθd sinθi mλ/d sin_d sin_i diff_order * wl_mm / d_mm if abs(sin_d) 1: return None # 该级次不存在衍射截止 # 构造衍射方向 (保持平行于刻线的分量不变) dir_parallel (ray_dir np.array([0.0, 1.0, 0.0])) * np.array([0.0, 1.0, 0.0]) dir_perp sin_d * k # 法线方向的z分量用归一化补足 dir_perp_norm np.sqrt(1 - dir_parallel dir_parallel - dir_perp dir_perp) return dir_parallel dir_perp dir_perp_norm * t这段代码里有个容易被忽略的细节光栅衍射只改变平行于光栅刻线垂直方向的分量平行于刻线方向的分量保持不变。很多第一次写追迹的人直接把入射方向按光栅方程整体旋转结果光线跑偏。上面代码先把方向分解成两个分量分别处理再合成衍射方向。3.3 评价函数怎么写RMS点列半径与波长权重优化需要一个量化像质的评价函数。光谱仪最关心的是各个波长在像面上的弥散斑大小用RMS半径做评价是常见做法。def rms_spot_radius(spots): 计算像面上光斑的RMS半径 参数 spots: N×2 数组每行是(x, y)坐标 centroid_x np.mean(spots[:, 0]) centroid_y np.mean(spots[:, 1]) rms np.sqrt(np.mean((spots[:, 0] - centroid_x)**2 (spots[:, 1] - centroid_y)**2)) return rms def evaluate_design(params, wavelengths, source_points): 评价函数: 加权RMS点列半径 参数: params: [f1, f2, alpha, beta, radius1, radius2, grating_pos_z] wavelengths: 需要评估的波长列表 source_points: 狭缝上均匀取样的点 f1, f2, alpha, beta, r1, r2, gpos params total_rms 0 for wl in wavelengths: spots [] for src in source_points: # 准直镜 collimated reflect_spherical(src, np.array([0, 0, 1]), np.array([0, 0, 0]), r1, alpha) if collimated is None: return 1e6 # 光栅衍射 diffracted diffract_grating(collimated[0], collimated[1], np.array([0, 0, 1]), 600, wl) if diffracted is None: return 1e6 # 聚焦镜 focused reflect_spherical(diffracted[0], diffracted[1], np.array([gpos, 0, f1 f2]), r2, beta) if focused is None: return 1e6 # 追迹到像面 (z f1 f2 gpos 的平面) t_image (f1 f2 gpos - focused[0][2]) / focused[1][2] image_point focused[0] t_image * focused[1] spots.append([image_point[0], image_point[1]]) total_rms rms_spot_radius(np.array(spots)) return total_rms / len(wavelengths)评价函数返回的是多个波长RMS半径的平均值。为什么要加权因为光谱仪两端波长的像差通常比中心波长大如果只优化中心波长两端会惨不忍睹。可以在每个波长的RMS乘上一个权重系数再求和把优化压力分配到整个工作波段。我这里没加权重如果你需要特定的响应平坦度可以在wavelengths对应的权重列表里调大两端的系数。3.4 优化循环用scipy的minimize做参数扫描评价函数有了剩下交给优化器。scipy.optimize.minimize支持多种算法坐标下降和Nelder-Mead是常用的选择。参数范围约束也很重要离轴角不能太小会挡光也不能太大彗差爆炸。from scipy.optimize import minimize def optimize_design(): # 初始参数: f1100, f2100, alpha8°, beta8°, r1200, r2200, gpos50 x0 np.array([100.0, 100.0, 8.0, 8.0, 200.0, 200.0, 50.0]) # 参数边界: 焦距80-150mm, 离轴角5°-15°, 曲率半径150-300mm bounds [(80, 150), (80, 150), (5, 15) * np.pi / 180, (5, 15) * np.pi / 180, (150, 300), (150, 300), (30, 80)] wavelengths np.array([400, 500, 600, 700, 800]) source_points np.array([[0, -0.25, 0], [0, 0, 0], [0, 0.25, 0]]) # 狭缝三个取样点 result minimize(evaluate_design, x0, args(wavelengths, source_points), methodNelder-Mead, boundsbounds, options{maxiter: 500, xatol: 1e-4}) return result.x, result.fun if __name__ __main__: best_params, best_rms optimize_design() print(最优参数:, best_params) print(最小RMS点列半径:, best_rms, mm)优化参数说明x0里的离轴角用角度制但reflect_spherical内部用弧度制我习惯在传入时统一转换避免混淆。狭缝源点在Y方向取-0.25、0、0.25三个位置这模拟了狭缝高度方向的视场。实际狭缝高度可能有几毫米取样点越多越接近真实但计算量呈线性增长通常取5~9个点就足够。跑完优化你会看到一个明显趋势优化的离轴角α和β不再相等而是出现几度的差异同时两个镜子的曲率半径也错开了。这就是M型结构能消彗差的原因——两边的离轴角和焦距配比产生方向相反的彗差。4. 在Zemax里复现M型光路建模参数与对比验证4.1 序列模式下搭建C-T光路的基本步骤Python优化只是第一步最终设计要在Zemax里建模验证。用序列模式Sequential Mode搭C-T光路核心操作是把坐标断点Coordinate Break用熟。基本的光路表长这样面号面型曲率半径厚度倾斜/偏心说明OBJ物面无限狭缝到准直镜距离-狭缝1坐标断点-0倾斜X/Y设置离轴角2球面反射镜准直镜曲率到光栅的距离-准直镜3坐标断点-0反向倾斜恢复光轴方向4坐标断点-0设置光栅入射角-5衍射光栅无限到聚焦镜距离-光栅6坐标断点-0设置聚焦镜离轴角-7球面反射镜聚焦镜曲率到像面距离-聚焦镜8坐标断点-0反向倾斜恢复像面方向IMG像面---探测器在Zemax里做zemax光学设计案例有一个通用技巧每个倾斜面后面紧跟一个符号相反的坐标断点把局部坐标恢复到全局坐标方向。不这样做的话后续面的坐标会基于前面的倾斜面累积光路会拧成一团新手很容易在这里翻车。4.2 光栅参数设置刻线密度、衍射级次与入射角校验衍射光栅面的关键参数有三个刻线密度lines/mm、衍射级次、以及光栅的倾斜角度。在Zemax里光栅方向是通过坐标断点设置的倾斜角来控制的它的作用是在X-Z平面内把入射光按衍射方程旋转特定角度。这里有一个经常搞错的点Zemax里光栅面的倾斜/偏心选项如果你选的是Decenter and Return或Tilt and Return光栅面倾斜后光轴方向会自动恢复不需要额外加坐标断点。但很多人习惯所有倾转面都用坐标断点控制结果同一个倾斜被计算了两次。验证光栅方向正确的方法是检查多重结构波长下的像点位置在Zemax里设置三个波长比如400/600/800nm看像面上三个波长的X坐标间隔是否符合光栅方程的线性色散关系。如果短波和长波的位置是反的说明衍射级次符号设置反了把光栅方程里的级次改成-1试试。4.3 Python优化结果进Zemax的映射关系与对比验证Python里的参数和Zemax的设置不是一一对应的需要做坐标变换。我的做法是先做一张参数映射表Python里的f1 → Zemax准直镜到光栅面的厚度Python里的f2 → Zemax光栅面到聚焦镜的厚度但实际厚度是光栅到聚焦镜的距禇在Zemax里输入的是沿光轴的距离需要重新算Python里的alpha → Zemax准直镜面的Tilt about YPython里的beta → Zemax聚焦镜面的Tilt about YPython里的半径r1/r2 → Zemax对应面的曲率半径符号要按Zemax的惯例检查一遍。验证对比方法是把Python追迹得到的像面点列图RMS半径和Zemax里的点列图RMS半径放在一起比。两者应该基本一致如果有5%以上的偏差优先检查坐标断点的倾斜方向是否镜像反了。5. M型Czerny-Turner设计避坑5个让仿真翻车的细节5.1 光栅衍射级次设错光谱方向反了现象仿真出来的光谱色散方向与预期相反短波在长波的位置上。 原因光栅方程里衍射级次的符号约定在不同软件里不一致。Python写的是sin_d sin_i mλ/dZemax里默认级次可能取-1导致方向镜像。 解决先做单波长追迹手动算出衍射角和代码里打出的中间结果比对。确认符号约定后在Python和Zemax里统一采用相同级次建议用m 1作为默认值。5.2 两片离轴镜的倾斜方向反了现象优化出来的焦距和离轴角明明与设计值一致但点列图极大系统一团糟。 原因C-T光路中准直镜和聚焦镜的倾斜方向必须相反——准直镜让光线向X正方向偏聚焦镜让光线向X负方向偏形成Z字形光路。如果两个镜子朝同一方向倾斜光路就打不开了。 解决在Zemax里显示3D光路图检查光线在准直镜和聚焦镜之间是否形成了清晰的Z字形。如果光线路径在镜间交叉、折返把其中一片镜子的Tilt about Y取负号。5.3 Python里追迹到像面的距离用错现象Python优化得到的RMS半径很小但同样的参数进Zemax点列图大得多。 原因像面位置的设定有误。C-T结构的像面不是简单的聚焦镜焦距后f2处而是要按反射镜后光线的实际汇聚点计算。很多人在追迹里用固定z坐标截取像面而这个位置未必是聚焦镜的焦平面。 解决追迹时不要固定像面位置而是先追迹中心波长的中心光线记录它在聚焦镜后的光线方向计算出与光轴的交点作为像面参考位置。然后在这个位置前后各步进几个毫米扫一遍找到RMS最小的实际像面位置——这个离焦扫描在Zemax里就是快速对焦工具。5.4 狭缝高度方向的光线取样不足现象理论分辨率看着没问题但实测光谱仪有杂散光、响应不均匀。 原因狭缝有一定高度Y方向弧矢方向的光线经过离轴球面镜后会引入像散。如果Python里只取了Y0的子午光线像散就被完全忽略了。 解决至少取3个Y方向的位置边缘、中心、负边缘更好的做法是取5个点并给边缘位置加权重因为边缘光线的像差贡献最大。在Zemax里这对应设置合适的Object Height或Field Height。5.5 公差分析没做仿真和实测对不上现象设计的RMS点列半径为5μm分辨率算出来比实测好一倍。 原因仿真用的是理想面形实际镜片有面形误差、装配有倾斜误差、光栅有刻线误差。C-T结构对离轴角特别敏感几个角分的偏差就能让边缘波长分辨率劣化明显。 解决在Zemax里跑一遍公差分析把准直镜和聚焦镜的Tilt about Y公差设置为±0.1°曲率半径公差设置为±0.5%看看像质恶化程度。如果恶化超出预期把公差收紧或者检查设计是不是正处在某个参数极值点上——优化出来的极值点往往就在公差敏感区的边缘。6. 验证设计的一套随手流程批量扫参与公差分配仿真做完了怎么确认这个M型设计是可信的我习惯做三件事。第一件事是残留像差拆解。在Zemax里打开RMS vs Field图把各波长、各视场的点列图RMS半径拉出来。只看平均值是不够的要看最大值出现在哪个角落——如果是边缘波长加狭缝边缘视场说明是彗差残留如果是中心波长但弧矢方向模糊说明像散为主。针对残留像差再微调一两轮离轴角通常能再挤掉20%的RMS。第二件事是离焦-像面位置扫描。把像面厚度设为变量从设计值前后各取10个位置记录每个位置的RMS点列半径画成一条U形曲线。曲线最低点如果不在设计值处说明聚焦镜的焦距取错了或者光栅到聚焦镜的距离偏了。这个曲线同时告诉你像面对离焦的敏感程度——U形曲线越陡说明系统景深越小装配时对距离控制的要求越高。第三件事是公差预算分配。把准直镜和聚焦镜的Tilt公差、厚度公差按等影响原则分配先用Zemax的灵敏度分析分别算每项公差对RMS的贡献然后把贡献大的项目收紧、贡献小的项目放松。这样做出来的图纸既不会过度加工浪费钱也不会因为某些项太松导致性能不稳定。最后提一个我踩过不少次的坑不要只优化中心波长。光谱仪是拿来测一段光谱范围的两端的像差通常比中心差一个量级。把工作波段均匀取5个波长点做加权评价优化出来的结构才是真的可用。现在我做光谱仪设计Python脚本里第一行就是波长列表从300nm到1100nm覆盖整个响应区间像差分析和优化都基于全波段进行这个习惯帮我少返了很多次工。希望帮到你。本文还有配套的精品资源点击获取
返回列表