MCU & FPGA DSP Detecção de Assobio com Goertzel e Aprendizado Estatístico no RP2040

Detecção de Assobio com Goertzel e Aprendizado Estatístico no RP2040


4 — Integração completa em tempo real: Goertzel + features + modelo estatístico + LED no RP2040

Nesta seção vamos ligar todas as peças construídas até agora e formar um sistema funcional em tempo real, mantendo o mesmo padrão de engenharia dos artigos anteriores da série.

O foco aqui não é despejar código final, mas mostrar claramente o fluxo, as decisões de integração e os pontos onde o aprendizado estatístico entra — e onde não deve entrar.


4.1 Visão geral do fluxo em tempo real

O pipeline completo agora é:

ADC
 └── DC-block
     └── Frame (256 amostras)
         └── Goertzel (varredura + harmônicos)
             └── Vetor de features
                 └── Distância estatística D
                     ├── Decisão (histerese)
                     ├── Atualização do modelo (condicional)
                     └── LED

Observe dois pontos importantes:

  1. O modelo estatístico não substitui o Goertzel
    Ele apenas decide o que fazer com o que o Goertzel mediu.
  2. O aprendizado é condicionado à decisão
    Isso evita “aprender o erro”.

4.2 Estruturas globais de estado

Vamos declarar explicitamente os estados globais do sistema:

static frame_buf_t    g_frame;
static dc_block_t     g_dc;

static stat_model_t   g_model;
static bool           g_state = false;

static float          g_f0_prev = 0.0f;

Esses estados são:

  • Pequenos
  • Determinísticos
  • Fáceis de depurar

4.3 Construção do vetor de features em tempo real

Quando um frame fecha, calculamos:

  1. Energia total
  2. Frequência fundamental \(f_0\)
  3. Potências \(p_0, p_2, p_3\)
  4. Features \(f_1, f_2, f_3, f_4\)

Código ilustrativo:

static bool build_feature_vector(const float *frame,
                                 feature_vec_t *v,
                                 float *f0_out)
{
    const float eps = 1e-9f;

    float E = frame_energy(frame);
    if (E < E_MIN) return false;

    float p0 = 0.0f;
    float f0 = find_best_f0_fast(frame, &p0);

    const float nyq = 0.5f * (float)FS_HZ;
    float p2 = 0.0f;
    float p3 = 0.0f;

    if (2.0f * f0 <= nyq)
        p2 = power_at_freq(frame, 2.0f * f0);

    if (3.0f * f0 <= nyq)
        p3 = power_at_freq(frame, 3.0f * f0);

    v->f1 = p0 / (E + eps);
    v->f2 = p2 / (p0 + eps);
    v->f3 = p3 / (p0 + eps);
    v->f4 = fabsf(f0 - g_f0_prev);

    g_f0_prev = f0;

    if (f0_out) *f0_out = f0;
    return true;
}

Note que:

  • Todas as features são normalizadas
  • Não usamos valores absolutos
  • O custo computacional permanece baixo

4.4 Avaliação estatística e decisão

Com o vetor pronto:

  1. Calculamos a distância estatística (D)
  2. Aplicamos histerese
  3. Atualizamos o estado
static bool update_decision(const feature_vec_t *v)
{
    float D = compute_stat_distance(&g_model, v);
    bool new_state = stat_decision(D, g_state);
    g_state = new_state;
    return new_state;
}

4.5 Atualização do modelo (aprendizado online controlado)

Aqui está o ponto mais delicado do sistema:

👉 Só aprendemos quando acreditamos que o padrão é válido.

static void update_model_if_valid(const feature_vec_t *v)
{
    const float alpha = 0.02f; // aprendizado lento e estável

    if (g_state) {
        stat_model_update(&g_model, v, alpha);
    }
}

Esse detalhe:

  • Evita adaptação ao ruído
  • Evita “drift” do modelo
  • Mantém estabilidade ao longo do tempo

4.6 Acionamento do LED (sem lógica escondida)

O LED agora reflete apenas o estado estatístico, não valores brutos:

static void update_led(uint led_gpio)
{
    gpio_put(led_gpio, g_state ? 1 : 0);
}

Isso torna o sistema:

  • Previsível
  • Fácil de explicar
  • Fácil de validar

4.7 Loop principal: simples, limpo e determinístico

O loop principal agora se resume a:

static void process_frame_if_ready(uint led_gpio)
{
    if (!g_frame.full) return;
    g_frame.full = false;

    feature_vec_t v;
    float f0 = 0.0f;

    if (!build_feature_vector(g_frame.data, &v, &f0)) {
        g_state = false;
        update_led(led_gpio);
        return;
    }

    update_decision(&v);
    update_model_if_valid(&v);
    update_led(led_gpio);
}

Repare:

  • Nenhuma decisão depende de “mágica”
  • Cada etapa é separada
  • Tudo é mensurável

4.8 O que temos agora (antes do código final)

Neste ponto, o sistema já:

  • Detecta assobios com Goertzel
  • Extrai features robustas
  • Aprende estatisticamente o padrão do usuário
  • Se adapta a mudanças ambientais
  • Decide com base probabilística
  • Aciona um LED em tempo real

E ainda:

  • Cabe confortavelmente no RP2040
  • Não depende de bibliotecas externas
  • É explicável e calibrável

0 0 votos
Classificação do artigo
Inscrever-se
Notificar de
guest
0 Comentários
mais antigos
mais recentes Mais votado
Feedbacks embutidos
Ver todos os comentários

Related Post

Quefrequência e Análise Cepstral: Uma Introdução Prática para Sistemas Embarcados (ESP32-P4)Quefrequência e Análise Cepstral: Uma Introdução Prática para Sistemas Embarcados (ESP32-P4)

A análise cepstral e o conceito de quefrequência são técnicas essencialmente poderosas no processamento de sinais de áudio, permitindo separar efeitos de excitação, resposta acústica e periodicidades espectrais que não

LPCC (Linear Prediction Cepstral Coefficients): Fundamentos, Algoritmos e Aplicações em Sistemas EmbarcadosLPCC (Linear Prediction Cepstral Coefficients): Fundamentos, Algoritmos e Aplicações em Sistemas Embarcados

Os coeficientes cepstrais por predição linear (LPCC) são uma técnica clássica e altamente eficiente para extração de características em sinais de fala, vibração e acústica industrial. Neste artigo, apresentamos uma

0
Adoraria saber sua opinião, comente.x