一、功能介绍
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平台上运行。源码请私信