当前位置:首页>python>Python光学仿真举例---单透镜

Python光学仿真举例---单透镜

  • 2026-02-28 01:11:46
Python光学仿真举例---单透镜

前言

在应用光学的学习与工程实践中,商用光学仿真软件(如 ZemaxCodeV)虽能快速输出仿真结果,但存在黑箱化问题,难以直观体现从光学公式到数值实现的核心逻辑;而课本中的公式推导又缺乏与工程化代码的直接映射。

本项目是一款面向光学专业人员的轻量级 Python 单透镜仿真系统,完全基于几何光学基本定律实现,无复杂封装,所有核心算法均与光学公式一一对应。项目实现了 3D 光线追迹、光路图 / 点列图 /光扇图可视化、成像质量量化分析(RMS 光斑)等功能,并采用模块化架构设计,可直接扩展至双胶合透镜、消色差系统、色散模拟等更复杂的光学场景。

本文将从光学专业视角,详细拆解项目的整体逻辑架构、核心模块的公式映射与代码实现、模块间调用关系,以及仿真结果的解读,为光学专业人员理解 “几何光学公式数值计算可视化呈现” 的完整流程提供参考。

一、项目定位与核心价值

1.1定位

本项目是几何光学框架下的单透镜数值仿真轻量化实现,聚焦几何光线追迹成像质量基础分析,不涉及衍射、波前分析、复杂镜头优化等高阶内容,核心目标是让光学公式在代码中可落地、可追溯

1.2设计原则

① 光学专业约定:所有参数定义、符号规则、公式实现均遵循光学行业通用标准(如曲率半径符号、光轴约定、Snell 定律矢量形式);

② 模块化解耦:按 “物理实体抽象算法实现质量分析可视化场景验证” 拆分模块,符合光学系统的分析逻辑;

③ 可扩展性:核心类 / 函数预留扩展接口(如材料折射率支持 Sellmeier 方程、透镜系统支持多表面扩展);

④ 量化分析:除可视化外,提供 RMS 光斑尺寸等定量评价指标,与光学工程中的成像质量评价标准一致。

1.3功能(光学专业视角)

功能模块

光学核心依据

工程价值

3D  矢量光线追迹

光线参数方程、Snell 定律

还原光线在光学系统中的真实传播

圆形光束均匀采样

入瞳光阑的光束能量分布

生成接近实际的光束模型

多视场追迹

视场光阑、离轴成像原理

分析场曲、像散等离轴像差

点列图可视化

光线像面聚焦分布原理

直观判断像差类型与大小

光扇图可视化

瞳坐标 - 像面偏差的对应关系

定量分析球差、彗差、像散

RMS  光斑计算

成像质量量化评价标准

客观对比不同光学系统的成像性能

二、项目整体逻辑架构与模块调用关系

项目采用分层模块化架构,按 “物理实体抽象核心算法实现成像质量分析可视化渲染工程场景验证” 的逻辑链路设计,所有模块均基于 Python 基础库(numpymatplotlib)实现,无第三方光学仿真依赖,代码可直接调试、修改。

2.1整体架构图

singlet_optics/

    __init__.py

    core/                 # 核心光学数据结构

  │     __init__.py

  │     ray.py            # 光线 Ray

  │     surface.py        # 表面 Surface(曲率、厚度、材料、孔径)

  │     material.py       # 材料 Material(折射率)

  │     lens.py           # 单透镜 / 系统 Lens

    trace/                # 几何光线追迹算法

  │     __init__.py

  │     geometrical.py    # Snell 折射、直线传播

  │     tracer.py         # RayTracer:按顺序追迹系统中所有表面

    visual/               # 绘图与可视化

  │     __init__.py

  │     rayplot.py        # 光路图(系统剖面 + 光线,使用实际透镜曲率和孔径绘制透镜外形)

  │     spotplot.py       # 点列图(像面光斑)

  │     ray_fan_plot.py   # 光扇图(子午光扇、弧矢光扇、合并光扇)

    analysis/             # 简单分析工具

  │     __init__.py

  │     spot.py           # 计算光斑 RMS 

  │     ray_fan.py        # 计算光扇图数据

    examples/             # 使用示例

  │     single_lens_demo.py      # 单透镜光线追迹 + 光路图 + 点列图 + 光扇图

  │     ray_fan_utils.py         # 光扇图光线生成工具

  │     field_utils.py           # 多视场光线生成工具

  │     multi_field_demo.py      # 多视场光线追迹 + 光路图 + 点列图 + 光扇图

    tests/                # 单元测试

  │     test_core.py

  │     test_trace.py

    README.md             # 项目说明文档

    .gitignore            # Git 忽略文件

    PROJECT_STRUCTURE.md  # 项目结构说明

2.2模块功能与输入输出关系

模块

核心功能

输入(光学实体 / 数据)

输出(仿真结果 / 数据)

依赖模块

core

光学物理实体的代码抽象

光学参数(焦距、曲率、折射率、厚度、半孔径)

Ray/Surface/Material/LensSystem  类实例

trace

几何光线追迹核心算法

core  层的实体类实例、光线初始参数(原点、方向、波长)

追迹后的光线(含传播路径 path

core

analysis

成像质量量化分析

trace  层的追迹后光线数据、视场角

RMS  光斑尺寸、光扇图像差数据、主光线像点位置

coretrace

visual

光学结果可视化

trace/analysis  层的仿真数据

光路图、点列图、光扇图

coretraceanalysis

field_utils

多视场光线生成工具

视场角、入瞳参数(半径、采样方式)

带视场标记的 3D 平行光束

core

ray_fan_utils

光扇光线生成工具

光瞳半径、采样点数、视场角

子午/弧矢光扇光线

core

examples

工程场景演示

所有底层模块

完整仿真结果(图表 + 量化数据)

所有模块

tests

单元测试

core/trace  层核心函数

算法正确性验证结果

coretrace

2.3数据流转逻辑

光线参数是整个系统的核心流转数据,其生命周期为:

光线初始生成(field_utils/examples→3D矢量定义(core/Ray几何追迹(trace路径记录(Ray.path质量分析(analysis,提取像面点)可视化(visual,提取路径/像面点数据)

所有可视化图表和量化分析结果,均来自追迹后 Ray 类的 path 属性(光线传播的离散 3D 坐标点),保证了数据的统一性和可追溯性。

三、核心光学实体的代码抽象(core 模块)

光学系统的本质是光线不同介质的光学表面上的传播与折射,core模块的核心作用是将光线、光学表面、材料、透镜系统这些物理实体,按几何光学规则抽象为可被代码计算的类,所有类的参数定义、符号规则均遵循光学行业通用标准,是后续算法实现的基础。

3.1 Ray 类:3D 矢量光线的抽象(core/ray.py

3.1.1光学依据

在几何光学中,光线被视为一条有向直线,其在三维空间中的轨迹可用参数方程表示为:

其中:

:光线起点的位置矢量

:光线的单位方向矢量),决定光线在空间中的传播方向;

标量参数,在 为单位向量的前提下,可直接理解为从起点沿方向 传播的距离;表示沿光线传播方向前进。

单透镜以及大多数成像系统通常为轴对称系统,因此本项目采用光学设计中通用的右手直角坐标系约定:

约定 轴为光轴,光线整体从左向右沿方向传播(物方 → 像方);

x-y 平面为垂直于光轴的横向平面,用于描述光线在光瞳/像面的横向分布;

在可视化时,常取 y–z 子午面投影,将光线在 y 方向的偏离随 z 的变化画出,便于直观展示单透镜的会聚过程。

在此约定下,一条 3D 光线可完全由「起点 单位方向 」唯一确定,后续所有几何追迹(与球面/平面的交点计算、表面法线计算、Snell 折射等)都建立在这一 3D 参数方程之上。因此,Ray 类在代码层面对上述物理量进行显式建模:

origin 对应 ,记录光线初始位置;

direction对应,在构造/更新时强制归一化,保证 (t) 与物理传播距离一致,有利于数值稳定;

path 用于离散记录光线在各光学表面及像面上的交点坐标 ((x,y,z)),为后续点列图、光扇图和 RMS 光斑计算提供统一数据源;

field_angle记录该光线所属的视场角(单位:度),在多视场仿真中用于区分轴上()与离轴光线。

通过上述抽象,几何光学中「一条 3D 有向直线」的物理概念,被完整映射为可计算、可追踪、可视化的 Ray 对象,为后续 traceanalysisvisual 模块的所有算法提供了统一的光线基础描述。

3.1.2代码实现与光学映射

class   Ray:
"""
三维光线:x, y, z 坐标系,为光轴"""
origin: Vec3# (x, y, z)
direction: Vec3
单位方向向量 (dx, dy, dz)
wavelength: float = 587.6e-9

追迹过程中累计的路径点:[(x0, y0,   z0), (x1, y1, z1), ...]
path: List[Vec3] =   field(default_factory=list)
field_angle: float = 0.0
这条光线对应的视场角(度)

def __post_init__(self):
if not self.path:
self.path.append(self.origin)

def add_point(self, point: Vec3):
self.path.append(point)

def normalize_direction(self):
d = np.array(self.direction,   dtype=float)
d /= np.linalg.norm(d)
self.direction = (float(d[0]),   float(d[1]), float(d[2]))

3.1.3属性的光学意义

origin/direction:严格遵循光线参数方程,direction 单位矢量(保证光线追迹的数值计算精度);

wavelength:默认取 d 光(587.6nm),为目视光学系统的基准波长,预留波长接口用于后续色散模拟;

path:追迹过程中记录的光线与各光学表面的交点 + 像面点,是可视化和分析的核心数据来源,每个元素为 (x,y,z) 3D 坐标;

field_angle:标记光线的离轴视场角,为多视场追迹的视场区分依据0° 表示轴上视场。

3.2 Surface 类:光学表面的抽象(core/surface.py

3.2.1光学依据

单透镜可以看作由两个光学表面(球面或平面)组成,光线在各表面处发生折射。每个表面的核心光学参数包括:曲率半径、有效通光口径、入射侧与出射侧介质以及到下一个表面的中心厚度。通常约定光线沿+z方向(从左向右)传播,在此约定下,曲率半径 (R) 的符号规定为:

:表面凸向光线传播方向,即球心位于表面顶点的 一侧;

:表面凹向光线传播方向,即球心位于表面顶点的 (-z) 一侧;

:表示平面表面,此时曲率为0

3.2.2代码实现与光学映射

class   Surface:
radius: float
曲率半径,>0 凸向右,<0 凹向右,=inf 为平面
thickness: float到下一个表面的距离(mm
material_before: Material入射侧介质
material_after: Material出射侧介质
semi_diameter: float半孔径(mm
is_stop: bool = False 是否光阑面(本最小版可暂不使用)

@property
def curvature(self) -> float:
if abs(self.radius) < 1e-9   or self.radius == float('inf'):
return 0.0
return 1.0 / self.radius

3.2.3属性的光学意义

radius:曲率半径的符号规则是光线追迹正确性的关键,直接决定球面的球心位置;

thickness:轴向厚度为轴方向的距离,是光学系统结构尺寸的核心参数,薄透镜可设为极小值(如 1e-3mm);

material_before/material_after:为 Snell 定律提供两侧介质的折射率,是折射计算的核心参数;

semi_diameter:通光口径的半值,用于判断光线是否超出通光范围(可扩展挡光判断逻辑);

is_stop:光阑面标记,为后续入瞳 / 出瞳计算、光束限制提供依据,单透镜系统可默认设为 False

3.3 Material 类:光学材料的抽象(core/material.py

3.3.1光学依据

在几何光学中,介质的折射率 (n)是决定光线如何在不同材料界面发生折射的核心参数。Material 类的作用,就是在代码层面为每一种光学材料提供一个统一的、可计算的折射率模型。

从物理上看,可以分两种典型情形:

单色光(monochromatic)近似在很多基础几何光学仿真中,只关心某一固定波长(比如 d 线 587.6 nm)下的成像情况,此时可以将材料的折射率视为常数

例如:BK7  d 光(587.6 nm)下的折射率约为 1.5168,在仅做单色轴上成像分析时,使用这个常数已经足够。

复色光与色散(polychromatic & dispersion当考虑白光或多波长成像时,折射率会随波长变化,即色散。常用的经验公式是Sellmeier 方程,它给出了折射率随波长变化的高精度拟合:

其中:

:光在真空中的波长(工程上常用 µm  nm 表示,使用时需统一单位)

:材料的 Sellmeier系数(与色散强度相关)

:材料的 Sellmeier系数(与色散特征波长相关)

这组 系数由玻璃厂商提供,典型如Schott 给出的 BK7SF11 等光学玻璃数据表。对给定波长,代入上式即可求得该波长处的折射率:

在本项目当前的单透镜几何光学仿真中:

默认采用单色光近似,用一个常数 描述材料折射率,例如 BK7取 光);

Material 类的n(wavelength) 方法预留了波长参数,后续只需在该方法内部将常数形式替换为 Sellmeier 方程,即可无缝扩展到多波长追迹和色散分析(如模拟色差、设计消色差透镜等),而无需修改 traceanalysisvisual等其他模块的代码结构。

3.3.2代码实现与光学映射

class   Material:
name: str
n_const: float
简化:不考虑色散,常数折射率

def n(self, wavelength: float) ->   float:
"""
将来可扩展成 Sellmeier,这里先返回常数。"""
return self.n_const

常用实例AIR =   Material("AIR", 1.0)

   def glass_bk7() -> Material:
return Material("BK7",   1.5168)

3.3.3设计的光学考量

n_const:为单色光仿真提供简化模型,取光学玻璃的基准折射率(如 BK7  d 光折射率 1.5168),符合单透镜基础仿真的需求;

n () 方法:预留波长入参和返回值,后续仅需修改该方法的实现即可支持色散模拟,无需改动其他模块的代码,保证了架构的可扩展性;

玻璃实例化函数:按光学玻璃牌号封装,符合工程中 “按牌号选择玻璃” 的习惯。

3.4 LensSystem 类:单透镜系统的抽象(core/lens.py

3.4.1 光学依据

单透镜是最基础的成像光学系统,其光路可以抽象为:

物面 → 前表面 → 后表面 → 像面

在几何光学中通常约定光线沿着 +z 方向从左向右传播,因此在代码中也要求光学系统按这一顺序组织。LensSystem 类正是以「顺序系统」的思想,将所有光学表面按光线实际通过的先后次序依次存入列表,使后续的光线追迹可以简单地从左到右遍历这些表面,从而与物理传播过程一一对应。

在一阶成像理论中,单透镜的焦距可由经典的透镜制造者公式(Lensmaker’s Equation给出。对折射率为 (n)、前后表面曲率半径分别为 、中心厚度为 (d) 的厚透镜,其有效焦距 (f) 满足:

其中符号约定为:

光线沿 +z 方向(从左到右)传播;

第一表面的曲率半径,顶点在该表面上,若球心位于顶点右侧(朝 +z)则 ,表示表面凸向光线传播方向

第二表面的曲率半径,定义方式同上,通常对对称双凸单透镜有

(d):两表面顶点之间的中心厚度;

当 时,对应平面表面,曲率为 0

当厚度 时,上式自然退化为薄透镜公式:

这就是通常意义下「薄透镜的焦距公式」。在一阶近似中,给定材料折射率 (n) 以及所需焦距 (f),可以反过来设计 (例如对称取 ),从而完成单透镜的初始参数设计。

本项目的 LensSystem 设计遵循如下原则:

① 顺序存储光学表面将物方起始面(如物面或入瞳平面)、透镜前表面、透镜后表面、像面等,按光线从左到右的顺序依次存入 surfaces 列表。这样,几何追迹算法只需按列表顺序迭代,便可以完成整个系统的光线传播仿真,逻辑与物理过程高度一致。

② 用一阶公式指导结构,用数值追迹验证焦距

在构造单透镜(如 build_single_lens(f=50.0))时,可以利用上述薄透镜公式,在给定目标焦距 (f) 和材料折射率 (n) 的前提下,反推出合适的曲率半径组合 ,完成「按焦距设计透镜」的一阶结构初选;

但在实际仿真与结果验证时,本项目并不依赖公式直接计算像距或焦距,而是采用 数值光线追迹:对平行入射光束进行全程追迹,再根据光线在像面附近的会聚位置,反推出系统的实际焦点位置与有效焦距,并用点列图、光扇图、RMS 光斑尺寸等手段评价成像质量。

③ 贴近真实光学系统的工程思路采用「一阶公式做初设 + 数值追迹做验证与优化」的方式,有以下优势:

避免只停留在理想薄透镜模型,能够自然涵盖厚度、非对称曲率等实际结构带来的偏差;

后续扩展到更复杂的系统(如双胶合透镜、消色差透镜、多片镜头)时,依然可以沿用同样的LensSystem + RayTracer 框架,而不必为每一种结构重新推导封闭形式的焦距公式;

更贴近光学工程中「先做一阶近似设计,再用光线追迹和像差分析修正结构」的实际工作流程。

LensSystem 在光学上相当于把「单透镜的前后表面及相关空间面」按传播顺序组织成一个有序的光学系统,并以一阶透镜制造者公式为理论参照,通过数值追迹来验证和理解该系统的真实成像行为和有效焦距,从而在代码层面自然地连接了理论公式与工程仿真。

3.4.2代码实现与光学映射

class   LensSystem:
"""
顺序光学系统:按从左到右的顺序存放表面"""
surfaces: List[Surface] =   field(default_factory=list)
name: str = "SingleLens"

def add_surface(self, surface:   Surface):
self.surfaces.append(surface)

   def build_single_lens(f: float = 50.0) -> LensSystem:
"""
构造一个简单单透镜系统:
左侧无限远物(用近似平行光射入表示)
一片 BK7 薄透镜,前后曲率对称,厚度忽略或给一个小值
右侧像面为 z   = image_plane_z
"""
lens =   LensSystem(name="SingleLensDemo")
glass = glass_bk7()

修改参数:增大半孔径和厚度,使10度光线能在透镜口径范围内
焦距约 50mm 的透镜,R1 = 100mm, R2 = -100mm, 中心厚 10mm
介质:左侧空气   -> 玻璃 -> 空气
surface1 = Surface(radius=100.0,   thickness=10.0,
material_before=AIR, material_after=glass,
semi_diameter=30.0)
增大半孔径到30mm
surface2 = Surface(radius=-100.0,   thickness=50.0,
material_before=glass, material_after=AIR,
semi_diameter=30.0)
增大半孔径到30mm

lens.add_surface(surface1)
lens.add_surface(surface2)

return lens

3.4.3设计的光学考量

表面顺序:按 ** 光线传播方向(从左到右)** 存储,与光线追迹的顺序一致,是追迹算法的基础;

build_single_lens 函数:封装薄透镜的表面构造逻辑,根据目标焦距自动计算曲率半径,符合 “按焦距设计透镜” 的工程需求;

对称透镜:前后表面曲率半径取R1=R2,是最常用的单透镜结构,可减少像差(如球差)。

四、几何光线追迹的核心算法实现(trace 模块)

光线追迹是光学仿真的核心算法,trace 模块基于几何光学基本定律,实现了光线与球面/平面的交点计算表面法线计算Snell 定律的 3D 矢量折射批量光线追迹四大核心功能,所有算法均为数值精确实现,无近似(除薄透镜的厚度近似),是后续所有分析和可视化的基础。

本模块所有函数均与core层实体类深度联动,直接调用Ray/Surface/LensSystem的光学参数,追迹结果也直接写入Ray类的path属性,保证了项目数据流转的统一性;同时算法实现严格遵循光学行业的符号规则和矢量运算规范,与 ZemaxCodeV 等商用软件的追迹逻辑一致,确保仿真结果的专业性和可比性。

4.1 光线与球面 / 平面的交点计算trace/geometrical.py - intersect_spherical_surface

4.1.1 完整光学公式推导

1)光线的 3D 参数方程

在本项目约定的右手坐标系下,轴为光轴,光线整体沿方向从左向右传播。一条三维光线可写为参数方程:

其中:

:光线起点位置矢量;

:光线单位方向矢量,满足 

(t) 为标量参数,表示沿光线传播方向前进,在 单位化的前提下可直接视为传播距离

2)球面方程与曲率半径符号约定

考虑光轴为 z 轴的旋转对称球面,其轴上顶点位于:

光学行业对曲率半径 (R) 采用统一符号约定(光线沿方向从左到右传播):

:表面凸向光线传播方向(凸向右),球心在顶点的 一侧;

:表面凹向光线传播方向(凹向右),球心在顶点的 (-z) 一侧;

:平面表面,曲率为 0

在这一约定下,球心坐标统一写为:

无论 (R) 正负,都自动给出球心在顶点左/右的位置。

球面方程为:

即:球心到表面上任意点的距离恒为 

3)代入光线方程,推导二次方程系数

将 代入球面方程:

令 ,则:

展开:

整理为关于 (t) 的一元二次方程:

其中系数严格推导为:

又因为本项目在 Ray 类中强制 direction 为单位向量,即:

故有:

这保证了参数 (t) 的数值就是几何意义上的传播距离,既方便物理理解,又有利于数值稳定性。这也是 Ray.normalize_direction() 的核心意义所在:只有在 单位化的前提下,求得的 (t) 才有明确的物理尺度含义。

4.1.2解的物理筛选规则

形式上,二次方程的解为:

但在光学追迹中,仅保留有物理意义的有效交点。本项目采用如下筛选规则:

① 判别式条件:确保有实交点

若 :光线与该球面无任何实交点,直接判定为无交点;

若 :光线与球面相切,只有一个交点;

若 :光线与球面有两个数学交点,需要进一步物理筛选。

② 传播方向限制:只保留 的解

才表示交点位于光线传播方向上;

实际数值运算中,由于浮点误差,极小的正值或接近 0 的数值有可能被算成微小负数,因此本项目引入一个容差:

这样可以避免将本应视为当前点附近、几乎不前进的交点误判为反方向上的负解

③ 最近交点优先:取最小的正解

当有两个正解 时:

选择:

对应光线从当前起点出发,在传播方向上首先遇到的表面交点;

这符合真实物理过程:光线总是先撞到离自己最近的那一段表面。

④ 通光口径校验:判断是否落在有效孔径内

对最终候选解 (t),计算交点坐标:

检查其相对于光轴的横向距离:

若满足:交点在表面有效通光口径内,视为有效交点

若不满足:说明该光线打在了透镜外缘之外(被机械装调或光阑挡住),在几何仿真中应视为无有效交点(即该光线不继续参与后续传播与像质计算)。

综合以上 4 条规则,intersect_spherical_surface 在数学上保证解存在且实数,在物理上保证交点在传播方向最近且落于有效孔径内,从而实现了既正确又符合工程实际的交点判定逻辑

4.1.3平面特例的算法优化

当表面为平面时,曲率半径 ,曲率 ,球面方程在轴向上退化为简单的平面方程:

此时无需再解一元二次方程,可以直接从光线的 z 分量出发,推导交点参数:

其中 (dz) 为光线方向矢量 z 分量。

物理与数值筛选规则如下:

① 平行性判断

若 (例如 ):说明光线方向几乎完全平行于平面 ,则它与该平面无交点(或在数值上交点远在无穷远处),可直接返回无交点

② 传播方向判断

若计算得 :交点在光线反向或刚好在起点之前,无物理意义

仅当 时,才认为光线在传播方向上真正走到了该平面。

③ 通光口径校验

对有效 (t) 计算交点坐标:

同样检查:

不满足时,认为光线在该平面上的交点超出了有效通光范围,视作无有效交点

④ 与球面情况的统一性

在代码实现层面,可以采用如下统一策略:

若 足够大(例如 )或显式标记为平面(如radius == float("inf")),则走平面专用分支,使用上述线性交点公式;

否则按一般球面逻辑,构造 (A,B,C) 并解一元二次方程。

这样既保留了理论上的严格性,又在工程实现上避免了在平面上解退化二次方程导致的数值不稳定;大幅简化了平面交点计算,提升运算效率;让球面 / 平面在同一 intersect_spherical_surface 接口下自然统一,便于扩展和维护。

4.1.4 代码实现与光学映射

函数输入直接调用coreRay类的origin/directionSurface类的radius/semi_diameter/顶点,输出为交点有效性标记参数3D交点坐标,同时嵌入通光口径校验逻辑,代码与公式一一对应:

def   intersect_spherical_surface(ray: Ray, surface: Surface, z_surface: float)   -> Tuple[float, float, float]:
"""
计算光线与球面/平面交点 (x, y, z)
3D
,光线方程 x =   x0 + t*dx, y = y0 + t*dy, z = z0 + t*dz
球面中心: (0,   0, z_c),半径 R(系统轴对称)
"""
x0, y0,   z0 = ray.path[-1]
dx, dy, dz = ray.direction

R = surface.radius
if surface.curvature == 0:
平面:z = z_surface
t =   (z_surface - z0) / dz
return x0 + t * dx, y0 + t *   dy, z0 + t * dz

球心坐标(系统轴对称,球心在光轴上)
zc =   z_surface + R约定:表面顶点在 z_surface,球心在其左右 R
xc, yc =   0.0, 0.0

 |P(t) - C|^2 = R^2
# (x0 + dx t - xc)^2 + (y0 + dy t -   yc)^2 + (z0 + dz t - zc)^2 = R^2
A = dx**2   + dy**2 + dz**2
B = 2 * (dx * (x0 - xc) + dy * (y0   - yc) + dz * (z0 - zc))
C = (x0 - xc)**2 + (y0 - yc)**2 +   (z0 - zc)**2 - R**2

disc = B**2 - 4 * A * C
if disc < 0:
raise RuntimeError("
光线与球面无实交点")

t1 = (-B - np.sqrt(disc)) / (2 * A)
t2 = (-B + np.sqrt(disc)) / (2 * A)
t = min(t1, t2) if t1 > 0 else   t2
取向前的交点

return   x0 + dx * t, y0 + dy * t, z0 + dz * t

4.2 表面法线计算(trace/(geometrical.py - calculate_surface_normal

4.2.1 光学依据

在几何光学中,光线在光学表面上发生折射或反射时,入射角、折射角(或反射角)都是相对于表面法线来定义的。对于本项目采用的 3D 矢量 Snell 定律形式,表面法线必须以单位矢量形式参与计算,因此在每个光线与表面的交点处,精确、统一地定义表面法矢量是折射计算的前提。

在约定光线沿 方向(从左向右)传播、光轴为轴的坐标系中,表面法线在光学上的标准定义为:

① 球面法线

对于曲率半径为 (R) 的旋转对称球面,其球心位于

其中 为该表面顶点在 z 轴上的坐标(与交点计算中完全一致),(R) 的符号遵循行业约定:

:表面凸向光线传播方向,球心在顶点的 一侧;

:表面凹向光线传播方向,球心在顶点的 (-z) 一侧。

设光线与表面的 3D 交点为

球面法线定义为过球心与交点的有向直线,从球心指向交点的方向。相应的法矢量为:

这种定义与球面几何完全一致:

为球心指向表面交点的半径矢量;

归一化后即为单位法矢量,垂直于该点处的球面切平面。

② 平面法线

当表面为平面时,曲率半径 ,曲率为 0,表面方程可写为:

对于这种与光轴垂直的平面,其几何法线沿 z 轴方向。根据本项目光线从左向右沿 传播的约定,将平面法线统一取为指向方向的单位矢量

这对应于「平面为 时,法矢量为((0,0,1)),指向光线传播方向」。

③ 法矢量统一指向光线入射侧

为了在 3D 矢量 Snell 定律中获得一致、无歧义的符号规则,本项目对所有表面的法矢量采用统一约定:

在每一个光学表面上,表面法矢量 的方向始终定义为指向光线入射侧的单位矢量。

这样做的意义在于:

入射角 一律通过来计算,其中 为入射光线单位方向矢量,指向入射侧,无需在代码中额外区分哪一面是物方/像方以及法线是否需要翻转

全反射判据、折射方向计算等公式均基于 的符号和大小,若法线方向不统一,就会导致同一公式在不同表面上出现符号反转甚至物理解读错误;

将法线统一指向入射侧,相当于为 Snell 定律建立了一个全局固定的正方向参考,使折射方向的求解在所有表面上都遵循同一套符号规则,有利于保证追迹结果的物理正确性和代码实现的一致性。

综上,表面法线的光学定义可以概括为

球面:以球心为起点、指向交点的单位半径矢量;

平面:沿光轴方向的单位向量(在本项目中统一取为((0,0,1)),指向光线传播方向一侧);

所有表面的法矢量在使用前必须归一化为单位矢量,并且其方向统一视为「指向光线入射侧」,以为后续 Snell 定律的 3D 矢量折射计算提供稳定、一致的几何基础。

4.2.2 代码实现与光学映射

函数输入为光线与表面的交点、表面参数,输出为归一化的3D单位法矢量,直接为后续Snell定律折射提供输入,与光学定义完全一致:

def surface_normal(surface:   Surface, point: Tuple[float, float, float], z_surface: float) ->   np.ndarray:
"""
计算表面在交点处的法线(指向入射介质一侧或出射一侧,可约定)"""
x, y, z =   point
if surface.curvature == 0:
平面,法线沿 +z  -z,这里取 +z
n =   np.array([0.0, 0.0, 1.0])
else:
R = surface.radius
zc = z_surface + R
xc, yc = 0.0, 0.0
n = np.array([x - xc, y - yc, z   - zc])
从球心指向表面
归一化
return n   / np.linalg.norm(n)

4.3 Snell 定律的3D 矢量折射(trace/geometrical.py- refract_ray

4.3.1 光学依据

几何光学中常见的 Snell 定律标量形式

只适用于子午面内的2D 情形,即光线、法线和光轴共面的情况。本项目采用的是3D 矢量光线追迹,光线和表面法线在空间中可指向任意方向,因此需要使用Snell 定律的 3D 矢量形式来计算折射方向。

① 3D 矢量 Snell 定律的基本形式

在入射介质折射率为 、出射介质折射率为 的界面上,设:

:入射光线的单位方向矢量

:折射光线的单位方向矢量

:表面交点处的单位法矢量,且方向统一定义为指向入射侧

Snell 定律的 3D 矢量形式可以写成:

上式含义是:在界面处,入射光线和折射光线在切向方向上的分量(即与法线垂直的部分)满足 Snell 定律。

在此基础上,可以推导出折射光线方向矢量 的显式表达式:

其中:

:光线入射侧介质折射率(Surface.material_before.n());

:光线出射侧介质折射率(Surface.material_after.n());

:入射光线单位方向矢量(Ray.direction,在 Ray.normalize_direction() 中已归一化);

:折射光线单位方向矢量(refract_ray计算输出);

:表面交点处的单位法矢量(由calculate_surface_normal 计算,方向统一为指向入射侧)。

这正是 trace/geometrical.py - refract_ray 函数中折射方向计算公式的理论依据。

② 入射角的定义与计算

入射角 定义为入射光线方向 与表面法线 之间的夹角。在法线已经统一为指向入射侧的前提下,有:

由于 与 都是单位矢量,故有:

这与项目中使用的入射角计算完全一致,为后续的全反射判断提供了直接依据。

③ 全反射判断条件

当光线从光密介质射向光疏介质时(即 ),可能发生全反射经典 Snell 定律给出全反射条件为:

结合上面对 的表达,有:

等价地,

一旦上述条件成立,将不再存在实数的折射方向解,物理上对应光线完全被反射。因此在代码实现中:

当判断出满足全反射条件时,refract_ray 应返回无折射光(例如 (False, None));

上层追迹逻辑据此判定:该光线在此表面不再产生透射部分,可视为追迹终止(或进入反射分支,若后续扩展反射模型)。

④ 与项目实现的一致性说明

结合上述光学依据与项目中已有实现,可以总结出refract_ray 的关键约定与行为:

输入量:

入射光线:Ray 实例,提供单位方向矢量 direction 和波长 wavelength

光学表面:Surface 实例,提供入射/出射侧材料(material_before / material_after),从而得到

表面法线:由calculate_surface_normal 计算的单位法矢量 ,方向统一为指向入射侧

内部计算:

通过 计算入射角的余弦;

利用 与 判定是否发生全反射;

若无全反射,则按 3D 矢量 Snell 定律公式计算折射方向 ,并对结果再归一化,保证仍为单位矢量。

输出量:

has_refract:布尔量,表示是否存在折射光(即是否未发生全反射);

d2:折射光线的 3D 单位方向矢量,用于更新 Ray.direction 并继续后续表面的追迹。

通过以上设计,refract_ray 在数值上与 Snell 定律的 3D 矢量形式一一对应,同时配合统一的法线方向约定和全反射判定,使得整个追迹流程在任意 3D 几何配置下都具有明确、正确的物理含义。

4.3.2 代码实现与光学映射

函数输入直接调用coreSurface类的介质属性(获取)、入射光线方向矢量、表面法矢量,输出为折射有效性标记3D折射单位方向矢量,嵌入全反射判断逻辑,算法与3D矢量Snell定律完全对应:

def   refract(direction: np.ndarray,
normal: np.ndarray,
n1: float, n2: float) ->   np.ndarray:
"""Snell 
折射,direction 为入射方向(指向传播方向)"""
# 3D 
版向量折射
d = direction /   np.linalg.norm(direction)
n = normal / np.linalg.norm(normal)

使法线指向入射介质
if np.dot(d, n) > 0:
n = -n

cos_i = -np.dot(d, n)
sin2_i = max(0.0, 1.0 - cos_i**2)
n_ratio = n1 / n2
sin2_t = n_ratio**2 * sin2_i

if sin2_t > 1.0:
全反射,这里可以先抛出异常或返回反射方向
raise RuntimeError("全反射发生")

cos_t = np.sqrt(1.0 - sin2_t)
折射方向公式
t =   n_ratio * d + (n_ratio * cos_i - cos_t) * n
return t / np.linalg.norm(t)

4.4 批量光线追迹(trace/tracer.py - RayTracer

 3 个功能(光线与球面/平面交点、表面法线、Snell 折射)为原子算法,实现单步的几何运算;RayTracer 类则将这些原子算法工程化封装为完整的追迹流程,实现:

单条光线在整个透镜系统中的连续传播(实例方法 trace_ray);

多条光线的批量追迹(实例方法 trace_rays)。

它是 trace 模块对外提供的核心接口,直接对接后续的 analysis  visual 模块:所有的 RMS 光斑、点列图、光扇图等分析与可视化,最终都只依赖于 Ray.path 中记录的 3D 坐标序列。

整体追迹流程仍然严格遵循光学系统的光线传播顺序:从左到右遍历 LensSystem 中的所有光学表面,对每条光线依次执行「交点计算 → 法线计算 → 折射计算」,并将每一表面的交点写入 Ray.path,最终再传播到像面并记录像面交点坐标。

4.4.1 RayTracer 类与光学系统坐标

当前工程实现采用一个面向对象的追迹器类 RayTracer,而不是若干个独立的函数。其核心结构如下:

class RayTracer:
def __init__(self, lens:   LensSystem, z0: float = 0.0):
"""
z0: 
第一表面的顶点位置(例如 z=0
系统中每个   surface.thickness 用来累加 z 位置
"""
self.lens   = lens
self.z0 = z0
self.surface_positions =   self._compute_surface_positions()

def   _compute_surface_positions(self) -> List[float]:
"""
计算每个 surface 顶点的绝对 z 坐标"""
positions   = []
z = self.z0
for s in self.lens.surfaces:
positions.append(z)
z += s.thickness
return positions

z0 作为第一表面顶点的 z 坐标,确定了整个光学系统在全局坐标中的位置。通常可取 z0 = 0.0

_compute_surface_positions()通过累加 surface.thickness,得到每一个表面顶点在 z 轴上的绝对坐标 z_surf,后续所有的交点/法线计算都在这一全局坐标系中进行。

RayTracer持有一个 LensSystem 实例,后续对所有光线的追迹都共享同一套表面数据和空间布局。

4.4.2单条光线完整追迹(trace_ray 实例方法)

RayTracer.trace_ray对单条光线执行完整 3D 追迹。当前实现采用实例方法,并且像面位置采用简单的固定距离策略,而不是显式传入image_plane_z 参数。

def trace_ray(self, ray: Ray)   -> Ray:
"""
单条光线在当前 LensSystem 中的完整 3D 追迹,追迹结果写入 ray.path

   :param ray: 
待追迹光线,core/Ray 实例
- ray.origin
:光线起点 3D 坐标
- ray.direction
:单位方向矢量
- ray.wavelength
:波长(用于查材料折射率)
   :return: 
更新后的 Ray 实例(与输入是同一个对象)追迹完成后:
   - ray.path 
中依次记录了光线在各表面以及像面上的 3D 交点坐标:
[
起点?, 表面1交点表面2交点, ..., 像面交点]
(是否包含起点取决于上层在调用前是否先 add_point
   """
current_ray   = ray
第一阶段:依次穿过所有光学表面
for   surface, z_surf in zip(self.lens.surfaces, self.surface_positions):
# 1) 
光线与当前表面的交点
p_int =   intersect_spherical_surface(current_ray, surface, z_surf)
current_ray.add_point(p_int)

# 2) 
计算交点处表面法线
n =   surface_normal(surface, p_int, z_surf)

# 3) 
根据 Snell 定律计算折射方向
n1 =   surface.material_before.n(current_ray.wavelength)
n2 =   surface.material_after.n(current_ray.wavelength)
d_new =   refract(np.array(current_ray.direction), n, n1, n2)
current_ray.direction =   (float(d_new[0]), float(d_new[1]), float(d_new[2]))

第二阶段:通过最后一个面后,直线传播到像面
z_last =   self.surface_positions[-1] + self.lens.surfaces[-1].thickness
z_img = z_last   + 50.0
像面位置,可根据需要调整/优化x0, y0, z0 = current_ray.path[-1]
dx, dy, dz = current_ray.direction
t = (z_img - z0) / dz
p_img = (x0 + dx * t, y0 + dy * t,   z0 + dz * t)
current_ray.add_point(p_img)

return current_ray

4.4.3多光线批量追迹(trace_rays 实例方法)

批量追迹的当前实现同样是 RayTracer 的实例方法:

def   trace_rays(self, rays: List[Ray]) -> List[Ray]:
"""
多光线批量追迹:
:param rays: 
待追迹光线列表,可包含多视场、多孔径光线
:return: 
追迹完成后的光线列表(与输入列表中的对象相同)

对每条光线依次调用   trace_ray,并将追迹结果写入对应 Ray.path
当前实现不筛选失败光线(无出瞳/全反射判断),所有输入光线都会被返回。
"""
return [self.trace_ray(r) for r in   rays]

4.4.4 trace 模块的联动机制

trace模块作为算法追迹层,是连接 core 实体层和 analysis/visual 应用层的核心桥梁,其与各层的联动严格遵循项目的数据流转逻辑,无任何冗余数据定义,保证了项目的模块化和解耦性。

 core 层的联动
trace 
模块直接读取 RaySurfaceLensSystem 的光学参数(如光线起点与方向、曲率半径、折射率、厚度等),不对这些物理量做任何形式的二次封装或重定义。追迹结果仅通过统一的数据出口写入 Ray.path 属性,记录光线在各光学表面及像面上的 3D 交点坐标,从而保证 core 实体层的唯一性与一致性。

 analysis 层的联动
analysis 
模块只依赖追迹完成后的 Ray 对象,不直接调用 trace 模块的内部函数。它直接从 Ray.path 中提取像面交点坐标,用于计算 RMS 光斑尺寸、光扇图像差等像质指标。这样一来,analysis 层对 trace 的依赖完全通过数据接口完成,而不是通过算法实现细节,便于后续替换或扩展追迹实现。

 visual 层的联动
visual 
模块同样只依赖 Ray.path

 Ray.path 提取光线传播路径上的 3D 坐标,并在 y–z 子午面或其他投影平面上绘制 2D 光路图;

 Ray.path 的像面交点提取 (x, y) 坐标,绘制点列图、光扇图等成像结果可视化。

整个可视化过程只关心轨迹数据本身,与具体的交点求解、法线计算、折射实现等追迹细节完全解耦。

五、成像质量量化分析(analysis 模块)

analysis 模块基于 trace 模块追迹后的 Ray.path 数据,实现基础的成像质量定量分析。当前版本聚焦两个最核心、也是工程中最常用的单透镜评价数据:

像面 RMS 光斑尺寸(analysis/spot.py

子午/弧矢光扇数据(analysis/ray_fan.py

模块本身不参与任何光线追迹,只读取已经由 RayTracer 写入的 Ray.path,对这些像面坐标做数值处理;计算结果又可直接被 visual 模块消费,用于点列图、光扇图等可视化,实现定量分析 + 定性可视化的闭环。

5.1分析指标的光学依据

本项目在单透镜场景下,围绕以下几个最基础的像质指标展开分析,这些指标都建立在像面交点分布的基础上:

RMS 光斑尺寸

所有光线在像面上的交点 相对于理想像点 有一定偏差。
RMS 
光斑尺寸(Root Mean Square Spot Size)定义为这些偏差距离的均方根,是衡量像斑扩散程度的标量指标,RMS 越小,光斑越集中,系统单色像差越小,成像越清晰。

子午/弧矢光扇数据

光扇图从瞳坐标 → 像面坐标的映射角度来刻画单色像差:

子午光扇(meridional fan):在子午面(通常是 y–z 面)内扫描不同入瞳高度的光线,记录它们在像面上的 坐标光瞳归一化坐标的变化;

弧矢光扇(sagittal fan):在弧矢方向上(对应 x 方向)记录同一批光线的 坐标 随瞳坐标的变化。

这些瞳坐标像面坐标数据是后续计算像面偏差(实际理想像点)并绘制标准光扇图的基础,用于分析球差、彗差、像散等单色像差。

5.2 RMS 光斑尺寸计算(analysis/spot.py - rms_spot_size

5.2.1光学公式

理论上,RMS 光斑尺寸的定义为:

其中:

N:追迹成功、到达像面的光线数量;

:第 (i) 条光线在像面上的横向坐标(mm),即 Ray.path[-1] ((x,y)) 分量;

:理想像点的横向坐标: 

轴上视场时,理想像点位于光轴上,

离轴视场时,理想像点由视场角和系统焦距结合高斯成像理论推导得到。

在当前实现中,为了简化接口,rms_spot_size 使用光斑的实际中心作为参考点,而不是显式传入。也就是说:先根据所有像面点的均值 计算光斑中心;然后将 视作参考点,计算每个点到该中心的距离的均方根。

从几何意义看,这相当于度量光斑自身的扩散程度,而不关心光斑整体是否偏离理想像点——这正是很多工程场景中最关心的斑点大小

5.2.2代码实现

对应的实现位于 analysis/spot.py

def spot_metrics(rays:   List[Ray],
ideal_x: float = 0.0,
ideal_y: float = 0.0)   -> Tuple[float, float, float, float]:
if not rays:
return 0.0, 0.0, 0.0, 0.0
points = np.array([r.path[-1] for r   in rays])
xs_mm = points[:, 0]
ys_mm = points[:, 1]
center_x_mm = float(np.mean(xs_mm))
center_y_mm = float(np.mean(ys_mm))
distances_sq_ideal = (xs_mm -   ideal_x)**2 + (ys_mm - ideal_y)**2
rms_mm =   float(np.sqrt(np.mean(distances_sq_ideal)))
rms_um = rms_mm * 1e3
distances_ideal =   np.sqrt(distances_sq_ideal)
geom_mm =   float(np.max(distances_ideal))
geom_um = geom_mm * 1e3
return rms_um, geom_um,   center_x_mm, center_y_mm

   def get_chief_ray_center(rays: List[Ray]) -> tuple[float, float]:
根据 field_angle  origin≈(0,0,*) 筛选 chief ray
for r in   rays:
if abs(r.path[0][0]) < 1e-3   and abs(r.path[0][1]) < 1e-3:
x_chief, y_chief, _ =   r.path[-1]
return x_chief, y_chief
# fallback
pts = np.array([r.path[-1] for   r in rays])
return float(np.mean(pts[:,0])),   float(np.mean(pts[:,1]))

   def rms_spot_size(rays: List[Ray]) -> float:
points = np.array([r.path[-1] for r   in rays])
xs_mm = points[:, 0]
ys_mm = points[:, 1]
x_mean = np.mean(xs_mm)
y_mean = np.mean(ys_mm)
distances_sq = (xs_mm - x_mean)**2   + (ys_mm - y_mean)**2
rms_mm =   np.sqrt(np.mean(distances_sq))
rms_um = rms_mm * 1e3
return float(rms_um)

函数输入是追迹成功的光线列表 rays: List[Ray],每条 Ray  path[-1] 被认为是像面交点

将所有像面点打包成二维数组 points,分别取出 x坐标(单位 mm);

 np.mean(xs_mm) np.mean(ys_mm) 求出光斑中心坐标 

计算每个点到中心的平方距离并平均,再开方得到 RMS 半径(mm);

最后将单位从 mm 转换为 μm 返回。

5.3光扇图数据计算(analysis/ray_fan.py

根据使用场景,将光扇数据的生成拆分为两个方向:

子午光扇:compute_meridional_ray_fan

弧矢光扇:compute_sagittal_ray_fan

这两个函数都位于 analysis/ray_fan.py,它们本质上做两件事:

调用examples.ray_fan_utils 中的光线生成工具,构造一批在不同瞳高处通过系统的光线;

使用 RayTracer 追迹这些光线,并提取像面坐标作为光扇数据。

5.3.1子午光扇数据(compute_meridional_ray_fan

def   compute_meridional_ray_fan(tracer: RayTracer,
pupil_radius: float,
n_rays:   int,
field_angle: float = 0.0) -> Tuple[List[float], List[float]]:
"""
计算子午光扇数据
- tracer: RayTracer
实例
- pupil_radius: 
入瞳半径(mm
- n_rays: 
采样点数
- field_angle: 
视场角(度)
返回:   (PY_list, EY_mm)
PY_list: 
归一化光瞳坐标 [-1, 1]
EY_mm: 
子午方向像差(mm),相对于主光线像点
"""
PY_list,   rays = generate_meridional_fan_rays(pupil_radius, n_rays, field_angle)
traced_rays =   tracer.trace_rays(rays)

找到主光线(光瞳中心,PY=0)的像点作为参考
y_chief =   0.0
for i, PY in enumerate(PY_list):
if abs(PY) < 1e-6:
主光线,PY=0
x_img,   y_chief, z_img = traced_rays[i].path[-1]
break

EY_mm = []
for ray in traced_rays:
x_img, y_img, z_img =   ray.path[-1]
EY_mm.append(y_img - y_chief)

return PY_list, EY_mm

generate_meridional_fan_rays 负责在给定入瞳半径、采样数和视场角下,生成一条沿子午面扫描的光扇:返回的 PY_list 是归一化的光瞳坐标(通常是 区间的均匀采样),rays 是对应的 Ray 列表;

RayTracer.trace_rays 对这组 rays 做完整几何追迹,ray.path[-1] 即为像面交点;

将每条光线的 y_img 组成EY_list 返回,单位为 mm

这里返回的 EY_list 像面绝对 y 坐标,不是已经减去理想像点的偏差。 

5.3.2弧矢光扇数据(compute_sagittal_ray_fan

def   compute_sagittal_ray_fan(tracer: RayTracer,
pupil_radius: float,
n_rays:   int,
field_angle: float = 0.0) -> Tuple[List[float], List[float]]:
"""
计算弧矢光扇数据
- tracer: RayTracer
实例
- pupil_radius: 
入瞳半径(mm
- n_rays: 
采样点数
- field_angle: 
视场角(度)
返回:   (PX_list, EX_mm)
PX_list: 
归一化光瞳坐标 [-1, 1]
EX_mm: 
弧矢方向像差(mm),相对于主光线像点
"""
PX_list,   rays = generate_sagittal_fan_rays(pupil_radius, n_rays, field_angle)
traced_rays =   tracer.trace_rays(rays)

找到主光线(光瞳中心,PX=0)的像点作为参考
x_chief =   0.0
for i, PX in enumerate(PX_list):
if abs(PX) < 1e-6:
主光线,PX=0
x_chief,   y_img, z_img = traced_rays[i].path[-1]
break

EX_mm = []
for ray in traced_rays:
x_img, y_img, z_img =   ray.path[-1]
EX_mm.append(x_img - x_chief)

return PX_list, EX_mm

PX_list 同样是归一化的光瞳坐标;

EX_list 是每条光线在像面的 x 坐标(mm),代表弧矢方向的像面坐标;

这样,analysis/ray_fan.py 提供的是标准化的瞳坐标像面坐标数据visual 模块则在 ray_fan_plot.py 中将它们统一转换为 μm,并绘制出子午/弧矢光扇图。

六、光学结果可视化(visual 模块)

visual 模块基于 trace 模块追迹后的 Ray.path 数据,以及 analysis 模块输出的基础量化指标,实现光路图、点列图、光扇图三大核心光学图表的绘制。所有图表均采用光学工程中常见的可视化规范,与 Zemax 等商用软件的图表样式在视觉上保持一致,使几何追迹结果更直观、更易解读。

本模块完全基于 matplotlib 实现,所有绘图函数均为独立的纯可视化接口

只读取 Ray.path analysis 输出的数值,不参与任何光线追迹和像差计算;

支持标题后缀、图形尺寸、颜色映射等自定义配置;

通过中文字体设置,支持中英文混排标注。

6.1光路图绘制(visual/rayplot.py

6.1.1光学可视化规范

光路图直接展示光线在光学系统中传播的路径,是理解透镜成像行为的第一视角。本项目中,单透镜系统的光路图采用 y–z 子午面投影,遵循如下规范:

① 坐标系约定

轴为光轴方向(从左到右);

轴为子午方向高度;

在图中统一采用「横轴 z、纵轴 y」的 2D 投影方式。

② 透镜几何表示

根据每个表面的曲率半径radius 和半孔径 semi_diameter 精确绘制表面轮廓;

对球面,通过曲率符号和球心位置计算截面曲线;

对平面或近似平面(R→∞ 或极小半径),绘制垂直于光轴的直线段。

③ 光线路径表示

所有光线都来自Ray.path 中的 3D 轨迹点,取其中的 (z, y) 分量进行 2D 绘制;

可绘制多条光线,形成光束的整体传播形态;

在基础光路图中统一使用蓝色线条,在视场分色光路图中对不同视场角使用不同颜色。

④ 视场信息展示

对于具有field_angle 属性的光线,可以按视场角分配颜色,并在图例中显示,例如 “0°10°”

图例自动去重,避免重复标签。

⑤ 图形设置

使用set_aspect('equal', 'box'),保证 z–y 比例真实;

打开网格线,便于读数和比较;

使用中文字体设置支持中文标题。

6.1.2代码实现

visual/rayplot.py 中包含三个核心函数:

1)透镜表面轮廓绘制:draw_lens_surfaces

def draw_lens_surfaces(ax,   lens: LensSystem, surface_positions: List[float]):
"""
 y–z 子午面上,根据曲率半径和孔径画出每个表面的轮廓。
只画表面线条(不填充整个透镜实体)。
"""
for s,   z_s in zip(lens.surfaces, surface_positions):
R = getattr(s,   "radius", None)
semi_dia = getattr(s,   "semi_diameter", None)
if semi_dia is None:
continue
没有孔径信息就不画

平面或近似平面
if (R is   None) or (abs(R) < 1e-6) or (abs(R) > 1e6):
ys = np.linspace(-semi_dia,   semi_dia, 50)
zs = np.full_like(ys, z_s)
ax.plot(zs, ys,   color="k", linewidth=1.0)
continue

球面:根据 R 计算球心,并在给定 y 范围内求 z(y)
z_c = z_s   + R球心 z
ys =   np.linspace(-semi_dia, semi_dia, 200)

inside = ys**2 <= R**2
ys_valid = ys[inside]
if ys_valid.size == 0:
continue

选择靠近透镜内部的一侧(以 z_s 为参考)
if R >   0:
zs_valid = z_c -   np.sqrt(R**2 - ys_valid**2)
球心在右侧,取左侧弧
else:
zs_valid = z_c +   np.sqrt(R**2 - ys_valid**2)
球心在左侧,取右侧弧

ax.plot(zs_valid,   ys_valid, color="k", linewidth=1.0)

该函数只负责画出每一个表面的二维轮廓,不填充内部实体,既保留几何精度又保证绘图简洁。

2)基础光路图:plot_ray_path

def plot_ray_path(rays:   List[Ray], lens: LensSystem, surface_positions: List[float], title_suffix:   str = ""):
"""
二维光路图,为横轴,为纵轴"""
fig, ax =   plt.subplots(figsize=(10, 4))

先画透镜表面实际轮廓
draw_lens_surfaces(ax,   lens, surface_positions)

再画光线
for r in   rays:
zs = [p[2] for p in r.path]
ys = [p[1] for p in r.path]
ax.plot(zs, ys, 'b-',   alpha=0.5)

ax.set_xlabel("z (mm)")
ax.set_ylabel("y (mm)")
ax.set_title(f"Ray Path -   {lens.name} {title_suffix}")
ax.grid(True)
ax.set_aspect('equal', 'box')
显式设置 y 轴范围为 ±40mm
if   lens.surfaces:
semi_dia = getattr(lens.surfaces[0],   "semi_diameter", 40.0)
ax.set_ylim(-semi_dia * 1.1,   semi_dia * 1.1)
plt.tight_layout()
plt.show()

3)按视场上色的光路图:plot_ray_path_by_field

def   plot_ray_path_by_field(rays: List[Ray], lens: LensSystem, surface_positions:   List[float], title_suffix: str = ""):
"""
按视场角自动上色的光路图,为横轴,为纵轴"""
fig, ax =   plt.subplots(figsize=(10, 4))

先画透镜表面实际轮廓
draw_lens_surfaces(ax,   lens, surface_positions)

不同 field_angle 用不同颜色
colors =   plt.cm.tab10.colors

先收集所有的视场角
field_angles   = set()
for ray in rays:
field_angles.add(ray.field_angle)

为每个视场角分配颜色
angle_list   = sorted(field_angles)
angle_color_map = {}
for i, angle in   enumerate(angle_list):
angle_color_map[angle] =   colors[i % len(colors)]

绘制光线
for ray   in rays:
zs = [p[2] for p in ray.path]
ys = [p[1] for p in ray.path]
获取该视场角对应的颜色
color =   angle_color_map[ray.field_angle]
ax.plot(zs, ys, color=color, alpha=0.7,   linewidth=1,
label=f"{ray.field_angle}°")

ax.set_xlabel("z (mm)")
ax.set_ylabel("y (mm)")
ax.set_title(f"
光路图{title_suffix}")
ax.set_aspect('equal', 'box')
ax.grid(True)
显式设置 y 轴范围为 ±40mm
if   lens.surfaces:
semi_dia =   getattr(lens.surfaces[0], "semi_diameter", 40.0)
ax.set_ylim(-semi_dia * 1.1,   semi_dia * 1.1)

图例去重
handles,   labels = ax.get_legend_handles_labels()
by_label = dict(zip(labels,   handles))
ax.legend(by_label.values(),   by_label.keys(), loc="upper right")

plt.tight_layout()
plt.show()

适用于:

多视场综合展示,如 0°10° 视场光线同时显示;

对比不同视场下光线在系统中的折射路径差异。

6.2点列图绘制(visual/spotplot.py

6.2.1光学可视化规范

点列图通过展示像面上各光线的交点分布,直观体现成像质量和像差类型。当前实现遵循如下规范:

① 坐标含义

横轴:像面 x 坐标;

纵轴:像面 y 坐标;

单位统一换算为 µm,以便观察微小光斑结构。

② 参考中心

不显式画出理论理想像点,而以主光线(chief ray)的像点作为几何中心;

所有光线终点相对于主光线像点做平移,得到相对坐标,保证不同仿真条件下的点列图均以主光线为中心对齐。

③ 视觉风格

使用蓝色 + 号标记,与 Zemax 点列图默认风格一致;

坐标轴等比例,保证光斑形状不会被拉伸;

自动根据光斑外接范围设置坐标轴限,使光斑居中显示。

6.2.2代码实现

def plot_spot_diagram(rays:   List[Ray], title_suffix: str = ""):
"""
像面处点列图:取每条光线的最后一个点,画x-y平面,以主光线为中心"""
获取所有光线的像平面位置
image_points   = np.array([r.path[-1] for r in rays])
xs_mm = image_points[:, 0]
ys_mm = image_points[:, 1]

寻找主光线(光瞳中心出发的光线,即原点出发的光线)
chief_ray   = None
for ray in rays:
检查光线原点是否接近光瞳中心 (0, 0, -100)
origin =   ray.path[0]
if abs(origin[0]) < 1e-3 and   abs(origin[1]) < 1e-3:
chief_ray = ray
break

计算主光线在像平面上的位置作为参考点
if   chief_ray:
chief_x_mm, chief_y_mm,   chief_z_mm = chief_ray.path[-1]
将所有光线的像平面位置减去主光线的位置,得到相对主光线的偏移
xs_mm -=   chief_x_mm
ys_mm -= chief_y_mm

# mm -> μm
xs_um = xs_mm * 1e3
ys_um = ys_mm * 1e3

fig, ax = plt.subplots(figsize=(6,   6))
使用 '+' 标记,与 Zemax 风格一致,调整大小和颜色
ax.plot(xs_um,   ys_um, 'b+', markersize=5, alpha=0.7)

设置坐标轴范围,使点列图居中
if   len(xs_um) > 0 and len(ys_um) > 0:
max_val = max(max(abs(xs_um)),   max(abs(ys_um))) * 1.2
ax.set_xlim(-max_val, max_val)
ax.set_ylim(-max_val, max_val)

添加网格线,便于观察光斑分布
ax.grid(True,   linestyle='--', alpha=0.3)

获取视场角信息,用于标题
field_angle   = None
if rays:
field_angle =   rays[0].field_angle

构建标题
title =   "Spot Diagram"
if field_angle is not None:
title += f" (Field:   {field_angle}°)"
if title_suffix:
title += f"   {title_suffix}"

ax.set_xlabel("x (μm)")
ax.set_ylabel("y (μm)")
ax.set_title(title)
ax.set_aspect('equal', 'box')
plt.tight_layout()
plt.show()

该函数只依赖:

ray.path[0]:判断主光线(原点出发);

ray.path[-1]:像面交点坐标;

ray.field_angle:用于标题标注视场角。

上层若需要数值 RMS 光斑尺寸,可由analysis/spot.py  rms_spot_size 计算,再自行叠加到标题或图中文本中。本函数保持纯绘图职责。

6.3光扇图绘制(光扇图绘图库)

6.3.1光学可视化规范

光扇图用于分析单色像差在瞳面上的分布趋势,当前实现中:

① 输入数据形式

横坐标:归一化光瞳坐标(PX PY),范围 

纵坐标:像面坐标(EX  EY,单位 mm),代表光线在像面的 y 坐标。

② 偏差基准

在子午/弧矢单图中,函数内部会以光瞳中心(PX  PY 最接近 0 的点)对应的像面坐标为参考,将所有数据平移,使该点作为 0 基准,形成交对称的像差曲线;

在合并光扇图中,直接以 µm为单位绘制 EXEY 两组数据,纵坐标标签统一为像差 (μm)”

③ 绘图风格

使用连续曲线 + 散点叠加的形式,同时展示趋势和采样点;

使用蓝色表示子午光扇、红色表示弧矢光扇;

添加横纵零线,明确像差为 0的理想基线位置;

横坐标范围固定为 ,纵坐标自动调整。

6.3.2代码实现

1)子午光扇图:plot_meridional_fan

def plot_meridional_fan(PY:   List[float],
EY_mm:   List[float],
title_suffix:   str = "",
y_lim: float =   None):
"""
绘制子午光扇图
- PY: 
归一化光瞳坐标 [-1, 1]
- EY_mm: 
子午方向像差(mm
- title_suffix: 
标题后缀
- y_lim: 
纵坐标范围(μm),如果为None则自动计算
"""
EY_um =   [ey * 1e3 for ey in EY_mm]

fig, ax = plt.subplots(figsize=(8,   5))
ax.plot(PY, EY_um, 'b-',   linewidth=2, alpha=0.7)
ax.scatter(PY, EY_um, c='b', s=20,   alpha=0.7)

ax.axhline(y=0, color='k',   linestyle='--', linewidth=1, alpha=0.5)
ax.axvline(x=0, color='k',   linestyle='--', linewidth=1, alpha=0.5)

ax.set_xlabel('
归一化光瞳坐标 PY')
ax.set_ylabel('
像差 (μm)')
if title_suffix:
ax.set_title(f'
子午光扇图 {title_suffix}')
else:
ax.set_title('
子午光扇图')
ax.grid(True, alpha=0.3)
ax.set_xlim(-1.1, 1.1)
if y_lim is not None:
ax.set_ylim(-y_lim, y_lim)
else:
自动 y 轴范围
y_min   = min(EY_um)
y_max = max(EY_um)
y_pad = (y_max - y_min) * 0.1   if y_max != y_min else 0.1
ax.set_ylim(y_min - y_pad,   y_max + y_pad)
plt.tight_layout()
plt.show()

2)弧矢光扇图:plot_sagittal_fan

def plot_sagittal_fan(PX:   List[float],
EX_mm:   List[float],
title_suffix: str   = "",
y_lim: float =   None):
"""
绘制弧矢光扇图
- PX: 
归一化光瞳坐标 [-1, 1]
- EX_mm: 
弧矢方向像差(mm
- title_suffix: 
标题后缀
- y_lim: 
纵坐标范围(μm),如果为None则自动计算
"""
EX_um =   [ex * 1e3 for ex in EX_mm]

fig, ax = plt.subplots(figsize=(8,   5))
ax.plot(PX, EX_um, 'r-',   linewidth=2, alpha=0.7)
ax.scatter(PX, EX_um, c='r', s=20,   alpha=0.7)

ax.axhline(y=0, color='k',   linestyle='--', linewidth=1, alpha=0.5)
ax.axvline(x=0, color='k',   linestyle='--', linewidth=1, alpha=0.5)

ax.set_xlabel('
归一化光瞳坐标 PX')
ax.set_ylabel('
像差 (μm)')
if title_suffix:
ax.set_title(f'
弧矢光扇图 {title_suffix}')
else:
ax.set_title('
弧矢光扇图')
ax.grid(True, alpha=0.3)
ax.set_xlim(-1.1, 1.1)
if y_lim is not None:
ax.set_ylim(-y_lim, y_lim)
else:
自动 y 轴范围
y_min   = min(EX_um)
y_max = max(EX_um)
y_pad = (y_max - y_min) * 0.1   if y_max != y_min else 0.1
ax.set_ylim(y_min - y_pad,   y_max + y_pad)
plt.tight_layout()
plt.show()

3)合并光扇图:plot_both_fans

def plot_both_fans(PX:   List[float],
EX_mm: List[float],
PY: List[float],
EY_mm: List[float],
title_suffix: str =   ""):
"""
绘制合并光扇图
- PX, EX_mm: 
弧矢光扇数据(mm
- PY, EY_mm: 
子午光扇数据(mm
- title_suffix: 
标题后缀
"""
EX_um =   [ex * 1e3 for ex in EX_mm]
EY_um = [ey * 1e3 for ey in EY_mm]

fig, ax = plt.subplots(figsize=(8,   5))
ax.plot(PY, EY_um, 'b-',   linewidth=2, alpha=0.7, label='
子午光扇')
ax.scatter(PY, EY_um, c='b', s=20,   alpha=0.7)
ax.plot(PX, EX_um, 'r-',   linewidth=2, alpha=0.7, label='
弧矢光扇')
ax.scatter(PX, EX_um, c='r', s=20,   alpha=0.7)

ax.axhline(y=0, color='k',   linestyle='--', linewidth=1, alpha=0.5)
ax.axvline(x=0, color='k',   linestyle='--', linewidth=1, alpha=0.5)

ax.set_xlabel('
归一化光瞳坐标')
ax.set_ylabel('
像差 (μm)')
if title_suffix:
ax.set_title(f'
合并光扇图 {title_suffix}')
else:
ax.set_title('
合并光扇图')
ax.grid(True, alpha=0.3)
ax.set_xlim(-1.1, 1.1)
ax.legend()

all_data = EX_um + EY_um
y_min = min(all_data)
y_max = max(all_data)
y_pad = (y_max - y_min) * 0.1 if   y_max != y_min else 0.1
ax.set_ylim(y_min - y_pad, y_max +   y_pad)

plt.tight_layout()
plt.show()

七、验证与对比

7.1 单透镜(单视场)

本项目提供examples模块中,可以实现100mm焦距、0.5876um单波长、单视场、BK7对称单透镜的仿真验证,涵盖从参数输入、光线生成、追迹到分析、可视化的全流程,仿真结果与Zemax交叉验证。

光路图对比:

点列图:

光扇图:

7.2单透镜(多视场)

本项目提供examples模块中,可以实现100mm焦距、0.5876um单波长、多视场、BK7对称单透镜的仿真验证,涵盖从参数输入、光线生成、追迹到分析、可视化的全流程,仿真结果与Zemax交叉验证。

光路图对比:

点列图:

光扇图:

八、项目扩展方向

本项目的模块化架构和预留接口让其具备极强的可扩展性,可基于现有模块快速扩展至更复杂的光学仿真场景,核心扩展方向包括:

色散模拟:修改Material类的n()方法,实现Sellmeier方程的折射率计算,支持复色光(红/绿/蓝)的追迹和色差分析;

厚透镜/双胶合透镜仿真:扩展LensSystem类,支持多表面、多玻璃的透镜系统,实现双胶合透镜的消色差仿真;

高级像差分析:扩展analysis模块,加入球差、彗差、像散、场曲、畸变的定量计算,实现单色像差的完整分析;

三维可视化:基于mpl_toolkits.mplot3d实现3D光路图和3D点列图,更直观地体现光线的3D传播规律;

交互化界面:基于Streamlit/PyQt实现可视化交互界面,支持用户实时调整透镜参数,实时查看仿真结果;

镜头优化:结合遗传算法/梯度下降算法,实现基于RMS光斑尺寸的透镜参数自动优化,满足工程化的设计需求。

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-02-28 09:02:17 HTTP/2.0 GET : https://f.mffb.com.cn/a/477530.html
  2. 运行时间 : 0.089960s [ 吞吐率:11.12req/s ] 内存消耗:4,705.30kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=7d45363e8a3c33b7931ca4165b51237a
  1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000591s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000689s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000311s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000251s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000541s ]
  6. SELECT * FROM `set` [ RunTime:0.007981s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000532s ]
  8. SELECT * FROM `article` WHERE `id` = 477530 LIMIT 1 [ RunTime:0.000769s ]
  9. UPDATE `article` SET `lasttime` = 1772240538 WHERE `id` = 477530 [ RunTime:0.010190s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 66 LIMIT 1 [ RunTime:0.000269s ]
  11. SELECT * FROM `article` WHERE `id` < 477530 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.000493s ]
  12. SELECT * FROM `article` WHERE `id` > 477530 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.000423s ]
  13. SELECT * FROM `article` WHERE `id` < 477530 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.000857s ]
  14. SELECT * FROM `article` WHERE `id` < 477530 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.000762s ]
  15. SELECT * FROM `article` WHERE `id` < 477530 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.000873s ]
0.091477s