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

该博客介绍了如何使用QT实现一个智能语音生活助手,包括语音识别、天气信息获取、地图显示和闹钟功能。通过集成百度AI开发平台的语音识别API,实现了语音控制设备联动,如开关灯、控制风扇等。同时,利用和风天气API获取生活指数,结合百度地图API显示地图,并且创建了一个简单的智能闹钟功能,能够根据设定时间提醒用户。
摘要由CSDN通过智能技术生成


一、功能介绍


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填入URL就能获取不同城市的生活指标。
具体代码参考下面:

//get方法获取信息
void LifeWidget::sendQuest(QString cityStr)
{
    QString key = "您申请的key";
    QString quest_url = "https://devapi.qweather.com/v7/indices/1d?type=0&location=%1&key=%2";
    quest_url = quest_url.arg(cityStr).arg(key);
    QNetworkRequest quest;
    quest.setUrl(QUrl(quest_url));
    //设置openssl签名配置,否则在ARM上会报错
        QSslConfiguration conf = quest.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
        quest.setSslConfiguration(conf);
    manager->get(quest);    /*发送get网络请求*/
}
//数据接收槽函数
void LifeWidget::replyFinished(QNetworkReply *reply)
{
    replyall = reply->readAll();
    reply->deleteLater(); //销毁请求对象
}
void LifeWidget::init_networt_life()
{
    manager = new QNetworkAccessManager(this);   
    connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));//关联信号和槽
}

2.2.3 解析JSON数据

{
  "code": "200",
  "updateTime": "2021-02-06T16:36+08:00",
  "fxLink": "http://hfx.link/2ax2",
  "daily": [
    {
      "date": "2021-02-06",
      "type": "2",
      "name": "洗车指数",
      "level": "2",
      "category": "较适宜",
      "text": "较适宜洗车,未来一天无雨,风力较小,擦洗一新的汽车至少能保持一天。"
    },
    {
      "date": "2021-02-06",
      "type": "1",
      "name": "运动指数",
      "level": "3",
      "category": "较不宜",
      "text": "天气较好,但考虑天气寒冷,推荐您进行室内运动,户外运动时请注意保暖并做好准备活动。"
    }
  ],
  "refer": {
    "sources": [
      "Weather China"
    ],
    "license": [
      "commercial license"
    ]
  }
}

我们通过get方法获取到的JOSN数据如上所示。只需要解析key为daily的值即可。其中daily的值是一个数组类型的数据,只需要取出我们需要的即可。

void DetaInfo::setInfo(QString info,int type)
{
    qDebug()<<"setINfo";
    QJsonParseError err;
    QJsonDocument json_recv = QJsonDocument::fromJson(info.toUtf8(), &err);//解析json对象
    if (!json_recv.isNull())
    {
        QJsonObject object = json_recv.object();
        if (object.contains("daily"))
        {
            QJsonValue value = object.value("daily");  // 获取指定 key 对应的 value
            if (value.isArray())
            {
                QJsonObject today_life = value.toArray().at(type).toObject();
                QString category = today_life.value("category").toString();
                QString text = today_life.value("text").toString();
                ui->label_category->setText(category);
                ui->label_text->setText(text);
            }
        }
    }
}

2.3 出行地图模块

2.3.1 申请百度地图API秘钥

进入百度地图官网
在这里插入图片描述
点击进入控制台。
在这里插入图片描述
这里需要登录百度账号,扫码或者输入用户名密码登录即可。
在这里插入图片描述
登录成功后点击应用管理下的我的应用。
在这里插入图片描述
点击创建应用
在这里插入图片描述
自定义应用名称后,应用类型选择浏览器端,在白名单输入框输入*。
在这里插入图片描述
这里就是我们需要的AK秘钥。
在这里插入图片描述

2.3.2 新建map.html

复制百度地图API源码。
在这里插入图片描述
新建map.html
在这里插入图片描述
将上面API的源码复制到吗map.html
在这里插入图片描述
将代码里面的红框里的您的秘钥替换刚才申请的AK即可。

2.3.3 在map.html文件添加方法

添加函数,通过QT程序传参来改变地图路线的起点,途经点,终点。
在这里插入图片描述
在这里插入图片描述

2.3.4 QT端实现

这里使用了webkit模块,在pro文件中添加

QT += webkit webkitwidgets 

具体代码如下

void MainWindow::mapinit()
{
    QWebSettings *settings = QWebSettings::globalSettings();
    settings->setAttribute(QWebSettings::PluginsEnabled, true);//允许插件
    settings->setAttribute(QWebSettings::JavascriptEnabled, true);//JavaScript
    settings->setAttribute(QWebSettings::DeveloperExtrasEnabled, true);//
    settings->setAttribute(QWebSettings::JavascriptCanOpenWindows, true);
    settings->setFontFamily(QWebSettings::FixedFont,"幼圆");
    ui->webView->setStyle(new CustomStyle());

    ui->webView->load(QUrl("qrc:/map.html"));
    connect(ui->webView->page()->mainFrame(), SIGNAL(javaScriptWindowObjectCleared()), this, SLOT(populateJavaScriptWindowObject()));

}
void MainWindow::populateJavaScriptWindowObject()
{
ui->webView->page()->mainFrame()->addToJavaScriptWindowObject("Mywebkit", this);
}
void MainWindow::onBtnCallJSClicked()
{
    QString strVal = QString("callfromqt(要传的参数);"));
    ui->webView->page()->mainFrame()->evaluateJavaScript(strVal);
}

2.4 智能闹钟模块

2.4.1 开启线程,检测时间

这里设置了四个闹钟,即在线程类里边设置了四个全局变量(闹钟时间)。当前时间戳等于设置的时间戳后设置蜂鸣器响起。

void TimeAlarmClock::run()
{
    while (1) {
        QDateTime time = QDateTime::currentDateTime();   //获取当前时间
        uint timeT = time.toTime_t();   //将当前时间转为时间戳
//        qDebug()<<timeT<<alarm_clocktime1;
        if(alarm_clocktime1==timeT){
            beep_on();
//            qDebug()<<"open";
        }else{

        }
        if(alarm_clocktime2==timeT){
            beep_on();
        }else{

        }
        if(alarm_clocktime3==timeT){
            beep_on();
        }else{

        }
        if(alarm_clocktime4==timeT){
            beep_on();
        }else{

        }
    }
}
void TimeAlarmClock::beep_on()
{
    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));
    close(fd);
}

2.4.2 提交闹钟时间

使用QdateTimeEdit设置闹钟时间,点击按钮后,将闹钟时间设置到线程中的全局变量中。
void MainWindow::on_time_btn1_clicked()
{
    if(ui->time_btn1->text()==" "){
        ui->time_btn1->setText("\n");
        timeAlarmClock.alarm_clocktime1 = ui->dateTimeEdit_1->dateTime().toTime_t();
    }else{
        ui->time_btn1->setText(" ");
        timeAlarmClock.alarm_clocktime1 = 0;
    }
}

总结

此项目可在ubuntu下运行,也可通过交叉编译在ARM平台上运行。源码请私信

基于QT完成的集计算器、电子相册、相机、记事本、多媒体音乐播放器、2048小游戏为一体的多媒体系统,有系统语音提示等。(遇到问题可以评论,注意注册和登录密码等数据默认保存在C盘下的Database文件夹下,使用前需先在C盘新建一个Database命名的文件夹) 项目使用软件: QtCreate5.12 项目使用模块: 数据库 GUI 界面设计 多媒体 摄像头 截图 文字转语音 动画显示 界面包含内容 一、 开机动画界面设计(自定义图片显示,图片放置欢迎字样) 二、 登录注册设计 注: 1. 注册 数据库+MD5 加密 让用户自由注册 a. 点击注册跳转到账户注册界面、点击注册中返回可以返回登录界面 b. 注册界面数据设置为不为空,用户名和用户账号不能有重复 c. 确认注册进行数据的插入,字段:用户名、性别、账户、密码 注册成功:消息盒子提示成功,清空输入框的内容 注册失败:消息盒子提示用户存在 d. 取消注册清空所有输入框内容 e. 限定输入框的输入长度,在对应的输入框设置提示内容 f. 如何确定选择的是男还是女提示:if 判断 ui-> QRadioButton->isChecked() 2. 登录 通过查阅数据库进行对比登录 成功登录:消息盒子提示成功,播报用户名,跳转到主界面 失败登录:消息盒子提示失败,清空账户和密码 3. 输入框设计为椭圆状,设置输入提示字符 4. 按钮设置点击和触摸时颜色切换效果或者使用图片作为背景 5. 界面背景设置为图片,标题设置为中文,图标设置为图片 三、 主界面设计 1. 按钮设置点击和触摸时颜色切换效果或者使用图片作为背景 2. 界面背景设置为图片,标题设置为中文,图标设置为图片 3. 时间显示控件自由选择 4. 进入其他界面操作时进行语音提示 5. 所有子界面能够返回主界面,在返回主界面时进行消息盒子提示 6. 设置所有子界面的标题文字
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值