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:
- Inlet entrega 10 kg/s (especificação de Vazão), depois sobe para 15 kg/s
- T-01 é o tanque de 2 m³; o nível de líquido é a variável controlada
- LIC-01 é o controlador PID, que lê o nível e escreve a abertura da V-02
- V-02 é a válvula de saída, Kv 400, a variável manipulada
- 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 PropertyLiquid Level, Unitsm - Manipulated Object
V-02, Manipulated PropertyOpening, 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

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
FeedStepcom um evento em00:02:00: objetoInlet, propriedadeMass Flow, valor15kg/s, degrau. - Schedules: adicione
Disturbancecom o integradorInt1, o conjunto de eventosFeedStepe o estado inicialSteady.
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.

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: Kp20, Ki1. 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: Kp0.5, Ki0.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
- Script Python:
examples/dynamics/02_pid_level_control.py - Flowsheet pronto:
examples/saved/dynamic_tank_level_control.dwxmz - No DWSIM: lista Samples na tela inicial,
Dynamic Simulation - Water Tank Level Control
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
- 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?
- 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.
- Ponha um evento
ManualOverride= 0 em 5 min no scheduleManual. 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.