左手阿飞 发表于 2025-1-14 23:53

【Follow me第二季第4期】-任务4:选做任务一(非必做):通过RGB LED不同颜色、亮...

<p>由于任务3是PDM麦克风相关任务,所以本次选择的可选任务是通过LED显示麦克风收到声音大小。</p>

<p>本次任务是在任务3 的基础上,结合任务1,对代码进行修改。</p>

<p>主要思路是对麦克风的取值进行存储,然后控制LED灯亮度。本次只用到红灯和绿灯。</p>

<p>观测到任务3中,麦克风采集的数据有两个问题;</p>

<p>1、麦克风采集到的声音波动非常大,不利于LED的控制,如下图(图片来自任务3的内容);</p>

<p>2、采集到的数值会出现负值。</p>

<p>所以需要对采集到的数值进行处理。</p>

<p>本次处理的方式是,先求绝对值,将负值变为正值,再进行滑动平均滤波。</p>

<p>&nbsp;</p>

<p> &nbsp;</p>

<p>全部带码如下:</p>

<pre>
<code class="language-cpp">#include "PDM.h"
#include "WiFiNINA.h"

#define Led1LEDR
#define Led2LEDG
#define Led3LEDB

#define MAX_SIZE 10

int head = 0;
int tail = 0;
int count = 0;

int queue;

static const char channels = 1;
static const int frequncy= 24000;
short sampleBuffer;
volatile int samplesRead;

int LEDR_Value;
int LEDG_Value;

double MIC_Value;

void setup() {
// put your setup code here, to run once:
Serial.begin(115200);

pinMode(Led1, OUTPUT);
pinMode(Led2, OUTPUT);


PDM.onReceive(onPDMdata);
if(!PDM.begin(channels,frequncy))
{
    Serial.println("Failed to start PDM!!!");
    while(1);


}

}

void loop() {
// put your main code here, to run repeatedly:

if(samplesRead)
{
    for(int i = 0; i &lt; samplesRead; i++)
    {
      //Serial.println(sampleBuffer);
      MIC_Value=get_sliding_average(sampleBuffer);

      Serial.println(MIC_Value);

      LEDR_Value=map(MIC_Value, 1, 3000, 1, 255);
      LEDG_Value=map(MIC_Value, 1, 3000, 255, 1);

//   analogWrite(Led1 ,LEDR_Value);
//   analogWrite(Led2 ,LEDG_Value);

      analogWrite(Led2 ,LEDR_Value);
      analogWrite(Led1 ,LEDG_Value);


    }
    samplesRead=0;

}
delay(1);

}

void onPDMdata(){
int bytesAvailable = PDM.available();
PDM.read(sampleBuffer,bytesAvailable);
samplesRead = bytesAvailable / 2;

}

void enqueue(int value) {
    if ((tail + 1) % MAX_SIZE == head) {
      // 队列已满,需要先移除一个元素
      head = (head + 1) % MAX_SIZE;
    }
    queue = value;
    tail = (tail + 1) % MAX_SIZE;
    count++;
}

int dequeue() {
    int value = queue;
    head = (head + 1) % MAX_SIZE;
    count--;
    return value;
}

double get_sliding_average(int new_value) {
    static int init = 1;
    if (init) {
      // 初始化添加MAX_SIZE-1个0,保证头尾指针不会相遇
      for (int i = 0; i &lt; MAX_SIZE - 1; i++) {
            enqueue(0);
      }
      init = 0;
    }

    // 添加新的样本值
    enqueue(sqrt(new_value * new_value));

    // 计算滑动平均值
    double sum = 0;
    for (int i = 0; i &lt; count; i++) {
      sum += queue[(head + i) % MAX_SIZE];
    }
    return sum / count;
}
</code></pre>

<p>滑动平均滤波函数中,直接对采集到的数值进行取绝对值;</p>

<p> &nbsp;</p>

<p>在主程序中,通过映射,将数值转换为1-255,控制LEDR和LEDG;当声音小的时候,绿灯亮,声音大的时候,红灯亮。</p>

<p> &nbsp;</p>

<p>下面看一下演示视频。</p>

Jacktang 发表于 2025-1-15 07:33

<p>下面看一下演示视频。</p>

<p>没有视频上传吧</p>

电子烂人 发表于 2025-1-15 10:53

<p>这个滑动平均滤波是选取了多少个数据,会不会对效果有影响?</p>

<p>&nbsp;</p>

左手阿飞 发表于 2025-1-15 11:24

<p><iframe allowfullscreen="true" frameborder="0" height="450" src="//player.bilibili.com/player.html?bvid=1A9ceefEMT&amp;page=1" style="background:#eee;margin-bottom:10px;" width="700"></iframe><br />
&nbsp;</p>

左手阿飞 发表于 2025-1-15 11:32

电子烂人 发表于 2025-1-15 10:53
这个滑动平均滤波是选取了多少个数据,会不会对效果有影响?

&nbsp;

<p>滑动平均滤波取得是10个数据。灯的变化可能会缓慢一点。</p>

左手阿飞 发表于 2025-1-15 11:33

Jacktang 发表于 2025-1-15 07:33
下面看一下演示视频。

没有视频上传吧

<p>上传失败了。重新发了。在楼下。</p>

戈壁滩上的辉煌 发表于 2025-1-15 11:49

<p>主要是通过采集数据的峰值转化,这个是最重要的,平滑</p>
页: [1]
查看完整版本: 【Follow me第二季第4期】-任务4:选做任务一(非必做):通过RGB LED不同颜色、亮...