Linux下基于QT的智能家居语音识别

本文介绍了如何在Linux环境下使用voicectl.c文件与XML文件交互,实现语音识别,通过TCP和UDP与QT应用程序通信,执行预设的命令并显示相关操作结果。
摘要由CSDN通过智能技术生成

1、选择合适的语音识别处理文件.XML(可从各大官网获取)

也会在文章的末尾附上本人使用的文件

2、在Linux上编写voicectl.c文件与编译后的XML文件进行交互

为了是代码更加清晰且模块化,讲所需函数写入了common.c文件中

放入common几个重要的函数模块

//进行TCP通讯套接字配置
int Setsockopt(int sockfd, int level, int optname,
	       const void *optval, socklen_t optlen)
{
	int retval = setsockopt(sockfd, level, optname, optval, optlen);
	if(retval == -1)
	{
		perror("setsockopt() error");
	}

	return retval;
}

int Select(int nfds, fd_set *readfds, fd_set *writefds,
	   fd_set *exceptfds, struct timeval *timeout)
{
	int n = select(nfds, readfds, writefds, exceptfds, timeout);
	if(n == -1)
	{
		perror("select() failed");
		exit(0);
	}

	return n;
}

//初始化套接字的封装函数
int init_sock(const char *ubuntu_ip)
{
	struct sockaddr_in sin;
	int sockfd = Socket(AF_INET, SOCK_STREAM, 0);
    
//进行端口复用和协议组,通讯模式的配置
	int on = 1;
	Setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(1));
	
	//连接到虚拟机中
	bzero((char *) &sin, sizeof(sin));
	sin.sin_family = AF_INET;
	inet_pton(AF_INET, ubuntu_ip, &sin.sin_addr);
	sin.sin_port = htons(DEF_PORT);
	
	Connect(sockfd, (struct sockaddr *)&sin, sizeof(sin));
	printf("connected.\n");

	return sockfd;
}


void send_pcm(int sockfd, char *pcmfile)
{
	// 打开PCM文件
	int fd = Open(pcmfile, O_RDONLY);

	// 取得PCM数据的大小
	off_t pcm_size = lseek(fd, 0, SEEK_END);
	lseek(fd, 0, SEEK_SET);

	// 读取PCM数据
	char *pcm = calloc(1, pcm_size);
	Read(fd, pcm, pcm_size);

	// 将PCM发送给语音识别引擎系统
	int m = Write(sockfd, pcm, pcm_size);
	printf("%d bytes has been write into socket!\n", m);

	free(pcm);
}

xmlChar *wait4id(int sockfd)
{
	char *xml = calloc(1, XMLSIZE);

	printf("wait4id: 1\n");
	// 从ubuntu接收XML结果
	int n = Read(sockfd, xml, XMLSIZE);
	printf("%d bytes has been recv from ubuntu.\n", n);

	// 将XML写入本地文件 XMLFILE 中
	FILE *fp = fopen(XMLFILE, "w");
	if(fp == NULL)
	{
		perror("fopen() failed");
		exit(0);
	}

	size_t m = fwrite(xml, 1, n, fp);
	if(m != n)
	{
		perror("fwrite() failed");
		exit(0);
	}

	fflush(fp);

	return parse_xml(XMLFILE);
}


/********************* XML函数列表 *******************/

xmlChar *__get_cmd_id(xmlDocPtr doc, xmlNodePtr cur)
{
	xmlChar *key, *id;
	cur = cur->xmlChildrenNode;
	while (cur != NULL)
	{
	    if ((!xmlStrcmp(cur->name, (const xmlChar *)"cmd")))
	    {
		    key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
		    printf("cmd: %s\n", key);
		    xmlFree(key);

		    id = xmlGetProp(cur, (const xmlChar *)"id");
		    printf("id: %s\n", id);

		    xmlFree(doc);
		    return id;
 	    }
		cur = cur->next;
	}

	xmlFree(doc);
    return NULL;
}

xmlChar *parse_xml(char *xmlfile)
{
	xmlDocPtr doc;
	xmlNodePtr cur1, cur2;

	doc = xmlParseFile(xmlfile);
	if (doc == NULL)
	{
		fprintf(stderr,"Document not parsed successfully. \n");
		return NULL;
	}
	
	cur1 = xmlDocGetRootElement(doc);
	if(cur1 == NULL)
	{
		fprintf(stderr,"empty document\n");
		xmlFreeDoc(doc);
		return NULL;
	}

	if(xmlStrcmp(cur1->name, (const xmlChar *)"nlp"))
	{
		fprintf(stderr,"document of the wrong type, root node != nlp");
		xmlFreeDoc(doc);
		return NULL;
	}
	
	cur1 = cur1->xmlChildrenNode;

	while (cur1 != NULL)
	{
		if ((!xmlStrcmp(cur1->name, (const xmlChar *)"result")))
		{
			cur2 = cur1->xmlChildrenNode;
			while(cur2 != NULL)
			{
				if((!xmlStrcmp(cur2->name, (const xmlChar *)"confidence")))
				{
					xmlChar *key = xmlNodeListGetString(doc, cur2->xmlChildrenNode, 1);
					if(atoi((char *)key) < 30)
					{
						xmlFree(doc);
						fprintf(stderr, "sorry, I'm NOT sure what you say.\n");
						return NULL;
					}
				}
				
				if((!xmlStrcmp(cur2->name, (const xmlChar *)"object")))
				{
					return __get_cmd_id(doc, cur2);
				}
				cur2 = cur2->next;
			}
		}
		cur1 = cur1->next;
	}

	xmlFreeDoc(doc);
	return NULL;
}

void send_data_to_8266(int id_num)
{
	int sockfd;
	/*客户程序开始建立sockfd描述符*/
	if((sockfd = socket(AF_INET,SOCK_DGRAM,0)) ==-1)
	{
		printf("socket error\n");
		exit(1);
	}
	
	struct sockaddr_in r_addr;
	bzero(&r_addr,sizeof(struct sockaddr_in));	/*将结构体里数据全部置0*/
	r_addr.sin_family = AF_INET;				/*结构体成员赋初值*/
	r_addr.sin_addr.s_addr = inet_addr("127.0.0.1");//广播发送信息到这个网段
	r_addr.sin_port = htons(10010); // 端口号

	int on=1;
	setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&on,sizeof(on));
	//设置socket为可广播模式。
    
//通过设置不同的命令,发送给XML文件进行判断分析
	switch(id_num)
	{
		case 1:sendto(sockfd,"open windows",strlen("open windows"),0,(struct sockaddr *)&r_addr,sizeof(struct sockaddr_in));
			printf("open windows\n");
			break;
		case 2:sendto(sockfd,"open door",strlen("open door"),0,(struct sockaddr *)&r_addr,sizeof(struct sockaddr_in));
			printf("open door\n");
			break;
		case 3:sendto(sockfd,"open led",strlen("open led"),0,(struct sockaddr *)&r_addr,sizeof(struct sockaddr_in));
			printf("open led\n");
			break;
		case 4:sendto(sockfd,"close windows",strlen("close windows"),0,(struct sockaddr *)&r_addr,sizeof(struct sockaddr_in));
			printf("close windows\n");
			break;	
		case 5:sendto(sockfd,"close door",strlen("close door"),0,(struct sockaddr *)&r_addr,sizeof(struct sockaddr_in));
			printf("close door\n");
			break;
		case 6:sendto(sockfd,"close led",strlen("close led"),0,(struct sockaddr *)&r_addr,sizeof(struct sockaddr_in));
			printf("close led\n");
			break;
		default:
			break;	
	}

}

在voicectl.c中调用common.c的函数模块实现与.XML文件的交互

//
//
//  Copyright(C), 2013-2017, GEC Tech. Co., Ltd.
//
//  File name: GPLE/voicectl.c
//
//  Author: GEC
//
//  Date: 2017-01
//  
//  Description: 获取语音指令,根据指令完成相应动作
//
//
//

#include "common.h"
#include <sys/ioctl.h>
  
#define TEST_MAGIC 'x'                           //定义幻数
#define TEST_MAX_NR 2                            //定义命令的最大序数

//定义LED的魔幻数
#define LED1 _IO(TEST_MAGIC, 0)              
#define LED2 _IO(TEST_MAGIC, 1)
#define LED3 _IO(TEST_MAGIC, 2)
#define LED4 _IO(TEST_MAGIC, 3)



//#define REC_CMD  "./arecord -d4 -c1 -r16000 -traw -fS16_LE cmd.pcm"
#define REC_CMD  "arecord -d3 -c1 -r16000 -traw -fS16_LE cmd.pcm"
#define PCM_FILE "./cmd.pcm"
/* -d:录音时长(duration)
-c:音轨(channels)
-r:采样频率(rate)
-t:封装格式(type)
-f:量化位数(format) */
int said=0;

void catch(int sig)
{
	if(sig == SIGPIPE)
	{
		printf("killed by SIGPIPE\n");
		exit(0);
	}
}


int main(int argc, char const *argv[]) // ./wav2pcm ubuntu-IP
{

	signal(SIGPIPE, catch);
	int id_num=0;
	if(argc != 2)
	{
		printf("Usage: %s <ubuntu-IP>\n", argv[0]);
		exit(0);
	}

	int sockfd = init_sock(argv[1]); //tcp
	
	while(1)
	{
		// 1,调用arecord来录一段音频
		printf("please to start REC in 3s...\n");

		// 在程序中执行一条命令  “录音的命令”
		system(REC_CMD);

		// 2,将录制好的PCM音频发送给语音识别引擎
		send_pcm(sockfd, PCM_FILE);

		// 3,等待对方回送识别结果(字符串ID)
		xmlChar *id = wait4id(sockfd);
		
		if(id == NULL)
		{
			said = 0; 
			continue;
		}
		id_num=atoi((char *)id);
		if(id_num == 999)
		{
			printf("bye-bye!\n");
			goto exit;
		}
		if(id_num == 100 )
		{
			printf("你好,主人!有什么吩咐\n");
			said = 1;  //唤醒它
			system("madplay ../mp3/我在.mp3"); // 放一个语音文件,MP3文件放入上一级目录
			
		}
		if( id_num != 100 )
		{
			send_data_to_8266(id_num);//处理芯片

			printf("id: %d\n",id_num);
            //调用Linux命令播放MP3文件
			 if(id_num == 1)
		    {
				 system("madplay ../mp3/开窗.mp3"); 
			}
			 if(id_num == 2)
		    {
				 system("madplay ../mp3/开门.mp3"); 
			}
		    if(id_num == 3)
		    {
			    system("madplay ../mp3/开灯.mp3"); 
			}
			 if(id_num == 4)
		    {
				 system("madplay ../mp3/关窗.mp3"); 
			}
			 if(id_num == 5)
		    {
				 system("madplay ../mp3/关门.mp3"); 
			}
			   if(id_num == 6)
		    {
			    system("madplay ../mp3/关灯.mp3"); 
			}
			 if(id_num == 7)
		    {
			 	system("madplay ../mp3/天气.mp3"); 
			}
			
		}
	}

exit:
	
	close(sockfd);
	return 0;
}

3、在Linux下运行.xml文件与voicectl.c文件

检验通讯连接是否建立,是否可以对语音文件进行处理,在一切准备就绪后在QT上进行实物的操作

QT界面和运行结果如下

设置了开门,关门,开灯,关灯,开窗关窗的QLable模块

通过按钮或者语音命令实现相关操作

 

 

 在mainwindow.h文件中加入#include <QUdpSocket>头文件确保可以进行通信

因为只需要接受数据所以采用了UDP通讯协议,在单纯的接受数据方面使用UDP协议比起TCP协议更加高效

附上mainwindow.h的代码

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QUdpSocket>
namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
private slots:
    void slotrecv();
    void on_btnOpenWin_clicked();

    void on_btnCloseWin_clicked();

    void on_btnOpenDoor_clicked();

    void on_btnCloseDoor_clicked();

    void on_btnOpenLight_clicked();

    void on_btnCloseLight_clicked();

private:
    Ui::MainWindow *ui;
    QUdpSocket *udp;
};

#endif // MAINWINDOW_H

在mainwindow.cpp中进行通信的链接和函数的实现

废话不多说,上代码

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QNetworkDatagram>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    udp = new QUdpSocket(this);//创建新的QUDPSocket对象
    udp->bind(QHostAddress::Any,10010);//连接服务器
    connect(udp,SIGNAL(readyRead()),this,SLOT(slotrecv()));//转到槽函数
}

void MainWindow::slotrecv()
{
    QNetworkDatagram datagram ;
    while (udp->hasPendingDatagrams()) {
           datagram = udp->receiveDatagram();//接受数据

        }
    if(datagram.data()=="open windows")
    {
        on_btnOpenWin_clicked();//进行对应的处理
    }
    else if(datagram.data()=="close windows")
    {
        on_btnCloseWin_clicked();
    }
    else if(datagram.data()=="open door")
    {
        on_btnOpenDoor_clicked();
    }
    else if(datagram.data()=="close door")
    {
        on_btnCloseDoor_clicked();
    }
    else if(datagram.data()=="open led")
    {
        on_btnOpenLight_clicked();
    }
    else if(datagram.data()=="close led")
    {
       on_btnCloseLight_clicked();
    }
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_btnOpenWin_clicked()
{
    QPixmap pix(":/pic/开窗.jpg");//接受图片资源
    ui->lbWin->setPixmap(pix);//QLabel上显示图片在显示图片
}

void MainWindow::on_btnCloseWin_clicked()
{
    QPixmap pix(":/pic/关窗.jpg");
    ui->lbWin->setPixmap(pix);
}

void MainWindow::on_btnOpenDoor_clicked()
{
    QPixmap pix(":/pic/开门.jpg");
    ui->lbDoor->setPixmap(pix);
}

void MainWindow::on_btnCloseDoor_clicked()
{
    QPixmap pix(":/pic/关门.jpg");
    ui->lbDoor->setPixmap(pix);
}

void MainWindow::on_btnOpenLight_clicked()
{
    QPixmap pix(":/pic/openLight.jpg");
    ui->lbLight->setPixmap(pix);
}

void MainWindow::on_btnCloseLight_clicked()
{
    QPixmap pix(":/pic/closeLight.jpg");
    ui->lbLight->setPixmap(pix);
}

此上就可以在Linux上实现语音识别并且与QT的交互了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值