智能语音生活助手实现(QT)

Posted 宿命呀

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了智能语音生活助手实现(QT)相关的知识,希望对你有一定的参考价值。


一、功能介绍


1、 调用百度 AI 开发平台 API 进行语音识别,进行语音控制传感器的联
动,实现智能语音识别平台的功能。
2、 调用天气生活指数 API,获取不同城市每天的运动指数、 舒适度指数、
化妆指数等等。
3、 调用百度地图 api,显示不同城市的地图。
4、 实现智能闹钟,定时提醒。








二、实现步骤

2.1 语音识别模块

示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。

2.1.1 录音

在 pro 文件添加

QT += network
QT += multimedia

在 mainwindow.h 头文件添加下面定义

void RecorderStart(QString fileName);//开始录音
void RecorderEnd();//结束录音并转换格式
QFile *outFile;//录音时的变量
QAudioInput *my_audio;//录音时的变量
QAudioFormat audioFormat;//录音时的变量

Mainwindow.cpp 录音函数实现:

void MainWindow::RecorderStart(QString fileName)

    QAudioDeviceInfo device = QAudioDeviceInfo::defaultInputDevice();
    if(device.isNull())
    
        QMessageBox::warning(NULL,"QAudioDeviceInfo","录音设备不存在");
        return;
    
//    设置通道数
    audioFormat.setChannelCount(1);
//    设置编码
    audioFormat.setCodec("audio/pcm");
//    设置采样频率
    audioFormat.setSampleRate(16000);
//    设置位深
    audioFormat.setSampleSize(16);
//    判断设备是否支持该格式
    if(!device.isFormatSupported(audioFormat)) //当前使用设备是否支持
        audioFormat = device.nearestFormat(audioFormat); //转换为最接近格式
    
//    创建录音对象
    my_audio = new QAudioInput(audioFormat,this);
    outFile = new QFile;
    outFile->setFileName(fileName); //语音原始文件
    outFile->open(QIODevice::WriteOnly);
//     开始录音
    my_audio->start(outFile);

结束录音函数实现

/**********************
 * 结束录音并转换格式
**********************/
void MainWindow::RecorderEnd()

//    结束录音
    my_audio->stop();

    outFile->close();
    delete outFile;
    outFile =NULL;

    delete my_audio;
    my_audio = NULL;


2.1.2 点击释放按钮槽函数

2.1.3 申请百度AI开发平台语音识别应用

语音识别是利用百度的 API 在线识别。所以需要申请项目 ID。进入百度AI开放平台
在产品服务下选择语音识别:

点击立即使用:

申请账号点击登录:

点击创建应用:

输入应用名称、应用描述,点击立即创建:

点击返回应用列表:

获取 AppID、API Key 和 Secret Key:

我们记住其中的API Key 和 Secret Key,下面会用到。

2.1.4 HTTP请求类实现

我们录好的音频文件需要通过HTTPS协议上传到百度AI开发平台进行语音识别,之后AI平台会返回给我们识别的结果。
http类只需要封装一个方法

 bool post_sync(QString url,QMap<QString,QString>header,QByteArray requestData,QByteArray &replyData);

使用这个方法去URL发送请求会收到URL的返回值。
http.h

#ifndef HTTP_H
#define HTTP_H

#include <QObject>
#include <QMap>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QEventLoop>
#include <QDebug>
class Http : public QObject

    Q_OBJECT
public:
    explicit Http(QObject *parent = nullptr);

    bool post_sync(QString url,QMap<QString,QString>header,QByteArray requestData,QByteArray &replyData);

;

#endif // HTTP_H

http.cpp
这个方法的第一个参数是 post方法发送请求的URL,第二个参数是请求的方法头,第三个参数是请求的数据,第四个参数是返回的数据。
这里要说的是必须要设置openssl签名配置,否则在ARM上会报错。

bool Http::post_sync(QString url,QMap<QString,QString>header,QByteArray requestData,QByteArray &replyData)

//    发送请求的对象
    QNetworkAccessManager manager;
//    请求 对象
    QNetworkRequest request;
    request.setUrl(url);
    QMapIterator<QString,QString> it(header);
    while (it.hasNext()) 
        it.next();
        request.setRawHeader(it.key().toLatin1() ,it.value().toLatin1());
    
//设置openssl签名配置,否则在ARM上会报错
    QSslConfiguration conf = request.sslConfiguration();
    conf.setPeerVerifyMode(QSslSocket::VerifyNone);
#if (QT_VERSION > QT_VERSION_CHECK(5,0,0))
    conf.setProtocol(QSsl::TlsV1_0);
#else
    conf.setProtocol(QSsl::TlsV1);
#endif
    request.setSslConfiguration(conf);

    QNetworkReply *reply = manager.post(request,requestData);
    QEventLoop l;
    //一旦服务器返回,reply会发出信号
    connect(reply,&QNetworkReply::finished,&l,&QEventLoop::quit);
    l.exec();
    if(reply != nullptr && reply->error() == QNetworkReply::NoError)
    

        replyData = reply->readAll();
        return true;
    
    else
    
        qDebug()<<"request error!";
        return false;
    

2.1.5 发送请求

这里需要向两个URL发送两个请求,第一个请求是把我们4.2.3创建应用得到的API Key 和 Secret Key组合成一个URL获取access_token,第二个请求是把音频文件发送请求到语音识别的URL才能返回语音识别的结果。
我们新建一个类Speech
Speech.h
这里我们把API Key和Secret Key作为参数传到const QString baiduTokenUrl里面去。把主机名和获取的access_token做为参数传入const QString baiduSpeechUrl。

#include <QObject>
#include <QJsonDocument>
#include <QJsonParseError>
#include <QJsonObject>
#include <QJsonValue>
#include <QJsonArray>
#include <QFile>
#include "http.h"
#include <QHostInfo>
//    获取Access Token
const QString baiduTokenUrl = "https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=%1&client_secret=%2&";
const QString client_id = "我们创建应用的API Key";
const QString client_secret = "我们创建应用的Secret Key";
//    语音识别url
const QString baiduSpeechUrl = "https://vop.baidu.com/server_api?dev_pid=1537&cuid=%1&token=%2";
class Speech:public QObject

    Q_OBJECT
public:
    Speech();
    QString speechIdentify(QString fileName);
private:
    QString getJsonValue(QByteArray ba,QString key);
;
#endif // SPEECH_H
Speech.cpp
QString Speech::speechIdentify(QString fileName)

//    获取Access Token
    QString tokenUrl =QString(baiduTokenUrl).arg(client_id).arg(client_secret);
    Http my_http;
    QMap<QString,QString>header;
    header.insert(QString("Content-Type"),QString("audio/pcm;rate=16000"));
    QByteArray requestData;//请求内容
    QByteArray replyData;//url返回内容

   qDebug()<<tokenUrl;
    bool result = my_http.post_sync(tokenUrl,header,requestData,replyData);
    if(result) 
        QString key = "access_token";
        QString accessToken =getJsonValue(replyData,key);
        qDebug()<<accessToken;
        //    语音识别
        QString speechUrl = QString(baiduSpeechUrl).arg(QHostInfo::localHostName()).arg(accessToken);
        QFile file;
        file.setFileName(fileName);
        file.open(QIODevice::ReadOnly);
        requestData = file.readAll();
        file.close();
        replyData.clear();
//        再次发起请求
        result = my_http.post_sync(speechUrl,header,requestData,replyData);
         if(result) 
             QString key = "result";
             QString retText =getJsonValue(replyData,key);
             qDebug()<<retText;
             return retText;
         
         else
             return NULL;
         
    
    else 
        return  "error";
    

解析返回的数据
返回的数据是这种Json类型的,我们只需要获取里边result的值就能得到我们想要的结果了。

"err_no":0,"err_msg":"success.","corpus_no":"15984125203285346378","sn":"481D633F-73BA-726F-49EF-8659ACCC2F3D","result":["北京天气"]
QString Speech::getJsonValue(QByteArray ba,QString key)

    QJsonParseError parseError;
    QJsonDocument jsondocument = QJsonDocument::fromJson(ba,&parseError);
    if(parseError.error ==QJsonParseError::NoError)
    
        if(jsondocument.isObject())
        
            QJsonObject jsonObject = jsondocument.object();
            if(jsonObject.contains(key))
                QJsonValue jsonvalue = jsonObject.value(key);
                if(jsonvalue.isString())
                    return jsonvalue.toString();
                else if(jsonvalue.isArray())
                    QJsonArray arr = jsonvalue.toArray();
                    QJsonValue val =arr.at(0);
                    return val.toString();
                
            
        
    
    return "";

2.1.6 MainWindow类调用函数

我们在释放按钮的槽函数里添加以下代码。

void MainWindow::on_pushButton_video_released()

    ui->pushButton_video->setText("按住说话");
    RecorderEnd();
    Speech my_speech;
    QString text =my_speech.speechIdentify("./1.pcm");
    ui->textEdit->append(text);
    audioCtrl(text);

2.1.7 语音控制设备联动

void MainWindow::audioCtrl(QString text)

    if(text == "开灯。")
    
        system("echo 1 >/sys/class/leds/user1/brightness");
        system("echo 1 >/sys/class/leds/user2/brightness");
        system("echo 1 >/sys/class/leds/user3/brightness");
        ui->textEdit_2->setText("灯已打开");
    
    else if(text == "关灯。")
    
        system("echo 0 >/sys/class/leds/user1/brightness");
        system("echo 0 >/sys/class/leds/user2/brightness");
        system("echo 0 >/sys/class/leds/user3/brightness");
        ui->textEdit_2->setText("灯已关闭");
    
    else if(text == "报警。")
    
        int fd;
        struct input_event event;
        struct timeval time;
        fd = open("/dev/input/by-path/platform-beeper-event", O_RDWR);
        event.type = EV_SND;
        event.code = SND_TONE;
        event.value = 1000;
        time.tv_sec = 1;
        time.tv_usec = 0;
        event.time = time;
        write(fd, &event, sizeof(struct input_event));
        ui->textEdit_2->setText("蜂鸣器已报警");
    
    else if(text == "关闭。")
    
        int fd;
        struct input_event event;
        struct timeval time;
        fd = open("/dev/input/by-path/platform-beeper-event", O_RDWR);
        event.type = EV_SND;
        event.code = SND_TONE;
        event.value = 0;
        time.tv_sec = 0;
        time.tv_usec = 0;
        event.time = time;
        write(fd, &event, sizeof(struct input_event));
        ui->textEdit_2->setText("蜂鸣器报警已关闭");
    
    else if(text == "关风扇。")
    
        unsigned char arg;
        Ioctl(EXIT_FAN,&arg);
        ui->textEdit_2->setText("风扇已关闭");
    
    else if(text == "开风扇。")
    
        unsigned char arg;
        Ioctl(EXIT_FAN,&arg);
        Ioctl(INIT_FAN,&arg);
        Ioctl(FAN_UP,&arg);
        ui->textEdit_2->setText("风扇已打开");
    
    else if(text == "温度。")
    
       QString tem = temCollect();
       ui->textEdit_2->setText(QString("此时温度为:").append(tem).append("'C"));
    
    else if(text == "湿度。")
    
        QString hum = humCollect();
        ui->textEdit_2->setText(QString("此时湿度为:").append(hum).append("%"));
    

2.2 智慧生活模块

2.2.1 创建API应用

浏览器进入和风天气官网,注册账号并登陆,点击进入控制台。

进入控制台后,点击应用管理。

点击创建应用,选择免费开发板


填写天气数据应用名称后选择WebAPI,自定义天气数据应用名称。



完成上述操作后,把key复制下来,后边代码需要用到。

2.2.2 Get方法获取API的JSON数据

我们将key填写到下面的URL里面,使用get方法就能从API爬下JSON数据来了。
https://devapi.qweather.com/v7/indices/1d?type=1,2&location=101010100&key=你的KEY
其中请求参数
Location:需要查询地区的LocationID或以英文逗号分隔的经度,纬度坐标(十进制),LocationID可通过城市搜索服务获取。例如 location=101010100
Key:用户认证key,即上面获取到的key。
Type:生活指数的类型ID,包括洗车指数、穿衣指数、钓鱼指数等。可以一次性获取多个类型的生活指数,多个类型用英文,分割。例如type=3,5。具体生活指数的ID和等级参考生活指数常量。各项生活指数并非适用于所有城市。
所以我们以参数的形式将城市的LocationID

以上是关于智能语音生活助手实现(QT)的主要内容,如果未能解决你的问题,请参考以下文章

如何快速搭建一个像“天猫精灵”的智能语音助手?

你的生活正悄悄被窃听,隐私的边界在哪?

Qt功能优化:Qt语音助手

小白如何做一个Python人工智能语音助手

语音助手-智能家居

我用3项微软 Azure人工智能认知服务打造定时语音提醒喝水助手