eefocus_3947073 发表于 5 天前

【瑞萨AI挑战赛】(RT-Thread + 瑞萨 FFT)板卡多种频率算法比较

## 一、工程整体思路

在一个 `while(1)` 循环里,对**同一组模拟信号**依次执行:

1. ​**原始信号**​(作为基准)
2. ​**滑动平均滤波**​(简单时域平滑)
3. ​**FFT + 频域滤波**​(频域处理,保留指定频率)
4. ​**阈值检测**​(简单异常点剔除)
5. ​**峰值检测**​(找信号波峰)

并在每次处理后打印:

* 算法名称
* 耗时(us)
* 部分结果

## 二、完整代码示例

```
#include "rtthread.h"
#include "r_fft.h"   // 瑞萨 FFT 库,若用 CMSIS-DSP 可替换为 arm_math.h
#include "math.h"

/* ================= 通用配置 ================= */
#define FFT_POINTS      256   // FFT点数,2的幂
#define SAMPLE_FREQ   1000    // 1kHz采样
#define DATA_LEN      FFT_POINTS// 算法统一输入长度

/* 输入信号:混合正弦波 + 噪声(模拟传感器信号) */
static void generate_test_signal(rt_int16_t *buf, rt_uint32_t len)
{
    rt_uint32_t i;
    for (i = 0; i < len; i++)
    {
      // 50Hz + 150Hz 正弦波 + 少量随机噪声
      float t = (float)i / SAMPLE_FREQ;
      float s = 3000.0f * sinf(2 * RT_PI *50 * t)
                + 2000.0f * sinf(2 * RT_PI * 150 * t)
                + (float)rand() / RAND_MAX * 200.0f - 100.0f;

      // 限幅到 16 位范围
      if (s > 32767.0f) s = 32767.0f;
      if (s < -32768.0f) s = -32768.0f;

      buf = (rt_int16_t)s;
    }
}

/* ================= 1. 滑动平均滤波(时域平滑) ================= */
static void moving_average_filter(rt_int16_t *in, rt_int16_t *out, rt_uint32_t len, rt_uint8_t window)
{
    rt_uint32_t i, j;
    rt_int32_t sum;

    for (i = 0; i < len; i++)
    {
      sum = 0;
      rt_uint32_t start = (i < window / 2) ? 0 : (i - window / 2);
      rt_uint32_t end   = (i + window / 2 < len - 1) ? (i + window / 2) : (len - 1);

      for (j = start; j <= end; j++)
      {
            sum += in;
      }

      out = (rt_int16_t)(sum / (end - start + 1));
    }
}

/* ================= 2. FFT 频域滤波(保留 0~100Hz) ================= */
static void fft_bandpass_filter(rt_int16_t *in, rt_int16_t *out, rt_uint32_t len)
{
    rt_uint16_t i;
    rt_int16_t fft_in, fft_out; // 实部+虚部

    // 1. 实部赋值,虚部补0
    for (i = 0; i < len; i++)
    {
      fft_in   = in;
      fft_in = 0;
    }

    // 2. 执行 FFT
    r_fft_execute(fft_in, fft_out, len);

    // 3. 置零 100Hz 以上的频点(简单低通)
    rt_uint32_t cutoff_bin = (rt_uint32_t)(100.0f * len / SAMPLE_FREQ);
    for (i = cutoff_bin; i < len / 2; i++)
    {
      fft_out   = 0;
      fft_out = 0;
    }

    // 4. 逆 FFT 转回时域
    r_fft_execute(fft_out, fft_in, len); // 这里可换为 r_ifft_execute,视库而定

    // 5. 取实部作为输出(虚部理论接近0)
    for (i = 0; i < len; i++)
    {
      out = fft_in;
    }
}

/* ================= 3. 阈值滤波(简单剔除异常点) ================= */
static void threshold_filter(rt_int16_t *in, rt_int16_t *out, rt_uint32_t len, rt_int16_t threshold)
{
    rt_uint32_t i;
    for (i = 0; i < len; i++)
    {
      if (in > threshold || in < -threshold)
      {
            // 异常点用前一个值替代(第一个点保留原值)
            out = (i > 0) ? out : in;
      }
      else
      {
            out = in;
      }
    }
}

/* ================= 4. 峰值检测(找波峰位置与幅值) ================= */
static void peak_detection(rt_int16_t *in, rt_uint32_t len, rt_uint8_t min_height, rt_uint32_t *peak_num, rt_uint32_t *peak_pos, rt_int16_t *peak_val)
{
    rt_uint32_t i;
    *peak_num = 0;

    for (i = 1; i < len - 1; i++)
    {
      if (in > in && in > in && in > min_height)
      {
            peak_pos[*peak_num] = i;
            peak_val[*peak_num] = in;
            (*peak_num)++;

            if (*peak_num >= 10) break; // 最多记录10个峰
      }
    }
}

/* ================= 统一入口:hal_entry ================= */
void hal_entry(void)
{
    rt_kprintf("\nHello RT-Thread!\n");
    rt_kprintf("==================================================\n");
    rt_kprintf("Algorithm Comparison Demo on FPB-RA6E2\n");
    rt_kprintf("Sample Freq: %dHz, FFT Points: %d\n", SAMPLE_FREQ, FFT_POINTS);
    rt_kprintf("==================================================\n");

    // 缓冲区
    rt_int16_t sig_in;
    rt_int16_t sig_avg;
    rt_int16_t sig_fft;
    rt_int16_t sig_th;

    rt_uint32_t t1, t2; // 用于计时

    do
    {
      // 1. 生成统一测试信号
      generate_test_signal(sig_in, DATA_LEN);

      // ================ 算法 A:原始信号(基准) ================
      t1 = rt_hw_us_tick_get();
      // 无处理,直接使用 sig_in
      t2 = rt_hw_us_tick_get();
      rt_kprintf("\n Cost: %d us", t2 - t1);
      rt_kprintf(" | Sample: %d, %d, %d, %d\n",
               sig_in, sig_in, sig_in, sig_in);

      // ================ 算法 B:滑动平均滤波 ================
      t1 = rt_hw_us_tick_get();
      moving_average_filter(sig_in, sig_avg, DATA_LEN, 5); // 窗口5
      t2 = rt_hw_us_tick_get();
      rt_kprintf(" Cost: %d us", t2 - t1);
      rt_kprintf(" | Sample: %d, %d, %d, %d\n",
               sig_avg, sig_avg, sig_avg, sig_avg);

      // ================ 算法 C:FFT 频域滤波 ================
      t1 = rt_hw_us_tick_get();
      fft_bandpass_filter(sig_in, sig_fft, DATA_LEN);
      t2 = rt_hw_us_tick_get();
      rt_kprintf(" Cost: %d us", t2 - t1);
      rt_kprintf(" | Sample: %d, %d, %d, %d\n",
               sig_fft, sig_fft, sig_fft, sig_fft);

      // ================ 算法 D:阈值滤波 ================
      t1 = rt_hw_us_tick_get();
      threshold_filter(sig_in, sig_th, DATA_LEN, 4000); // 阈值4000
      t2 = rt_hw_us_tick_get();
      rt_kprintf(" Cost: %d us", t2 - t1);
      rt_kprintf(" | Sample: %d, %d, %d, %d\n",
               sig_th, sig_th, sig_th, sig_th);

      // ================ 算法 E:峰值检测(以FFT滤波结果为例) ================
      rt_uint32_t peak_num;
      rt_uint32_t peak_pos;
      rt_int16_tpeak_val;

      t1 = rt_hw_us_tick_get();
      peak_detection(sig_fft, DATA_LEN, 2000, &peak_num, peak_pos, peak_val);
      t2 = rt_hw_us_tick_get();

      rt_kprintf(" Cost: %d us", t2 - t1);
      rt_kprintf(" | Peak Num: %d\n", peak_num);
      for (rt_uint8_t k = 0; k < peak_num; k++)
      {
            rt_kprintf("Peak %d: pos=%d, val=%d\n", k + 1, peak_pos, peak_val);
      }

      rt_thread_mdelay(1000); // 1秒循环一次

    } while (1);
}
```

## 三、算法横向对比分析

| 算法         | 处理域 | 主要用途                   | 资源占用(RAM/ROM) | 耗时(256 点) | 优点                               | 缺点                                    |
| -------------- | -------- | ---------------------------- | --------------------- | ---------------- | ------------------------------------ | ----------------------------------------- |
| 原始信号   | 时域   | 基准对比                   | 极低                | ≈0 us         | 无处理,完全保留原始信息         | 噪声、异常点全部保留,不实用            |
| 滑动平均滤波 | 时域   | 平滑随机噪声、低频信号   | 低                  | \~ 几十 us   | 简单、快、抗随机噪声               | 对突变信号有延迟,窗口过大滞后明显      |
| FFT 频域滤波 | 频域   | 滤除指定频带、抑制谐波干扰 | 中                  | \~ 几百 us   | 频带控制精确,适合特定频带噪声抑制 | 需整段缓存、RAM/ROM 大,实时性略差      |
| 阈值滤波   | 时域   | 剔除异常点 / 毛刺          | 极低                | \~ 几十 us   | 极快,专门对付单点异常             | 阈值难定,对连续异常 / 密集噪声效果有限 |
| 峰值检测   | 时域   | 提取特征峰、变化点         | 低                  | \~ 几十 us   | 可给出特征点信息,便于特征分析   | 对噪声敏感,必须配合滤波                |

## 四、各种算法分析

### 1. 原始信号(基准)

* ​**功能**​:不做任何处理,仅打印前几个采样点。
* ​**用途**​:作为所有算法的 “原始输入” 参考,便于对比其他算法对信号的改变程度。
* ​**耗时**​:几乎为 0 us(仅拷贝 / 打印)。
* ​**特点**​:无处理,噪声和异常点全部保留。

### 2. 滑动平均滤波(时域平滑)

* ​**功能**​:用局部邻域平均值替代当前值,平滑高频噪声。
* ​**适用场景**​:缓慢变化的传感器信号(温度、压力、缓慢振动)。
* ​**耗时(RA6E2 @ 100MHz)**​:
* 窗口 5,256 点:约 **几十 us** 级。
* ​**优点**​:
* 实现简单,资源占用低;
* 对**随机噪声**有明显平滑效果。
* ​**缺点**​:
* 对**突变信号**会产生延迟与 “抹平”;
* 窗口越大,延迟越大。

### 3. FFT频域滤波(时域平滑)

* ​**功能**​:把信号转到频域,把**高于某频率**的成分置零,再逆变换回时域。
* ​**适用场景**​:需要**精确滤除特定频带**噪声(如工频干扰、高频振动噪声)。
* ​**耗时**​:
* 256 点 FFT + IFFT:约 **几百 us** 级(视库优化程度)。
* ​**优点**​:
* 可以​**精确控制保留 / 滤除哪些频率**​;
* 对周期性噪声、特定频带干扰效果好。
* ​**缺点**​:
* 嵌入式 RAM/ROM 开销较大(需要复数缓冲区、FFT 工作区);
* 非实时性稍差(需要整段数据缓存)。

### 4.阈值滤波

* ​**功能**​:把明显超出正常范围的 “尖峰 / 毛刺” 替换为邻近值。
* ​**适用场景**​:传感器偶尔跳变、强干扰造成的单帧异常。
* ​**耗时**​:
* 256 点:约 **几十 us** 级。
* ​**优点**​:
* 对**单点异常**剔除非常快;
* 实现简单,几乎不占资源。
* ​**缺点**​:
* 对**连续异常**或噪声分布不均匀的情况效果有限;
* 阈值选得不好会误删正常信号。

### 5.峰值检测

* ​**功能**​:从信号中识别 “波峰”,记录位置和幅值。
* ​**适用场景**​:
* 脉搏波、心率检测;
* 振动信号特征提取;
* 寻找信号中的显著变化点。
* ​**耗时**​:
* 256 点扫描:约 **几十 us** 级。
* ​**优点**​:
* 可以给出​**特征点信息**​(频率、幅值),便于**判断;
* 结合 FFT 结果,可做​**频域 + 时域联合分析**​。
* ​**缺点**​:
* 对噪声敏感,必须配合滤波使用;
* 简单算法容易 “漏检 / 误检” 峰。

## 五、结论

本次多种算法并行可以观察出来,板卡的计算处理功能还是很强大的。

页: [1]
查看完整版本: 【瑞萨AI挑战赛】(RT-Thread + 瑞萨 FFT)板卡多种频率算法比较