物联网实战--入门篇之(十)安卓QT--后端开发

目录

一、项目配置

二、MQTT连接

三、数据解析

四、数据更新

五、数据发送

六、指令下发


一、项目配置

        按常规新建一个Quick空项目后,我们需要对项目内容稍微改造、规划下。

        首先根据我们的需要在.pro文件内添加必要的模块,其中quick就是qml了,core那是核心模块,必须的,network是网络模块,MQTT需要使用网络。

        然后就是为QML文件和图片文件各自建立一个资源文件,这样编译的时候会把这些资源带上,否则的话打包发布的时候你需要把QML文件和图片文件放到指定文件夹内,这在安卓里就不方便了。

        最后就是如何加载前端QML文件的问题了,如下图所示,后端通过QML加载引擎QQmlApplicationEngine把QML主文件加载进来就能显示界面了,下一行是前端到后端的设置接口名称,这样在QML文件里就可以用theMainInterface这个名称引用后端的函数了,完成开关、调速等动作。

        在新工程默认的文件里,加载前端文件这一步是在main.c文件里完成的,我们这里为了整体前后端的交互,特意建了一个MainInterface的类,在主函数中直接定义这个类的变量即可,这样整个工程结构比较清晰。

二、MQTT连接

        QT标准库里没有mqtt,需要自己单独下载GitCode - 开发者的代码家园,我的项目里已经集成了,只要右键添加进来即可,这里主要是要写个自己的MQTT管理类,把状态保活、话题订阅等任务内部处理掉,就是BaseMqtt类了,里面还有个内容是域名解析需要处理。

#include "BaseMqtt.h"BaseMqtt::BaseMqtt(QObject *parent) : QObject(parent)
{isConnected=false;checkTimer = new QTimer(this);checkTimer->setInterval(1*100);//心跳检测checkTimer->start();m_heartTime=0;connect(checkTimer, SIGNAL(timeout()),this,SLOT(slotCheckTimeout()));m_mqttClient=nullptr;m_hostAddress="";
}
BaseMqtt::~BaseMqtt(void)
{qDebug()<<"~BaseMqtt, hostName="<<m_connectParam.hostName;
}void BaseMqtt::slotLookUpHost(QHostInfo info)
{if(info.error() == QHostInfo::NoError){foreach (QHostAddress address, info.addresses()){m_hostAddress=address.toString();qDebug()<<m_connectParam.hostName<<" mqtt found ip= "<<m_hostAddress;break;}}
}void BaseMqtt::slotCheckTimeout(void)
{m_heartTime++;if(m_hostAddress.isEmpty()){if(m_heartTime%10==0){qDebug("mqtt start dns!");QHostInfo::lookupHost(m_connectParam.hostName, this, SLOT(slotLookUpHost(QHostInfo)));}return;}if(m_mqttClient==nullptr){if(!m_connectParam.certPath.isEmpty())//使用SSL连接{QFile file;QByteArray client_key_text, client_crt_text, root_crt_text;QString certPath=m_connectParam.certPath;QSslSocket ssl_socket;file.setFileName(certPath+"/client.key");file.open(QIODevice::ReadOnly | QIODevice::Text);client_key_text = file.readAll();file.close();file.setFileName(certPath+"/client.crt");file.open(QIODevice::ReadOnly | QIODevice::Text);client_crt_text = file.readAll();file.close();file.setFileName(certPath+"/rootCA.crt");file.open(QIODevice::ReadOnly | QIODevice::Text);root_crt_text = file.readAll();file.close();QSslKey client_key(client_key_text, QSsl::Rsa);QSslCertificate client_crt(client_crt_text);            ssl_socket.setPrivateKey(client_key);ssl_socket.setLocalCertificate(client_crt);QSslConfiguration ssl_config=QSslConfiguration::defaultConfiguration();ssl_config.setCaCertificates(QSslCertificate::fromData(root_crt_text)); //QSslCertificate::fromPath(certPath+"/rootCA.crt"); ssl_config.setPrivateKey(ssl_socket.privateKey());ssl_config.setLocalCertificate(ssl_socket.localCertificate());ssl_config.setPeerVerifyMode(QSslSocket::QueryPeer);ssl_config.setPeerVerifyDepth(10);ssl_config.setProtocol(QSsl::TlsV1_2);m_mqttClient = new QMQTT::Client(m_hostAddress, m_connectParam.hostPort, ssl_config, true, this);
//            qDebug()<<"\n###SSL PrivateKey="<<ssl_config.privateKey();
//            qDebug()<<"###SSL Certificate="<<ssl_config.localCertificate();
//            qDebug()<<"###SSL rootCA="<<ssl_config.caCertificates();
//            qDebug()<<"hostName="<<m_hostAddress<<", hostPort="<<m_connectParam.hostPort;
//            qDebug()<<"userName="<<m_connectParam.userName<<", passWord="<<m_connectParam.passWord<<", clientID="<<m_connectParam.clientID;}else//普通连接{QHostAddress host(m_hostAddress);m_mqttClient = new QMQTT::Client(host, m_connectParam.hostPort, this);}signalSlotInit();m_mqttClient->setUsername(m_connectParam.userName);m_mqttClient->setPassword(m_connectParam.passWord);m_mqttClient->setClientId(m_connectParam.clientID);m_mqttClient->setAutoReconnect(true);m_mqttClient->setCleanSession(true);m_mqttClient->setKeepAlive(30);m_mqttClient->connectToHost();}else if(this->mqttIsConnected()){for(auto iter : m_subTopicList)//订阅话题{if(iter.isSubed==false){mqttSubscribeMessage(iter.subTopic, iter.qos);break;}}if(m_heartTime%200==0)//保持连接{
//            qDebug()<<"mqtt send keep";mqttPublishMessage("dev/sub/data1", QByteArray("heart"));}}else{
//        qDebug()<<"BaseMqtt no connected!";}
}void BaseMqtt::mqttConnect(QString hostName, u16 hostPort, QString userName, QByteArray passWord, QString clientID, QString certPath)
{clientID=clientID+QString("_")+takeHostMac().remove(":");m_connectParam.hostName=hostName;m_connectParam.hostPort=hostPort;m_connectParam.userName=userName;m_connectParam.passWord=passWord;m_connectParam.clientID=clientID;m_connectParam.certPath=certPath;u8 *pData=(u8*)hostName.toUtf8().data();if(pData[0]>='0' && pData[0]<='9')//判断是否为域名,使用域名时 域名的第一个字符不能是数字{m_hostAddress=hostName;}
}bool BaseMqtt::mqttIsConnected(void)
{
//    if(m_mqttClient!=nullptr)
//        return m_mqttClient->isConnectedToHost();return isConnected;
}void BaseMqtt::mqttPublishMessage(QString topicFilter, QByteArray msgBa)
{if(m_mqttClient==nullptr || m_mqttClient->isConnectedToHost()==false)return;QMQTT::Message message;message.setTopic(topicFilter);message.setPayload(msgBa);m_mqttClient->publish(message);
}void BaseMqtt::mqttPingresp(void)
{
//    m_mqttClient->pingresp();
}void BaseMqtt::mqttSubscribeMessage(QString topicFilter, quint8 qos)
{if(m_mqttClient==nullptr)return;m_mqttClient->subscribe(topicFilter, qos);
}void BaseMqtt::mqttUnsubscribeMessage(QString topicFilter)
{if(m_mqttClient==nullptr)return;m_mqttClient->unsubscribe(topicFilter);
}void BaseMqtt::signalSlotInit(void)
{connect(m_mqttClient, SIGNAL(connected()), this, SLOT(slotMqttConnected()));connect(m_mqttClient, SIGNAL(disconnected()), this, SLOT(slotMqttDisconnected()));connect(m_mqttClient, SIGNAL(error(QMQTT::ClientError)), this, SLOT(slotMqttError(QMQTT::ClientError)));connect(m_mqttClient, SIGNAL(pingresp()), this, SLOT(slotMqttPingresp()));connect(m_mqttClient, SIGNAL(published(QMQTT::Message,quint16)), this, SLOT(slotMqttPuslished(QMQTT::Message,quint16)));connect(m_mqttClient, SIGNAL(received(QMQTT::Message)), this, SLOT(slotMqttReceived(QMQTT::Message)));connect(m_mqttClient, SIGNAL(subscribed(QString,quint8)), this, SLOT(slotMqttSubscribed(QString,quint8)));connect(m_mqttClient, SIGNAL(unsubscribed(QString)), this, SLOT(slotMqttUnsubscribed(QString)));
}void BaseMqtt::mqttAddTopic(QString topic, u8 qos)
{for(auto iter : m_subTopicList){if(iter.subTopic==topic){qDebug()<<"have the same topic="<<topic;return;}}SubTopicStruct tag_subTopic;tag_subTopic.subTopic=topic;tag_subTopic.isSubed=false;tag_subTopic.qos=qos;m_subTopicList.append(tag_subTopic);qDebug()<<"mqttAddTopic="<<topic;
}void BaseMqtt::mqttDelTopic(QString topic)
{int i=0;for(auto iter : m_subTopicList){if(topic==iter.subTopic){if(iter.isSubed==true){this->mqttUnsubscribeMessage(topic);}m_subTopicList.removeAt(i);qDebug()<<"remove topic="<<topic;return;}i++;}
}void BaseMqtt::slotMqttConnected(void)
{isConnected=true;emit sigMqttConnected();qDebug()<<"clientId="<<m_mqttClient->clientId()<<"connected";
}void BaseMqtt::slotMqttDisconnected(void)
{isConnected=false;int nSize=m_subTopicList.size();for(int i=0; i<nSize; i++){m_subTopicList[i].isSubed=false;}qDebug()<<"clientId="<<m_mqttClient->clientId()<<"disconnected";emit sigMqttDisconnected();
}void BaseMqtt::slotMqttError(const QMQTT::ClientError error)
{qDebug()<<"clientId="<<m_mqttClient->clientId()<<"slotMqttError="<<error;
}void BaseMqtt::slotMqttPingresp(void)
{
//    qDebug("BaseMqtt::slotMqttPingresp");
}void BaseMqtt::slotMqttPuslished(const QMQTT::Message &message, quint16 msgid)
{msgid=message.id();msgid=msgid;
//    qDebug("BaseMqtt::slotMqttPuslished, msgid=%d ", msgid);
//    qDebug()<<"msg="<<message.payload().toHex();
}void BaseMqtt::slotMqttReceived(const QMQTT::Message &message)
{emit sigtMqttReceived(message);}void BaseMqtt::slotMqttSubscribed(const QString &topic, const quint8 qos)
{int i=0;
//    qDebug()<<"slotMqttSubscribed, topic="<<topic<<", qos="<<qos;for(auto iter : m_subTopicList){if(iter.subTopic==topic){m_subTopicList[i].isSubed=true;break;}i++;}emit sigMqttSubscribed(topic, qos);
}void BaseMqtt::slotMqttUnsubscribed(const QString &topic)
{int i=0;for(auto iter : m_subTopicList){if(iter.subTopic==topic){m_subTopicList[i].isSubed=false;break;}i++;}emit sigMqttUnsubscribed(topic);
}QString BaseMqtt::takeHostMac(void)
{DrvCommon drv_com;    return drv_com.takeRandMac();
}

        对于应用层就很简单了,就是创建对象、连接和添加订阅话题即可。其中有个槽函数slotMqttReceived就是用来接收设备发来的数据的。

三、数据解析

        数据解析跟嵌入式端是差不多的,下面是代码,像剥洋葱一样,查找帧头、校验、根据命令类型执行解析。


void MainInterface::slotMqttReceived(const QMQTT::Message &message)
{QByteArray msg_ba=message.payload();u8 *pData=(u8*)msg_ba.data();
//    qDebug()<<"mqtt recv="<<msg_ba.toHex(':');u8 head[2]={0xAA, 0x55};pData=drv_com.memstr(pData, msg_ba.size(), head, 2);if(pData!=nullptr){u16 total_len=pData[2]<<8 | pData[3];u16 crcValue=pData[total_len]<<8 | pData[total_len+1];if(crcValue==drv_com.crc16(pData, total_len)){pData+=4;u32 device_sn=pData[0]<<24|pData[1]<<16|pData[2]<<8|pData[3];pData+=4;m_currDevSn=device_sn;u8 cmd_type=pData[0];pData+=1;qDebug("recv device_sn=%08X, cmd_type=%d", device_sn, cmd_type);m_keepTime=m_secCounts;switch(cmd_type){case AIR_CMD_HEART:{break;}case AIR_CMD_DATA:{int temp=pData[0]<<8|pData[1];//温度 原始数据float temp_f=(temp-1000)/10.f;//温度浮点数据pData+=2;int humi=pData[0]<<8|pData[1];float humi_f=humi/10.f;pData+=2;int pm25=pData[0]<<8|pData[1];pData+=2;u8 speed=pData[0];pData+=1;u8 state=pData[0];pData+=1;qDebug("temp_f=%.1f C, humi_f=%.1f%%, pm25=%d ug/m3, speed=%d, state=%d", temp_f,  humi_f, pm25, speed,state);QString dev_sn_str=QString::asprintf("%08X", device_sn);QString temp_str=QString::asprintf("%.0f", temp_f);QString humi_str=QString::asprintf("%.0f", humi_f);QString pm25_str=QString::asprintf("%03d", pm25);int alarm_level=0;if(pm25<20)alarm_level=0;else if(pm25<30)alarm_level=1;else alarm_level=2;emit siqUpdateSensorValues(dev_sn_str, temp_str, humi_str, pm25_str);emit siqUpdateAlarmLevel(alarm_level);emit siqUpdateSwitchState(state);break;}case AIR_CMD_SET_SPEED:{break;}}}}
}

        这里我们需要把数据送到前端去显示,所以定义了几个信号内容,如下图所示,从上到下依次是状态数据,污染等级和开关状态,这些数据都是设备端发送上来的,通过后端处理加工后发到前端显示。这里对于污染等级的数值可以自定义,我这边为了方便测试是20、30两个分界线,小米的净化器应该是30和80两条线。

 四、数据更新

        对于前端显示,这里先提一下如何接收后端发来的数据的,如下图所示。以Connections对象为基础,设置它的属性target为theMainInterface,这个其实就是我们加载QML文件时候设置的前后端交互名称,这里用上了,相当于是信号发射者;信号接收器就是C++文件里定义的信号函数前面加个on,然后首字母改成大写就可以了,这里是s改为S,这样这里就能接收到后端发送过来的传感器数据了,很简单吧。至于如何显示,放到前端部分再讲解。

五、数据发送

        数据发送底层就是跟嵌入式端一样,组合后通过mqtt发送出去就行了,有点区别就是这时候要带上目标的序列号dev_sn,这样带有序列号的话题设备端才能收到数据。


void MainInterface::airSendLevel(u32 dev_sn, int cmd_type, u8 *cmd_buff, u16 cmd_len)
{u8 make_buff[500]={0};u16 make_len=0;make_buff[make_len++]=0xAA;make_buff[make_len++]=0x55;make_buff[make_len++]=0;make_buff[make_len++]=0;make_buff[make_len++]=dev_sn>>24;make_buff[make_len++]=dev_sn>>16;make_buff[make_len++]=dev_sn>>8;make_buff[make_len++]=dev_sn;make_buff[make_len++]=cmd_type;memcpy(&make_buff[make_len], cmd_buff, cmd_len);make_len+=cmd_len;make_buff[2]=make_len>>8;make_buff[3]=make_len;u16 crcValue=drv_com.crc16(make_buff, make_len);make_buff[make_len++]=crcValue>>8;make_buff[make_len++]=crcValue;QByteArray msg_ba((char*)make_buff, make_len);QString topic=QString::asprintf("air/dev/sub/%08X", dev_sn);if(m_mqttClient){m_mqttClient->mqttPublishMessage(topic, msg_ba);}
}
六、指令下发

        在应用层,主要就是开关和调速两个功能,这里要看下这两个函数的定义,比较特别,在头文件定义的函数名称前多了Q_INVOKABLE,添加了这个关键字后,这个函数就可以在QML文件里直接调用了,是不是很方便。置于函数的内容应该比较简单了,就是组合下报文给底层函数发送出去就行了,这里的命令定义跟嵌入式端是一样的,两边有改动的话一定要及时同步,不然就乱了。对于调速设置,函数的输入是0~1的浮点数,当小于0.1的时候会强制等于0.1,这样调速最低时才不会停掉,只是慢速转动。

        对于这里的后端,总的来说没什么难点,主要还是做好前后端数据交互的准备。这里有个细节提一下,细心的也会发现,有个定时器槽函数slotCheckTimeout(),虽然这里没什么用,但是在其他有复杂任务或者多线程的时候很有用,它可以定时执行任务,相当于局部的main函数了,以后有机会再慢慢学习。

本项目的交流QQ群:701889554

   写于2024-4-2

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/788652.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Windows下编译TinyXML(XML文件解析)

作者&#xff1a;翟天保Steven 版权声明&#xff1a;著作权归作者所有&#xff0c;商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处 TinyXML是什么&#xff1f; TinyXML是一个轻量级的C XML解析器&#xff0c;它提供了一种简单的方法来解析和操作XML文档。TinyXM…

【六 (2)机器学习-机器学习建模步骤/kaggle房价回归实战】

一、确定问题和目标&#xff1a; 1、业务需求分析&#xff1a; 与业务团队或相关利益方进行深入沟通&#xff0c;了解他们的需求和期望。 分析业务流程&#xff0c;找出可能的瓶颈、机会或挑战。 思考机器学习如何帮助解决这些问题或实现业务目标。 2、问题定义&#xff1a;…

docker搭建CI/CD环境配置过程中的常见问题

一、Jenkins 1、pull镜像问题 docker pull jenkins/jenkins:lts Using default tag: latest Trying to pull repository docker.io/library/centos ... Get https://registry-1.docker.io/v2/library/centos/manifests/latest: Get https://auth.docker.io/token?scoperepo…

激发创新活力:算力券与模型券,科技企业的新动力

激发创新活力&#xff1a;算力券与模型券&#xff0c;科技企业的新动力 在数字化转型的大潮中&#xff0c;科技创新已成为推动企业发展的核心动力。为了进一步激发企业的创新活力&#xff0c;政府和相关机构开始探索一种新的激励机制——发放“算力券”和“模型券”。这些创新…

golang语言系列:Web框架+路由 之 Echo

云原生学习路线导航页&#xff08;持续更新中&#xff09; 本文是golang语言系列文章&#xff0c;本篇主要对 Echo 框架 的基本使用方法 进行学习 1.Echo是什么 Go 有众多Web框架&#xff0c;Echo 是其中的一个&#xff0c;官网介绍Echo有高性能、可扩展性、极简的特点。使用E…

非关系型数据库-----------探索 Redis高可用 与持久化

目录 一、Redis 高可用 1.1什么是高可用 1.2Redis的高可用技术 二、 Redis 持久化 2.1持久化的功能 2.2Redis 提供两种方式进行持久化 三、Redis 持久化之----------RDB 3.1触发条件 3.1.1手动触发 3.1.2自动触发 3.1.3其他自动触发机制 3.2执行流程 3.3启动时加载…

将excel数据拆分成多个excel文件

一、背景&#xff1a; 平时在日常工作中&#xff0c;经常需要将excel的文件数据进行拆分&#xff0c;拆分成多个excel文件&#xff0c;然而用人工来处理这个既耗时&#xff0c;又费精力&#xff0c;眼睛会疲劳&#xff0c;时间长了操作上会出现失误&#xff0c;导致数据拆分错…

Redis缓存设计与性能优化【缓存和数据库不一致问题,解决方案:1.加过期时间这样可以一段时间后自动刷新 2.分布式的读写锁】

Redis缓存设计与性能优化 缓存与数据库双写不一致 缓存与数据库双写不一致 在大并发下&#xff0c;同时操作数据库与缓存会存在数据不一致性问题 1、双写不一致情况 2、读写并发不一致 解决方案&#xff1a; 1、对于并发几率很小的数据(如个人维度的订单数据、用户数据等)&a…

Linux系统---进程间通信与管道入门

顾得泉&#xff1a;个人主页 个人专栏&#xff1a;《Linux操作系统》 《C从入门到精通》 《LeedCode刷题》 键盘敲烂&#xff0c;年薪百万&#xff01; 一、进程间通信 1.进程间通信的目的 1.数据传输&#xff1a;一个进程需要把他的数据传给另外一个进程。 2.资源共享&…

SAR教程系列7——在cadence中用Spectrum工具FFT仿真ADC的ENOB、SNR等动态性能指标

首先在仿真之前&#xff0c;你得有一个ADC。然后是思考如何仿真的问题&#xff0c;如何加激励&#xff0c;如何使用相关工具查看仿真结果。假定你有一个可以仿真的ADC&#xff0c;大致经过下列步骤可以得到ADC的相关动态性能指标。 第一步&#xff1a;在ADC后面接一个理想的DA…

idea快速找到maven中冲突的依赖,解决依赖冲突

红色实线&#xff1a;冲突&#xff0c;红色虚线&#xff1a;依赖于同一个包的多版本 选择包&#xff0c;右键Excluede&#xff0c;排除 问题原因: 一个项目中需要jar包A和jar包B,而jar包A和jar包B都需要依赖jar包C,但A需要1.2.16版本的C,B需要1.2.17版本的C,这时候就可能会产…

基于MPPT的风力机发电系统simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 4.1风能与风力发电机模型 4.2风力机功率特性与最大功率点 4.3 MPPT 5.完整工程文件 1.课题概述 基于MPPT的风力机发电系统simulink建模与仿真。MPPT使用S函数编写实现。基于最大功率点跟踪&#xff08…

Python快速入门系列-8(Python数据分析与可视化)

第八章:Python数据分析与可视化 8.1 数据处理与清洗8.1.1 数据加载与查看8.1.2 数据清洗与处理8.1.3 数据转换与整理8.2 数据可视化工具介绍8.2.1 Matplotlib8.2.2 Seaborn8.2.3 Plotly8.3 数据挖掘与机器学习简介8.3.1 Scikit-learn8.3.2 TensorFlow总结在本章中,我们将探讨…

构建第一个ArkTS应用(FA模型)

创建ArkTS工程 若首次打开DevEco Studio&#xff0c;请点击Create Project创建工程。如果已经打开了一个工程&#xff0c;请在菜单栏选择File > New > Create Project来创建一个新工程。选择Application应用开发&#xff08;本文以应用开发为例&#xff0c;Atomic Servi…

权限管理系统【BUG】

1.1.简介 忙里偷闲&#xff0c;学点Java知识。越发觉得世界语言千千万&#xff0c;最核心的还是思想&#xff0c;一味死记硬背只会让人觉得很死板不灵活&#xff0c;嗯~要灵活~ 1.2.问题 permission.js:37 [Vue warn]: Error in render: "TypeError: Cannot read prope…

Nginx反向代理和缓存

一、Nginx反向代理 1.调度和代理的区别&#xff1a; 1.调度基于内核层面&#xff0c;代理基于应用层面 2.代理必须实现一手托两家 3.调度不需要监听任何端口&#xff0c;不需要工作任何应用程序&#xff0c;代理需要工作和上游服务器一模一样的进程 4.调度没有并发上限&am…

django-haystack,具有全文搜索功能的 Python 库!

目录 前言 安装与配置 全文搜索基础 搜索引擎配置 索引配置 搜索视图与模板 过滤器与排序 自定义搜索逻辑 应用场景 1. 电子商务网站的商品搜索 2. 新闻网站的文章搜索 3. 社交网站的用户搜索 4.企业内部系统的文档搜索 总结 前言 大家好&#xff0c;今天为大家分享…

【项目新功能开发篇】需求分析和开发设计

作者介绍&#xff1a;本人笔名姑苏老陈&#xff0c;从事JAVA开发工作十多年了&#xff0c;带过大学刚毕业的实习生&#xff0c;也带过技术团队。最近有个朋友的表弟&#xff0c;马上要大学毕业了&#xff0c;想从事JAVA开发工作&#xff0c;但不知道从何处入手。于是&#xff0…

vue 加 websocket 聊天

<template><div style="height: 100%; width: 100%; background-color: #fff"><div class="wrap"><!-- 头部 --><div class="titleBox"><imgsrc="@/assets/image/avatar.png"style="argin: 10p…

分类预测 | Matlab实现TCN-BiGRU-Mutilhead-Attention时间卷积双向门控循环单元多头注意力机制多特征分类预测/故障识别

分类预测 | Matlab实现TCN-BiGRU-Mutilhead-Attention时间卷积双向门控循环单元多头注意力机制多特征分类预测/故障识别 目录 分类预测 | Matlab实现TCN-BiGRU-Mutilhead-Attention时间卷积双向门控循环单元多头注意力机制多特征分类预测/故障识别分类效果基本介绍模型描述程序…