Engee 文档
Notebook

透镜模型结构

让我们使用GeometricalOptics.jl 软件包建立一个理想透镜模型。我们将展示如何在一张画布上绘制多幅光束传播图,并在此图上实施光线优化程序,使图的信息量最大。

光学系统参数

要指定镜头参数,您需要连接(并可能安装GeometricalOptics 库。

In [ ]:
Pkg.add( url="https://github.com/airspaced-nk5/GeometricalOptics.jl#master" )
Pkg.add( "Optim" )
In [ ]:
using GeometricalOptics
gr(); # Библиотека лучше работает с этим механизмом отрисовки графиков

现在我们可以指定多个球面透镜和图像平面:

| 对象 | 类型 | 材料 | 折射率 | 半径 R1 (mm) | 半径 R2 (mm) | 厚度 | 到跟踪 pov.| 的距离 | ----------- | ----------- | ----------- | ----------- | ----------- | ----------- | ----------- | ----------- | | 透镜 | 双折射 | SK16 | 1.6204 | 22 | 430 | 3 | 6 | | 透镜 | 双曲面 | F2 | 1.6200 | 22 | 20 | 20 | 1 | 6 | | 透镜 | 双凸面 | SK16 | 1.6204 | 78 | 16 | 16 | 3 | 34 |

光学系统的最后一个元件是像面

只要我们不考虑衍射和波长,测量单位(毫米)在计算中并不重要。

透镜的厚度等于光束沿光轴穿过透镜的路径长度。

半径 R1 和 R2 指的是每个透镜两侧的曲率半径:R1 表示离光源较近的 "输入 "侧的曲率半径,R2 表示离像面较近的透镜输出侧的曲率半径。

让我们来描述一下这个光学系统,并建立一个小的可视化模型:

In [ ]:
surfsList = [ spherical, spherical,
              spherical, spherical,
              spherical, spherical,
              zplane]

p_s = 10; # Расстояние от левого края графика до первой преломляющей поверхности
stations = p_s .+ [ 0., 3., 9., 10., 16., 19., 53. ]
radii = [ 22., -430., -22., 20., 78., -16., 0.0 ]
coeffsList = [ [s, r] for (s,r) in zip(stations, radii)]

# Материал линз, по порядку: SK16, F2, SK16
nList = [ 1., 1.6204,
          1., 1.6200,
          1., 1.6204,
          1.]

optSta = opticalstack( coeffsList, surfsList, nList );

test_bundle = bundle( [0.], (-3:3:3), 0., 0., 0. );
p_lens = optSta( test_bundle; rend = "YZ", halfdomain=3.5 )

plot!( ylimits=(-5,5), aspect_ratio=:none, size=(600,200) )
Out[0]:

通过这个小型可视化系统,我们可以看到所有给定的元素。

将光线对准光圈光阑

现在假设系统在凹透镜后 2 毫米处有一个光圈光阑。

我们希望让光束以一定的入射角度通过它,并确保它们聚焦在像面上。

我们可以将模型分成两部分,并对从光圈向两个方向的传播进行建模(一个模型用于表面[6:end] ,另一个用于[5:-1:1] )。但由于光线只需在接近第一个透镜时平行(光线通过光圈时不会相互平行),因此我们必须找出每条光线通过光圈时的角度。

让我们反其道而行之,"优化 "两个参数:光束宽度和中心光束在 Y 轴上的指向。*我们的目标是找到射线正好穿过光阑的光束参数。

In [ ]:
angles = [0.0, 0.1, 0.2];             # углы каждого из пучков лучей
colors = [:blue, :green, :red];       # каждый пучок характеризуется своим цветом
ypositions_init = [ 0., 3.0, 5.0];    # приблизительное положение точки отправки центральных лучей по оси Y
bundle_halfwidth = 3;                 # радиус пучков света

p = plot();                           # нанесем несколько графиков

for (angle, ypos, pc, plot_surface) in zip(angles, ypositions_init, colors, [true, false, false])
    b = bundle([0.], range(-bundle_halfwidth,bundle_halfwidth,length=3) .- ypos, 0., angle, 0.)
    optSta( b; rend = "YZ", color = pc, plobj = p, halfdomain=5.5, issurfplot=plot_surface )
end

plot!( p, p_s.+[12., 12.], [-2.5, 2.5], lw=2, lc=:black, markershape=:diamond, markercolor=:white );
plot!( p, ylimits=(-8,8), size=(900,400) )
Out[0]:

如果我们放大光圈的图像,就会发现我们的第一近似值与理想值相差不大,但为了进行精确计算,我们需要将光束的发射点稍微移动一下。

光束与光轴的角度不会改变。我们只需对光束进行定位,使其正好通过光圈。这有助于计算传感器尺寸和其他有趣的结果。

In [ ]:
plot!( xlimits=(p_s .+ [10, 14]), ylimits=(-3,3), aspect_ratio=:none, size=(500,500) )
Out[0]:

现在让我们创建一个优化程序。

In [ ]:
using Optim

# Новая конфигурация, которая "заканчивается" диафрагмой (хотя можно добавить zplane между любыми линзами)
optSta2 = opticalstack( [coeffsList[1:4]; [[p_s+12.]]], [surfsList[1:4]; zplane], [nList[1:4] ; 1.0] );

# Функция, которую мы будем оптимизировать
function f(x, in_angle)
    b_hw = x[1]       # радиус пучка
    ypos = x[2]       # смещение по Y центра пучка
    
    b = bundle([0.], range(-b_hw,b_hw,length=3) .- ypos, 0., in_angle, 0.);
    
    # Изучим пятно на пересечении пучка с 6-ой поверхностью
    t_info = GeometricalOptics.trace_extract_terminus( optSta2( b ), 6, coord="y" );
    
    # Два критерия оптимизации: обе границы светового пятна должны совпадать с размером диафрагмы
    q1 = abs( minimum( t_info ) - (-2.5))
    q2 = abs( maximum( t_info ) - ( 2.5)) 
    return q1 + q2
end

angles_hw_list = []
y_positions_list = []
for (angle,y_pos_init) in zip(angles, ypositions_init)
    # Выполняем оптимизацию для каждого нужного нам значения угла по-отдельности
    res = optimize(x -> f(x, angle), [bundle_halfwidth, y_pos_init])
    angle_optim, ypos_optim = Optim.minimizer( res )
    push!( angles_hw_list, angle_optim )
    push!( y_positions_list, ypos_optim )
end

# Выведем таблицу результатов оптимизации
display([["Направление луча"; "Ширина пучка"; "Отправная точка по Y"] [angles angles_hw_list y_positions_list]'] )
3×4 Matrix{Any}:
 "Направление луча"       0.0         0.1      0.2
 "Ширина пучка"           3.1232      3.13102  3.15749
 "Отправная точка по Y"  -5.40173e-8  2.44082  4.98689

优化后的光学系统如下:

In [ ]:
p = plot(); # Полотно для нанесения нескольких графиков

for (angle, ypos, b_hw, pc, plot_surface) in zip(angles, y_positions_list, angles_hw_list, colors, [true, false, false])
    b = bundle([0.], range(-b_hw,b_hw,length=3) .- ypos, 0., angle, 0.)
    optSta( b; rend = "YZ", color = pc, plobj = p, halfdomain=5.5, issurfplot=plot_surface )
end

plot!( p, p_s.+[12., 12.], [-2.5, 2.5], lw=2, lc=:black, markershape=:diamond, markercolor=:white );
plot!( p, ylimits=(-8,8), size=(900,400) )

仔细观察光圈,可以看到光斑的正确位置。

In [ ]:
plot!( xlimits=(p_s .+ [10, 14]), ylimits=(-3,3), aspect_ratio=:none, size=(500,500) )
Out[0]:

图库GeometricalOptics.jl 中没有明确考虑镜头尺寸。在某些情况下,例如在创建 Zernike 多项式时,光束通过特定区域外会导致开发者定义的错误

结论

通过将光学设计功能纳入优化程序,光学系统设计的许多步骤都可以实现自动化。光束由单条光线组成,使用函数trace_extract_terminus其类似函数可以获得它们在系统任意截面上的参数。

只需少量附加代码,就能完成自动对焦范围的计算和其他任务。

数据源

[1] 在 Zemax 中构建理想光学[电子资源] //科普杂志 "3D 世界":网站。URL: http://mir-3d-world.ipo.spb.ru/2016/3dworld_4_2016.pdf (地址日期:2024 年 11 月 5 日)。