Tutorial · Iniciante · 18 min de leitura

Como ajustar um controlador PID: guia prático

Ajuste um controlador PID passo a passo: comece pelo ganho proporcional, adicione derivativo e integral na ordem correta e evite windup integral.

Um controlador PID converte o erro entre onde um sistema está e onde você quer que ele esteja em uma correção. Ele tem três termos — proporcional, integral e derivativo — e o ajuste consiste em escolher a intensidade da ação de cada um. A maneira confiável de fazer isso é um termo por vez, em uma ordem fixa, observando a resposta após cada alteração. Você pode acompanhar no simulador de controlador PID.

Comece com o ganho proporcional

Defina os ganhos integral e derivativo como zero. Aumente o ganho proporcional (Kp) até o sistema responder rapidamente a uma alteração da referência. Se for pouco, a reação será lenta; se for demais, haverá sobrepasso e oscilação. Pare quando a resposta for rápida, mas apenas levemente oscilante — esse é seu ponto de trabalho para adicionar os outros termos.

Uma regra útil: P fornece velocidade. Ele reage ao erro no momento e a nada mais.

Quatro respostas de passo da mesma planta sob diferentes ganhos PID, representadas como posição em função do tempo ao longo de quatro segundos, com uma linha horizontal tracejada de referência em um. Um ganho proporcional baixo sobe lentamente e estabiliza bem abaixo da referência. Um ganho proporcional alto ultrapassa muito a referência e oscila em várias oscilações grandes antes de estabilizar. O ganho proporcional moderado sozinho ultrapassa uma vez e depois estabiliza um pouco abaixo da referência. O traço combinado dos termos proporcional, integral e derivativo sobe rapidamente, ultrapassa apenas ligeiramente e estabiliza exatamente na referência.
Uma planta, quatro ajustes. Cada falha tem sua própria forma: uma diferença permanente, oscilações sustentadas ou uma curva que para pouco antes do alvo. Baixar SVG

Adicione o derivativo para controlar o sobrepasso

Agora aumente o ganho derivativo (Kd). A ação derivativa responde à velocidade de variação do erro, portanto atua como freio à medida que o sistema se aproxima da referência. É isso que elimina o sobrepasso e as oscilações deixados pelo controle puramente proporcional.

Adicione apenas um termo derivativo suficiente para eliminar a oscilação. Excesso de ganho faz o controlador saltar, porque o termo derivativo amplifica qualquer ruído presente na medição. Se o sinal estiver ruidoso, filtre-o antes da derivação ou dependa mais dos termos proporcional e integral.

Adicione o integral por último

Os termos proporcional e derivativo juntos ainda podem deixar um pequeno erro constante — o sistema para pouco antes da referência. O termo integral (Ki) corrige isso: ele acumula o erro ao longo do tempo e continua empurrando até que o desvio desapareça.

Aumente o ganho integral lentamente e somente se permanecer um erro em regime permanente reproduzível. O termo integral é o mais lento e o mais fácil de exagerar, portanto até uma pequena parcela já produz um efeito significativo.

Fique atento ao windup integral

O windup integral é a falha clássica do PID. Quando o atuador satura — um motor já na velocidade máxima, uma válvula completamente aberta — o sistema não consegue responder, mas o termo integral continua acumulando erro. Quando o erro finalmente muda de sentido, toda a ação armazenada é liberada de uma vez e o sistema apresenta um sobrepasso acentuado.

Evite-o limitando o integrador (pare de somar valores a ele enquanto a saída estiver saturada) ou por retro-cálculo (back-calculation, reduza o integrador usando a diferença entre a saída solicitada e a entregue). Uma limitação simples:

float candidateIntegral = integral + error * dt;
float raw = kp * error + ki * candidateIntegral + kd * derivative;
float output = constrain(raw, outputMin, outputMax);
if (raw == output) integral = candidateIntegral; // only accumulate when not saturated

Mantenha a temporização do ciclo precisa

Todos os três termos dependem de um passo de tempo consistente. Em um microcontrolador, um delay() longo no ciclo adiciona tempo morto que torna o termo derivativo inútil, por maior que seja seu valor, e distorce o termo integral. Execute o ciclo rapidamente e em intervalos constantes e passe o tempo real decorrido para o cálculo.

Chegue perto rapidamente: ponto de partida de Ziegler–Nichols

Aumentar P até encontrar a oscilação não é apenas um teste de consistência — fornece um número a partir do qual é possível calcular todo o ajuste. Defina I e D como zero e aumente P até a saída oscilar de forma constante, sem crescer nem decair, e registre duas coisas:

  • Ku — o ganho proporcional no qual isso acontece (o ganho crítico)
  • Tu — o período da oscilação, em segundos

Depois:

Controlador Kp Ki Kd
Somente P 0.5 Ku — —
PI 0.45 Ku 0.54 Ku / Tu —
PID (clássico) 0.6 Ku 1.2 Ku / Tu 0.075 Ku Tu
PID (sem sobrepasso) 0.2 Ku 0.4 Ku / Tu 0.066 Ku Tu

Esses valores são um ponto de partida, não uma resposta. A linha clássica é deliberadamente agressiva e normalmente produz um sobrepasso de 20–40%, o que é aceitável para um motor e inaceitável para um braço robótico perto de uma mesa. A última linha troca velocidade de resposta por chegada sem sobrepasso.

O benefício do método é substituir três incógnitas por duas medições. Vinte minutos para encontrar Ku e Tu deixam você mais perto do que uma tarde de tentativas, e você pode ajustar por intuição a partir de um ponto sensato.

Tome o derivativo da medição, não do erro

Esta é uma alteração de uma linha que corrige o problema mais comum relacionado ao termo D.

Quando a referência dá um salto, o erro dá um salto com ela. O derivativo de um salto é enorme por um instante, portanto o controlador comanda uma correção abrupta por um motivo que não tem relação com a física — nada se moveu, você apenas pediu algo diferente. Em um robô, isso provoca um solavanco brusco toda vez que a referência muda.

// Derivative kick: the setpoint step goes straight into the output.
float derivative = (error - lastError) / dt;

// Derivative on measurement: the setpoint contributes nothing at all.
float derivative = -(measurement - lastMeasurement) / dt;

O sinal é invertido porque o erro é setpoint − measurement; portanto, derivar a medição em vez do erro exige negação. Quando a referência é constante, as duas formas são matematicamente idênticas; quando ela muda, somente a segunda se comporta bem.

Use o derivativo sobre a medição, a menos que você tenha um motivo específico para não fazer isso. Raramente o salto do derivativo é desejável.

Filtre o derivativo, ou D amplificará o ruído

A derivação amplifica conteúdo de alta frequência, e o ruído dos sensores é de alta frequência por definição. Um sinal com variação de ±2 contagens, amostrado a cada 10 ms, produz uma estimativa derivativa que varia em ±200 contagens por segundo — valor que o termo D converte em comandos de motor.

Normalmente, as pessoas concluem que D é inútil e o definem como zero. O problema real está antes:

// A first-order filter on the derivative term only.
// alpha = dt / (tau + dt); tau is the filter time constant in seconds.
const float tau = 0.05f;                       // 50 ms
const float alpha = dt / (tau + dt);
dFiltered += alpha * (dRaw - dFiltered);

Escolher tau é uma troca: se for pequeno, o ruído permanece; se for grande, D atua sobre informação que já está desatualizada, exatamente o que D existe para evitar. Comece com três a cinco vezes o período do seu ciclo e aumente somente até os solavancos cessarem.

Um controlador completo

Tudo acima, em um único lugar:

struct Pid {
  float kp, ki, kd;
  float outMin, outMax;
  float tau;                 // derivative filter time constant

  float integral = 0;
  float lastMeasurement = 0;
  float dFiltered = 0;
  bool  primed = false;
};

float pidUpdate(Pid &c, float setpoint, float measurement, float dt) {
  if (dt <= 0) return 0;

  const float error = setpoint - measurement;

  // Derivative on measurement, low-pass filtered.
  if (!c.primed) { c.lastMeasurement = measurement; c.primed = true; }
  const float dRaw = -(measurement - c.lastMeasurement) / dt;
  const float alpha = dt / (c.tau + dt);
  c.dFiltered += alpha * (dRaw - c.dFiltered);
  c.lastMeasurement = measurement;

  // Provisional integral, committed only if the output does not saturate.
  const float candidate = c.integral + error * dt;
  const float raw = c.kp * error + c.ki * candidate + c.kd * c.dFiltered;
  const float out = constrain(raw, c.outMin, c.outMax);
  if (raw == out) c.integral = candidate;      // conditional integration

  return out;
}

Três elementos fazem a diferença entre um controlador que funciona em um robô e outro que funciona apenas em um livro didático: derivativo sobre a medição, derivativo filtrado e integração condicional, que impede o acúmulo do integrador enquanto o atuador está saturado.

Diagnosticando pela forma da resposta

É possível nomear quase todo defeito de ajuste apenas pela curva de resposta:

O que você observa O que significa O que alterar
Sobe devagar e nunca chega P baixo demais, sem I Aumente P; adicione I se permanecer um desvio
Estabiliza sempre antes do alvo Não há termo integral Adicione I lentamente
Ultrapassa uma vez e depois estabiliza P levemente alto, sem D Adicione D
Ultrapassa repetidamente com oscilações decrescentes P alto demais Reduza P ou adicione D
Oscila para sempre com amplitude constante P no ganho crítico Você encontrou Ku — divida por dois
A oscilação cresce P acima de Ku Reduza imediatamente
Saída rápida, irregular e instável D atuando sobre ruído Filtre o derivativo ou o sensor
Dá trancos fortes sempre que a referência muda Salto do derivativo Use o derivativo sobre a medição
Passa muito longe após um movimento grande Windup integral Use integração condicional ou limite o integrador
Funcionava ontem e está lento hoje A planta mudou — normalmente a bateria Os ganhos são propriedades da planta, não do código

A última linha merece destaque porque não é um defeito. Os ganhos descrevem uma planta específica, e a planta inclui a carga da bateria, a massa do chassi, a superfície e a temperatura da caixa de engrenagens. Um ajuste perfeito com baterias novas ficará lento com baterias gastas, porque o mesmo ciclo de trabalho agora produz menos torque. As soluções usadas em sistemas profissionais são um ciclo de velocidade por baixo (de modo que o controlador comanda velocidade, não ciclo de trabalho) ou escalonamento de ganhos (vários ajustes, selecionados pela condição de operação).

Tempo do ciclo, quantificado

Tanto I quanto D são calculados em relação ao tempo decorrido, portanto um ciclo executado sempre que termina calcula os dois com base em um valor falso.

Comportamento do ciclo Efeito no controlador
Intervalo fixo Tudo funciona como projetado
Variável, mas com dt medido e passado como argumento Também correto — esta é a resposta prática
Variável, com dt assumido como constante I acumula incorretamente; D se transforma em ruído
Contém um delay() Tempo morto — nenhum valor de D compensa

Meça dt com micros() e passe-o como argumento. Isso não custa nada, continua válido quando a duração do ciclo muda conforme recursos são adicionados e elimina toda uma categoria de mistérios durante o ajuste.

Se também for possível deixar o ciclo rápido, faça — um controlador que amostra a 20 Hz está agindo sobre informações com até 50 ms, e nenhum ganho corrige informações desatualizadas. Remover chamadas a delay() normalmente leva um primeiro robô de 20 Hz para várias centenas de hertz, e o acompanhamento melhora sem alterar nenhum ganho.

Experimente você mesmo

Abra o simulador de controlador PID, defina os ganhos I e D como zero e siga esta ordem. Observe como o sobrepasso, o tempo de acomodação e o erro em regime permanente respondem a cada termo isoladamente. Depois que a forma ficar clara aqui, a mesma intuição será transferível para um motor, aquecedor ou ciclo de direção no simulador de seguidor de linha.

Explore o grafo

Faz parte destas montagens

Projetos e caminhos de aprendizagem que incluem este tutorial.

Leitura complementar

Referências