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


5 — Código completo e funcional (Goertzel + aprendizado estatístico) para BitDogLab / RP2040

A seguir está um main.c único, pronto para compilar com o Pico SDK, integrando Goertzel + extração de features + modelo estatístico adaptativo + LED.
O código mantém o mesmo padrão dos artigos anteriores: determinístico, leve, explicável e calibrável.

Ajustes esperados por placa: revise LED_GPIO, ADC_GPIO e ADC_CH conforme a BitDogLab utilizada.


/**
 * main.c — Detector de assobio com Goertzel + Aprendizado Estatístico
 * Plataforma: RP2040 (BitDogLab / Pico SDK)
 *
 * Pipeline:
 * ADC -> DC-block -> frame (256)
 * -> Goertzel (800..3000 Hz + harmônicos)
 * -> vetor de features
 * -> modelo estatístico online (media/variancia)
 * -> decisão probabilística (histerese)
 * -> LED
 */

#include <stdio.h>
#include <stdbool.h>
#include <math.h>

#include "pico/stdlib.h"
#include "pico/time.h"
#include "hardware/adc.h"

// =============================
// Configurações principais
// =============================
#define FS_HZ       8000
#define FRAME_N     256

#define ADC_GPIO    28
#define ADC_CH      0
#define LED_GPIO    13

#define F_MIN       800.0f
#define F_MAX       3000.0f
#define MAX_BINS    64

#define E_MIN       0.0008f

// Estatística
#define ALPHA_STAT  0.02f
#define D_ON        9.0f
#define D_OFF       12.0f

// =============================
// Buffer de frame
// =============================
typedef struct {
    float data[FRAME_N];
    uint32_t idx;
    bool full;
} frame_buf_t;

static inline void frame_buf_init(frame_buf_t *b){
    b->idx = 0; b->full = false;
}
static inline void frame_buf_push(frame_buf_t *b, float v){
    b->data[b->idx++] = v;
    if (b->idx >= FRAME_N){
        b->idx = 0; b->full = true;
    }
}

// =============================
// DC-block
// =============================
typedef struct { float mean; } dc_block_t;
static inline void dc_block_init(dc_block_t *d){ d->mean = 0.0f; }
static inline float dc_block_step(dc_block_t *d, float x){
    const float beta = 0.0015f;
    d->mean += beta * (x - d->mean);
    return x - d->mean;
}

// =============================
// Goertzel (bins pré-calculados)
// =============================
typedef struct {
    float f;
    float coeff;
} goertzel_bin_t;

static goertzel_bin_t g_bins[MAX_BINS];
static int g_bins_count = 0;

static float goertzel_power_coeff(const float *x, float coeff){
    float s1 = 0.0f, s2 = 0.0f;
    for (int n = 0; n < FRAME_N; n++){
        float s0 = x[n] + coeff * s1 - s2;
        s2 = s1; s1 = s0;
    }
    return (s1*s1) + (s2*s2) - (coeff*s1*s2);
}

static inline float coeff_for_freq(float f){
    int k = (int)(0.5f + ((float)FRAME_N * f) / (float)FS_HZ);
    float w = (2.0f * (float)M_PI * (float)k) / (float)FRAME_N;
    return 2.0f * cosf(w);
}

static inline float power_at_freq(const float *x, float f){
    return goertzel_power_coeff(x, coeff_for_freq(f));
}

static void goertzel_bins_init(void){
    float df = (float)FS_HZ / (float)FRAME_N;
    float step = 2.0f * df;
    g_bins_count = 0;

    for (float f = F_MIN; f <= F_MAX && g_bins_count < MAX_BINS; f += step){
        int k = (int)(0.5f + ((float)FRAME_N * f) / (float)FS_HZ);
        float w = (2.0f * (float)M_PI * (float)k) / (float)FRAME_N;
        g_bins[g_bins_count++] = (goertzel_bin_t){ f, 2.0f * cosf(w) };
    }
}

static float find_best_f0(const float *x, float *p0_out){
    float best_f = g_bins[0].f;
    float best_p = -1.0f;
    for (int i = 0; i < g_bins_count; i++){
        float p = goertzel_power_coeff(x, g_bins[i].coeff);
        if (p > best_p){
            best_p = p; best_f = g_bins[i].f;
        }
    }
    if (p0_out) *p0_out = best_p;
    return best_f;
}

// =============================
// Features
// =============================
typedef struct {
    float f1, f2, f3, f4;
} feature_vec_t;

static float frame_energy(const float *x){
    float acc = 0.0f;
    for (int i = 0; i < FRAME_N; i++) acc += x[i] * x[i];
    return acc / (float)FRAME_N;
}

// =============================
// Modelo estatístico
// =============================
typedef struct { float mean, var; } stat_feat_t;
typedef struct {
    stat_feat_t f1, f2, f3, f4;
} stat_model_t;

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

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);
}

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

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

static float stat_distance(const stat_model_t *m, const feature_vec_t *v){
    float z1 = zscore(&m->f1, v->f1);
    float z2 = zscore(&m->f2, v->f2);
    float z3 = zscore(&m->f3, v->f3);
    float z4 = zscore(&m->f4, v->f4);
    return z1*z1 + z2*z2 + z3*z3 + z4*z4;
}

// =============================
// Hardware e tempo real
// =============================
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;
static repeating_timer_t g_timer;

static inline float read_adc_norm(void){
    uint16_t raw = adc_read();
    return ((float)raw - 2048.0f) / 2048.0f;
}

static bool sample_timer_cb(repeating_timer_t *rt){
    (void)rt;
    float x = dc_block_step(&g_dc, read_adc_norm());
    frame_buf_push(&g_frame, x);
    return true;
}

static void hw_init(void){
    stdio_init_all();
    gpio_init(LED_GPIO);
    gpio_set_dir(LED_GPIO, GPIO_OUT);
    gpio_put(LED_GPIO, 0);

    adc_init();
    adc_gpio_init(ADC_GPIO);
    adc_select_input(ADC_CH);
}

// =============================
// Processamento do frame
// =============================
static bool build_features(const float *x, feature_vec_t *v){
    const float eps = 1e-9f;
    float E = frame_energy(x);
    if (E < E_MIN) return false;

    float p0 = 0.0f;
    float f0 = find_best_f0(x, &p0);
    float nyq = 0.5f * (float)FS_HZ;

    float p2 = (2.0f*f0 <= nyq) ? power_at_freq(x, 2.0f*f0) : 0.0f;
    float p3 = (3.0f*f0 <= nyq) ? power_at_freq(x, 3.0f*f0) : 0.0f;

    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;
    return true;
}

static void process_frame(void){
    if (!g_frame.full) return;
    g_frame.full = false;

    feature_vec_t v;
    if (!build_features(g_frame.data, &v)){
        g_state = false;
        gpio_put(LED_GPIO, 0);
        return;
    }

    float D = stat_distance(&g_model, &v);
    bool new_state = g_state ? (D < D_OFF) : (D < D_ON);
    g_state = new_state;

    if (g_state){
        stat_update(&g_model.f1, v.f1);
        stat_update(&g_model.f2, v.f2);
        stat_update(&g_model.f3, v.f3);
        stat_update(&g_model.f4, v.f4);
    }

    gpio_put(LED_GPIO, g_state ? 1 : 0);
}

// =============================
// main
// =============================
int main(void){
    hw_init();
    frame_buf_init(&g_frame);
    dc_block_init(&g_dc);
    stat_model_init(&g_model);
    goertzel_bins_init();

    int period_us = 1000000 / FS_HZ;
    add_repeating_timer_us(-period_us, sample_timer_cb, NULL, &g_timer);

    while (true){
        process_frame();
        sleep_ms(1);
    }
}

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