Сообщество Engee

Ручная настройка PID регулятора

Автор
avatar-nkapyrinnkapyrin
Notebook

Окружение для ручной настройки PID регулятора

В этом проекте предлагаем вам применить минималистичное окружение для настройки PID регуляторов для графической модели системы.

Описание системы

Имеется модель, заданная в виде передаточной функции следующего вида:

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

Шаги решения

Если эта модель решается в редакторе моделей, но не демонстрирует интересующих нас параметров переходного процесса, приходит время перейти к следующему этапу и попробовать огрубленно подобрать параметры вручную.

Поскольку мы заведомо не знаем масштаб нужных параметров, в этом примере подбор каждого параметра PID регулятора осуществляется двумя числами:

  • максимальное значение (без ограничений),

  • пропорциональный коэффициент (от 0 до 1)

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

In [ ]:
gr()
engee.open("pid_manual_tuning.engee")
using DataFrames, Statistics
all_runs = DataFrame[];
colors = [1, "skyblue", "lightblue", "powderblue", "lightblue1"];

Код следующей ячейки можно спрятать, если дважды кликнуть по форме с элементами управления. Кроме того, вывод ячейки удобно расположить справа от формы ввода параметров (кнопка ).

In [ ]:
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!()
══════════════════════════════════════════════════
АНАЛИЗ ПЕРЕХОДНОГО ПРОЦЕССА
══════════════════════════════════════════════════
Установившееся значение: 0.999
Перерегулирование: 56.7%
Время установления (5%): 22.61 с
Время нарастания (10%-90%): 0.5 с
Статическая ошибка: 0.0007
Количество колебаний: 17
══════════════════════════════════════════════════
Out[0]:

Заключение

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