Pular para conteúdo

Controle de Nível PID

Neste tutorial você põe um controlador PID no tanque da página anterior para que a válvula de saída mantenha o nível num setpoint, na GUI Clássica do DWSIM. Você enche o tanque até o setpoint, guarda esse estado, depois submete a malha a uma mudança de alimentação e a uma mudança de setpoint e observa o que as ações proporcional e integral fazem cada uma. Também vai ver o que acontece com o controlador em manual, com ganhos altos demais e com ganhos baixos demais.

O que você vai aprender

  • Como ligar um controlador PID: variável controlada, variável manipulada, setpoint, direção, limites de saída, offset e span
  • O que o controle só proporcional deixa para trás (um offset) e o que a ação integral remove
  • Como guardar um estado do flowsheet e iniciar um schedule a partir dele, como passar um controlador para manual, e o que a ferramenta de sintonia de PID faz

Pré-requisitos

  • Ter completado Do Permanente ao Dinâmico: o tanque, as duas válvulas, o integrador e o schedule são reaproveitados como estão
  • A equação do PID, pelo menos em palavras: a saída é um ganho vezes o erro, mais um ganho vezes a integral do erro

Visão Geral do Processo

O tanque do tutorial anterior com um controlador de nível fechado em torno dele:

  1. Inlet entrega 10 kg/s (especificação de Vazão), depois sobe para 15 kg/s
  2. T-01 é o tanque de 2 m³; o nível de líquido é a variável controlada
  3. LIC-01 é o controlador PID, que lê o nível e escreve a abertura da V-02
  4. V-02 é a válvula de saída, Kv 400, a variável manipulada
  5. Outlet é mantida a 1 atm

Diagrama de Fluxo do Processo

graph LR
    IN["Inlet<br/>10 kg/s → 15 kg/s"] --> V1["V-01"]
    V1 --> T["T-01<br/>(Tanque)"]
    T --> V2["V-02<br/>abertura = MV"]
    V2 --> OUT["Outlet<br/>1 atm"]
    T -. nível (PV) .-> LIC["LIC-01<br/>SP 1,0 m"]
    LIC -. abertura (MV) .-> V2

Parâmetros de Projeto

Parâmetro Valor Unidade
Variável controlada (PV) Nível de líquido de T-01 m
Variável manipulada (MV) Abertura da V-02 %
Setpoint 1,0 m
Ação Reversa (nível sobe, válvula abre) -
Limites de saída 0 a 100 %
Offset (MV com saída zero) 29 %
Span da variável manipulada 100 %
Kp, Ki, Kd (sintonia base) 2, 0,1, 0 -
Passo e duração da integração 5, 10 s, min

Passo a Passo na GUI Clássica

1. Abrir o flowsheet do tanque

Abra o arquivo salvo do tutorial anterior, dynamic_tank_open_loop.dwxmz, e confira que Dynamics > Dynamic Mode Active está ligado (o arquivo abre com ele desligado). O integrador Int1 e o schedule Sch1 existem. Este Sch1 parte do estado guardado NewState1, salvo com o tanque vazio, então o enchimento do passo 3 parte de um tanque vazio; os números dele foram medidos neste arquivo.

2. Inserir o controlador

Arraste um PID Controller da Object Palette (Logical Blocks) para o canvas e renomeie para LIC-01. Abra-o:

  • Controlled Object T-01, Controlled Property Liquid Level, Units m
  • Manipulated Object V-02, Manipulated Property Opening, Units %
  • Set-Point: 1.0
  • Reverse Acting: marcado
  • Min./Max Output (Absolute): 0 / 100
  • Offset (Bias): 29
  • Kp / Ki / Kd: 2 / 0.1 / 0
  • Manipulated Variable Span (aba Advanced): 100

O editor do controlador PID

Direção, offset e span

O controlador calcula um erro adimensional (PV − SP)/|SP| e uma saída = Kp·erro + Ki·∫erro + Kd·d(erro)/dt. Com um span definido, a abertura da válvula é Offset + Saída × Span para um controlador de ação reversa. Ação reversa é o que uma malha de nível numa válvula de saída precisa: o nível sobe acima do setpoint, o erro é positivo, a válvula abre mais. O offset é a abertura que a válvula deve ter quando o erro é zero; 29 % é o que a V-02 precisa para passar 10 kg/s com 1 m de coluna. Deixado em zero, o span passa o controlador para a aritmética antiga, em que a saída é escalada pelo setpoint; isso só faz sentido quando a variável manipulada e a controlada são números do mesmo tamanho.

3. Encher o tanque até o setpoint

Rode Sch1 pelo Integrator Controls (10 minutos). O tanque começa vazio. O controlador fecha a V-02 em 10 s e a mantém quase fechada enquanto o nível está longe abaixo do setpoint, depois a abre conforme o nível se aproxima de 1 m. O nível passa de 1 m em 115 s, chega ao pico de 1,022 m em 120 s e fica em 1,000 m a partir de uns 3 min, com a V-02 em 28,8 %.

Enchendo a partir do seu próprio flowsheet

Se você continua com o seu próprio flowsheet da página anterior (Outlet de volta a 101325 Pa, V-02 em 50 %, passo de 5 s), o Sch1 dele parte do estado atual e esvazia o tanque antes da corrida. O enchimento então é um pouco diferente: a V-02 fecha no primeiro passo, o nível passa de 1 m aos 105 s, chega a 1,039 m aos 115 s e se mantém em 1,000 m a partir de uns 3 min. Ele ainda termina em 1,000 m com a V-02 em 28,8 %, e toda corrida a partir do passo 5 parte do estado guardado no passo 4, então o resto da página dá os mesmos números.

4. Guardar o estado estacionário

Pressione Store Current no grupo Flowsheet States da barra de ferramentas do flowsheet e dê ao estado o nome Steady. Um estado guardado contém todos os objetos, inclusive o conteúdo do tanque e o controlador, então um schedule que parte dele parte sempre dos mesmos 1,000 m e 28,8 %.

5. Uma perturbação na alimentação

No Dynamics Manager:

  • Event Sets: adicione FeedStep com um evento em 00:02:00: objeto Inlet, propriedade Mass Flow, valor 15 kg/s, degrau.
  • Schedules: adicione Disturbance com o integrador Int1, o conjunto de eventos FeedStep e o estado inicial Steady.

Rode Disturbance. O nível fica em 1,000 m por dois minutos, sobe até 1,062 m 15 s depois do degrau, e volta a 1,000 m por volta de 4 min; a V-02 vai de 28,8 % a 43,3 % e fica lá, passando os novos 15 kg/s.

Degrau de alimentação com controle PI

6. Só proporcional

Adicione um conjunto de eventos POnly com dois eventos: em 00:00:05 ponha o Ki de LIC-01 em 0, e o mesmo degrau de alimentação em 00:02:00. Adicione um schedule POnly a partir de Steady com ele e rode.

O nível sobe até 1,070 m e se acomoda em 1,065 m. Nunca volta: só com ação proporcional, a válvula só abre mais enquanto existe erro, e passar 15 kg/s exige 41,9 % de abertura, que precisa de um erro de 0,065 m para ser produzida. Esse erro permanente é o offset do controle proporcional. Suba o Kp e ele encolhe; só a ação integral o remove.

Por que mudar o ganho por um evento

Um schedule que parte de um estado guardado restaura o controlador com aquele estado, sintonia incluída. Pôr Ki = 0 no editor antes da corrida seria desfeito no instante em que a corrida partisse de Steady. Um evento em 5 s muda o ganho dentro da corrida; a alternativa é mudar a sintonia e depois guardar um novo estado.

7. Modo manual

Adicione um conjunto de eventos Manual com um evento em 00:00:05 que põe o ManualOverride de LIC-01 em 1, mais o degrau de alimentação em 2 min. Rode um schedule a partir de Steady com ele.

A V-02 fica em 28,8 %. Depois do degrau o nível sobe 0,26 m no primeiro minuto, passa o topo do tanque (2 m) em 9,4 min e marca 2,03 m em 10 min. Quando passa de 2 m o tanque escreve um aviso no log: o tanque não tem transbordo neste modelo, então o nível continua subindo. Esta é a resposta em malha aberta que o controlador escondia: 5 kg/s a mais entrando do que saindo são 5 L/s num tanque de 1 m², 0,3 m por minuto no começo, cada vez mais devagar conforme a coluna crescente empurra mais água pela V-02.

Mover a válvula em manual

Em manual o controlador escreve a saída dele na V-02 a cada passo, então um evento na abertura da V-02 é desfeito no passo seguinte. Para mover a válvula com a malha em manual, ponha o evento no OutputAbs de LIC-01, a saída em %.

8. Demais e de menos

Mais dois conjuntos de eventos, cada um com o degrau de alimentação em 2 min e mudanças de ganho em 5 s:

  • Aggressive: Kp 20, Ki 1. A malha fica instável assim que os ganhos mudam em 5 s, antes do degrau de alimentação: o nível oscila entre 0,84 e 1,13 m pelo resto da corrida, a V-02 bate entre 0 e 100 %, e a integral do erro absoluto (IAE) é 36,3 m·s.
  • Sluggish: Kp 0.5, Ki 0.005. O nível sobe até 1,158 m e ainda está em 1,060 m depois de 10 min; IAE 40,3 m·s.

A sintonia base (Kp 2, Ki 0,1) deu um pico de 1,062 m e IAE de 1,7 m·s. Os dois erros são mais de vinte vezes piores.

9. Mudança de setpoint

Adicione um conjunto de eventos SPStep com um evento em 00:01:00: SetPointAbs de LIC-01 = 1.5. Rode a partir de Steady. A V-02 fecha por completo para acumular nível, o nível chega a 1,49 m em 2 min, ultrapassa até 1,53 m e se acomoda em 1,500 m por volta de 4 min com a V-02 de volta em 23,6 %: menos abertura do que antes, porque 1,5 m de coluna empurra os mesmos 10 kg/s por um Kv menor.

10. A ferramenta de sintonia e o tempo real

Dynamics > PID Controller Tuning roda um schedule repetidas vezes com uma busca simplex sobre os ganhos, minimizando a soma dos erros absolutos dos controladores escolhidos. Aponte-a para Disturbance e LIC-01 para ver se ela acha algo melhor do que a sintonia base.

Para acompanhar a malha em tempo real, passe o flowsheet para o Control Panel Mode, escolha um schedule no Integrator Controls e pressione Real-Time (o botão de relógio). Uma corrida em tempo real parte do estado inicial do schedule e executa os eventos dele, então Disturbance traz o degrau de alimentação aos dois minutos. Mude o setpoint pela face do controlador enquanto ele roda.

Resultados e Validação

Caso Nível de pico Nível final V-02 final Unidade
Enchimento do vazio, PI 1,022 1,000 28,8 m, m, %
Degrau de alimentação, PI (Kp 2, Ki 0,1) 1,062 1,000 43,3 m, m, %
Degrau de alimentação, só P (Kp 2) 1,070 1,065 41,9 m, m, %
Degrau de alimentação, manual 2,03 em 10 min (acima do topo de 2 m desde 9,4 min) 2,03, ainda subindo 28,8 m, m, %
Degrau de alimentação, Kp 20, Ki 1 1,13, oscilando 0,84 a 1,13, ainda oscilando 0 a 100 m, m, %
Degrau de alimentação, Kp 0,5, Ki 0,005 1,158 1,060 (ainda em movimento) - m, m
Setpoint 1,0 → 1,5 m, PI 1,53 1,500 23,6 m, m, %

Resultados esperados

Com a sintonia base a malha rejeita um aumento de 50 % na alimentação com um pico de 6 cm e sem offset, e segue uma mudança de setpoint de 50 cm com 5 % de ultrapassagem. A ação proporcional sozinha deixa 6,5 cm de offset. Em manual o nível passa o topo do tanque em menos de dez minutos.

Entendendo os Resultados

Uma malha de nível numa válvula de saída é a malha mais simples que existe, e mostra todas as características do controle PID. A ação proporcional move a válvula em proporção ao erro, então precisa de um erro para manter um novo estado estacionário: o offset. A ação integral continua movendo a válvula enquanto restar algum erro, então o offset vai a zero; com integral demais, a válvula continua se movendo depois que o erro mudou de sinal, que é a oscilação do caso agressivo. A ação derivativa ficou em zero aqui; numa medição ruidosa ela faz mais mal do que bem, e um nível de tanque não tem dinâmica rápida o bastante para precisar dela.

O caso manual é o que fica. Com a V-02 parada, a vazão de saída ainda cresce com a raiz quadrada da coluna, então este tanque acha um novo equilíbrio, em cerca de 2,25 m (2,246 m depois de 30 min), acima do topo. O controlador escondia até onde, e com que lentidão, o nível vai quando nada move a válvula. Um tanque drenado por uma bomba, cuja vazão não depende do nível, não tem equilíbrio nenhum: ele enche ou esvazia a uma taxa constante. É por isso que malhas de nível estão sempre fechadas, e por isso um controlador de nível em manual é a primeira coisa que um operador procura quando um alarme de tanque soa.

Transferência sem solavanco

Quando o controlador volta de manual para automático, o DWSIM inicializa o termo integral de modo que a saída parta da posição atual da válvula, sem salto. Experimente: um evento ManualOverride = 0 em 5 min no schedule Manual traz a malha de volta sem salto na troca; o que vem depois é a resposta normal do controlador a um erro de 0,6 m.

Automatizando Este Tutorial

Arquivos neste repositório

import os
import sys

import pythonnet
pythonnet.load("netfx")              # DWSIM is a .NET Framework application
import clr  # noqa: E402

DWSIM_BIN = os.environ.get("DWSIM_BIN")
if not DWSIM_BIN:
    raise SystemExit("Set DWSIM_BIN to the DWSIM installation folder before running.")
for sub in ("", "unitops", "ppacks", "extenders"):
    sys.path.append(os.path.join(DWSIM_BIN, sub))
clr.AddReference("DWSIM.Automation")
clr.AddReference("DWSIM.Automation.FluentAPI")
clr.AddReference("DWSIM.UnitOperations")

from DWSIM.Automation.FluentAPI import Flowsheet, PropertyPackages, Q
from DWSIM.UnitOperations.UnitOperations import Valve
from System import TimeSpan

fs = (Flowsheet.Create("TankLevelControl")
      .WithCompound("Water").WithPropertyPackage(PropertyPackages.SteamTables))
inlet = (fs.AddMaterialStream("Inlet").At(Q.Kelvin(298.15), Q.Pascal(130000.0))
         .WithMassFlow(Q.KgPerSecond(10.0)).AsFlowSpec())
s1 = fs.AddMaterialStream("S-1").AsPressureSpec()
s2 = fs.AddMaterialStream("S-2").AsPressureSpec()
outlet = fs.AddMaterialStream("Outlet").AsPressureSpec()
(fs.AddValve("V-01").WithCalcMode(Valve.CalculationMode.Kv_Liquid).WithKv(100.0)
   .WithOpeningPercent(50.0).ConnectFeed(inlet, 0).ConnectProduct(s1, 0))
tank = (fs.AddTank("T-01").WithVolume(Q.CubicMeters(2.0)).WithHeight(Q.Meters(2.0))
        .ConnectFeed(s1, 0).ConnectProduct(s2, 0))
(fs.AddValve("V-02").WithCalcMode(Valve.CalculationMode.Kv_Liquid).WithKv(400.0)
   .WithOpeningKvRelationship().WithOpeningPercent(50.0)
   .ConnectFeed(s2, 0).ConnectProduct(outlet, 0))
fs.AutoLayout()
fs.Solve()
outlet.Object.Phases[0].Properties.pressure = 101325.0

lic = (fs.AddPIDController("LIC-01")
       .Controls("T-01", "Liquid Level", "m")
       .Manipulates("V-02", "PROP_VA_5", "")
       .WithSetPoint(1.0).WithTuning(2.0, 0.1, 0.0)
       .WithOutputLimits(0.0, 100.0).WithOffset(29.0).ReverseActing(True))
lic.Object.ManipulatedVariableSpan = 100.0

(fs.Dynamics.DefineIntegrator("Int1")
   .WithIntegrationStep(TimeSpan.FromSeconds(5)).WithDuration(TimeSpan.FromMinutes(10))
   .Monitor("T-01", "Liquid Level", "m", "level")
   .Monitor("V-02", "PROP_VA_5", "", "V-02 opening")
   .Monitor("Inlet", "PROP_MS_2", "kg/s", "inlet flow"))
fs.Dynamics.WithHistorian(True, 5000)

# fill the tank to the setpoint, then keep that state
fs.Dynamics.DefineSchedule("Fill").WithIntegrator("Int1").UseCurrentStateAsInitial(True).MakeCurrent()
fs.RunDynamics("Fill").Execute()
fs.Dynamics.StoreCurrentStateAs("Steady")

# a feed step from the stored state
(fs.Dynamics.DefineEventSet("FeedStep")
   .AddStepChange("Inlet", "PROP_MS_2", 15.0, Q.Seconds(120.0), "kg/s", "feed step"))
(fs.Dynamics.DefineSchedule("Disturbance").WithIntegrator("Int1")
   .WithEventSet("FeedStep").WithInitialState("Steady").MakeCurrent())
result = fs.RunDynamics("Disturbance").Execute()
level = result.GetSeries("level")
print(f"peak = {level.Max:.3f} m, final = {level.Final:.3f} m, IAE = {level.IAE(1.0):.2f} m.s")
print(f"V-02 final = {result.GetSeries('V-02 opening').Final:.1f} %")

# proportional only: change the gain inside the run, because the stored state restores it
(fs.Dynamics.DefineEventSet("POnly")
   .AddStepChange("LIC-01", "Ki", 0.0, Q.Seconds(5.0), "", "Ki off")
   .AddStepChange("Inlet", "PROP_MS_2", 15.0, Q.Seconds(120.0), "kg/s", "feed step"))
(fs.Dynamics.DefineSchedule("POnly").WithIntegrator("Int1")
   .WithEventSet("POnly").WithInitialState("Steady").MakeCurrent())
level = fs.RunDynamics("POnly").Execute().GetSeries("level")
print(f"P only: offset = {level.Final - 1.0:.3f} m")

# save the settled tank at 10 kg/s with the base tuning
fs.Inner.LoadProcessData(fs.Inner.StoredSolutions["Steady"])
fs.Dynamics.Schedule("Fill").MakeCurrent()
fs.Save("dynamic_tank_level_control.dwxmz")

Adicione o controlador com dwsim.unitop.add (tipo PID Controller) e defina suas propriedades com dwsim.unitop.set; defina conjuntos de eventos e schedules com as ferramentas dwsim.dynamics.*, guarde o estado e rode cada schedule.

A saída pode variar

Os resultados dependem da qualidade de raciocínio do LLM e da precisão no uso das ferramentas. Sempre verifique se a simulação corresponde à sua intenção antes de confiar nos números.

Abra o flowsheet dinâmico do tanque de água (tanque entre duas válvulas Kv,
entrada 10 kg/s com spec de vazão, saída a 1 atm) no DWSIM via servidor MCP e:

- Adicione um controlador PID "LIC-01": variável controlada nível de T-01 (m),
  variável manipulada abertura da V-02 (%), setpoint 1,0 m, ação reversa,
  limites de saída 0-100 %, offset 29 %, span da variável manipulada 100,
  Kp 2, Ki 0,1, Kd 0
- Rode o schedule existente (passo 5 s, 10 min) para encher o tanque até o
  setpoint, depois guarde o estado do flowsheet como "Steady"
- Defina um conjunto de eventos com um degrau da vazão de entrada para 15 kg/s
  em 2 min e um schedule "Disturbance" que parte de "Steady" com ele; rode
- Informe o nível de pico, o nível final e a abertura final da V-02
- Repita com um evento extra em 5 s que põe o Ki do controlador em 0, e informe
  o nível final (o offset proporcional)

Exercícios

  1. Só com ação proporcional, dobre o Kp para 4 e depois para 8. Como o offset muda? O que acontece com o movimento da válvula?
  2. Adicione Kd = 5 à sintonia base e rode o degrau de alimentação de novo. O pico muda? Olhe a abertura da válvula no primeiro minuto depois do degrau.
  3. Ponha um evento ManualOverride = 0 em 5 min no schedule Manual. Onde o nível termina, e quanto tempo o controlador leva para trazê-lo de volta?

Leitura Complementar

Referências selecionadas da bibliografia técnica do DWSIM.

  • K. J. Åström & T. Hägglund. (2006). Advanced PID Control. ISA
  • D. E. Seborg, T. F. Edgar, D. A. Mellichamp & F. J. Doyle. (2016). Process Dynamics and Control. Wiley

Próximos Passos

Em Controle de Pressão e Nível do Separador, duas malhas dividem um vaso: um controlador de pressão na saída de gás e um controlador de nível na saída de líquido, com mudanças de setpoint programadas como eventos.