Ручная настройка 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 = 30 # @param {type:"slider",min:0,max:100,step:1}
P_max = 6.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.09 # @param {type:"slider",min:0,max:1,step:0.01}
I = 0.91 # @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!()
Как это работает
Заключение
Мы реализовали сравнительно огрубленный метод ручного подбора коэффициентов, который позволит настроить параметры для нелинейной, физической, линейной или линеаризованной системы. Наиболее цитируемым является метод подбора коэффициентов Циглера-Никольса, его и можно было бы реализовать при помощи созданного нами окружения.