基于樹(shù)莓派的語(yǔ)音機(jī)器人
近年來(lái)語(yǔ)音識(shí)別發(fā)展迅速也帶動(dòng)了人工智能的發(fā)展。曾經(jīng)渴望自己做一個(gè)機(jī)器人,但是無(wú)奈,心有余而力不足,經(jīng)過(guò)多年的積累,小白的我也能用站著巨人的肩膀上玩下機(jī)器人了。
準(zhǔn)備工作:樹(shù)莓派,音頻模塊,stm32單片機(jī),百度語(yǔ)音識(shí)別接口,喇叭。
整體思路:
1. 由于樹(shù)莓派沒(méi)有ADC模塊,所以這里借助于stm32的ADC模塊來(lái)實(shí)現(xiàn)將語(yǔ)音信號(hào)轉(zhuǎn)換成數(shù)字信號(hào),然后通過(guò)串口傳 輸 到樹(shù)莓派你中,樹(shù)莓派你將數(shù)據(jù)組裝成wave文件,便于語(yǔ)音識(shí)別。
2. 通過(guò)http協(xié)議將組裝的語(yǔ)音文件上傳到百度語(yǔ)音識(shí)別平臺(tái)進(jìn)行識(shí)別。文檔說(shuō)明(免費(fèi)調(diào)用)
3. 根據(jù)識(shí)別結(jié)果做出相應(yīng)的處理。
4. 對(duì)于需要播放語(yǔ)音時(shí),根據(jù)百度語(yǔ)音合成接口合成語(yǔ)音然后使用mplayer播放出來(lái)。mplayer安裝參考 博客
部分代碼:
將音頻轉(zhuǎn)換成wave文件
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <wiringPi.h>
#include <wiringSerial.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include "listen.h"
//gcc -o uart uart.c -lwiringPi
typedef struct WAV_Format WAVHEADER;
#define MAX_LISTEN_SIZES 1024*70 //定義接收數(shù)據(jù)的大小
#define bty 460800//串口的波特率
struct listen*listenUart()
{
int fd,file;
char buff,buff2;
struct listen*liste=(struct listen*)malloc(sizeof(struct listen));
unsigned short size;
unsigned short*music,temp=0;
unsigned short max=0,min=0;
char*result=NULL;//存儲(chǔ)最后的返回值
int index=0,i=0;
char stop=1;
WAVHEADER wavHead;
music=(unsigned short*)malloc(MAX_LISTEN_SIZES*2);
result=(char*)malloc(MAX_LISTEN_SIZES*2+sizeof(WAVHEADER));
if(wiringPiSetup() < 0)return NULL;
if((fd = serialOpen ("/dev/ttyAMA0",bty))<0)
{
return NULL;
printf("serial err\n");
}
//file=open("abc.wav", O_RDWR|O_CREAT);
printf("oepn success\n");
//serialPrintf(fd,"Hello World!!!");
//需要對(duì)音頻信號(hào)作出處理,當(dāng)大于或者閾值時(shí)開(kāi)始統(tǒng)計(jì),知道錄制完成
int countTotal=0;
int countNumber= 1000;//統(tǒng)計(jì)個(gè)數(shù)
int countMax=2860;//最大值
int countMin=2840;//最小值
int startCount=1;
while(1)
{
if(index==MAX_LISTEN_SIZES)
{
break;
}
buff=serialGetchar(fd);
buff2=serialGetchar(fd);
if((buff2&0x0F0)!=0)
{
buff2=serialGetchar(fd);
}
else
{
size=buff2;
size=size<<8;
size=(size&0xFF00)|(buff&0xFF);
music[index]=size;
if(startCount==1)
{
countTotal=countTotal+size;
if(index>=countNumber)
{
int temp=countTotal/(countNumber+1);
if(temp>countMax||temp<countMin)
{
startCount=0;
//開(kāi)始錄音
printf(":::::%d\n",temp);
index++;
}
else
{
printf("temp:%d\n",temp);
index=0;
}
countTotal=0;
}
else
{
index++;
}
}
else
{
index++;
}
}
}
serialClose(fd);
printf("end\n");
//對(duì)音頻進(jìn)行放大處理
max=music[0];
min=music[0];
for(i=i;i<MAX_LISTEN_SIZES;i++){
temp=music[i];
if(temp>max)
{
max=temp;
}
if(temp<min)
{
min=temp;
}
}
size=max-min;
for(i=0;i<MAX_LISTEN_SIZES;i++)
{
music[i]=(unsigned short)((music[i]-min)*1.0*6000/size);
}
wavHead.ChunkID=0x46464952; /* "RIFF" */
wavHead.ChunkSize=sizeof(wavHead)+MAX_LISTEN_SIZES*2 -8; /* 36 + Subchunk2Size */
wavHead.Format=0x45564157; /* "WAVE" */
wavHead.Subchunk1ID=0x20746D66; /* "fmt " */
wavHead.Subchunk1Size=0x10; /* 16 for PCM */
wavHead.AudioFormat=0x01; /* PCM = 1*/
wavHead.NumChannels=0x01; /* Mono = 1, Stereo = 2, etc. */
wavHead.SampleRate=0x3E80; /* 8000, 44100, etc. */
wavHead.ByteRate=0x7D00; /* = SampleRate * NumChannels * BitsPerSample/8 */
wavHead.BlockAlign=0x02; /* = NumChannels * BitsPerSample/8 */
wavHead.BitsPerSample=0x10; /* 8bits, 16bits, etc. */
wavHead.Subchunk2ID=0x61746164; /* "data" */
wavHead.Subchunk2Size=MAX_LISTEN_SIZES*2; /* data size */
//返回?cái)?shù)據(jù)賦值
memcpy(result,(char*)&wavHead,sizeof(WAVHEADER));
memcpy(result+sizeof(WAVHEADER),(char*)music,MAX_LISTEN_SIZES*2);
liste->length=sizeof(WAVHEADER)+MAX_LISTEN_SIZES*2;
liste->data=result;
return liste;
}
將音頻識(shí)別成文字
#include "convertText.h"
static Buffer *listen_buff2=NULL;
size_t listen_getData2(void *ptr, size_t size, size_t nmemb, void *stream)
{
appendBuffer(listen_buff2,ptr,nmemb);
return nmemb;
}
int listenText(char*result2)
{
listen_buff2=initBuffer();
struct listen*lsn=listenUart();
char*base;
int fileLength=lsn->length;
int result=1;
int baseSize=(lsn->length/3)*4+(lsn->length%3)*2+1;
base=(char*)malloc(baseSize);
base64_encode(lsn->data,lsn->length,base);
//發(fā)送請(qǐng)求
free(lsn->data);
free(lsn);
int code=initToken();
if(code==1)
{
char*token=getToken();
///開(kāi)始創(chuàng)建json字符串
cJSON * root = cJSON_CreateObject();
cJSON_AddItemToObject(root, "format", cJSON_CreateString("wav"));
cJSON_AddItemToObject(root, "rate", cJSON_CreateString("16000"));
cJSON_AddItemToObject(root, "channel", cJSON_CreateString("1"));
cJSON_AddItemToObject(root, "cuid", cJSON_CreateString("34-68-95-91-77-43"));
cJSON_AddItemToObject(root, "token", cJSON_CreateString(token));
cJSON_AddItemToObject(root, "dev_pid", cJSON_CreateString("1537"));
cJSON_AddItemToObject(root, "speech", cJSON_CreateString(base));
cJSON_AddItemToObject(root, "len", cJSON_CreateNumber(fileLength));
char*jsonParam=cJSON_PrintUnformatted(root);
char*apiurl="http://vop.baidu.com/server_api";
CURL* curl;
CURLcode res;
// ptr = curl_easy_escape(NULL, (char *)a, asize);
curl = curl_easy_init();
struct curl_slist* headers = NULL;
headers = curl_slist_append(headers, "Content-Type:application/json");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_URL, apiurl);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 60);
curl_easy_setopt(curl, CURLOPT_POST, 1);
//http://vop.baidu.com/server_api
//CURLOPT_POSTFIELDS,CURLOPT_POSTFIELDSIZE
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, jsonParam);
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, strlen(jsonParam));
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, listen_getData2);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
cJSON_Delete(root);
curl_slist_free_all(headers);
free(token);
free(jsonParam);
if (res == CURLE_OK)
{
char*chars;
char*tempresult=(char*)malloc(listen_buff2->length+1);
memcpy(tempresult,listen_buff2->buff,listen_buff2->length);
tempresult[listen_buff2->length]=0;
cJSON *json;
cJSON * item = NULL;
cJSON*errCode;
json=cJSON_Parse(tempresult);
item=cJSON_GetObjectItem(json, "result");
errCode=cJSON_GetObjectItem(json, "err_no");
if(errCode->valueint!=0)
{
return -3;
}
chars=cJSON_GetArrayItem(item,0)->valuestring;
strcpy(result2,chars);
free(tempresult);
cJSON_Delete(json);
return 0;
}
else
{
return -3;
}
}
else
{
return -2;
}
return -1;
}
主程序
#include<stdio.h>
#include<string.h>
#include "convertText.h"
#include "mp3.h"
#include "led.h"
#include "say.h"
//gcc -o robot robot.o mp3.o Buffer.o base64.o token.o cJSON.o listen.o convertText.o led.o say.o -lcurl -lm -lwiringPi -lmad
void sayChina(char*china)
{
int resp=initSay(china);
printf("resp:%d\n",resp);
if(resp==1)
{
int tte=playData("temp.mp3");
printf("tte:%d\n",tte);
}
}
int main()
{
char text[100]={0};
sayChina("你好,我是小志,有什么可以為你服務(wù)");
while(1)
{
printf(";;;;;;;;");
int code= listenText(text);
if(code==0)
{
printf("result:%s\n",text);
if(strstr(text,"播放音樂(lè),")!=NULL||strstr(text,"打開(kāi)音樂(lè),")!=NULL)
{
sayChina("正在為你打開(kāi)音樂(lè)");
musicPlayFile("mu.mp3");
}
if(strstr(text,"打開(kāi)燈,")!=NULL||strstr(text,"打開(kāi),")!=NULL)
{
sayChina("好的");
printf("正在打開(kāi)");
ledOn();
}
if(strstr(text,"關(guān)閉燈,")!=NULL||strstr(text,"關(guān)閉,")!=NULL||strstr(text,"完畢,")!=NULL)
{
sayChina("好的");
printf("正在關(guān)閉");
ledOff();
}
if(strstr(text,"你叫什么")!=NULL||strstr(text,"你叫什么名字")!=NULL||strstr(text,"名字")!=NULL)
{
sayChina("我叫小志");
}
if(strstr(text,"今天天氣咋樣")!=NULL||strstr(text,"天氣")!=NULL)
{
sayChina("外面在下雨,有點(diǎn)冷");
}
if(strstr(text,"中午好")!=NULL||strstr(text,"中午")!=NULL)
{
sayChina("好什么啊,我還沒(méi)吃飯呢");
}
if(strstr(text,"你多大了")!=NULL||strstr(text,"今年幾歲")!=NULL||strstr(text,"幾歲")!=NULL)
{
sayChina("我才出生,還沒(méi)滿(mǎn)月");
}
}
else
{
printf("error\n");
}
}
return 0;
}
這里只是貼出來(lái)部分程序,所有代碼請(qǐng)查看 鏈接 希望能和大家一起交流下心得。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單猜數(shù)字游戲
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單猜數(shù)字游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08
詳解C語(yǔ)言中symlink()函數(shù)和readlink()函數(shù)的使用
這篇文章主要介紹了詳解C語(yǔ)言中symlink()函數(shù)和readlink()函數(shù)的使用,是C語(yǔ)言入門(mén)學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-09-09
C語(yǔ)言中操作utmp文件的相關(guān)函數(shù)用法
這篇文章主要介紹了C語(yǔ)言中操作utmp文件的相關(guān)函數(shù)用法,包括getutent()函數(shù)和setutent()函數(shù)以及endutent()函數(shù),需要的朋友可以參考下2015-08-08
C++ 中strcpy標(biāo)準(zhǔn)寫(xiě)法實(shí)例詳解
這篇文章主要介紹了C++ 中strcpy標(biāo)準(zhǔn)寫(xiě)法實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-06-06
C語(yǔ)言進(jìn)階練習(xí)二叉樹(shù)的遞歸遍歷
樹(shù)是一種重要的非線(xiàn)性數(shù)據(jù)結(jié)構(gòu),直觀(guān)地看,它是數(shù)據(jù)元素(在樹(shù)中稱(chēng)為結(jié)點(diǎn))按分支關(guān)系組織起來(lái)的結(jié)構(gòu),很象自然界中的樹(shù)那樣。樹(shù)結(jié)構(gòu)在客觀(guān)世界中廣泛存在,如人類(lèi)社會(huì)的族譜和各種社會(huì)組織機(jī)構(gòu)都可用樹(shù)形象表示,本篇介紹二叉樹(shù)的遞歸與非遞歸遍歷的方法2022-06-06
C++實(shí)現(xiàn)LeetCode(9.驗(yàn)證回文數(shù)字)
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(9.驗(yàn)證回文數(shù)字),本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07

