LusoRobótica - Robótica em Português

Robótica => Eventos e Concurso de robótica => Concursos e Desafios => Tópico iniciado por: Njay em 21 de Junho de 2015, 16:45

Título: Desafio: State Variable Filter
Enviado por: Njay em 21 de Junho de 2015, 16:45
Desta vez é só o desafio, não há ofertas.
Andava aí a instruir-me um bocado por causa dum projecto que tenho em mãos e encontrei esta topologia de filtro que achei bastante interessante, especialmente para aplicar no domínio digital, e para áudio: o State Variable.

O filtro nasceu no mundo analógico (podem procurar na wikipedia) onde se constitui por 3 ampops e depois também saltou para o mundo digital. Tem algumas particularidades que o tornam muito interessante para audio e implementação em microcontroladores:

1) É relativamente leve de computar (e só precisa de 2 valores anteriores).

2) Os parâmetros são independentes e fáceis de calcular; os dados de entrada são a frequência de corte/central e o "damping" ou Q e saiem o kf e o kq usados no filtro.

3) É um filtro de 2ª ordem (12dB/oitava em vez dos 6dB/oitava de um filtro de 1ª ordem), portanto já tem um efeito que se nota bem.

4) Da mesma implementação podemos obter um LP (low pass), HP (high pass), ou um BP (band pass), só muda o ponto no cálculo de onde tiramos o output. Supostamente ele também faz um BR (band reject), mas nas experiências que fiz pareceu-me não fazer nada.

Vejam no final desta página uma demo interactiva de um LP com este filtro:
http://worrydream.com/Tangle/ (http://worrydream.com/Tangle/)

Não encontrei nenhum "diagrama de diferença" (acho que é este o nome destes diagramas) que me agradasse por isso peguei no do site acima e modifiquei-o para mostrar aqui:

(https://lusorobotica.com/index.php?action=dlattach;topic=8200.0;attach=3505)

Baseado no diagrama fiz esta implementação (excerto), que incrivelmente saiu bem logo à 1ª :) ; não foi difícíl perceber como passar do diagrama para código; reparem que o diagrama está anotado com os nomes das variáveis no código:

    static int16  lp_prev = 0;
    static int16  bp_prev = 0;
    // State variable filter calculation.
    int16  s1 = ISCALE((int32)bp_prev, -kq, 256);
    int16  hp = sample + s1 - lp_prev;                   // high pass
    int16  bp = ISCALE((int32)hp, kf, 256) + bp_prev;    // bandpass
    int16  lp = ISCALE((int32)bp, kf, 256) + lp_prev;    // lowpass
    lp_prev = lp;
    bp_prev = bp;
    sample = lp ou hp ou bp...


O sinal de áudio (amostra) entra e sai na variável sample, que é de 16 bits. A macro ISCALE é uma multiplicação em ponto fixo, podem ignorar o 3º argumento e olhar para ela apenas como multiplicação dos 2 primeiros argumentos. Se quisermos um LP tiramos o sinal da variável lp, um HP da variável hp, etc.

Com isto acabei também por experimentar um efeito de guitarra que é muito fixe, o wah-wah, que consiste na variação periódica (auto-wah) ou consoante o sinal de entrada (wah "normal"), da frequência central de um filtro BP.

Alguém aceita o desafio de implementar isto num Arduino ou microcontrolador que quiserem? Portanto o desafio é, e se o aceitares (muahh ahh ahh ahh), fazer uma "caixa" onde entra audio por um lado e sai o áudio com efeitos pelo outro. Os efeitos que podem ser feitos só com este filtro incluem equalização (atenuação e boost) e wah wah. Posso ajudar.
Título: Re: Desafio: State Variable Filter
Enviado por: KammutierSpule em 21 de Junho de 2015, 17:56
Good good! Mas nao vou aceitar o desafio :/ :)

Ja agora deixo aqui uma dica, um algoritmo idêntico mas apenas para detectar a presença de uma frequência
https://en.wikipedia.org/wiki/Goertzel_algorithm

Por exemplo para detectar DTMF, beacons, etc..
Na altura que descobri isto pensei usa-lo para fazer os detectores de obstáculos IV e para detectar o beacon IV para um robot.
Título: Re: Desafio: State Variable Filter
Enviado por: Njay em 21 de Junho de 2015, 18:12
Então, não te sentes capaz :)?

Conheço o Goertzel, já tive que integrar num PBX uma lib de dominio publico que o usa para detectar DTMF (tone_detect.c/.h).
Título: Re: Desafio: State Variable Filter
Enviado por: andElectrons em 22 de Junho de 2015, 14:37

kF é a frequencia do cutoff ?

e o kQ ?
Título: Re: Desafio: State Variable Filter
Enviado por: jm_araujo em 22 de Junho de 2015, 14:43
São parâmetros calculados:
Citar
2) Os parâmetros são independentes e fáceis de calcular; os dados de entrada são a frequência de corte/central e o "damping" ou Q e saiem o kf e o kq usados no filtro.
Título: Re: Desafio: State Variable Filter
Enviado por: Njay em 22 de Junho de 2015, 15:40
kf e kq são os "coeficientes" do filtro. São calculados assim:

kf = 2 * sin(pi * Fc / Fs)
kq = 1 / Q


Fs é a frequência de amostragem que estiveres a usar, Fc é a frequência de corte do filtro (passa alto e passa baixo) ou central (caso do passa banda). O Q é uma espécie de multiplicador (amplificador/atenuador) que actua na Fc; Q=1 não afecta o volume das frequências em torno de Fc (face ao que o filtro faz normalmente), se Q > 1 então o efeito do filtro é reforçado, se < 1 o efeito é atenuado. Na página que deixei há um simulador interactivo disto, para um passa baixo, podes mexer no gráfico e ver os valores de Q e Fc a mudarem e o efeito no gráfico da amplitude versus frequência.

Como temos aqui números reais, ou se utilizam floats (lento lento e código grande) ou simulam-se os reais com números em ponto fixo. Se nunca mexeram com números em ponto fixo, podem começar por implementar usando floats e uma frequência de amostragem baixa, e depois quando estiver a funcionar então passar para números em ponto fixo e aumentar a frequência de amostragem.
Título: Re: Desafio: State Variable Filter
Enviado por: jm_araujo em 22 de Junho de 2015, 16:19
Estava a ler mais um bocado sobre essa topoligia e encontrei este site:
http://www.earlevel.com/main/2003/03/02/the-digital-state-variable-filter/ (http://www.earlevel.com/main/2003/03/02/the-digital-state-variable-filter/)

Tem uma pequena diferença na topologia em relação ao teu diagrama:
(https://lusorobotica.com/proxy.php?request=http%3A%2F%2Fwww.earlevel.com%2FDigitalAudio%2Fimages%2FStateVarBlock.gif&hash=c2ef334356f2068331c71322243c75fe3ed383c5)
O bandpass é todo "atrasado", e não só no feedback para o inicio como no teu. Será por isso que o teu Band reject não funcionou?
Título: Re: Desafio: State Variable Filter
Enviado por: jm_araujo em 22 de Junho de 2015, 16:25
https://www.google.pt/search?q=digitAL+STATE+Variable+filter&source=lnms&tbm=isch&sa=X&ei=CyaIVbTKOKiGywOOmr1o&ved=0CAcQ_AUoAQ&biw=1366&bih=653#imgrc=_ (https://www.google.pt/search?q=digitAL+STATE+Variable+filter&source=lnms&tbm=isch&sa=X&ei=CyaIVbTKOKiGywOOmr1o&ved=0CAcQ_AUoAQ&biw=1366&bih=653#imgrc=_)

Mas também aparecem outras fontes com a tua topologia... é para a confusão :D
Título: Re: Desafio: State Variable Filter
Enviado por: Njay em 22 de Junho de 2015, 17:36
Sim, é para a confusão :). Também vi um livro sobre o assunto e não mencionam o bandpass. Depois volto a investigar, na altura não era muito importante.
Título: Re: Desafio: State Variable Filter
Enviado por: ivitro em 22 de Junho de 2015, 17:43
O que é suposto ter na saída?

Posso tentar implementar isso... diz-me a frequência de amostragem.
Título: Re: Desafio: State Variable Filter
Enviado por: Njay em 22 de Junho de 2015, 19:01
O que é suposto ter na saída?
Áudio?... O objectivo é inventar. Entra audio, processa-se com filtros, passa bandas/baixo/bass-boost/whatever e sai esse audio processado. Em tempo real.

Citar
Posso tentar implementar isso... diz-me a frequência de amostragem.
A que tu quiseres/conseguires.
Título: Re: Desafio: State Variable Filter
Enviado por: senso em 22 de Junho de 2015, 19:13
Será que isso cabe nos pedacinho de "FPGA" que um PSoC4 tem?
Título: Re: Desafio: State Variable Filter
Enviado por: Njay em 22 de Junho de 2015, 19:43
Ainda não experimentei. Se couber, deve ser uma implementação "apertada", porque não há multiplicador em hw, teria que ser implementado. De resto faz-se bem com um PSoC4 ;), tens ampops e tudo.
Título: Re: Desafio: State Variable Filter
Enviado por: jm_araujo em 22 de Junho de 2015, 22:42
Tem aqui umas dicas boas para uma implementação em Arduino com Atmega:
http://interface.khm.de/index.php/lab/interfaces-advanced/arduino-realtime-audio-processing/ (http://interface.khm.de/index.php/lab/interfaces-advanced/arduino-realtime-audio-processing/)

Se calhar ainda meto mãos ao desafio :D
Título: Re: Desafio: State Variable Filter
Enviado por: Njay em 23 de Junho de 2015, 01:48
Eheheh... se usares a tensão de referência interna para o ADC, ficas com mais "amplificação" da entrada.
Título: Re: Desafio: State Variable Filter
Enviado por: Njay em 23 de Junho de 2015, 14:16
Fica aqui um referência muito interessante. Tem código de matlab mas ignorem isso, o sumo está na na explicação dos vários efeitos tipicamente usados para guitarra eléctrica:

http://www.cs.cf.ac.uk/Dave/CM0268/PDF/10_CM0268_Audio_FX.pdf (http://www.cs.cf.ac.uk/Dave/CM0268/PDF/10_CM0268_Audio_FX.pdf)
Título: Re: Desafio: State Variable Filter
Enviado por: MAntunes em 26 de Junho de 2015, 00:36
Boas pessoal, desde que tive Eletrónica I e Sinais e Sistemas que fiquei interessado nisto dos filtros, mas sempre mais nos filtros analógicos.. Depois em Processamento de Sinal falamos sobre os filtros digitais, mas tudo demasiado teorico, quase sem exemplos praticos.. Alguém tem alguma literatura sobre a implementação de filtros em C/C++, apenas sobre o básico??
Cumprimentos :)
Título: Re: Desafio: State Variable Filter
Enviado por: msr em 26 de Junho de 2015, 22:42
Boas pessoal, desde que tive Eletrónica I e Sinais e Sistemas que fiquei interessado nisto dos filtros, mas sempre mais nos filtros analógicos.. Depois em Processamento de Sinal falamos sobre os filtros digitais, mas tudo demasiado teorico, quase sem exemplos praticos.. Alguém tem alguma literatura sobre a implementação de filtros em C/C++, apenas sobre o básico??
Cumprimentos :)

Tenho na calha uns "blog posts" para escrever sobre isso mesmo! Há medida que forem saindo venho aqui ao forum avisar.
Entretanto, tens aqui código C com a implementação de alguns filtros (MA, EMA, FIR e IIR):
https://github.com/qkthings/qkdsp (https://github.com/qkthings/qkdsp)
O repositorio inclui um projecto no Code::Blocks (http://www.codeblocks.org/) e exemplos (para já só do MA e EMA), por isso é muito fácil de usar e experimentar.

De um ponto de vista muito prático, aquilo que precisas de saber para implementar filtros digitais em microcontroladores sem FPU (floating-point unit) é isto:
1) Como representar números de virgula flutuante (floating-point, ie, variaveis do tipo "float" ou "double") em virgula fixa (fixed-point, ie, "int"), e perceber os truques da "fixed-point arithmetic" (multiplicações e adições)
2) Como calcular coeficientes (no caso dos filtros FIR e IIR) tendo em conta as especificações do filtro que pretendes realizar. Para isso podes usar o Octave ou usar SciPy (Python), basta chamares uma função com os parâmetros que queres e ele dá-te os coeficientes

Googla sobre isto.

Desconhecia no entanto este "state variable filter" que me parece muito interessante também. É claramente um candidato a entrar na QkDSP. Alguem se quer voluntariar com um Pull-Request? :D
(mais tarde ou mais cedo, se não houver voluntários tenciono fazê-lo)


Título: Re: Desafio: State Variable Filter
Enviado por: Njay em 27 de Junho de 2015, 17:32
Venham de lá esses posts! Quanto ao pull request, vamos ver :)

A representação de números em ponto fixo é uma técnica extremamente simples e intuitiva, que eu inventei quando tinha uns 20 e andava a brincar com demos gráficas em PC - claro que eu não inventei nada :D , já existia, e vim depois mais tarde a descobrir que se chamava ponto fixo.

Como é que se representa um número real, por exemplo 1.25, com números inteiros? Multiplicando por uma constante, digamos 1000, e ficando assim 1250, que é nitidamente um número inteiro. Se quisermos "recuperar" o número original, só temos que o dividir pela mesma constante: 1250 / 1000 = 1.25. Como se vê, não tem nada de especial :)

1250 é uma representação em ponto fixo do número 1.25 quando usamos 1000 como a "unidade": 1000, nesta representação, é 1.000 (.000 para evidenciar qtas casas decimais podemos representar), a unidade. Podemos chamar U (de Unidade) ao 1000, dizer que 1000 é a nossa unidade, nesta representação. Com uma unidade de 1000, o valor real mais pequeno que podemos representar com um inteiro é 0.001 .

O que fazer se o número real tem mais casas decimais do que consigo representar com a unidade escolhida, neste caso 1000? Arredondar. Por exemplo, 0.44584. Ao multiplicar por 1000 ficamos com 445.84 mas como temos que ficar com um inteiro, arredondamos para o inteiro mais próximo e ficamos com 446. Aqui estamos a criar um pequeno erro na representação; "quanto erro" é tolerável só depende da nossa aplicação. Aqui o nosso erro é inferior a +/-0.001, que equivale a 1 em ponto fixo no nosso exemplo de U = 1000; se precisarmos de um erro menor, teremos que usar um U maior, por exemplo U = 10000.

Como é que se somam 2 números em ponto fixo com U = 1000 ("PF1000", para simplicidade)?
Se começar com 1.25 e 0.25, tenho que os converter para PF1000: 1.25 = 1.25 x 1000 = 1250 e 0.25 x 1000 = 250.
Podemos ver que basta adicioná-los como inteiros, não é preciso nada de especial: 1250 + 250 = 1500. Convertendo 1500, que é um PF1000, para real, vemos que o resultado está certo: 1500 / 1000 = 1.5 (= 1.25 + 0.25)
Subtrair é igualmente fácil e a mesma coisa que somar, pois subtrair é somar um número negativo.

A multiplicação é que já é um pouco diferente, mas é muito fácil perceber porquê. Vamos pegar outra vez no 1.25 e 0.25 e multiplicá-los em ponto fixo com unidade 1000 (PF1000):

1.25 -> 1250
0.25 -> 250
1250 x 250 = 312500


Se convertermos 312500 "de volta" para real, ficamos com 312.5, quando o resultado deveria ser 1.25 x 0.25 = 0.3125. O que é que aconteceu? O que aconteceu foi que pelo caminho criámos um resultado em PF1000000! Para mostrar como, em vez de usar 1250 e 250 para representar os números em PF1000, vou usar (1.25 x U) e (0.25 x U), e então fica evidente:

(1.25 x U) x (0.25 x U)
= 1.25 x U x 0.25 x U
= 1.25 x 0.25 x U x U
= 0.3125 x U x U


ou seja acabámos com o resultado (0.3125) multiplicado pelo quadrado da unidade, U x U, e não pela unidade U como se quer. Como resolver isto? Muito simples, basta dividir o resultado por U, e voltamos a ter o número em PF1000

0.3125 x U x U
--------------- = 0.3125 x U = 0.3125 x 1000 = 312.5 -> 313 (arredondado)
       U


Portanto para multiplicar 2 números que estão em ponto fixo temos que multiplicá-los e dividir o resultado pela unidade U da representação.

Na divisão de numeros em PF ocorre um "problema" semelhante ao que acontece na multiplicação. Se fizermos 1250 / 250 ficamos com 5, quando o resultado em PF1000 deveria ser (1.25 / 0.25) x 1000 = 5000. O que aconteceu foi que, na divisão, dividimos a Unidade por ela própria, eliminando-a:

 1.25 x U     1.25
---------- = ------ = 5
 0.25 x U     0.25


Para resolver isto, temos que multiplicar o dividendo pela unidade U, antes de fazer a divisão:

 1.25 x U x U     1.25 x U
-------------- = ---------- = 5 x U = 5 x 1000 = 5000
   0.25 x U         0.25


Espero que se entenda :) . Se alguém quiser discutir sou todo ouvidos.

Há ainda 3 detalhes práticos, sobre melhorar a precisão nas divisões (a divisão inteira dos CPUs é uma divisão por defeito, o que faz com que o resultado não seja sempre o número inteiro mais próximo do número real), velocidade de cálculo (dividir/multiplicar por 1000 é relativamente lento num CPU, mas e se for por uma potência de 2?) e tamanho da representação intermédia (quando dividimos números em PF temos um valor intermédio que tem U2, e isso ocupa muito "espaço", bits, que não precisamos para as representações finais em PF), mas por agora já me alonguei mais do que queria :)
Título: Re: Desafio: State Variable Filter
Enviado por: MAntunes em 28 de Junho de 2015, 13:56
Obrigado aos dois pelo esclarecimento!
Fico à espera desses "blog posts", msr :)
Cumprimentos!
Título: Re: Desafio: State Variable Filter
Enviado por: msr em 01 de Julho de 2015, 11:23
Aqui está o post sobre representação de números e aritmética em virgula-fixa: http://theramblingness.com/2015/07/01/the-art-of-representing-floating-point-numbers-as-integers/ (http://theramblingness.com/2015/07/01/the-art-of-representing-floating-point-numbers-as-integers/)

Espero que o inglês não seja problema :P Qualquer dúvida, erro que detectem ou melhoria que queiram sugerir, mandem vir que será muito apreciada.

No próximo post vou escrever sobre alguns tipos de filtros digitais e como os dimensionar.



Título: Re: Desafio: State Variable Filter
Enviado por: msr em 09 de Julho de 2015, 02:01
Então malta anda tudo sem tempo para ler coisas sobre "fixed-point representation"? :P

Nas próximas semanas tenciono publicar um post sobre implementação de filtros. Desta vez vai ser menos extenso e vai incluir imagens com cores ;)

Alguem tentou implementar o state variable filter em C ?
Título: Re: Desafio: State Variable Filter
Enviado por: Njay em 10 de Julho de 2015, 14:15
Aqui está o post sobre representação de números e aritmética em virgula-fixa: http://theramblingness.com/2015/07/01/the-art-of-representing-floating-point-numbers-as-integers/ (http://theramblingness.com/2015/07/01/the-art-of-representing-floating-point-numbers-as-integers/)
Boa, bem explicadinho com rigor matemático e bom Inglês ;)

Não falta qualquer coisa ao limite inferior?

-2m-1 <= value <= 2m-1 - 2-n

É que o limite negativo também cresce, em - (1 - 2-n), certo?

Eu tenho o filtro implementado em C, mas eu não conto :D
Título: Re: Desafio: State Variable Filter
Enviado por: Njay em 12 de Julho de 2015, 14:48
Ontem implementei também um "overdrive" (distorção), que deu uma "investigação" interessante, com arctan à mistura, mas tem que ficar para outro tópico. A matemática é mesmo uma coisa tann bunita :D
Título: Re: Desafio: State Variable Filter
Enviado por: msr em 16 de Julho de 2015, 03:46
Aqui está o post sobre representação de números e aritmética em virgula-fixa: http://theramblingness.com/2015/07/01/the-art-of-representing-floating-point-numbers-as-integers/ (http://theramblingness.com/2015/07/01/the-art-of-representing-floating-point-numbers-as-integers/)

Não falta qualquer coisa ao limite inferior?

-2m-1 <= value <= 2m-1 - 2-n

É que o limite negativo também cresce, em - (1 - 2-n), certo?

Realmente tenho aí um erro, deveria ser: -2m <= value <= 2m - 2-n (já editei no post)

O limite é mesmo este, como tens um bit para sinal, quando tens todos os outros bits a zero, o valor minimo que se consegue representar, em complemento para dois (https://en.wikipedia.org/wiki/Two%27s_complement), é -(2^m). Por exemplo, se m=0, o valor minimo que se consegue representar é -1 (corresponde a ter apenas o bit de sinal a um) e máximo, com n=15, será 0.9997.


Quanto ao overdrive, partilha aí os resultados! :D
Título: Re: Desafio: State Variable Filter
Enviado por: Njay em 17 de Julho de 2015, 20:17
Citar
Qm.n

Where m corresponds to the bits available to represent the integer part of and n corresponds to the bits available to represent the fractional part.

com o bit de sinal à parte (reparei agora que na imagem tens o n e o m trocados, e o "of" tá a mais).

Reservar 1 bit para sinal não é exactamente a mesma coisa que complemento para 2, pois a 1ª permite ter -0 e +0 enquanto que a 2ª usa o -0 para estender o limite negativo... Se vais definir matematicamente, tens que ser rigoroso, e a tua definição diz que o limite negativo é o bit de sinal a negar a soma do maior número positivo em m bits de inteiro com o maior de n bits de fraccionário (ambos só com representação positiva, já que o sinal é à parte).

Já iremos ao overdrive.
Título: Re: Desafio: State Variable Filter
Enviado por: msr em 21 de Julho de 2015, 00:41
É isso. Quando refiro esse bit de sinal assumi sempre representação em complemento para dois sem deixar isso claro por escrito, porque de facto pode estar lá o bit de sinal e o resto nao estar em complemento para dois.
A imagem foi reaproveitada de outro texto, obrigado pelo alerta, tenho de lhe dar um jeito também.
Título: Re: Desafio: State Variable Filter
Enviado por: Njay em 22 de Setembro de 2015, 02:35
Em relação ao overdrive/distorção cá fica:

http://lusorobotica.com/index.php?topic=8324.0 (http://lusorobotica.com/index.php?topic=8324.0)

Ainda queria fazer uns testes com mais rigor (ainda não meti o scope, mas de qq maneira soa bem) e sacar uns WAV, mas é melhor publicar já senão não sai :)