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


3 — Modelo estatístico adaptativo: média, variância e decisão probabilística (online, leve e interpretável)

Nesta seção vamos construir o coração decisório do novo artigo: um modelo estatístico adaptativo, atualizado online, que decide se o vetor de features extraído pelo Goertzel se comporta como um assobio humano.

A proposta é deliberadamente simples, robusta e adequada a MCU:

  • Sem redes neurais
  • Sem matrizes grandes
  • Sem treinamento pesado offline
  • Totalmente determinística em tempo real

3.1 Princípio do modelo: aprender o “normal” e medir desvio

A ideia central é a seguinte:

Um assobio humano produz features que oscilam em torno de um padrão médio, com variabilidade limitada.

Logo, podemos modelar cada feature \(f_i\) como uma variável aleatória aproximadamente gaussiana, caracterizada por:

  • Média \(\mu_i\)
  • Variância \(\sigma_i^2\)

Quando um novo vetor chega:

  • Se ele estiver próximo do padrão aprendido, aceitamos como assobio
  • Se estiver longe demais, rejeitamos

Esse tipo de decisão é extremamente comum em:

  • Detecção de anomalias
  • Sensores industriais
  • Sistemas embarcados adaptativos

3.2 Modelo estatístico por feature (independente)

Para manter o custo baixo, assumimos independência entre as features.
Isso evita cálculo de matriz de covariância (Mahalanobis completa).

Para cada feature \(f_i\), mantemos:

  • média \(\mu_i\)
  • variância \(\sigma_i^2\)

Estrutura em C:

typedef struct {
    float mean;
    float var;
} stat_feat_t;

E o modelo completo:

typedef struct {
    stat_feat_t f1;
    stat_feat_t f2;
    stat_feat_t f3;
    stat_feat_t f4;
} stat_model_t;

3.3 Atualização online da média (EMA)

A média é atualizada com uma média móvel exponencial:

\[
\mu[n] = \mu[n-1] + \alpha (x[n] – \mu[n-1])
\]

Onde:

  • \(\alpha\) pequeno → aprendizado lento (estável)
  • \(\alpha\) maior → aprendizado rápido (adaptativo)

Implementação genérica:

static inline void stat_update_mean(stat_feat_t *s, float x, float alpha)
{
    s->mean += alpha * (x - s->mean);
}

3.4 Atualização online da variância (forma estável)

A variância pode ser atualizada de forma incremental como:

\[
\sigma^2[n] = (1-\alpha)\sigma^2[n-1] + \alpha (x[n]-\mu[n])^2
\]

Implementação:

static inline void stat_update_var(stat_feat_t *s, float x, float alpha)
{
    float d = x - s->mean;
    s->var = (1.0f - alpha) * s->var + alpha * (d * d);
}

Essa forma é estável numericamente e suficiente para aprendizado embarcado.


3.5 Inicialização segura do modelo

No boot, não temos dados. Precisamos:

  • Evitar variância zero
  • Evitar divisões instáveis

Inicialização recomendada:

static inline void stat_feat_init(stat_feat_t *s, float mean0, float var0)
{
    s->mean = mean0;
    s->var  = var0;
}

static void stat_model_init(stat_model_t *m)
{
    stat_feat_init(&m->f1, 0.5f, 0.1f);
    stat_feat_init(&m->f2, 0.1f, 0.05f);
    stat_feat_init(&m->f3, 0.05f, 0.05f);
    stat_feat_init(&m->f4, 0.0f, 50.0f); // delta f0 em Hz
}

Esses valores não precisam ser perfeitos — o modelo aprende com o tempo.


3.6 Medida de desvio: Z-score por feature

Para decidir se uma feature é “normal”, usamos o Z-score:

\[
z_i = \frac{x_i – \mu_i}{\sqrt{\sigma_i^2 + \epsilon}}
\]

O valor absoluto indica quão longe estamos do padrão.

Implementação:

static inline float stat_zscore(const stat_feat_t *s, float x)
{
    const float eps = 1e-6f;
    float std = sqrtf(s->var + eps);
    return (x - s->mean) / std;
}

3.7 Distância estatística simplificada (score global)

Agora combinamos os Z-scores das features:

\[
D = \sum_i z_i^2
\]

Isso é uma distância estatística diagonal, equivalente a uma Mahalanobis simplificada.

Implementação:

static float compute_stat_distance(const stat_model_t *m,
                                   const feature_vec_t *v)
{
    float z1 = stat_zscore(&m->f1, v->f1);
    float z2 = stat_zscore(&m->f2, v->f2);
    float z3 = stat_zscore(&m->f3, v->f3);
    float z4 = stat_zscore(&m->f4, v->f4);

    return (z1*z1) + (z2*z2) + (z3*z3) + (z4*z4);
}

Interpretação:

  • D pequeno → padrão parecido com assobio aprendido
  • D grande → padrão estranho (fala, ruído, batida)

3.8 Regra de decisão probabilística simples

Como (D) segue aproximadamente uma distribuição qui-quadrado com 4 graus de liberdade, usamos um limiar prático:

  • (D < T_{ON}) → assobio
  • (D > T_{OFF}) → não assobio

Com histerese:

#define D_ON   9.0f
#define D_OFF  12.0f

Implementação:

static bool stat_decision(float D, bool prev_state)
{
    if (!prev_state) {
        return (D < D_ON);
    } else {
        return (D < D_OFF);
    }
}

3.9 Aprendizado controlado (não aprender ruído)

Regra de ouro:
👉 só atualize o modelo quando você acredita que o padrão é válido.

Estratégia:

  • Se o estado atual é “assobio”
  • Atualize média e variância
  • Caso contrário, não aprenda
static void stat_model_update(stat_model_t *m,
                              const feature_vec_t *v,
                              float alpha)
{
    stat_update_mean(&m->f1, v->f1, alpha);
    stat_update_var (&m->f1, v->f1, alpha);

    stat_update_mean(&m->f2, v->f2, alpha);
    stat_update_var (&m->f2, v->f2, alpha);

    stat_update_mean(&m->f3, v->f3, alpha);
    stat_update_var (&m->f3, v->f3, alpha);

    stat_update_mean(&m->f4, v->f4, alpha);
    stat_update_var (&m->f4, v->f4, alpha);
}

3.10 O que conquistamos até aqui

Até este ponto, temos:

  • Goertzel → features
  • Features → modelo estatístico
  • Modelo → distância probabilística
  • Distância → decisão com histerese
  • Aprendizado online, leve e estável

Tudo isso:

  • Cabe facilmente no RP2040
  • Não exige FPU pesada
  • Não exige memória dinâmica
  • É explicável e ajustá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

0
Adoraria saber sua opinião, comente.x