PLL para Sincronismo com a Rede – Exemplo de Projeto e Implementação

Agora que você já entendeu como funcionam as técnicas de sincronismo com redes monofásicas, vamos para um exemplo de projeto e implementação. Para isso, vou considerar a técnica de PLL com filtro Notch pois é a mais simples de implementar e foi a que eu utilizei em meu TCC.

Se você ainda não leu o artigo anterior, corre lá antes de iniciar este aqui. Isso vai te ajudar a entender melhor o conteúdo.


Confira outros artigos do blog sobre sistemas fotovoltaicos:

PLL para sincronismo com a rede | A técnica usada em inversores fotovoltaicos e nobreaks

Projeto do Controlador PI

Antes de mais nada, é importante saber que o projeto do PLL em questão está diretamente relacionado ao projeto do controlador PI e do filtro Notch. Na literatura, existem diversas metodologias para se projetar o controlador PI. Uma delas é utilizando os conceitos que eu já apresentei no artigo intitulado “Teoria de Controle aplicada a Conversores Estáticos.”

O primeiro passo é identificar a função de transferência de laço aberto não compensada, FTLANC(s), a partir do diagrama de blocos da Figura 1.

Diagrama de blocos que representa o modelo linear de pequenos sinais do PLL.
Figura 1. Diagrama de blocos que representa o modelo linear de pequenos sinais do PLL.

Assim, temos que

FTLA_{NC}(s) = K_D \cdot \frac{1}{s} (1)

sendo KD = 0,5 para o PLL com filtro Notch e KD = 1 para o PLL baseado na transformada de Park.

Seja a função de transferência do PI dada por

PI(s) = K_c\cdot \frac{(s + \omega_z)}{s} (2)

então a frequência do zero (ωz) e o ganho do controlador (Kc) podem ser calculados como segue

\omega_z = \frac{\omega_c}{\text{tan}[MF - 90^\circ - \angle FTLA_{NC}(j\omega_c)]} = \frac{\omega_c}{\text{tan}(MF)} (3)
K_c = \frac{\omega_c}{\sqrt{\omega_c^2 + \omega_z^2}}\cdot\frac{1}{|FTLA_{NC}(j\omega_c)|} = \frac{\omega_c}{K_D}\cdot\sqrt{\frac{\text{tan}^2(MF)}{1 + \text{tan}^2(MF)}} (4)

Ou seja, basta definir a frequência de cruzamento (ω­c) e a margem de fase (MF) desejadas para o sistema em malha fechada, e depois calcular os parâmetros do controlador. Tipicamente, se utiliza uma frequência de cruzamento entre 6 Hz e 12 Hz para auxiliar na atenuação das componentes harmônicas de baixa frequência. Já a margem de fase pode ser definida entre 45º e 60º, a fim de se garantir uma boa estabilidade sem comprometer a rapidez da resposta dinâmica.

Logo, para ω­c = 2π∙(6 Hz) e MF = 60°, tem-se

PI(s) = 56,55\cdot \frac{(s + 21,76)}{s} = 56,55 + \frac{1230,53}{s} (5)

Projeto do Filtro Notch

O projeto do filtro Notch deve seguir as recomendações mencionadas no artigo anterior. Sendo assim, irei adotar: ωn = (2∙π∙120) rad/s, ζ2 = 0,0001 e ζ = 0,1, o que resulta na função de transferência da equação (6) e na resposta em frequência da Figura 2.

(6)
Resposta em frequência do filtro Notch sintonizado na frequência de 120 Hz.
Figura 2. Resposta em frequência do filtro Notch sintonizado na frequência de 120 Hz.

Discretização da Malha de PLL

Tendo em vista que a implementação da estratégia de controle em um inversor solar é majoritariamente digital, então o último passo é discretizar o controlador PI e o filtro Notch.

Aqui fica uma questão. Existe algum problema em se projetar o controlador no domínio contínuo e depois implementá-lo de forma digital? A resposta é SIM. O atraso provocado pela implementação discreta pode reduzir a margem de fase e, consequentemente, instabilizar a malha de controle. No entanto, como a frequência de cruzamento do PLL é significativamente menor que a frequência de amostragem, o atraso digital não terá efeito na banda passante do controlador. Por isso, vamos ignorá-lo da nossa análise.

Em outras situações, todavia, é extremamente importante considerar o efeito da implementação digital. Futuramente, irei mostrar como fazer isso no projeto de controladores discretos para conversores de potência. 

Controlador PI Digital

Existem diversos métodos para discretização de controladores lineares. Basicamente, estes consistem em substituir a variável complexa “s” da função de transferência no tempo contínuo, por uma aproximação no tempo discreto. Depois, utilizando-se da propriedade de deslocamento no tempo da transformada Z, obtém-se a equação de diferenças para a implementação digital.

Particularmente, eu prefiro discretizar a ação proporcional e a ação integral de forma independente. Isso simplifica a implementação, facilita a sintonização do controlador e permite adicionar técnicas de anti-windup no integrador [1], [2]. Seguindo essa linha de raciocínio, a função de transferência do PI no domínio discreto pode ser descrita por

PI(z) = K_P + K_I'\cdot\frac{1}{1 - z^{-1}} = K_c + (K_c\cdot \omega_z\cdot T_a)\cdot\frac{1}{1 - z^{-1}} (7)

onde a ação integral foi aproximada pelo método de Backward Euler: s = (1 – z-1)/Ta, em que KP representa o ganho proporcional, KI o ganho integral e Ta a taxa de amostragem.

Aplicando a transformada Z inversa em (7), obtém-se a equação de diferenças do controlador PI pronta para ser implementada em um microcontrolador. Essa equação é representada pelo diagrama de blocos da Figura 3, onde se observa perfeitamente a possibilidade de inserção de técnicas anti-windup no integrador.

Diagrama de blocos do controlador PI discreto.
Figura 3. Diagrama de blocos do controlador PI discreto.

A implementação do diagrama da Figura 3 usando a linguagem C está exemplificada no código abaixo. O que achou? Simples, não é?


// Calcula o erro: e[n]
erro = referencia - sinalDeFeedback;

// Computa a acao e integral: i[n] 
acaoIntegral = acaoIntegral + (KI * erro);

// Satura o integrador, caso necessario
if(acaoIntegral > SAIDA_MAXIMA)
	acaoIntegral = SAIDA_MAXIMA;
else if(acaoIntegral < SAIDA_MINIMA)
	acaoIntegral = SAIDA_MINIMA;

// Computa a saida total do controlado PI: x[n] = p[n] + i[n]
// acao proporcional p[n] + acao integral i[n]
saidaDoPI = (KP * erro) + acaoIntegral;

// Satura a saida caso necessario
if(saidaDoPI > SAIDA_MAXIMA)
	saidaDoPI = SAIDA_MAXIMA;
else if(saidaDoPI < SAIDA_MINIMA)
	saidaDoPI = SAIDA_MINIMA;

Filtro Notch Digital

A discretização do filtro Notch, por sua vez, pode ser feita usando Zero Order Hold, s = (z – 1)/Ta. Nesse caso, a função de transferência no domínio discreto resulta em [3]:

(8)

A equação (8) é implementada como uma estrutura digital 2p2z (dois polos, dois zeros) típica. Além disso, os coeficientes do filtro podem ser adaptados conforme as variações na frequência da rede elétrica, por meio de uma rotina de cálculo funcionando em segundo plano. Para ficar mais claro, o código a seguir mostra um exemplo de implementação do filtro Notch digital usando linguagem C.


//------------------------------------------------------------------------
// Filtro Notch 
//------------------------------------------------------------------------
saida[0] = A0*entrada[0] + A1*entrada[1] + A2*entrada[2] - 
           B1*saida[1] - B2*saida[2];

// Atualiza o array para uso futuro
entrada[2] = entrada[1];
entrada[1] = entrada[0];
saida[2] = saida[1];
saida[1] = saida[0];

Exemplo de Implementação

Para testar o algoritmo de PLL, eu utilizei um microcontrolador dsPIC33F da Microchip®, juntamente com um gerador de funções e um osciloscópio. Nesse setup, o gerador de funções tem o papel de emular a tensão da rede elétrica.

Quanto aos periféricos do microcontrolador, eu utilizei uma entrada analógica para ler o sinal do gerador de funções e o módulo PWM para externalizar os sinais do PLL  (ver Figura 4). Vale acrescentar que o código foi implementado em ponto fixo, com auxílio da biblioteca do MPLAB X. Caso você tenha interesse, os arquivos usados para implementar o PLL estão presentes no final deste artigo.

 

Setup utilizado para testar o algoritmo de PLL na prática.
Figura 4. Setup utilizado para testar o algoritmo de PLL.

Sabendo que esse tipo de algoritmo requer uma frequência de amostragem fixa, eu configurei o módulo PWM para habilitar o ADC a cada período. Em outras palavras, sempre que a contagem do PWM vai a zero, o ADC inicia frequência de 10 kHz.  

Resultados Experimentais

Na Figura 5 é possível observar o momento de partida do algoritmo de sincronismo, sendo o sinal em amarelo a amostra da tensão de entrada e o sinal em azul a saída do PLL. A Figura 6 mostra a resposta em regime permanente, onde se obteve um erro de fase de apenas 0,435°. Por fim, a Figura 7 mostra o comportamento do ângulo de sincronismo juntamente com a tensão, também em regime permanente.

Momento da partida do PLL.
Figura 5. Momento da partida do PLL. Em amarelo: amostra da tensão da rede. Em azul: sinal de saída do PLL.
Comportamento do PLL em regime permanente
Figura 6. Comportamento do PLL em regime permanente. Em amarelo: amostra da tensão da rede. Em azul: sinal de saída do PLL.
Ângulo de sincronismo em regime permanente.
Figura 7. Regime permanente. Em amarelo: amostra da tensão da rede. Em azul: ângulo de sincronismo.

Conclusão

Este artigo apresentou os principais aspectos do algoritmo PLL baseado no filtro Notch, abordando desde a metodologia de projeto até a implementação prática em linguagem C. Conforme destacado nos resultados experimentais, o algoritmo levou cerca de dois ciclos da rede para atingir o sincronismo por completo, apresentando um erro de fase de 0,435° em regime permanente.

Se você gostou do conteúdo, compartilhe com outras pessoas que também se interessam pelo assunto. E caso tenha ficado com alguma dúvida, sinta-se a vontade para comentar aqui embaixo. Até o próximo artigo.

Arquivo Header


#ifndef SPLL_H
#define	SPLL_H

//--------------------------------------------------------------------------
// Pre-processor Directives
//--------------------------------------------------------------------------
#include <libq.h>

typedef short int16_t;
typedef long int32_t;

//--------------------------------------------------------------------------
// Defines for PLL control loop
//--------------------------------------------------------------------------
// Loop Filter definitions
#define KP (float)56.55          // KP = Kc
#define KI (float)0.123053       // KI = Kc*wz/Fs

#define MAX_OUTPUT 0x01F40000    // Q16(500.0)
#define MIN_OUTPUT 0

// Notch Filter definitions
#define C1 (float)0.1
#define C2 (float)0.0001

// Other informations
#define FGRID 60
#define FSAMPLE 10000
#define DELTA_T (float)(1.0/FSAMPLE)

// Float to Fixed point conversion
// Defines for Q16 format
#define Q16 _Q16ftoi
#define Q16mpy _Q16mpy
#define Q16cos _Q16cos
#define Q16sin _Q16sin

// Variables for PLL control Loop
extern int32_t bNotch[3];        // Notch Filter coefficients
extern int32_t aNotch[2];
extern int32_t bLpf[2];          // Loop Filter coefficients  
extern int32_t integral;         // Loop Filter integrator
extern int32_t lpfOutput;        // Loop Filter output
extern int32_t theta;            // phase angle
extern int32_t cos, sin;         // save sine and cosine results
extern int32_t wn;               // Grid frequency in rad/s
extern int32_t w0;               // PLL output frequency 
extern int32_t uPD[3];           // Phase Detector variable
extern int32_t yNotch[3];        // Notch filter output
extern int32_t deltaT;           // Sample period

//--------------------------------------------------------------------------
// Function Prototypes
//--------------------------------------------------------------------------
void SPllInit(int16_t gridFreq, float tSample);
int16_t SPllRun(int16_t vGrid);

#endif	

Arquivo Fonte


#include "SPLL_NOTCH.h"

//--------------------------------------------------------------------------
// Function: SPllInit()
// Definition: this function initializes all variables used by PLL routine
// Parameters: Grid frequency and sample period
// Output: none
//--------------------------------------------------------------------------
void SPllInit(int16_t gridFreq, float tSample)
{
    // Local Variables
    float x, y, z;
    float _wn;
    
    // Initialize all variables for SPLL controller
    uPD[0] = Q16(0.0);
    uPD[1] = Q16(0.0);
    uPD[2] = Q16(0.0);
    
    yNotch[0] = Q16(0.0);
    yNotch[1] = Q16(0.0);
    yNotch[2] = Q16(0.0);
    
    integral = Q16(0.0);
    
    sin = Q16(0.0);
    cos = Q16(0.0);
    
    theta = Q16(0.0);
    
    _wn = (float)(4.0*3.1415926*gridFreq);
    wn = Q16(_wn);
    deltaT = Q16(tSample);
    
    //----------------------------------------------------------------------
    // Notch Filter Coefficients
    //----------------------------------------------------------------------
    x = (float)(2.0*C2*_wn*tSample);
    y = (float)(2.0*C1*_wn*tSample);
    z = (float)(_wn*tSample*_wn*tSample);
    
    aNotch[0] = Q16(1.0);
    aNotch[1] = Q16(x-2);
    aNotch[2] = Q16(z-x+1);
    bNotch[0] = Q16(y-2);
    bNotch[1] = Q16(z-y+1);

    //----------------------------------------------------------------------
    // Loop Filter Coefficients
    //----------------------------------------------------------------------
    bLpf[0] = Q16(KI);
    bLpf[1] = Q16(KP);
	
}//end SPllInit()

//--------------------------------------------------------------------------
// Function: SPllRun()
// Definition: Executes a software PLL control based on Notch filter
// Parameters: Grid voltage sampling
// Output: none
//--------------------------------------------------------------------------
int16_t SPllRun(int16_t vGrid)
{ 
    //----------------------------------------------------------------------
    // Phase Detect
    //----------------------------------------------------------------------
    int32_t temp = ((int32_t)(vGrid))<<1;
     
    uPD[0] = Q16mpy(temp, cos);
    
    //----------------------------------------------------------------------
    // Notch Filter
    //----------------------------------------------------------------------
    yNotch[0] = Q16mpy(aNotch[0], uPD[0]) + Q16mpy(aNotch[1], uPD[1]) + 
            Q16mpy(aNotch[2], uPD[2]) - Q16mpy(bNotch[0], yNotch[1]) -
            Q16mpy(bNotch[1], yNotch[2]);
    
    // Update arrays for future use
    yNotch[2] = yNotch[1];
    yNotch[1] = yNotch[0];
    uPD[2] = uPD[1];
    uPD[1] = uPD[0];
    
    //----------------------------------------------------------------------
    // Loop Filter
    //----------------------------------------------------------------------
    integral = integral + Q16mpy(bLpf[0], yNotch[0]);
	
    if(integral > MAX_OUT) integral = MAX_OUT;
    else if(integral < MIN_OUT) integral = MIN_OUT;
	
    // Computes the PI output
    lpfOutput = Q16mpy(bLpf[1], yNotch[0]) + integral;
	
    if(lpfOutput > MAX_OUT) lpfOutput = MAX_OUT;
    else if(lpfOutput < MIN_OUT) lpfOutput = MIN_OUT;
    
    //----------------------------------------------------------------------
    // VCO
    //----------------------------------------------------------------------
    w0 = wn + lpfOutput;
    
    // Computes theta value
    theta = theta + Q16mpy(w0,deltaT);
    
    // Reset theta 
    if(theta > Q16(2.0*3.14159))
    {
        theta = Q16(0.0); 
    }
    
    sin = Q16sin(theta);
    cos = Q16cos(theta);
    
    // Return the sine result in Q15 format
    return ((int16_t)(sin>>1));
    
}//end SPllRun()

Deixe seu comentário.

error: