Loops de controle PID com anomalias grandes e imprevisíveis

10

Pergunta curta
Existe uma maneira comum de lidar com anomalias muito grandes (ordem de magnitude) dentro de uma região de controle uniforme?

Antecedentes
Estou trabalhando em um algoritmo de controle que aciona um motor em uma região de controle geralmente uniforme. Sem carregamento mínimo / mínimo, o controle PID funciona muito bem (resposta rápida, pouca ou nenhuma ultrapassagem). O problema que estou enfrentando é que geralmente haverá pelo menos um local de alta carga. A posição é determinada pelo usuário durante a instalação, portanto, não há uma maneira razoável de saber quando / onde esperar.

Quando ajusto o PID para lidar com o local de alta carga, ele causa grandes disparos excessivos nas áreas não carregadas (o que eu esperava totalmente). Embora seja bom ultrapassar a metade do percurso, não há paradas mecânicas no gabinete. A falta de batentes rígidos significa que qualquer ultrapassagem significativa pode / faz com que o braço de controle seja desconectado do motor (produzindo uma unidade morta).

Coisas que estou fazendo prototipagem

  • PIDs aninhados (muito agressivos quando estão longe do alvo, conservadores quando estão próximos)
  • Ganho fixo quando longe, PID quando fechado
  • PID conservador (funciona sem carga) + um controle externo que procura o PID travar e aplicar energia adicional até que: o objetivo seja alcançado ou seja detectada uma taxa rápida de mudança (por exemplo, deixando a área de alta carga)

Limitações

  • Curso completo definido
  • Hardstops não podem ser adicionados (neste momento)
  • O erro provavelmente nunca será zerado
  • A carga alta pode ter sido obtida com um deslocamento inferior a 10% (o que significa que não há "partida inicial")
Adam Lewis
fonte

Respostas:

2

Seu cálculo de erro parece não acumular erro ao trabalhar com o termo derivativo, e você pode modificá-lo, pois apenas o termo derivado é capaz de reagir a mudanças rápidas no processo.

Se eu entendi direito, seu código

// Determine the error delta
dE = abs(last_error - new_error);
last_error = new_error;

sempre calculará o termo de controle com base no erro atual, que é a maneira tradicional como o PID foi implementado. Tudo bem, já que o termo I deve tratar o erro acumulado de qualquer maneira.

No entanto, tive um cliente que teve a seguinte ideia que você pode querer experimentar. Como você tem uma parte da curva do processo em que são necessárias alterações mais agressivas, você pode deixar até o erro da parte D acumular:

if(TD)                                                 // Calculate D term
{  
   Last_C += (Error - Last_C) / TD;                    // D term simulates
   Dterm = (Error - Last_C) * KD;                      // capacitor discharging
}
else    
   Dterm = 0;                                          // D term is OFF (TD is 0)

Há duas coisas interessantes a serem observadas aqui:

  • O valor de TD não é o ganho derivativo (que é KD), mas o tempo derivativo, uma constante do usuário que controla o tempo de acumulação de erros. Se foi definido como zero, a parte D do PID é desativada, desconsiderando o valor de ganho KD definido.

  • Observe como o erro atual foi usado para 'cobrar' o valor Last_C antes de levá-lo ao cálculo da parte D. A variável Last_C está agindo como um capacitor, acumularia enquanto o erro era grande, de modo que sua parte derivada atuaria com base também em um 'histórico' recente do erro e, depois disso (quando o erro era menor), esse histórico 'descarregará como um capacitor.

Obviamente, você deve limitar a produção total da maneira que provavelmente já faz (redefinição anti-corda, transferência automática acidentada para manual e outras coisas comuns).

Posso postar mais detalhes sobre outros termos do meu algoritmo PID, se você achar que é útil, mas você pode tentar isso e ver o que acontece. Atendeu bem meu cliente por anos.

Drazen Cika
fonte
Obrigado pela contribuição. Vou ter que tentar. De relance, parece fazer sentido.
Adam Lewis
Entendo, então você tem uma contribuição do termo D dentro do seu PID 'principal' mais o que a detecção de paralisação traz para o cálculo.
Drazen Cika
11
Está correto. O Dterm do PID é usado, embora não seja muito agressivo, durante o ajuste. O que torna esse problema ainda mais difícil é que a carga pode se libertar em um período de tempo muito curto. Ou seja, um link sendo desativado. Essa remoção abrupta da força causa grandes excedentes quando existe alguma função de suavização (soma) aplicada às forças de estol.
Adam Lewis
Problema perverso, seria interessante saber quão bem algum algoritmo de lógica nebulosa lidaria com isso. Pelo menos você pode incorporar no algoritmo mais da sua experiência relacionada ao problema, em vez de ficar confinado nas soluções padrão. De qualquer forma, boa sorte com isso :-)
Drazen Cika
1

Solução inicial

stalled_pwm_output = PWM / | ΔE

PWM = Valor máximo do PWM
ΔE = last_error - new_error

O relacionamento inicial aumenta com êxito a saída PWM com base na falta de mudança no motor. Veja o gráfico abaixo para obter a amostra de saída.

Essa abordagem leva desde a situação em que o PID não agressivo parou. No entanto, há a infeliz (e óbvia) questão de que, quando o PID não agressivo é capaz de atingir o ponto de ajuste e tenta desacelerar, o stalled_pwm_output aumenta. Essa aceleração causa um grande overshoot ao viajar para uma posição sem carga.

1 / ΔE vs ΔE

Solução Atual

Teoria

stalled_pwm_output = (kE * PID_PWM) / | ΔE

kE = Constante de dimensionamento
PID_PWM = Solicitação PWM atual do PID não agressivo
ΔE = last_error - new_error

Meu relacionamento atual ainda usa o conceito 1 / ΔE, mas usa a saída PID PWM não agressiva para determinar o stall_pwm_output. Isso permite que o PID acelere o stall_pwm_output quando ele começa a se aproximar do ponto de ajuste de destino, e ainda permite 100% de saída PWM quando parado. A constante de escala kE é necessária para garantir que o PWM chegue ao ponto de saturação (acima de 10.000 nos gráficos abaixo).

Pseudo-código

Observe que o resultado de cal_stall_pwm é adicionado à saída do PID PWM na minha lógica de controle atual.

int calc_stall_pwm(int pid_pwm, int new_error)
{
    int ret = 0;
    int dE = 0;
    static int last_error = 0;
    const int kE = 1;

    // Allow the stall_control until the setpoint is achived
    if( FALSE == motor_has_reached_target())
    {
        // Determine the error delta
        dE = abs(last_error - new_error);
        last_error = new_error;

        // Protect from divide by zeros
        dE = (dE == 0) ? 1 : dE;

        // Determine the stall_pwm_output
        ret = (kE * pid_pwm) / dE;
    }

    return ret;
}

Dados de saída

Saída PWM travada Saída PWM travada

Observe que no gráfico de saída do PWM parado, a queda repentina do PWM em ~ 3400 é um recurso de segurança interno ativado porque o motor não conseguiu alcançar a posição dentro de um determinado período de tempo.

Saída PWM não carregada Saída PWM sem carga

Adam Lewis
fonte
1

Você não diz o que está controlando ... velocidade do motor? posição? Bem, seja o que for, o primeiro passo seria definir o que seria um erro aceitável. Por exemplo, se o controle for para velocidade, um erro máximo de 1% do alvo pode ser definido. Sem definir o erro aceitável, você não pode determinar quanta resolução precisa para a contagem de ADCs ou PWM. Sem isso, a compensação do PID poderia ser perfeita, mas ainda teria oscilações de ciclo limite.

Então você precisa conhecer a dinâmica do sistema de loop aberto. Sem isso, você não pode saber quais ganhos são necessários para as partes proporcional (P), integral (I) e derivada (D) do loop. Você pode medir a dinâmica com a etapa de entrada (alteração da etapa no nível do inversor ou PWM) ou alteração da carga (parece que isso seria relevante para você).

O uso da alteração de erro ciclo a ciclo, no denominador do seu item de controle, para modificar o valor PWM garante que o loop nunca aconteça. Isso garante uma oscilação do ciclo limite no controle. A maioria dos clientes não aguentaria isso.

A parte P do loop cuida do erro imediato (responde imediatamente a um erro). Mas ele terá um ganho finito, então alguns erros serão deixados. A parte I do loop reage lentamente ao longo do tempo para aplicar ganho infinito (tempo infinito para ganho infinito) para corrigir o erro que foi deixado pela parte P.

Como a parte I é lenta, ela pode sair de fase com a correção necessária para minimizar os erros, mesmo se você tiver o ganho certo definido. Então, ele acaba, levando muito tempo para se recuperar. Ou, é deixado em oposição à parte P.

A melhor maneira de lidar com a conclusão é limitar o valor máximo armazenado no integrador a um pouco mais do que o necessário para corrigir o erro proporcional na pior das hipóteses. Se o integrador ficar fora de fase e em oposição ao P, a melhor coisa a fazer é definir o valor do integrador como zero. O algo pode ser projetado para detectar isso e redefinir o integrador quando necessário.

gsills
fonte