一、功能介绍


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平台上运行。源码请私信

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐