Lens model construction¶
Let's build a model of an ideal lens using the package GeometricalOptics.jl
. We will show how to plot several graphs of light beam propagation on a single canvas and implement the procedure of ray optimisation on this graph to make the graph most informative.
Parameters of the optical system¶
To specify lens parameters, you will need to connect (and possibly install) the library GeometricalOptics
.
Pkg.add( url="https://github.com/airspaced-nk5/GeometricalOptics.jl#master" )
Pkg.add( "Optim" )
using GeometricalOptics
gr(); # Библиотека лучше работает с этим механизмом отрисовки графиков
Now we can specify several spherical lenses and the image plane:
| Object | Type | Type | Material | Refractive index | Radius R1 (mm) | Radius R2 (mm) | Thickness | Distance to trace pov.| | ----------- | ----------- | ----------- | ----------- | ----------- | ----------- | ----------- | ----------- | | Lens | birefringent | SK16 | 1.6204 | 22 | 430 | 3 | 6 | | Lens | double curved | F2 | 1.6200 | 22 | 20 | 20 | 1 | 6 | | Lens | biconvex | SK16 | 1.6204 | 78 | 16 | 16 | 3 | 34 |
The last element of the optical system is the image plane.
The unit of measurement (millimetre) does not play a major role in the calculations as long as we do not take diffraction and wavelength into account.
The thickness of the lens is equal to the length of the path that the light beam takes through the lens when travelling along the optical axis.
The radii R1 and R2 refer to the radius of curvature of each side of each lens: R1 characterises the radius of curvature of the "input" side, which is closer to the light source, while R2 is the output side of the lens, which is closer to the image plane.
Let's describe this optical system and build a small visualisation:
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) )
The small visualisation allowed us to see all the given elements.
Aiming rays at the aperture diaphragm¶
Now suppose the system has an aperture diaphragm located 2 mm after the concave lens.
We want to pass beams of light through it at a certain angle of incidence and make sure that they focus on the image plane.
We could split the model into two parts and model the propagation from the aperture in both directions (one model for the surfaces
[6:end]
, the other for[5:-1:1]
). But since the rays only need to be parallel as they approach the first lens (they will not be parallel to each other as they pass the aperture), we would have to find the angle of each ray as it passes through the aperture.
Let's go the other way and "optimise" two parameters: the beam width and the point on the Y axis from which the central beam is directed. *Our goal is to find the parameters of the beam whose rays pass exactly through the diaphragm.
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) )
If we zoom in on the image of the aperture, we can see that our first approximation is not very far from the desirable one, but for an accurate calculation we need to move the points from which the beam beams are sent a little.
The angle of the beam to the optical axis will not change. We are simply looking to position the beam so that it passes exactly through the aperture. This is useful for calculating sensor size and other interesting results.
plot!( xlimits=(p_s .+ [10, 14]), ylimits=(-3,3), aspect_ratio=:none, size=(500,500) )
Now let's create an optimisation procedure.
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]'] )
After optimisation the optical system looks as follows:
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) )
A closer look at the aperture allows you to see the correct position of the light spot.
plot!( xlimits=(p_s .+ [10, 14]), ylimits=(-3,3), aspect_ratio=:none, size=(500,500) )
Lens dimensions are not explicitly taken into account in the library
GeometricalOptics.jl
. In some cases, such as when creating Zernike polynomials, passing the beam outside a certain area causes a developer-defined error.
Conclusion¶
By wrapping the optical design function into an optimisation procedure, many steps of optical system design can be automated. Beams consist of individual light rays, and their parameters at any cross-section of the system can be obtained using the function trace_extract_terminus
and its similar.
With a small amount of additional code it will be possible to perform calculations of the autofocus range and other tasks.
Data source¶
[1] Construction of ideal optics in Zemax [Electronic resource] // Popular science magazine "3D world" : website. URL: http://mir-3d-world.ipo.spb.ru/2016/3dworld_4_2016.pdf (date of address: 05.11.2024).