【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> </p>
<p> </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 < 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 < MAX_SIZE - 1; i++) {
enqueue(0);
}
init = 0;
}
// 添加新的样本值
enqueue(sqrt(new_value * new_value));
// 计算滑动平均值
double sum = 0;
for (int i = 0; i < count; i++) {
sum += queue[(head + i) % MAX_SIZE];
}
return sum / count;
}
</code></pre>
<p>滑动平均滤波函数中,直接对采集到的数值进行取绝对值;</p>
<p> </p>
<p>在主程序中,通过映射,将数值转换为1-255,控制LEDR和LEDG;当声音小的时候,绿灯亮,声音大的时候,红灯亮。</p>
<p> </p>
<p>下面看一下演示视频。</p>
<p>下面看一下演示视频。</p>
<p>没有视频上传吧</p>
<p>这个滑动平均滤波是选取了多少个数据,会不会对效果有影响?</p>
<p> </p>
<p><iframe allowfullscreen="true" frameborder="0" height="450" src="//player.bilibili.com/player.html?bvid=1A9ceefEMT&page=1" style="background:#eee;margin-bottom:10px;" width="700"></iframe><br />
</p>
电子烂人 发表于 2025-1-15 10:53
这个滑动平均滤波是选取了多少个数据,会不会对效果有影响?
<p>滑动平均滤波取得是10个数据。灯的变化可能会缓慢一点。</p>
Jacktang 发表于 2025-1-15 07:33
下面看一下演示视频。
没有视频上传吧
<p>上传失败了。重新发了。在楼下。</p>
<p>主要是通过采集数据的峰值转化,这个是最重要的,平滑</p>
页:
[1]