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:
- Célula Fotovoltaica: Tudo que você queria saber.
- Uma Introdução aos Sistemas Fotovoltaicos.
- AS 7 Principais Técnicas de MPPT.
- Os 3 Tipos de Microinversores Fotovoltaicos.
- Microinversor Solar – Como funciona a estratégia de controle.
- Sincronismo com a Rede Elétrica em Inversores Fotovoltaicos On-grid.
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.
Assim, temos que
(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
(2) |
então a frequência do zero (ωz) e o ganho do controlador (Kc) podem ser calculados como segue
(3) |
(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
(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) |
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
(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.
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.
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.
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()
Caio, o vídeo de simulação no Youtube, você tem como disponibilizar o arquivo .asc ou fazer um vídeo ensinando a construir.
Abraço!