Ручная настройка PID регулятора
Окружение для ручной настройки PID регулятора
В этом проекте предлагаем вам применить минималистичное окружение для настройки PID регуляторов для графической модели системы.
Описание системы
Имеется модель, заданная в виде передаточной функции следующего вида:
Задача состоит в том, чтобы найти параметры PID регулятора, при которых система удовлетворительно отрабатывает единичное ступенчатое воздействие.
Шаги решения
Если эта модель решается в редакторе моделей, но не демонстрирует интересующих нас параметров переходного процесса, приходит время перейти к следующему этапу и попробовать огрубленно подобрать параметры вручную.
Поскольку мы заведомо не знаем масштаб нужных параметров, в этом примере подбор каждого параметра PID регулятора осуществляется двумя числами:
-
максимальное значение (без ограничений),
-
пропорциональный коэффициент (от 0 до 1)
Такой метод обеспечивает некоторый компромисс между удобством и широтой подбираемых параметров. Не внося модификаций в код программы, проектировщик может выставить любой масштаб коэффициентов регулятора, а потом плавно подбирать более точное значение при помощи плавного регулятора.
gr()
engee.open("pid_manual_tuning.engee")
using DataFrames, Statistics
all_runs = DataFrame[];
colors = [1, "skyblue", "lightblue", "powderblue", "lightblue1"];
Код следующей ячейки можно спрятать, если дважды кликнуть по форме с элементами управления. Кроме того, вывод ячейки удобно расположить справа от формы ввода параметров (кнопка ).
StopTime = 41 # @param {type:"slider",min:0,max:100,step:1}
P_max = 5.0 # @param {type:"number",placeholder:"1.0"}
I_max = 1.0 # @param {type:"number",placeholder:"1.0"}
D_max = 1.0 # @param {type:"number",placeholder:"1.0"}
P = 0.98 # @param {type:"slider",min:0,max:1,step:0.01}
I = 0.66 # @param {type:"slider",min:0,max:1,step:0.01}
D = 0 # @param {type:"slider",min:0,max:1,step:0.01}
engee.set_param!("pid_manual_tuning/ПИД-регулятор", "P"=>P*P_max, "I"=>I*I_max, "D"=>D*D_max)
engee.set_param!("pid_manual_tuning", "StopTime"=>StopTime)
# Запусти модель и получим результаты
data = engee.run("pid_manual_tuning")
x = collect(data["x"])
y = collect(data["y"])
# Удалим последний элемент если список переполнился
pushfirst!(all_runs, y)
if length(all_runs) > 5 pop!(all_runs); end;
# Анализ последнего переходного процесса
if length(all_runs) > 0
last_run = all_runs[1]
time_data = last_run.time
value_data = last_run.value
# Определяем установившееся значение (последние 10% данных)
steady_state_idx = Int.(collect((round(0.9 * length(value_data)):length(value_data))))
steady_state_value = mean(value_data[steady_state_idx])
# Определяем начальное значение
initial_value = value_data[1]
# Целевое значение (берем из задания)
target_value = x.value[end] # предполагая, что x - это задание
# Находим максимальное отклонение (перерегулирование)
max_overshoot = maximum(value_data)
overshoot_percentage = abs((max_overshoot - steady_state_value) / (target_value - initial_value)) * 100
# Время установления (5%)
tolerance = 0.05 * abs(target_value - initial_value)
settling_time = nothing
for i in length(value_data):-1:1
if abs(value_data[i] - steady_state_value) > tolerance
settling_time = time_data[i]
break
end
end
# Время нарастания (10%-90%)
rise_time_10 = nothing
rise_time_90 = nothing
threshold_10 = initial_value + 0.1 * (steady_state_value - initial_value)
threshold_90 = initial_value + 0.9 * (steady_state_value - initial_value)
for i in 1:length(value_data)
if value_data[i] >= threshold_10 && rise_time_10 === nothing
rise_time_10 = time_data[i]
end
if value_data[i] >= threshold_90 && rise_time_90 === nothing
rise_time_90 = time_data[i]
break
end
end
rise_time = rise_time_90 - rise_time_10
# Выводим результаты анализа
println("═"^50)
println("АНАЛИЗ ПЕРЕХОДНОГО ПРОЦЕССА")
println("═"^50)
println("Установившееся значение: $(round(steady_state_value, digits=3))")
println("Перерегулирование: $(round(overshoot_percentage, digits=1))%")
if settling_time !== nothing
println("Время установления (5%): $(round(settling_time, digits=2)) с")
else
println("Время установления: процесс не установился")
end
if rise_time !== nothing && rise_time_10 !== nothing
println("Время нарастания (10%-90%): $(round(rise_time, digits=2)) с")
end
# Статическая ошибка (если есть)
static_error = abs(target_value - steady_state_value)
if static_error > 1e-6
println("Статическая ошибка: $(round(static_error, digits=4))")
else
println("Статическая ошибка: отсутствует")
end
# Количество колебаний
peaks = 0
for i in 2:length(value_data)-1
if value_data[i] > value_data[i-1] && value_data[i] > value_data[i+1] &&
value_data[i] > steady_state_value
peaks += 1
end
end
println("Количество колебаний: $peaks")
println("═"^50)
end
# Выведем графики
plot(x.time, x.value, c=:red, lw=1, size=(800,300), leg=false)
for i in length(all_runs):-1:1
plot!(all_runs[i].time, all_runs[i].value, c=colors[i], lw=3)
end
plot!()
Заключение
Мы реализовали сравнительно огрубленный метод ручного подбора коэффициентов, который позволит настроить параметры для нелинейной, физической, линейной или линеаризованной системы. Наиболее цитируемым является метод подбора коэффициентов Циглера-Никольса, его и можно было бы реализовать при помощи созданного нами окружения.