Построение модели объектива¶
Построим модель идеального объектива при помощи пакета GeometricalOptics.jl
. Покажем, как вывести несколько графиков распространения пучков света на одно полотно и реализуем процедуру оптимизации лучей на этом графике, чтобы сделать график наиболее информативным.
Параметры оптической системы¶
Для указания параметров объектива потребуется подключить (а, возможно, и установить) библиотеку GeometricalOptics
.
Pkg.add( url="https://github.com/airspaced-nk5/GeometricalOptics.jl#master" )
Pkg.add( "Optim" )
using GeometricalOptics
gr(); # Библиотека лучше работает с этим механизмом отрисовки графиков
Теперь мы можем задать несколько сферических линз и плоскость изображения:
Объект | Тип | Материал | Индекс рефракции | Радиус R1 (мм) | Радиус R2 (мм) | Толщина | Расстояние до след.пов. |
---|---|---|---|---|---|---|---|
Линза | двояковыпуклая | SK16 | 1.6204 | 22 | 430 | 3 | 6 |
Линза | двояковогнутая | F2 | 1.6200 | 22 | 20 | 1 | 6 |
Линза | двояковыпуклая | SK16 | 1.6204 | 78 | 16 | 3 | 34 |
Последним элементом оптической системы является плоскость изображения.
Единица измерения (миллиметр) не играет особой роли в расчетах пока мы не учитываем дифракцию и длину волны.
Толщина линзы равна длине пути, который луч света проходит через линзу, если идет по оптической оси.
Под радиусами R1 и R2 понимаются радиус кривизны каждой стороны каждой линзы: R1 характеризует радиус кривизны "входной" стороны, расположенной ближе к источнику света, R2 – выходная сторона линзы, расположенная ближе к плоскости изображения.
Опишем эту оптическую систему и построим небольшую визуализацию:
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) )
Небольшая визуализация позволила нам увидеть все заданные элементы.
Нацеливание лучей в апертурную диафрагму¶
Теперь предположим, что в системе есть диафрагма, расположенная в 2 мм после вогнутой линзы.
Мы хотим пропустить пучки света через нее, задав определенный угол падения, и убедиться, что они сфокусируются на плоскости изображения.
Мы могли бы разбить модель на две части и моделировать распространение от диафрагмы в оба направления (одна модель для поверхностей
[6:end]
, другая для[5:-1:1]
). Но поскольку лучи должны быть параллельны только при подлете к первой линзе (в при прохождении диафрагмы они не будут параллельны друг другу), то нам пришлось бы искать угол каждого луча при проохождении через диафрагму.
Пойдем другим путем и "оптимизируем" два параметра: ширину пучка и точку на оси Y, из которой направляется центральный луч пучка. Наша цель – найти параметры пучка, лучи которого проходят точно через диафрагму.
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) )
Если приблизить изображение диафрагмы, мы увидим, что наше первое приближение не очень далеко от желательного, но для точного расчета нужно немного передвинуть точки, откуда отправляются пучки лучей.
Угол пучка к оптической оси не изменится. Мы просто ищем, как расположлить пучок таким образом, чтобы он прошел ровно через диафрагму. Это полезно для расчета размера сенсора и для других интересных результатов.
plot!( xlimits=(p_s .+ [10, 14]), ylimits=(-3,3), aspect_ratio=:none, size=(500,500) )
Теперь создадим процедуру оптимизации.
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]'] )
После оптимизации оптическая система выглядит следующим образом:
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) )
Более пристальное изучение диафрагмы позволяет увидеть правильное положение светового пятна.
plot!( xlimits=(p_s .+ [10, 14]), ylimits=(-3,3), aspect_ratio=:none, size=(500,500) )
Размеры линз в библиотеке
GeometricalOptics.jl
наглядно не учитываются. В некоторых случаях, например при создании многочленов Цернике, прохождение луча за пределами определенной области, вызывает предусмотренную разработчиками ошибку.
Заключение¶
Если обернуть функцию расчета оптической схемы в процедуру оптимизации, можно автоматизировать многие шаги проектирования оптической системы. Пучки состоят из отдельных лучей света, а их параметры на любом сечении системы можно получить при помощи функции trace_extract_terminus
и ей подобных.
При помощи небольшого количества дополнительного кода можно будет производить расчеты диапазона автофокусировки и других задач.
Источник данных¶
[1] Построение идеальной оптики в Zemax [Электронный ресурс] // Научно-популярный журнал "Мир 3D world" : сайт. URL: http://mir-3d-world.ipo.spb.ru/2016/3dworld_4_2016.pdf (дата обращения: 05.11.2024).