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

news/2024/10/30 9:42:52/

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

  • 一、功能介绍
  • 二、实现步骤
    • 2.1 语音识别模块
      • 2.1.1 录音
      • 2.1.2 点击释放按钮槽函数
      • 2.1.3 申请百度AI开发平台语音识别应用
      • 2.1.4 HTTP请求类实现
      • 2.1.5 发送请求
      • 2.1.6 MainWindow类调用函数
      • 2.1.7 语音控制设备联动
    • 2.2 智慧生活模块
      • 2.2.1 创建API应用
      • 2.2.2 Get方法获取API的JSON数据
      • 2.2.3 解析JSON数据
    • 2.3 出行地图模块
      • 2.3.1 申请百度地图API秘钥
      • 2.3.2 新建map.html
      • 2.3.3 在map.html文件添加方法
      • 2.3.4 QT端实现
    • 2.4 智能闹钟模块
      • 2.4.1 开启线程,检测时间
      • 2.4.2 提交闹钟时间
  • 总结


一、功能介绍


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);
#elseconf.setProtocol(QSsl::TlsV1);
#endifrequest.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 TokenQString 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);#elseconf.setProtocol(QSsl::TlsV1);#endifquest.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 对应的 valueif (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);//JavaScriptsettings->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平台上运行。源码请私信


http://www.ppmy.cn/news/274074.html

相关文章

Java字节码编程:从入门到精通

前言 Java是一种高级语言&#xff0c;其代码在编译后被转换为Java字节码文件。Java字节码文件包含了Java平台上的指令集&#xff0c;这些指令可以在Java虚拟机&#xff08;JVM&#xff09;上执行。因此&#xff0c;通过编写和操作字节码&#xff0c;我们可以在运行时动态地修改…

编程谜题:提升你解决问题的训练场

摘要&#xff1a;有趣的编程谜题可以练习你解决问题的能力&#xff0c;快来挑战吧~~ 本文分享自华为云社区《代码的出现&#xff1a;用 Python 解决你的难题》&#xff0c;作者&#xff1a; Yuchuan 。 代码谜题的出现旨在让任何对解决问题感兴趣的人都能上手。您不需要具有深…

在myeclipse中部署jwebmail

1.部署tomcat 2.下载jwebmail源码&#xff08;jwebmail-1.0.1rc1-src.zip&#xff09;及jwebmail.war(在jwebmail-1.0.1rc1.zip中) 3.首先把jwebmail.war放在tomcat安装目录下的webapps里&#xff0c;然后在myeclipse中启动刚才部署的tomcat&#xff0c;不是myeclipse tomcat,这…

2018最后一战:25天编程PK赛!

作者 | Jane 出品 | AI科技大本营 2018 年余额已不足一个月&#xff01;是惊喜还是惊恐&#xff1f;这一年是充实有意义的一年&#xff0c;还是觉得略有失望&#xff1f;最后这三十天&#xff0c;我们还能学些什么&#xff1f;获得哪些成长&#xff1f;如何为 2019 年准备一个好…

日历 节假日计算_假日季节的7种科技降临日历

日历 节假日计算 假期即将到来&#xff0c;我们将其节日混乱的特殊品牌带入了我们的许多生活。 尽管一年中的这个时间可能有点忙&#xff0c;但许多技术人员仍然可以通过参加技术出现日历来磨练自己的技能。 那些在基督教传统中长大的人可能已经熟悉了降临日历的概念&#xff…

【Rust日报】 2019-02-05

本文转载自&#xff1a;https://rust.cc/article?id70499a9b-ce09-457a-a653-83e924a7e39d Rust日报祝大家新的一年诸事六六六 浏览器插件&#xff1a;用于自动定向Rust Book的过期链接 #trpl #rustbook 真是有心人 trpl-redirect 「Blog OS系列文章中文翻译」使用Rust创造操作…

前端技术搭建拼图小游戏(内含源码)

The sand accumulates to form a pagoda ✨ 写在前面✨ 功能介绍✨ 页面搭建✨ 样式设置✨ 逻辑部分 ✨ 写在前面 上周我们实通过前端基础实现了俄罗斯方块游戏&#xff0c;今天还是继续按照我们原定的节奏来带领大家完成一个拼图游戏&#xff0c;功能也比较简单简单&#xff…

__call__函数介绍

__call__ 方法的作用是使对象可以像函数一样被调用。通过在类中定义 __call__ 方法&#xff0c;你可以将对象实例视为可调用的函数&#xff0c;并在对象被调用时执行特定的操作。__call__ 方法是在你将一个对象作为函数调用时被调用的。在 Python 中&#xff0c;使用括号将一个…