Índice:

EasyFFT: Fast Fourier Transform (FFT) para Arduino: 6 etapas
EasyFFT: Fast Fourier Transform (FFT) para Arduino: 6 etapas
Anonim
Image
Image

A medição da frequência do sinal capturado pode ser uma tarefa difícil, especialmente no Arduino, pois tem menor poder computacional. Existem métodos disponíveis para capturar o cruzamento zero, onde a frequência é capturada, verificando quantas vezes o sinal cruza as linhas zero dentro do tempo determinado. Esse método pode não funcionar quando o sinal é uma combinação de várias frequências.

Isso é de alguma forma difícil de codificar se você não tiver esse histórico. Mas sendo um remendador, este código pode ser muito útil para vários projetos relacionados à música, análise de sinais. O motivo deste projeto foi preparar um código que seja fácil de implementar no Arduino, sem entrar em seus antecedentes.

Este projeto não explica o funcionamento da FFT, mas explica a aplicação da função FFT. O mesmo processo também é explicado no vídeo anexo.

Se você está interessado apenas na aplicação do código e não em uma explicação sobre ele. Você pode pular diretamente para a etapa 3.

Etapa 1: Introdução à Transformação de Frequência

Introdução à Transformação de Frequência
Introdução à Transformação de Frequência
Introdução à Transformação de Frequência
Introdução à Transformação de Frequência

Qualquer sinal pode ser composto de uma combinação de várias ondas sinusoidais. Portanto, qualquer sinal baseado no tempo também pode ser mostrado como uma combinação dos vários seno de diferentes amplitudes.

Tentei explicar o funcionamento do DFT (transformada discreta de Fourier) em um dos instrutíveis anteriores (https://www.instructables.com/id/Arduino-Frequency …). Esses métodos são extremamente lentos para qualquer aplicativo em tempo real. o que o torna quase inútil.

Na imagem, é mostrado um sinal que é uma combinação de duas frequências f2 e f5. Este sinal é multiplicado por ondas senoidais de teste de valores f1 a f5.

Pode ser mostrado matematicamente que a soma da multiplicação de dois conjuntos de dados harmônicos com frequências diferentes tende a zero (um número maior de dados pode levar ao resultado da massa). Em nosso caso, se essas duas frequências de multiplicação tiverem a mesma frequência (ou muito próxima), essa soma da multiplicação será um número diferente de zero.

Portanto, se nosso sinal for multiplicado por f1, a soma da multiplicação será zero (próximo de zero para aplicação real). semelhante é o caso de f3, f4. No entanto, para o valor, a saída de f2 e f5 não será zero, mas significativamente maior do que o restante dos valores.

Aqui, um sinal é testado com 5 frequências, então o sinal precisa ser multiplicado por cinco frequências. Esse cálculo intenso leva mais tempo. Matematicamente, é mostrado que para N número de amostras é necessária N * N multiplicação complexa.

Etapa 2: Transformação Rápida de Fourier

Para tornar o cálculo do DFT mais rápido, o algoritmo FFT foi desenvolvido por James Cooley e John Tukey. Este algoritmo também é considerado um dos algoritmos mais importantes do século XX. Ele divide um sinal em uma parte de sequência ímpar e par, o que reduz o número de cálculos necessários. Ao usá-lo, a multiplicação complexa total necessária pode ser reduzida a NlogN. o que é uma melhoria significativa.

Você pode consultar as referências abaixo que me referi ao escrever o código para uma compreensão detalhada da matemática por trás da FFT:

1.

2.

3.

4.

Etapa 3: Explicação do Código

1. Seno e cosseno rápidos:

O cálculo FFT leva o valor de vários seno e cosseno várias vezes. A função embutida do Arduino não é rápida o suficiente e leva muito tempo para fornecer o valor necessário. O que torna o código significativamente mais lento (dobra o tempo para 64 amostras). Para neutralizar esse valor de emissão de seno de 0 a 90 graus, é armazenado como múltiplo de 255. Isso eliminará a necessidade de armazenar números como flutuante e podemos armazená-lo como byte, que ocupa 1/4 de espaço no Arduino. O sine_data precisa ser colado no topo do código para declará-lo como uma variável global.

Além de sine_data, uma matriz chamada f_peaks declarada como uma variável global. Após cada execução da função FFT, este array é atualizado. Onde f_peaks [0] é a frequência mais dominante e outros valores em ordem decrescente.

byte sine_data [91] = {0, 4, 9, 13, 18, 22, 27, 31, 35, 40, 44, 49, 53, 57, 62, 66, 70, 75, 79, 83, 87, 91, 96, 100, 104, 108, 112, 116, 120, 124, 127, 131, 135, 139, 143, 146, 150, 153, 157, 160, 164, 167, 171, 174, 177, 180, 183, 186, 189, 192, 195, 198, 201, 204, 206, 209, 211, 214, 216, 219, 221, 223, 225, 227, 229, 231, 233, 235, 236, 238, 240, 241, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 253, 254, 254, 254, 255, 255, 255, 255}; float f_peaks [5];

Como armazenamos o valor de seno de 0 a 90 graus, qualquer valor de seno ou cosseno pode ser calculado. Abaixo da função a primeira rodada do número para a casa decimal zero e o valor de retorno dos dados armazenados. este método precisa de apenas uma divisão flutuante. Isso pode ser reduzido ainda mais, armazenando diretamente os valores do seno (não o múltiplo de 255). mas isso consome muita memória no Arduino.

Usar o procedimento acima reduz a precisão, mas melhora a velocidade. Para 64 pontos dá uma vantagem de 8ms e para 128 pontos dá uma vantagem de 20ms.

Etapa 4: Explicação do Código: Função FFT

A FFT só pode ser realizada para o tamanho da amostra de 2, 4, 8, 16, 32, 64 e assim por diante. se o valor não for 2 ^ n, ele assumirá o lado inferior do valor. Por exemplo, se escolhermos o tamanho da amostra de 70, ele irá considerar apenas as primeiras 64 amostras e omitir o resto.

É sempre recomendado ter um tamanho de amostra de 2 ^ n. que pode ser:

2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, …

Dois flutuadores out_r e out_im ocuparão uma grande quantidade de memória. para Arduino nano não funcionará para amostras superiores a 128 (e em alguns casos 128) devido à falta de memória disponível.

dados int sem sinal [13] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048};

int a, c1, f, o, x; a = N; for (int i = 0; i <12; i ++) // calculando os níveis {if (data <= a) {o = i;}} int in_ps [data [o] = {}; // entrada para sequenciamento float out_r [dados [o] = {}; // parte real da transformação float out_im [data [o] = {}; // parte da imaginação da transformação

O fluxo adicional é o seguinte:

1. O código gera um bit invertido na ordem para o tamanho de amostra fornecido (detalhes sobre a reversão do bit nas referências: etapa 2)

2. Dados de entrada ordenados de acordo com o pedido gerado, 3. FFT realizada

4. A amplitude do número complexo calculado, 5. Os picos são detectados e ordenados em ordem decrescente

6. os resultados podem ser acessados em f_peaks.

[para acessar outros dados (além da frequência de pico), o código deve ser modificado, de modo que a variável local possa ser copiada para alguma variável global predefinida]

Etapa 5: Testando o Código

Testando o Código
Testando o Código
Testando o Código
Testando o Código

Uma amostra de onda de triângulo é fornecida como entrada. para esta onda, a frequência de amostragem é 10 Hz e a frequência da própria onda é 1,25 Hz.

Como pode ser mostrado na saída bruta, o valor está combinando com o FFT calculado pelo Scilab. no entanto, esses valores não são exatamente os mesmos que temos baixa precisão, mas onda senoidal mais rápida.

Na frequência de saída, a frequência da matriz é 1,25 e 3,75. não é necessário obter o valor exato todas as vezes. normalmente, esses números são chamados de compartimentos de frequência. portanto, o valor de saída pode estar em qualquer lugar dentro das caixas especificadas.

Velocidade:

para o Arduino nano é preciso:

16 Pontos: 4ms32 Pontos: 10ms 64 Pontos: 26ms 128 Pontos: 53ms

Etapa 6: Conclusão

Este código FFT pode ser usado em aplicativos em tempo real. Uma vez que leva cerca de 30 ms para completar o cálculo. No entanto, sua resolução é limitada por um número de amostras. O número da amostra é limitado pela memória do Arduino. Usando o Arduino Mega ou outra placa de maior desempenho, a precisão pode ser melhorada.

se você tiver alguma dúvida, sugestão ou correção, sinta-se à vontade para comentar.

Atualizar (2/5/21)

Atualizações: // ----------------------------- Função FFT --------------- ------------------------------- // float FFT (int in , int N, frequência float)

O tipo de dados de N mudou para Inteiro (Byte existente) para suportar o tamanho de amostra> 255. Se o tamanho da amostra for <= 128, o tipo de dados de byte deve ser usado.

Recomendado: