STM32 f103搭配LM386声音传感器实现简单音乐识别
1.前言
2019年12月初,有一个中国机器人技能大赛中的双足机器人比赛项目,意思是机器人识别音乐跳对应节奏的舞蹈,五首音乐随机抽三首歌曲,音乐停,机器人停。
新比赛,新项目,难度自然有,坑也不少。希望这篇文章能给大家带来一点帮助。废话不多说,进入正题。
2.效果
(健康歌)每100ms采样一次,歌曲前5秒内共测50次数据,重复12组
(卡路里) 重复7组
可以看出一首歌经过多次测值,其采样值数组呈现出有规律的特征;不同的歌曲的特征也有较好的区分度。达到了区分歌曲的效果。下面讲讲具体实现步骤。
3.思路
- 做什么:识别不同音乐,识别声音有无。
- 怎么做:a.利用传感器判断出音乐或声音(网上资料极少);
b.利用手机app听歌识曲,返回对应值(app感觉太难) ;
c.检测到声音就随机跳(下策,保命方案);
d.遥控(作弊);
e.人在旁边说出歌曲有关的词语,语音模块识别(干扰大) - 我的选择:a+c
- 技术路线:
4.硬件
找到一块具有模拟量输出功能的声音传感器模块,我用的是下面这块,感觉不错,其他的没尝试过。将f103芯片的A1脚与模块的AOUT引脚相连(奇怪的是我与DOUT相连也会得到和AOUT差不多的模拟量值,很迷,有大佬懂的话麻烦指出一下问题所在)。
5.软件
利用正点原子的adc.c文件来处理模拟量值,并最终返回给Get_Adc_Average()函数
//初始化ADC
//这里我们仅以规则通道为例
//我们默认将开启通道0~3
void Adc_Init(void)
{
ADC_InitTypeDef ADC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
//使能ADC1通道时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1 , ENABLE );
RCC_ADCCLKConfig(RCC_PCLK2_Div6);//设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
//PA1 作为模拟通道输入引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚
GPIO_Init(GPIOA, &GPIO_InitStructure);
ADC_DeInit(ADC1); //复位ADC1
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
//ADC工作模式:ADC1和ADC2工作在独立模式
ADC_InitStructure.ADC_ScanConvMode = DISABLE; //模数转换工作在单通道模式
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //模数转换工作在单次转换模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
//转换由软件而不是外部触发启动
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐
ADC_InitStructure.ADC_NbrOfChannel = 1; //顺序进行规则转换的ADC通道的数目
ADC_Init(ADC1, &ADC_InitStructure);
//根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器
ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1
ADC_ResetCalibration(ADC1); //使能复位校准
while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束
ADC_StartCalibration(ADC1); //开启AD校准
while(ADC_GetCalibrationStatus(ADC1)); //等待校准结束
//ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能
}
//获得ADC值
//ch:通道值 0~3
u16 Get_Adc(u8 ch)
{
//设置指定ADC的规则组通道,一个序列,采样时间
ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 );
//ADC1,ADC通道,采样时间为239.5周期
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束
return ADC_GetConversionValue(ADC1); //返回最近一次ADC1规则组的转换结果
}
u16 Get_Adc_Average(u8 ch,u8 times)
{
u32 temp_val=0;
u8 t;
for(t=0;t<times;t++)
{
temp_val+=Get_Adc(ch);
delay_ms(5);
}
return temp_val/times;
}
将Get_Adc_Average()拿到的值通过串口输出在电脑屏幕上,适当调整其数值范围,刷新时间间隔。我在代码中就进行了(4093-adcx),(delay_ms(100))相关处理。
while(1)
{
printf("\r\n");
for(i=0;;i++)
{
for(j=0;j<50;j++)
{
adcx=Get_Adc_Average(ADC_Channel_1,10);
printf("%-4d \r",4093-adcx);
delay_ms(100);
}
printf("\r\n");
delay_ms(1500);
delay_ms(1500);
}
}
判断歌曲就更简单了,把采样值存入数组,写一个条件语句判断数组的特征就好了,如下:
u8 check_song(void)
{
u16 adcx,adc[35]={0};
u8 i,flag=0;
printf("\r\n");
for(i=0;i<35;i++)//存储数据
{
adcx=CurrentAdc();
adc[i]=adcx;
if (adc[i]>300)
flag++;
printf("%-4d \r", adc[i]);
delay_ms(100);
}
if(flag>32)
{
printf("flag%d\r",flag);
return 0;//太极拳
}
else
{
printf("flag%d\r",flag);
if((adc[23]<5&&adc[24]<5)
||(adc[24]<5&&adc[25]<5)
||(adc[25]<5&&adc[26]<5)
||(adc[26]<5&&adc[27]<5)
||(adc[27]<5&&adc[28]<5))
return 1;//健康歌
else return 2;//翻跟头
}
}
6.总结
a.未知是最大的恐惧,行动是最好的解药
一看到比赛项目的时候,卧槽,感觉很难,果然网上一查,什么资料也没有。。绝望,想放弃。。比赛前半个月,老师开始问进度了,很慌,啥也没有。但不好意思空手去,于是和队友总结出上面几套方案,发现有个保命方案,心里稍微有点底,开始去探索更好的方法。最后搞出来声音的adc采集,其实现在看看代码,实在是简单,很惭愧。其实比赛现场能真正识别出音乐的不超过10个(共30个队伍),我难人亦难,但我只要去做,山重水复疑无路,柳暗花明又一村。这已超过了不少的人,最重要的是战胜了自己。一定要去做,用心做!
b.做项目就跟取西经样,人生何尝不是
准备过程中是不可能一帆风顺的,一天一个小自闭,三天一个大自闭,烧板子查不出问题,断结构重新打印,破代码运行不出效果,软件崩溃文件没保存…简直怀疑人生。。一般遇到这时候,我就放下手中的工作,跑个步,洗个澡,吃个饭,归来还是少年。做人嘛,最重要的就是心态好啦。
c.持续学习,模仿优秀的人
现阶段自身的能力还不足以输出很多很好的内容。模仿大佬,总结经验是比较好的成长路线。慢慢地就会有自己的风格了。加油!
需要源代码的同学请到Github自取
https://github.com/Iamliuguo