Документация Engee
Notebook

Автоматизированная настройка регулятора

Введение

Настройка регулятора для системы управления это классическая задача, которую изучают в рамках курса по ТАУ. Однако все методы, которые изучаются в рамках этого курса занимают много времени. В этой публикации мы рассмотрим применение алгоритмов оптимизации для ускорения и автоматизации настройки регуляторов.

Для демонстрации будем использовать модель stepfun:

image.png

Подготовительные работы

Нам понадобятся две библиотеки: ControlSystemBase и Optim. Установим их:

In [ ]:
import Pkg
Pkg.add(["ControlSystemsBase","Optim"])
   Resolving package versions...
   Installed ControlSystems ─ v1.15.4
Warning: detected a stack overflow; program state may be corrupted, so further execution might be unreliable.
     Project No packages added to or removed from `~/.project/Project.toml`
    Manifest No packages added to or removed from `~/.project/Manifest.toml`

Теперь, создадим функцию для симуляции модели. Результаты симуляции будем передавать в функцию stepinfo() из ControlSystemBase. Эта функция рассчитывает характеристики переходного процесса и порождает объект типа StepInfo, который можно визуализировать. Будем возвращать такой объект:

In [ ]:
gr()
engee.load(joinpath(@__DIR__,"stepfun.engee"))
using ControlSystemsBase
function sim_parameterized()
    simres = engee.run("stepfun")
    y = collect(simres["y"]);
    u = collect(simres["u"]);
    yv = reshape(y.value,(1,length(y.value)))
    rt = ControlSystemsBase.SimResult(yv,y.time,yv,u.value,ss(tf([1.0],[1.0, 0.0])))
    si = stepinfo(rt)
    return si
end
Out[0]:
sim_parameterized (generic function with 1 method)

Начало работы

Посмотрим на то, как система работает с настройками регулятора по умолчанию:

In [ ]:
    engee.set_param!("stepfun/reg","P"=>1.0)
    engee.set_param!("stepfun/reg","I"=>1.0)
    si = sim_parameterized()
    ref = plot(si)
    display(ref)
No description has been provided for this image

Исследуем систему с помощью перебора параметров

Перебор параметров - достаточно распространенный метод для исследования влияния параметра на поведение системы. Минусом данного метода является то, что перебирать параметры - долго. Даже если мы распараллелим перебор, то на больших выборках мы будем ждать результата непозволительно долго. А если параметр будет варьироваться большими скачками, то качество анализа упадет. Но даже это может помочь в нашей задаче. Давайте посмотрим какой эффект даст изменение коэффициента усиления в интегрирующем звене регулятора:

In [ ]:
par_variation_l = 10;
param = collect(range(0.5,4.5,par_variation_l));
In [ ]:
using ControlSystemsBase
#mySystem = feedback(tf([1.0],[1.0,1.0])*tf([1.0],[1.0, 0.0]))

K = 1.0

overshoot = zeros(par_variation_l);
setting_time = zeros(par_variation_l);
rise_time = zeros(par_variation_l);

for i in 1:par_variation_l
    engee.set_param!("stepfun/reg","I"=>"$(param[i])")
    si = sim_parameterized()
    overshoot[i] = si.overshoot;
    setting_time[i] = si.settlingtime;
    rise_time[i] = si.risetime;
end
Warning: detected a stack overflow; program state may be corrupted, so further execution might be unreliable.

Исследуем результаты перебора параметров

Самый простой способ исследовать какие-либо данные - посмотреть их глазами. Давайте посмотрим на графики перерегулирования (overshoot), времени нарастания (rise_time) и времени уставки (setting_time)

In [ ]:
p1 = plot(param, overshoot,label="overshoot")
p2 = plot(param,rise_time,color=:red,label="rise_time")
p3 = plot(param,setting_time,color=:green,label="setting_time")
plot(p1,p2,p3,layout=(3,1))
Out[0]:
No description has been provided for this image

Здесь нам важны не количественные результаты, а то как ведет себя система. Мы видим, что по мере увеличения коэффициента усиления в интегрирующем звене, нарастает перерегулирование и время уставки. Таким образом мы можем сказать, что искомый оптимальный коэффициента должен быть меньше 1,5. Если мы проведем такой анализ для усилиющего звена, то мы тоже сможем выдвинуть требование к его коэфициенту усиления.

Автоматическая настройка параметров регулятора

Для автоматической настройки регулятора будем использовать методы оптимизации.

Для применения оптимизации нужно создать целевую функцию, у которой будем искать минимум. Воспользуемся объектом StepInfo, чтобы достать из результатов характеристики переходного процесса, а функция будет возвращать сумму этих характеристик, помноженную на веса. Веса назначаются исходя из требований к переходному процессу. Допустим, нам хочется сократить время уставки, при этом минимизировать перерегулирование, а время нарастания нам не важно. Тогда, наша целевая функция примет вид:

In [ ]:
function cost_fun(p::Vector{Float64})
    
    engee.set_param!("stepfun/reg","P"=>p[1])
    engee.set_param!("stepfun/reg","I"=>p[2])
    si = sim_parameterized()
    
    return 2*si.overshoot + 0.5*si.risetime + 10*si.settlingtime
end
Out[0]:
cost_fun (generic function with 1 method)

Прежде чем запускать оптимизацию и настраивать регулятор, требуется настроить саму оптимизацию. Мы хотим ограничить поиск параметров, так как выяснили что параметры иметь значения меньше 1.5. А нижняя граница у них будет 0.5. Далее, требуется ограничить сам алгоритм.

Задача оптимизации будет использовать сразу два алгоритма - Fminbox как "внешний", и градиентный спуск как "внутренний". На каждом шаге внешнего алгоритма будет запускаться несколько шагов внутреннего алгоритма, и если мы не ограничим количество шагов обоих алгоритмов, то оптимизация займет много времени или вообще зависнет. Для более подробного описания настроек обратитесь к документации.

После выполнения всех настроек, запустим оптимизацию:

In [ ]:
using Optim
lowbound = [0.5, 0.5];
highbound = [1.5, 1.5];
opt = Optim.Options(
	iterations = 5,
	outer_iterations = 25,
	f_abstol = 1e-5,
	f_reltol = 1e-2,
	show_trace = true);
res = optimize(cost_fun, lowbound, highbound, [1.0, 1.0], Fminbox(GradientDescent()),opt)
Warning: detected a stack overflow; program state may be corrupted, so further execution might be unreliable.
Fminbox
-------
Initial mu = 0.0

Fminbox iteration 1
-------------------
Calling inner optimizer with mu = 0.0

(numbers below include barrier contribution)
Iter     Function value   Gradient norm 
     0     2.248455e+02     8.399782e+01
 * time: 0.037448883056640625
Warning: detected a stack overflow; program state may be corrupted, so further execution might be unreliable.
Warning: detected a stack overflow; program state may be corrupted, so further execution might be unreliable.
     1     5.645585e+01     3.724740e+01
 * time: 139.6843729019165
Warning: detected a stack overflow; program state may be corrupted, so further execution might be unreliable.
     2     5.495090e+01     8.098034e+01
 * time: 207.34265685081482
Warning: detected a stack overflow; program state may be corrupted, so further execution might be unreliable.
Warning: detected a stack overflow; program state may be corrupted, so further execution might be unreliable.
Warning: detected a stack overflow; program state may be corrupted, so further execution might be unreliable.
     3     5.494912e+01     4.866916e+02
 * time: 362.8312578201294
     4     5.494560e+01     7.379368e+01
 * time: 379.5791518688202
Warning: detected a stack overflow; program state may be corrupted, so further execution might be unreliable.
Exiting inner optimizer with x = [1.340378007208218, 0.5002145746699308]
Current distance to box: 0.000214575
Decreasing barrier term μ.

Out[0]:
 * Status: success

 * Candidate solution
    Final objective value:     5.478000e+01

 * Found with
    Algorithm:     Fminbox with Gradient Descent

 * Convergence measures
    |x - x'|               = 5.00e-01 ≰ 0.0e+00
    |x - x'|/|x'|          = 3.73e-01 ≰ 0.0e+00
    |f(x) - f(x')|         = 1.70e+02 ≰ 1.0e-05
    |f(x) - f(x')|/|f(x')| = 3.10e+00 ≰ 1.0e-02
    |g(x)|                 = 0.00e+00 ≤ 1.0e-08

 * Work counters
    Seconds run:   401  (vs limit Inf)
    Iterations:    1
    f(x) calls:    23
    ∇f(x) calls:   23
    ∇f(x)ᵀv calls: 0

Были найдены следующие значения.

In [ ]:
optimal_params = Optim.minimizer(res)
Out[0]:
2-element Vector{Float64}:
 1.340378007208218
 0.5002145746699308

Построим графики переходных процессов для исходного и настроенного регуляторов:

In [ ]:
    engee.set_param!("stepfun/reg","P"=>optimal_params[1])
    engee.set_param!("stepfun/reg","I"=>optimal_params[2])
    si = sim_parameterized()
    fin = plot(si)
    plot(ref,fin,layout=(2,1))
Out[0]:
No description has been provided for this image

Выводы

Мы посмотрели на процесс автоматизации настройки регулятора. При помощи перебора параметров были получены границы, внутри которых лежат параметры регулятора, а затем были найдены их точные значения.