Tutoriais

Filtro Digital no Arduino

Eletrogate 15 de abril de 2021

Introdução

Para entender o que é um filtro e entender a necessidade dele, primeiro precisamos compreender o que é um ruído.

Na eletrônica e em outras áreas é muito importante medir as coisas, para tomar decisões baseadas nessas medidas.

Por exemplo, em nossas aplicações com Arduino é muito comum querermos medir a temperatura para que o microcontrolador possa acionar um ventilador, ar-condicionado, um freezer, etc. Ou até mesmo só medir para registrar, para fazer análises, como neste post.

Ai entra o problema do ruído. O ruído é um sinal aleatório parasita que entra no meio da nossa medição. O problema é que ele infiltra na medição, como uma erva daninha em uma plantação, e se passa por aquele sinal que a gente está medindo.

Exemplo de ruído severo em medição – crédito: Wikipedia

Agora voltando para o nosso exemplo com o Arduino, vamos imaginar que estamos fazendo uma aplicação onde estamos medindo a distância entre um carro e um obstaculo qualquer, e que se caso a distância for menor que 2 metros o carro precise freiar bruscamente. Imagina o quanto um ruído pode ocasionar um evento drástico? Esse ruído pode fazer com que o Arduino entenda que o carro está muito perto, e muito rápido de um obstáculo, sendo que na verdade não existe obstáculo nenhum.

Os ruídos em sistemas eletrônicos, como no Arduino, podem ser ocasionados por diversas formas, as mais comuns são os ruídos que vem da fonte de alimentação, ruídos por capacitância parasita dos cabos e por indução eletromagnética nos fios. Toda vez que uma corrente elétrica percorre por um fio condutor ela gera um campo magnético no fio, e esse campo magnético pode gerar uma tensão em outro fio, que gera o ruído caso essa corrente for oscilante.

Alguns exemplos de ruído em medições utilizando Arduino estão nos gifs abaixo:

O primeiro exemplo é uma simples leitura em um potenciômetro, onde o ruído pode ter grandes oscilações, mesmo o potenciômetro estando parado.

O segundo exemplo é medindo distância do Arduino a uma caixa com um sensor ultrassônico. A caixa está parada, e o sensor também, mas mesmo assim existe uma variação de 1 cm na medição.

E o último exemplo é medindo a temperatura com um sensor de temperatura do tipo NTC. Onde a temperatura está constante, e mesmo assim existe um pequeno ruído.

Como remover esse sinal impostor que pode atrapalhar bastante na medição de algo que queremos? Veremos aqui algumas estratégias.


Filtro Passa-Baixa

Talvez os novatos em eletrônica talvez nunca tenha ouvido falar sobre esse termo, mas tenho certeza que os mais experientes que estão lendo esse post já ouviram muito falar sobre esse tipo de filtro.

Isso pode ser novidade para algumas pessoas, mas qualquer sinal que você possa imaginar pode ser decomposto em uma soma de senóides (daquelas mesmo que você aprendeu nas aulas do ensino fundamental e médio). Cada senóide com uma frequência determinada que compõe o sinal pode ser chamado de harmônicos (super recomendo ver esse video antes de prosseguir).

Um exemplo simples da soma de sinal é o sinal vermelho abaixo, que resulta da soma do sinal verde e do azul, onde cada harmônico ,

Crédito: musicweb

Ou do ciano abaixo, que é a soma do vermelho, verde e azul.

Crédito: musicweb

O legal do sinal resultante é que ele possui a frequência e peso de todos os sinais que originou ele. É como se ele tivesse um DNA que a gente pudesse rastrear todas as frequências de sinais que compõe esse sinal.

Para facilitar o exemplo, basta ver a imagem abaixo, onde o sinal em vermelho no canto esquerdo está no domínio do tempo enquanto o sinal azul do lado direito está no domínio da frequência.

Analisar um sinal no domínio da frequência é interessante porque conseguimos rastrear o “DNA” do sinal, quais frequências compõe esse sinal, como mostra na imagem em azul.

Se alguém ficou curioso em como se chama essa técnica de poder ver um sinal vendo as frequências que compõe ele, o nome é Transformada de Fourier. A equação de Fourier transforma um sinal que está visto no tempo, em um sinal visto somente na frequência dele. O Arduino possui algumas bibliotecas que fazem essa conversão automaticamente, e uma delas é: ArduinoFFT. Caso queira um dia implementar um analisador de frequências, o caminho é esse.

Agora vem o pulo do gato em relação ao filtro. Vendo os GIFs na seção de cima, conseguimos notar que os ruídos possuem frequência muito maior que o do nosso sinal de interesse, então basta realizar alguma técnica onde seja possível remover as frequências que não temos interesse (que nesse caso são as frequências do ruído).

É por isso que o nome dessa técnica é “filtro passa-baixa”, pois ele permite apenas passagem de frequências mais baixas no sinal final, retendo as frequências mais altas. Dessa forma o ruído fica retido.

Um filtro passa-baixa perfeito seria aquele onde a gente pudesse eliminar completamente o ruído e deixar apenas o sinal do sensor, como na imagem abaixo, onde o filtro corta perfeitamente toda a frequência mais alta.

Infelizmente isso não é possível, porque nenhum filtro consegue ser perfeito, porque o que conseguimos fazer é atenuar (reduzir o tamanho) das frequências mais altas e deixar intacto o sinal do sensor, como na imagem abaixo:

Dessa forma, quanto maior for a frequência do ruído, menor ele ficará no valor final.

Existem vários tipos de filtros passa-baixa, que vão diferenciar principalmente em serem:

  • Analógicos ou Digitais
  • Ordem do filtro
  • Passivo ou Ativo

Os filtros analógicos são filtros montados com componentes da eletrônica analógica, como capacitores, indutores, resistores. Um filtro simples analógico é um circuito RC:

Que a resposta dele é exatamente como mostrado no filtro real.

A ordem do filtro diz respeito a como ele corta as frequências fora da zona de interesse. Se a curva cai mais suave, o filtro é de menor ordem, enquanto filtros que caem mais rápido são de ordens maiores. Como mostrado na imagem abaixo:

Filtros de ordens maiores conseguem filtrar muito bem ruídos que possuem frequência muito próxima a do sinal de interesse, mas são mais difíceis de implementar.

Sobre os filtros serem ativos ou passivos, são filtros analógicos que utilizam amplificadores operacionais. Não entraremos no assunto pois o tema de hoje são filtros digitais.

Até aqui espero que você tenha entendido o fundamento básico sobre sinais e filtro passa-baixa. Caso não tenha entendido, convido você a reler, porque vai ser muito importante para prosseguirmos.


Filtros Passa-Baixa digital

A grande vantagem de um filtro digital é que ele consegue ser tão eficaz quanto um filtro analógico que necessita de componentes externos, mas ele não necessita de componente externo algum, economizando o tamanho que ficaria a placa. Outra vantagem de filtros digitais é que ele consegue filtrar também sensores que funcionam através de medição puramente digital, como o sensor ultrassônico.

A desvantagem de um filtro digital em comparação com um analógico, por sua vez, é que ele consome processamento do microcontrolador e em algumas implementações consome memória. Isso faz com que, em algumas aplicações, não seja possível utilizar esse tipo de filtro por gargalo do software.

Mas para a maioria das aplicações um filtro digital será suficiente e funcionará muito bem.

Os filtros digitais são divididos em dois tipos:

Filtro FIR

Os filtros do tipo FIR possuem uma resposta finita a um evento, digamos, a uma amostra. Isso significa que se estivessemos lendo um sinal de tensão da rede elétrica, e ocorre um pico no sinal de alguns milhares de Volts, o nosso filtro irá considerar essa informação por um tempo finito, isso é, após um tempo ele esquece dessa informação e o filtro volta a estabilizar.

Nesse tempo de Covid, você já ouviu muito falar em um filtro do tipo FIR, que é o filtro de média móvel que se usam muito.

Créditos: globo.com

Esse filtro da imagem acima está filtrando o número lamentável de mortes por Covid nos últimos 7 dias. Sendo assim, se acontecesse um pico único, o filtro não reagiria a ele depois de 7 dias. Ele funciona somando o número dos últimos 7 dias e tira uma média simples. No próximo dia ele soma novamente o número dos últimos 7 dias e tira média.

A grande vantagem do filtro FIR é que ele é bastante rápido para convergir para a variável medida (como discutimos, nenhum filtro é perfeito, e por isso eles acabam inserindo um pequeno atraso no sinal.  Esse tipo de filtro também é bem simples de implementar e sempre é estável.

Mas como desvantagem, esse filtro precisa de memória RAM do seu microcontrolador, o que acaba sendo um recurso muito caro, e caso for usado uma implementação onde precisa inserir pesos nas medições, haverá um esforço computacional muito grande, se tornando muitas vezes para microcontroladores implementações de média móvel ponderada.

Filtro IIR

Esse tipo de filtro, diferente do anterior, terá memória infinita a um evento. No exemplo de medição da tensão da rede elétrica, caso haja um pico de tensão enorme de milhares de volts, esse filtro irá ter resquício desse valor de tensão grande infinitamente, e irá demorar bastante para convergir novamente para o valor correto da medição após o pico.

Essa categoria de filtro busca implementar exatamente um filtro analógico, como os feitos com capacitores, mas de forma digital. A implementação parte do princípio em fazer um algoritmo em que a resposta matemática do filtro digital seja igual a de um feito com componentes discretos. Por isso a resposta é infinita.

A grande vantagem desse filtro é que ele não se utiliza de muita memória como o de média móvel, e o custo computacional dele é razoavelmente baixo, e é bastante confiável se bem implementado (se comportará tão bem quanto um filtro RC, por exemplo).

A desvantagem é que ele é mais lento em relação ao FIR, pode ser bem mais devagar para convergir em casos de pico isolado na medição.

Parâmetros de um filtro

Os parâmetros de um filtro digital fazem que cada filtro seja único e que funcione de uma forma diferente. Não existe um filtro absoluto que funcionará bem para todos os projetos, porque cada projeto pode ter perturbação que são mais rápidas ou mais lentas, e saber calibrar bem o filtro é muito importante.

Em um filtro sempre o mais importante é saber qual é a variação máxima esperada para a variável de medição sua, e qual é a frequência do ruído. Com isso, você pode projetar um filtro que deixe passar somente a variável que você deseja.

Em um filtro digital os parâmetros para você calibrar seu filtro são:

  • Intervalo de Amostragem
  • Quantidade (ou peso) de amostras

Entender o intervalo de amostragem é simples, é em quanto tempo o nosso filtro vai captar cada amostra. O que diferencia um filtro analógico de um filtro digital é justamente esse teor discreto. O filtro digital não atua todo o tempo, até porque é impossível implementar algo do tipo em um processador, já que o processador funciona com um clock, que faz com que ele faça uma instrução agora, e faça outra só depois de um pequeno intervalo de tempo.

Para escolher um bom intervalo de amostragem, existe uma boa prática de projeto é que seu intervalo de amostragem precisa ser no mínimo 10x menor que o tempo que o seu sensor pode ter uma oscilação real.

Por exemplo, se estivermos medindo a distância de um carrinho até uma parede, sabemos ser meio impossível esse carrinho se movimentar 10 centímetros em 1 milissegundo, logo, podemos definir que qualquer oscilação que tenha uma velocidade parecida com essa é ruído. Mas no caso do carrinho, ele pode mover 1 milímetro em 1 milissegundo, e pode ser até esperado isso.

No caso do carrinho, a taxa de amostragem ideal seria de 1/10 milissegundos (100 microssegundos) caso a gente queira medir na resolução dos milímetros a distância. (Vale ressaltar que o sensor precisa ter uma resolução adequada).

Agora, para entender a quantidade de amostras e sua importância, é só saber que quanto mais amostras você coletar para implementar o filtro, mais lento ele ficará, mas ele filtrará melhor. Quanto menor for esse parâmetro, mais rápido o filtro fica, mas ele ficará menos preciso.

Falei em quantidade de amostras ou peso, porque no caso do filtro do tipo FIR, de fato, o filtro trabalha com quantidades de amostras. Mas no caso dos filtros IIR é melhor entender como peso das amostras, que faz com que o filtro seja rápido ou lento. Mas neste post iremos tratar ambas as coisas como sendo quantidade de amostras, okay?

Lembra que citamos o filtro de média móvel que está sendo usado no Covid? Ele também se utiliza de um intervalo de amostragem e uma quantidade de amostras. Ele é programado para ter um intervalo de amostragem de 1 dia e quantidade de amostras de 7 dias.


Um Sinal Ruidoso

Um exemplo muito bom de uma saída extremamente ruidosa no Arduino é fazer a leitura de uma entrada analógica sem ligar nenhum circuito, somente o Arduino ligado na USB e mais nada. Isso faz com que a entrada analógica vire uma verdadeira antena de ruídos eletrostáticos de alta frequência.

Para fazer esse exemplo, conecte seu Arduino na USB e carregue o código abaixo:

void setup() {
  Serial.begin(9600);
}

void loop() {
  Serial.println(analogRead(0));
  delay(1);
}

Abra o seu Plotter Serial indo em “Ferramentas > Plotter Serial” (usaremos ele bastante neste tutorial) .

Como podemos ver, se essa fosse uma saída de um sensor, seria bem complicada a leitura, por ter um ruído muito forte e com algumas frequências altas associadas. Mas como iremos aprender aqui, existe solução para sinais muito ruidosos.

Como dito anteriormente, ruídos geralmente são aleatórios. Logo, qualquer variável aleatória a gente pode resolver tirando a média, porque a média de K amostras de qualquer sinal aleatório (sem offset) tenderá a 0.

A primeira estratégia abordada será um filtro do tipo FIR, que neste caso será a média móvel.


Média Móvel

O filtro de média móvel para entender como funciona é bem simples, basta tirar a média das últimas N amostras (onde N é o número que a gente define no número de amostras do filtro).

Mas caso uma solução elegante não seja usada, o esforço computacional pode ser grande, porque além de termos que registrar as últimas N amostras, também teremos que a cada amostragem somar esse número e tirar a média, e na próxima amostra somar tudo de novo e tirar média. Imagina caso a quantidade de amostras fossem mil, por exemplo. Imagina somar 1000 números a cada intervalo de amostras? Isso demandaria mais de 1000 ciclos de clock e seria um esforço muito grande.

Uma estratégia bem melhor é usar um buffer circular e uma variável de soma total.

Esse buffer circular é um vetor que termina na primeira posição (sim, a primeira posição está do lado da última). Neste essa estrutura será responsável por armazenar os valores, já que não temos de onde fugir de ter que armazenar os valores. A grande vantagem é que quando o filtro tiver cheio, não teremos gasto para ter que voltar para a primeira posição, ele volta automaticamente e reescreve.

A grande sacada nessa solução mais elegante está na variável soma total. Os comandos nessa variável funciona da seguinte forma:

  •  Subtrai dela o valor que será sobrescrito no buffer (vetor) pelo novo valor. Enquanto o buffer não estiver cheio não existe sobrescrição, logo, subtrairá zero.  Após estar cheio ele começará subtrair o valor que seria sobrescrito (o que não deveria mais contar para o filtro).
  • O novo valor de amostragem é somado a variável de soma total e sobrescreve último valor do buffer circular.

Com isso, é necessário somente 1 soma e 1 subtração a cada amostragem, independente do tamanho do filtro de média móvel. Isso faz com que essa solução tenha um esforço computacional seja muito menor e com a mesmo desempenho prático.

Código

Nas soluções apresentadas não terá montagem, porque basta plugar a placa Arduino no USB e carregar o código abaixo e abrir o Serial Plotter, como visto anteriormente.

#define Qtd_Amostras 10    // ***Quantas Amostras o filtro terá para filtrar, mude para testar outros filtros***
#define Intervalo_Amostragem 1 // ***definindo o intervalo de amostragem em ms. Mude para testar novos filtros***

int Leitura_analogica = 0; // Variável global que salva o dado bruto lido da porta serial.
// A estratégia usada aqui é porque o comando analogRead possui um custo alto para o Arduino.
// Com isso salvamos na variável para essa leitura ser feita apenas uma vez a cada interação de loop. 

unsigned long timer1=0; // A variável que irá contar o útimo 

void setup() {
  Serial.begin(9600); // Inicio da comunicação serial
  Serial.println("CLEARDATA"); // Comando para o Serial Plotter
  Serial.println("Sem_Filtro, Filtro_Media_Movel"); // Inicia os titulos dos eixos do Serial Plotter.
}

void loop() {
  Leitura_analogica = analogRead(A0);  // Leitura_analogica aqui é o valor bruto
  Amostragem(); // Essa é a função que fará a amostragem no tempo que determinamos no intervalo de amostragem que definimos na segunda linha de código.
  Serial.print(Leitura_analogica); // Imprime o dado bruto
  Serial.print(",");
  Serial.println(filtroMediaMovel(0)); // Imprime o dado filtrado
  // A função recebe o valor 0 que é para saber que não é para alterar o seu valor, somente imprimir o que está lá.
}

void Amostragem(){ // Essa função verifica se o tempo de amostragem  selecionado ocorreu
  if(millis() - timer1>Intervalo_Amostragem){ // Caso o tempo de amostragem tenha ocorrido, ele envia 1 para a função de filtro de media movel
    //Dessa forma a função sabe que é para atualizar o valor de saída para um novo valor filtrado
    filtroMediaMovel(1);
    timer1 = millis(); // atualiza para contar o tempo mais uma vez
  }
}

float filtroMediaMovel(bool atualiza_saida){ // A função de media movel trabalha com variavel estática, que salva as variaveis sem perder
  static  int Leituras_anteriores[Qtd_Amostras]; // Esse é o vetor que servirá como buffer circular
  static  int Posicao = 0; // A posicao atual de leitura, que deverá ficar salva
  static long Soma=0; // A soma total do buffer circular
  static float Media = 0; // A media, que é a saída da função quando é chamada
  static bool zera_vetor = 1;  // A variavel para saber se é a primeira execução. Se for, ele zera todo o buffer circular.
  
  if (zera_vetor){ // Zerando todo o buffer circular, para que as subtrações das sobrescrição não atrapalhe o filtro
    for(int i=0; i<= Qtd_Amostras; i++){
      Leituras_anteriores[i] = 0;
    }
    zera_vetor = 0;
//    Serial.println("Não entra mais no laço");
  }

  if(atualiza_saida == 0) return((double)Media); // Se o parametro recebido na funcao for zero, ele retorna somente o valor de media calculado anteriormente
  
  else{ // Caso seja 1, signfica que está no tempo de amostragem, e ai atualiza a variável média
  Soma = Leitura_analogica - Leituras_anteriores[Posicao%Qtd_Amostras] + Soma;
  Leituras_anteriores[Posicao%Qtd_Amostras] = Leitura_analogica;
  Media = (float)Soma/(float)(Qtd_Amostras);
  Posicao = (Posicao+1)%Qtd_Amostras;
  return((double)Media);
  }
}

Resultado:

Filtro com 10 amostras e intervalo de amostragem de 1ms,

Filtro com 100 amostras e intervalo de amostragem de 1ms,

Nesse comparativo podemos ver como os parâmetros mudam completamente a qualidade de um filtro digital.

Abaixo veremos um pouco mais sobre outros tripos de filtro.


Filtro Lógico

Um outro tipo de filtro que fácil de entender é um que podemos desenvolver usando somente a lógica.

Na verdade esse é um filtro digital de primeira ordem, mas chamarei ele aqui de filtro lógico, para que possamos entender o quão simples é a lógica por trás do modelo matemático deste filtro. A saída dele é idêntica ao do filtro matemático que será mostrado posteriormente.

A lógica para implementar esse filtro será:

  • Ao receber um valor, checamos se o valor analógico maior ou menor que o valor de saída do filtro
  • Caso seja maior que saída do filtro, a gente  faz a diferença desse valor e divide por um peso (que chamaremos da quantidade de amostras)  e soma esse valor encontrado a saída do filtro
  • Caso seja menor, fazemos o mesmo processo de achar a diferença e dividir por um valor, e subtraímos no valor de saída do filtro.

Como é possível ver, quanto maior for o valor da variável de peso, menor será o valor que será somado. Isso significa que o filtro será mais lento, mas consequentemente ruídos terão menor influência.

Código:

#define Qtd_Amostras 10    // ***Quantas Amostras o filtro terá para filtrar, mude para testar outros filtros***
#define Intervalo_Amostragem 1 // ***definindo o intervalo de amostragem em ms. Mude para testar novos filtros***

int Leitura_analogica = 0; // Variável global que salva o dado bruto lido da porta serial.
// A estratégia usada aqui é porque o comando analogRead possui um custo alto para o Arduino.
// Com isso salvamos na variável para essa leitura ser feita apenas uma vez a cada interação de loop. 

unsigned long timer1=0; // A variável que irá contar o útimo 

void setup() {
  Serial.begin(9600); // Inicio da comunicação serial
  Serial.println("CLEARDATA"); // Comando para o Serial Plotter
  Serial.println("Sem_Filtro, Filtro_Logico"); // Inicia os titulos dos eixos do Serial Plotter.
}

void loop() {
  Leitura_analogica = analogRead(A0);  // Leitura_analogica aqui é o valor bruto
  Amostragem(); // Essa é a função que fará a amostragem no tempo que determinamos no intervalo de amostragem que definimos na segunda linha de código.
  Serial.print(Leitura_analogica); // Imprime o dado bruto
  Serial.print(",");
  Serial.println(filtroLogico(0)); // Imprime o dado filtrado
  // A função recebe o valor 0 que é para saber que não é para alterar o seu valor, somente imprimir o que está lá.
}

void Amostragem(){ // Essa função verifica se o tempo de amostragem  selecionado ocorreu
  if(millis() - timer1>Intervalo_Amostragem){ // Caso o tempo de amostragem tenha ocorrido, ele envia 1 para a função de filtro de media movel
    //Dessa forma a função sabe que é para atualizar o valor de saída para um novo valor filtrado
    filtroLogico(1);
    timer1 = millis(); // atualiza para contar o tempo mais uma vez
  }
}

 float filtroLogico(bool atualiza_saida){ // Igual nos outros exemplos, ele usa variavel estática.
  int diferenca; // Variavel que salvará a diferenca entre o valor do filtro e a saída
  static float Saida_Filtro = 0; // A variavel 

  if(atualiza_saida == 0) return((double)Saida_Filtro);
  
  else{
    if(Leitura_analogica<Saida_Filtro){
      diferenca = abs(Leitura_analogica - Saida_Filtro);
      Saida_Filtro -= (float)diferenca/(float)Qtd_Amostras;
    }
    else if(Leitura_analogica>Saida_Filtro){
      diferenca = abs(Leitura_analogica - Saida_Filtro);
      Saida_Filtro += (float)diferenca/(float)Qtd_Amostras;
    }
  }
}

Resultado:

Filtro com 10 amostras (como dito anteriormente, o correto é dizer peso 10) e intervalo de amostragem de 1ms,

Filtro com 100 amostras e intervalo de amostragem de 1ms,

Se alguém já implementou um filtro analógico, deve percebeu que a curva do filtro é exatamente como a curva de um filtro RC. Porque matematicamente os dois filtros são iguais.

Abaixo veremos o modelo matemático deste filtro lógico e sua implementação. O chamaremos de filtro recursivo.

 


Filtro Recursivo

O filtro recursivo é uma implementação matemática de um filtro de primeira ordem analógico, mas passando ele para discreto.

A matemática para chegar no resultado deste filtro pode ser um pouco complicada para quem não domina alguns assuntos matemáticos relacionados às equações diferenciais como transformada de Laplace  e transformada Z. Se for o seu caso, pule até o fim da demonstração matemática do filtro.

Inicio da demonstração matemática

Para entender o filtro digital, primeiro precisamos entender o filtro analógico, que é de onde tudo deriva. O filtro analógico mais comum, já dito, é o RC, e ele possui uma função de transferência bem característica, no qual o desenvolvimento pode ser visto neste link. Partindo da função de transferência, podemos chegar no filtro lógico utilizado anteriormente.

Sabendo que essa é a função de transferência, podemos chegar em uma equação diferencial, fazendo a operação de inversa de Laplace.

A constante τ nessa equação significa a constante que define a velocidade do filtro, que no filtro RC é o produto da resistência vezes a capacitância.

A partir da equação acima será usado um recurso para discretizar que é fazer a equação de diferenças. Sabemos que dy/dt pode ser aproximado por Δy/Δt, onde Δ significa uma variação. Logo surgirá algumas distorções relacionados a essa aproximação. A multiplicação dessa distorção vezes o tau (τ ), chamaremos de W.

Com isso, toda vez que for visto W, é só saber que é um valor muito próximo a τ , e tem a ver com a taxa de amostragem e quantidade de amostras.

Como agora a equação já está em tempo discreto, ao invés de a notação ser y(t), agora é y[k], que é a notação de tempo discreto. Onde [K] significa a ultima amostra, e [K – 1] é a amostra anterior.

Com isso agora é só fazer alguns algebrismos e se acha que:

Aqui já dá para implementar esse filtro em um microcontrolador, mas não dá para enxergar direito o filtro lógico. Para ser possível enxergar bem o filtro lógico, chamaremos a constante que acompanha x de alfa, e a que acompanha y[k-1] de beta.

Com isso ficou fácil observar uma coisa, que se somar alfa mais beta o resultado é 1. Como isso é verdade, podemos substituir Beta por 1 – Alfa, como é visto na equação abaixo:

Aqui já chegamos a um resultado interessante. Fazendo um pouco de algebrismo conseguimos ver a seguinte equação abaixo:

Agora ficou extremamente interessante, já se pode notar que a saída do filtro é igual à saída anterior somando a diferença da saída do filtro com a amostra atual, multiplicada por um peso alfa.

Se lembrarmos, alfa foi substituído. Podemos voltar lá e substituir de volta pelo valor original:

E aqui está a equação final, o filtro lógico! Como é linda a matemática, não é mesmo?

Essa equação está dizendo exatamente o filtro lógico, que a saída do filtro atual é a saída anterior somando a diferença entre a amostra atual e o filtro e dividindo por um peso que chamamos de “quantidade de amostras”. Nessa equação podemos ver que na verdade essa “quantidade de amostras” não era bem a quantidade de amostras, como foi discutido, e sim uma constante (1 + W), onde esse W depende de uma série de fatores, como o tempo de amostragem e a quantidade de amostras de fato.

Iremos prosseguir chamando esse (1 + W) de quantidade de amostras, somente para fins didáticos.

Fim da demonstração matemática

Vimos a lindeza da matemática por trás do filtro lógico, e iremos implementar o filtro de primeira ordem utilizando a equação de diferenças demonstrada aqui.

Código

Carregue o código e veja como esse filtro é idêntico ao filtro lógico.

#define Qtd_Amostras 100    // ***Quantas Amostras o filtro terá para filtrar, mude para testar outros filtros***
#define Intervalo_Amostragem 1 // ***definindo o intervalo de amostragem em ms. Mude para testar novos filtros***

int Leitura_analogica = 0; // Variável global que salva o dado bruto lido da porta serial.
// A estratégia usada aqui é porque o comando analogRead possui um custo alto para o Arduino.
// Com isso salvamos na variável para essa leitura ser feita apenas uma vez a cada interação de loop. 

unsigned long timer1=0; // A variável que irá contar o útimo 

void setup() {
  Serial.begin(9600); // Inicio da comunicação serial
  Serial.println("CLEARDATA"); // Comando para o Serial Plotter
  Serial.println("Sem_Filtro, Filtro_Recursivo"); // Inicia os titulos dos eixos do Serial Plotter.
}

void loop() {
  Leitura_analogica = analogRead(A0);  // Leitura_analogica aqui é o valor bruto
  Amostragem(); // Essa é a função que fará a amostragem no tempo que determinamos no intervalo de amostragem que definimos na segunda linha de código.
  Serial.print(Leitura_analogica); // Imprime o dado bruto
  Serial.print(",");
  Serial.println(filtroRecursivo(0)); // Imprime o dado filtrado
  // A função recebe o valor 0 que é para saber que não é para alterar o seu valor, somente imprimir o que está lá.
}

void Amostragem(){ // Essa função verifica se o tempo de amostragem  selecionado ocorreu
  if(millis() - timer1>Intervalo_Amostragem){ // Caso o tempo de amostragem tenha ocorrido, ele envia 1 para a função de filtro de media movel
    //Dessa forma a função sabe que é para atualizar o valor de saída para um novo valor filtrado
    filtroRecursivo(1);
    timer1 = millis(); // atualiza para contar o tempo mais uma vez
  }
}

float filtroRecursivo(bool atualiza_saida){ // Implementação matematica do filtro de 1a ordem analogico.
  
  static float Saida_Filtro = 0;

  if(atualiza_saida == 0) return((double)Saida_Filtro);
  
  else{
  Saida_Filtro += (float)(Leitura_analogica - Saida_Filtro)/(float)Qtd_Amostras;
  }
}

Resultado:

Filtro com 10 amostras e intervalo de amostragem de 1ms,

Filtro com 100 amostras e intervalo de amostragem de 1ms,


Comparação Entre os Filtros

No vídeo abaixo você vai poder conferir a comparação do desempenho de todos os filtros que desenvolvemos e abordamos:


Conclusão

Neste post de hoje podemos aprender muito sobre filtros digitais, a diferença entre um filtro FIR e um IIR, e como implementar estes filtros e as vantagens que eles possuem em relação ao outro.

Existem diversas bibliotecas de filtros para Arduino, onde você pode encontrar biblioteca de média móvel, mediana móvel, filtro exponencial, etc. Propositalmente não apresentei nenhuma biblioteca neste post, porque estávamos interessados em saber como funciona a teoria do filtro, e como faz para implementar.

Não utilizar uma biblioteca te deixa livre para implementar um filtro em absolutamente qualquer microcontrolador que não seja uma placa Arduino, enquanto uma biblioteca te deixa refém.

Desde já peço desculpas se o post ficou muito grande. Filtros é um assunto bem extenso e gostaria que um iniciante pudesse ler e sair com muitos conhecimentos novos, apesar de talvez não absorver tudo por ter detalhes técnicos um pouco complexo.

Caso você tenha interesse em mergulhar no assunto e aprender toda a parte teórica por trás, recomendo o livro Sinais e Sistemas do Oppenheim. Ele aborda de forma muito interessante toda a teoria de sinais contínuos e discretos, transformadas, amostragem e filtro. Pode ser um pouco complexo para entender de cara, por isso, recomendo que anteriormente você faça uma base aprendendo um pouco sobre equações diferenciais.

Espero que você tenha tido um excelente aprendizado até aqui, e caso você tenha implementado um filtro desse em seu projeto que envolve um sensor, e tenha resolvido o seu problema, faça um videozinho e marque a gente no instagram @eletrogate. Vai ser um enorme prazer para nós ver que seu projeto deu uma melhorada.

Em nosso instagram além de dicas, curiosidades e tutoriais, rola eventuais sorteios de kits 😱. Não fique de fora, siga a gente e fique por dentro de tudo.

Muito obrigado por sua leitura e atenção até aqui. Caso tenha alguma dúvida ou sugestão, utilize o campo dos comentários que irei responder o mais rápido possível.

Forte abraço e até a próxima!

Conheça a Metodologia Eletrogate e ofereça aulas de robótica em sua escola!


Sobre o Autor


Gustavo Nery

Cursando Engenharia de Controle e Automação pela UFMG. Apaixonado por eletrônica, computação e tecnologias na área de sistemas embarcados. Nos tempos livres me divido entre desenvolver pesquisa na universidade, adquirir novos conhecimentos e estar com a família.


Eletrogate

15 de abril de 2021

A Eletrogate é uma loja virtual de componentes eletrônicos do Brasil e possui diversos produtos relacionados à Arduino, Automação, Robótica e Eletrônica em geral.

Conheça a Metodologia Eletrogate e Lecione um Curso de Robótica nas Escolas da sua Região!

Eletrogate Robô

Cadastre-se e fique por
dentro de novidades!