赞
踩
一种基于深度学习的轴承故障诊断系统,对振动信号进行监测,实现从数据采集到特征识别的端到端智能故障诊断。
基于I.MX6ULL进行开发,完成IIC总线框架下的adxl345振动传感器驱动程序编写,其采用中断读取数据;下位机应用层采用QT多线程同步处理驱动层异步通知信号,实现不同频率下的数据采集,以及数据的实时绘制、网络传输等;开发windows客户端,在Qt中部署pytorch训练的神经网络模型,对来自下位机的数据进行特征识别,以及如频域实时绘制、监测信息显示、数据处理等功能。其主要功能如下表所示:
下位机(I.MX6ULL) | 数据采集控制(6.25hz-3200hz频率选择、量程设置、采样方向) | 开发环境及相应技术: ARM-linux-gcc 、arm-poky-linux-gnueabi-gcc Qt多线程、socket网络、界面、信号与槽、linux C、 Linux 驱动、C++、pytorch、libtorch |
时域数据实时绘制(≥800hz就会出现卡顿) | ||
网络数据传输与接收/服务器端 | ||
故障报警(蜂鸣器) | ||
上位机(windows 客户端) | 数据接收与发送/客户端 | |
故障诊断(特征识别) | ||
接受下位机的状态信息进行参数更新 | ||
实时傅里叶变换、时域特征参数计算并显示 | ||
其他(模型加载、数据保存、绘图、数据导入导出等) | ||
离线数据分析系统(全局、局部数据处理等) |
传感器采用adxl345数字振动传感器,量程最高为±16g,采样频率为3200hz,根据奈奎斯特采样定理,获取到的最大信息在1600hz以内。驱动程序主要采用platform框架、iic协议读取数据、中断实现高速和低速数据采集(中断下半部读取adxl345的fifo然后使用异步通知使应用层读取数据)、利用IIO框架对传感器参数进行设置。(最开始想利用IIO框架的触发器和缓冲区来实现数据采集,后面发现并不能达到一定频率下的实时数据,所以利用中断来读取数据,但IIO框架也没有删除,利用互斥锁机制来对数据读取和写入进行保护)。
- #include <linux/types.h>
- #include <linux/kernel.h>
- #include <linux/delay.h>
- #include <linux/ide.h>
- #include <linux/init.h>
- #include <linux/module.h>
- #include <linux/errno.h>
- #include <linux/gpio.h>
- #include <linux/cdev.h>
- #include <linux/device.h>
- #include <linux/of_gpio.h>
- #include <linux/semaphore.h>
- #include <linux/timer.h>
- #include <linux/i2c.h>
- #include <asm/mach/map.h>
- #include <asm/uaccess.h>
- #include <asm/io.h>
- #include <linux/of_irq.h>
- #include <linux/irq.h>
- #include <linux/regmap.h>
- #include <linux/iio/iio.h>
- #include <linux/iio/sysfs.h>
- #include <linux/iio/buffer.h>
- #include <linux/iio/trigger.h>
- #include <linux/iio/triggered_buffer.h>
- #include <linux/iio/trigger_consumer.h>
- #include <linux/types.h>
- #include <linux/unaligned/be_byteshift.h>
- #include "adxl345.h"
-
- #define DATA_SIZE 32
- #define ADXL345_CNT 1
- #define ADXL345_NAME "adxl345"
- #define ADXL345_INTERRUPT_NAME "ADXL345"
- #define ADXL345_CHAN(_type, _channel2, _index) \
- { \
- .type = _type, \
- .modified = 1, \
- .channel2 = _channel2, \
- .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_FREQUENCY), \
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
- BIT(IIO_CHAN_INFO_CALIBBIAS), \
- .scan_index = _index, \
- .scan_type = { \
- .sign = 's', \
- .realbits = 16, \
- .storagebits = 16, \
- .shift = 0, \
- .endianness = IIO_BE, \
- }, \
- }
- static short BUF_DATA_X_Y_Z[DATA_SIZE];
- unsigned char X_Y_Z_FLAG=2; /*采样方向,默认Z轴*/
- static int interrupt_conter=0; /*中断计数*/
- /* adxl345的扫描元素,三路振动数据*/
- enum inv_adxl345_scan {
- INV_ADXL345_SCAN_ACCL_X,
- INV_ADXL345_SCAN_ACCL_Y,
- INV_ADXL345_SCAN_ACCL_Z,
- };
- /*
- * 扫描掩码,两种情况,全启动0X111,或者都不启动0X0
- */
- static const unsigned long adxl345_scan_masks[]={
- BIT(INV_ADXL345_SCAN_ACCL_X)|
- BIT(INV_ADXL345_SCAN_ACCL_Y) |
- BIT(INV_ADXL345_SCAN_ACCL_Z) ,
- 0,
- };
- static const struct iio_chan_spec adxl345_channels[] =
- {
- ADXL345_CHAN(IIO_ACCEL,IIO_MOD_X,INV_ADXL345_SCAN_ACCL_X),
- ADXL345_CHAN(IIO_ACCEL,IIO_MOD_Y,INV_ADXL345_SCAN_ACCL_Y),
- ADXL345_CHAN(IIO_ACCEL,IIO_MOD_Z,INV_ADXL345_SCAN_ACCL_Z),
- };
- /*设备结构体*/
- struct adxl345_dev
- {
- dev_t devid; /* 设备号 */
- struct cdev cdev; /* cdev */
- struct class *class; /* 类 */
- struct device *device; /* 设备 */
- int major; /* 主设备号 */
- struct i2c_client *client; /* i2c 设备 */
- struct mutex lock;
- int irqnum;
- irqreturn_t (*handler)(int ,void *);
- struct work_struct adxl345_irq_work;
- struct device_node *nd;
- int gpio;
- struct fasync_struct *async_queue; /* 异步相关结构体 */
- };
- static struct adxl345_dev *myprivate_data; /* 私有数据 */
- /* 以正负2g量程为例,4/2^13=,扩大10^9倍,就是488280加速度计分辨率*/
- static const int accel_scale_adxl345[] =
- {488280,976560,1953120,3906250};
- /* 输出速率*/
- static const int accel_frequency_adxl345[]=
- {0.10,0.20,0.39,0.78,1.56,3.13,6.25,12.5,25,50,100,200,400,800,1600,3200};
- /*
- * @description: iic从adxl345读取寄存器的值
- * @para-dev adxl345设备 reg 要读取的寄存器 val 读取数据缓冲区 len 要读取的数据长度
- */
- static int adxl345_read_regs(struct adxl345_dev *dev, u8 reg, void *val, int len)
- {
- int ret=0;
- struct i2c_msg msg[2];
- struct i2c_client *client = dev->client;
- /* msg[0]为发送要读取的首地址 */
- msg[0].addr = client->addr; /* adxl345地址 */
- msg[0].flags = 0; /* 标记为发送数据 */
- msg[0].buf = ® /* 读取的寄存器首地址 */
- msg[0].len = 1; /* reg长度*/
- /* msg[1]读取数据 */
- msg[1].addr = client->addr; /* adxl345地址 */
- msg[1].flags = I2C_M_RD; /* 标记为读取数据*/
- msg[1].buf = val; /* 读取数据缓冲区 */
- msg[1].len = len; /* 要读取的数据长度*/
- ret=i2c_transfer(client->adapter, msg, 2);
- if(ret==2)
- ret=0;
- else
- ret=-EREMOTEIO;
- return ret;
- }
- /*
- * @description: iic从adxl345写值
- * @para-dev adxl345设备 reg 要写的寄存器 buf 写数据缓冲区 len 要写的数据长度
- */
- static int adxl345_write_regs(struct adxl345_dev *dev, u8 reg, u8 *buf, u8 len)
- {
- int ret;
- u8 b[256];
- struct i2c_msg msg;
- struct i2c_client *client =dev->client;
- b[0] = reg; /* 寄存器首地址 */
- memcpy(&b[1],buf,len); /* 将要写入的数据拷贝到数组b里面 */
- msg.addr = client->addr; /* adxl345地址 */
- msg.flags = 0; /* 标记为写数据 */
- msg.buf = b; /* 要写入的数据缓冲区 */ /*根据iic时序 adxl345*/
- msg.len = len + 1; /* 要写入的数据长度,加上寄存器地址的长度*/
- ret= i2c_transfer(client->adapter, &msg, 1);
- if(ret==1)
- ret=0;
- else
- ret=-EREMOTEIO;
- return ret;
- }
- /*
- * @description: 向adxl345指定寄存器读一个寄存器的值
- * @param -dev: adxl345设备
- * @param - reg: 要读的寄存器
- * @return
- */
- static u8 adxl345_read_reg(struct adxl345_dev *dev, u8 reg)
- {
- u8 data = 0;
- adxl345_read_regs(dev, reg, &data, 1);
- return data;
- }
- /*
- * @description : 向adxl345指定寄存器写入指定的值,写一个寄存器
- * @param - dev: adxl345设备
- * @param - reg: 要写的寄存器
- * @param - data: 要写入的值
- * @return : 无
- */
- static void adxl345_write_reg(struct adxl345_dev *dev, u8 reg, u8 data)
- {
- u8 buf = 0;
- buf = data;
- adxl345_write_regs(dev, reg, &buf, 1);
- }
- /*利用IIO框架读取adxl345寄存器的数据*/
- static int adxl345_read_channel_data(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- int *val)
- {
- int ind;
- __be16 d ;
- int axis=chan->channel2;
- struct adxl345_dev *dev=iio_priv(indio_dev);
- ind = (axis - IIO_MOD_X) * 2;
- adxl345_read_regs(dev,DATAX0+ind,(u8*)&d,2);// 大端:d[0]低地址 d[1]高地址
- *val=(short)d;
- return IIO_VAL_INT;
- }
- /*
- * @description : 读函数,当读取 sysfs 中的文件的时候最终此函数会执,
- 此函数里面会从传感器里面读取各种数据,然后上传给应用。
- * @param - indio_dev : iio_dev
- * @param - chan : 通道
- * @param - val : 读取的值,如果是小数值的话,val 是整数部分。
- * @param - val2 : 读取的值,如果是小数值的话,val2 是小数部分。
- * @param - mask : 掩码。
- */
- static int adxl345_read_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- int *val, int *val2, long mask)
- {
- struct adxl345_dev *dev=iio_priv(indio_dev);
- int ret = 0;
- unsigned char regdata = 0;
- switch (mask){
- case IIO_CHAN_INFO_RAW:
- mutex_lock(&dev->lock);
- ret = adxl345_read_channel_data(indio_dev, chan, val); /* 读取通道值 */
- mutex_unlock(&dev->lock);
- return ret;
- break;
- case IIO_CHAN_INFO_SCALE:
- mutex_lock(&dev->lock);
- regdata=(adxl345_read_reg(dev,DATA_FORMAT)&0x3);
- mutex_unlock(&dev->lock);
- *val=0;
- *val2=accel_scale_adxl345[regdata];
- return IIO_VAL_INT_PLUS_NANO;/*组合宏, 值为val+val2/1000000000 */
- break;
- case IIO_CHAN_INFO_CALIBBIAS: //偏移量
- switch (chan->channel2)
- {
- case IIO_MOD_X:
- *val=(int)adxl345_read_reg(dev,OFSX);
- return IIO_VAL_INT;
- break;
- case IIO_MOD_Y:
- *val=(int)adxl345_read_reg(dev,OFSY);
- return IIO_VAL_INT;
- break;
- case IIO_MOD_Z:
- *val=(int)adxl345_read_reg(dev,OFSZ);
- return IIO_VAL_INT;
- break;
- default:
- break;
- }
- break;
- case IIO_CHAN_INFO_FREQUENCY:
- *val=accel_frequency_adxl345[adxl345_read_reg(dev,BW_RATE)];
- return IIO_VAL_INT;
- break;
- default:
- break;
- }
- return 0;
- }
- /*
- * @description : 写函数,向 sysfs 中的文件写数据的时候最终此函数会
- 执行,一般在此函数里面设置传感器,比如量程等。
- * @param - indio_dev : iio_dev
- * @param - chan : 通道
- * @param - val : 读取的值,如果是小数值的话,val 是整数部分。
- * @param - val2 : 读取的值,如果是小数值的话,val2 是小数部分。
- * @param - mask : 掩码。
- */
- static int adxl345_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
- int val, int val2, long mask)
- {
- struct adxl345_dev *dev = iio_priv(indio_dev);
- int ret = 0;
- int i=0;
- switch (mask){
- case IIO_CHAN_INFO_SCALE:
- for(i=0;i<ARRAY_SIZE(accel_scale_adxl345);i++){
- if(accel_scale_adxl345[i]==val2){
- switch (i){
- case 0:
- adxl345_write_reg(dev,DATA_FORMAT ,0X08);
- break;
- case 1:
- adxl345_write_reg(dev,DATA_FORMAT ,0X09);
- break;
- case 2:
- adxl345_write_reg(dev,DATA_FORMAT ,0X0A);
- break;
- case 3:
- adxl345_write_reg(dev,DATA_FORMAT ,0X0B);
- break;
- default:
- break;
- }
- }
- }
- break;
- case IIO_CHAN_INFO_CALIBBIAS:
- break;
- case IIO_CHAN_INFO_FREQUENCY:
- for(i=0;i<ARRAY_SIZE(accel_frequency_adxl345);i++){
- if (accel_frequency_adxl345[i]==val){
- adxl345_write_reg(dev,BW_RATE,i);
- mdelay(50); // 等待更新
- }
- }
- break;
- default:
- ret = -EINVAL;
- break;
- }
- return ret;
- }
- /*
- * @description : 用户空间写数据格式,比如我们在用户空间操作 sysfs 来设置传
- * :感器的分辨率,如果分辨率带小数,那么这个小数传递到内核空间
- * : 应该扩大多少倍,此函数就是用来设置这个的。
- * @param - indio_dev : iio_dev
- * @param - chan : 通道
- * @param - mask : 掩码
- */
- static int adxl345_write_raw_get_fmt(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan, long mask)
- {
- switch (mask) {
- case IIO_CHAN_INFO_SCALE: /* 用户空间写的加速度计分辨率数据要乘以1000000000 */
- return IIO_VAL_INT_PLUS_NANO;
- break;
- case IIO_CHAN_INFO_CALIBBIAS:
- return IIO_VAL_INT_PLUS_NANO;
- // return IIO_VAL_INT; //这个宏会报错
- break;
- case IIO_CHAN_INFO_FREQUENCY:
- return IIO_VAL_INT_PLUS_NANO;
- // return IIO_VAL_INT;
- break;
- }
- return -EINVAL;
- }
- /*
- * iio_info结构体变量
- */
- static const struct iio_info adxl345_info =
- {
- .read_raw = adxl345_read_raw,
- .write_raw = adxl345_write_raw,
- .write_raw_get_fmt = &adxl345_write_raw_get_fmt,
- };
- /**
- * @description: adxl345寄存器初始化函数
- * @return {*}
- */
- static int adxl345reg_init(struct adxl345_dev *dev)
- {
- u8 value;
- /* 初始化adxl345*/
- adxl345_write_reg(dev,INTENABLE,0X00); //使能中断
- adxl345_write_reg(dev,DATA_FORMAT ,0X0B); //+-16g 中断高电平有效
- adxl345_write_reg(dev,BW_RATE,0XF); //输出速率
- adxl345_write_reg(dev,POWER_CTL,0X08);
- adxl345_write_reg(dev,OFSX,0X00);
- adxl345_write_reg(dev,OFSY,0X00);
- adxl345_write_reg(dev,OFSZ,0X00);
- // 中断配置
- adxl345_write_reg(dev,INT_MAP,0X02);
- adxl345_write_reg(dev,FIFO_CTL,0X9F);
- adxl345_write_reg(dev,INTENABLE,0X83); //使能中断
- value=(unsigned char)adxl345_read_reg(dev,DEVICEID);
- printk("device id=%#x\r\n",value);
- mdelay(5);
- return 0;
- }
- /*中断下半步处理函数*/
- void adxl345_irqwork_func(struct work_struct *work)
- {
- __be16 d ;
- int i;
- struct adxl345_dev *dev=container_of(work,struct adxl345_dev,adxl345_irq_work);
- if(adxl345_read_reg(dev,INT_SORCE)&0X02){
- mutex_lock(&dev->lock);
- if(X_Y_Z_FLAG==0){
- for(i=0;i<DATA_SIZE;i++) {
- adxl345_read_regs(dev,DATAX0,(u8*)&d,2);
- BUF_DATA_X_Y_Z[i]=(short)d;
- }
- }
- else if(X_Y_Z_FLAG==1){
- for(i=0;i<DATA_SIZE;i++) {
- adxl345_read_regs(dev,DATAY0,(u8*)&d,2);
- BUF_DATA_X_Y_Z[i]=(short)d;
- }
- }
- else if(X_Y_Z_FLAG==2){
- for(i=0;i<DATA_SIZE;i++) {
- adxl345_read_regs(dev,DATAZ0,(u8*)&d,2);
- BUF_DATA_X_Y_Z[i]=(short)d;
- }
- }
- mutex_unlock(&dev->lock);
- }
- if(interrupt_conter%100==0)
- printk("this is interuupt_%d\n",interrupt_conter);
- interrupt_conter++;
- kill_fasync(&dev->async_queue,SIGIO,POLL_IN);
- adxl345_read_reg(dev,INT_SORCE); // 清除中断标志位
- }
- /*adxl345中断使能与关闭*/
- static void interrupt_enbale(struct adxl345_dev *dev,bool this_flag)
- {
- mutex_lock(&dev->lock);
- if(this_flag){
- adxl345_write_reg(dev,INTENABLE,0X00);
- adxl345_write_reg(dev,INT_MAP,0X02);
- adxl345_write_reg(dev,FIFO_CTL,0X9F);
- adxl345_write_reg(dev,INTENABLE,0X83);
- }
- else
- adxl345_write_reg(dev,INTENABLE,0X00);
- mutex_unlock(&dev->lock);
- }
- /*使用工作队列处理下半部*/
- static irqreturn_t adxl345_handler(int irq, void *p)
- { struct iio_dev *in_dev=(struct iio_dev*) p;
- struct adxl345_dev *dev=iio_priv(in_dev);
- schedule_work(&dev->adxl345_irq_work);
- return IRQ_RETVAL(IRQ_HANDLED);
- }
- /*打开设备,然后打开中断*/
- static int adxl345_open(struct inode *inode, struct file *filp)
- {
- struct adxl345_dev *dev;
- filp->private_data = myprivate_data;
- dev =(struct adxl345_dev *)filp->private_data;
- interrupt_enbale(dev,true);
- return 0;
- }
- /*fasync函数,用于处理异步通知*/
- static int adxl345_fasync(int fd, struct file *filp, int on)
- {
- struct adxl345_dev *dev =(struct adxl345_dev *)filp->private_data;
- return fasync_helper(fd, filp, on, &dev->async_queue);
- }
- /*数据读取*/
- static ssize_t adxl345_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
- {
- int err=0;
- struct adxl345_dev *dev =(struct adxl345_dev *)filp->private_data;
- interrupt_enbale(dev,false);
- mutex_lock(&dev->lock);
- err=copy_to_user(buf,BUF_DATA_X_Y_Z,sizeof(BUF_DATA_X_Y_Z));
- mutex_unlock(&dev->lock);
- interrupt_enbale(dev,true);
- if(err==0)
- return DATA_SIZE;
- else
- return -1;
- }
- /*数据写入*/
- /*应该重新定义write函数的读写格式,buf[0]为寄存器的地址,buf[1]为要写入的值*/
- static ssize_t adxl345_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
- {
- int retvalue=0;
- unsigned char databuf[1];
- struct adxl345_dev *dev =(struct adxl345_dev *)filp->private_data;
- retvalue = copy_from_user(databuf, buf, cnt);
- if(retvalue < 0) {
- printk(KERN_ERR"kernel write failed!\n");
- return -EFAULT;
- }
- mutex_lock(&dev->lock);
- X_Y_Z_FLAG=databuf[0];
- mutex_unlock(&dev->lock);
- return retvalue;
- }
-
- static int adxl345_release(struct inode *inode, struct file *filp)
- {
- struct adxl345_dev *dev =(struct adxl345_dev *)filp->private_data;
- interrupt_enbale(dev,false);
- return adxl345_fasync(-1, filp, 0);
- }
- /* adxl345操作函数 */
- static const struct file_operations adxl345_ops =
- {
- .owner = THIS_MODULE,
- .open = adxl345_open,
- .read = adxl345_read,
- .release = adxl345_release,
- .write =adxl345_write,
- .fasync =adxl345_fasync,
- };
-
- static int adxl345_probe(struct i2c_client *client, const struct i2c_device_id *id)
- {
- int ret;
- struct adxl345_dev *dev;
- struct iio_dev *indio_dev;
- /* 1、申请iio_dev内存 */
- indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*dev));//使用 iio_device_alloc 函数来申请 iio_dev,并且一起申请了 adxl345_dev 的内存。
- if (!indio_dev)
- return -ENOMEM;
- /* 2、获取adxl345_dev结构体地址 */
- dev = iio_priv(indio_dev);
- myprivate_data=dev;
- dev->client = client;
- i2c_set_clientdata(client, indio_dev); /* 保存indio_dev */
- mutex_init(&dev->lock);
- /*注册设备*/
- /* 1、构建设备号 */
- dev->major=0;
- ret=alloc_chrdev_region(&dev->devid, 0, ADXL345_CNT, ADXL345_NAME);
- dev->major = MAJOR(dev->devid);
- if(ret<0) printk(KERN_ERR"adxl345 chardev err!\n");
- printk("adxl345 major=%d,minor=%d\n",dev->major,MINOR(dev->devid));
- /* 2、注册设备 */
- dev->cdev.owner=THIS_MODULE;
- cdev_init(&dev->cdev, &adxl345_ops);
- ret=cdev_add(&dev->cdev, dev->devid, ADXL345_CNT);
- if(ret<0) printk(KERN_ERR"adxl345 cedvadd err!\n");
- /* 3、创建类 */
- dev->class = class_create(THIS_MODULE, ADXL345_NAME);
- if (IS_ERR(dev->class)) {
- printk(KERN_ERR"adxl345 class creat failed!\n");
- return PTR_ERR(dev->class);
- }
- /* 4、创建设备 */
- dev->device = device_create(dev->class, NULL, dev->devid, NULL, ADXL345_NAME);
- if (IS_ERR(dev->device))
- return PTR_ERR(dev->device);
- /*申请中断*/
- // 初始化gpio
- dev->nd=of_find_node_by_path("/soc/aips-bus@02100000/i2c@021a0000/adxl345@53");
- if (dev->nd== NULL)
- printk(KERN_INFO"adxl345interrupt-gpios node not find!\n");
- dev->gpio=of_get_named_gpio(dev->nd,"adxl345_interrupt_gpios",0);
- if(dev->gpio<0)
- printk(KERN_INFO"can't get gpio\n");
- /* 初始化 IO,并且设置成中断模式 */
- gpio_request(dev->gpio,ADXL345_INTERRUPT_NAME);
- gpio_direction_input(dev->gpio);
- dev->irqnum=irq_of_parse_and_map(dev->nd,0);
- printk("adxl345_gpio=[%d],irq_num=[%d]\n",dev->gpio,dev->irqnum);
- dev->handler=adxl345_handler;
- ret=request_irq(dev->irqnum,dev->handler,IRQF_TRIGGER_RISING,ADXL345_INTERRUPT_NAME,indio_dev);
- if(ret < 0)
- printk(KERN_INFO"irq %d request failed!\n",dev->irqnum);
- /* 初始化 work */
- INIT_WORK(&dev->adxl345_irq_work,adxl345_irqwork_func);
- /* 3、iio_dev的其他成员变量 */
- indio_dev->dev.parent = &client->dev;
- indio_dev->info = &adxl345_info;
- indio_dev->name = ADXL345_NAME;
- indio_dev->modes = INDIO_DIRECT_MODE; /* 直接模式,提供sysfs接口 */
- indio_dev->channels = adxl345_channels;
- indio_dev->num_channels =ARRAY_SIZE(adxl345_channels);
- indio_dev->available_scan_masks = adxl345_scan_masks;
- /* 4、注册iio_dev */
- ret = iio_device_register(indio_dev);
- if (ret < 0) {
- dev_err(&client->dev, "iio_device_register failed\n");
- return ret;
- }
- adxl345reg_init(dev);
- interrupt_enbale(dev,false);
- return 0;
- }
- static int adxl345_remove(struct i2c_client *client)
- {
- struct iio_dev *indio_dev = i2c_get_clientdata(client);
- struct adxl345_dev *dev=iio_priv(indio_dev);
- /* 删除设备 */
- cdev_del(&dev->cdev);
- unregister_chrdev_region(dev->devid, ADXL345_CNT);
- /* 注销掉类和设备 */
- device_destroy(dev->class, dev->devid);
- class_destroy(dev->class);
- free_irq(dev->irqnum,indio_dev);
- /*注销IIO */
- iio_device_unregister(indio_dev);
- return 0;
- }
- /* 传统匹配方式ID列表 */
- static const struct i2c_device_id adxl345_id[] = {
- {"adxl345", 0},
- {}
- };
- /* 设备树匹配列表 */
- static const struct of_device_id adxl345_of_match[] = {
- { .compatible = "adxl345" },
- { /* Sentinel */ }
- };
- /* i2c驱动结构体 */
- static struct i2c_driver adxl345_driver = {
- .probe = adxl345_probe,
- .remove = adxl345_remove,
- .driver = {
- .owner = THIS_MODULE,
- .name = "adxl345_driver",
- .of_match_table = adxl345_of_match,
- },
- .id_table = adxl345_id,
- };
-
- static int __init adxl345_init(void)
- {
- int ret = 0;
- ret = i2c_add_driver(&adxl345_driver);
- return ret;
- }
- static void __exit adxl345_exit(void)
- {
- i2c_del_driver(&adxl345_driver);
- }
- module_init(adxl345_init);
- module_exit(adxl345_exit);
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("ZHW-SYSTEAM-FAULT");
- MODULE_INFO(intree, "Y");
-
应用层采用Qt 多线程同步处理异步通知信号,因为线程共享信号屏蔽字,主线程在创建子线程后,调用该函数,屏蔽信号,主线程只做界面绘制工作,空闲的子线程拿到信号就进行数据的读取。
- void pthread_myset()
- {
- int n;
- sigset_t set, oldset;
- sigemptyset(&set);
- sigemptyset(&oldset);
- sigaddset(&set, SIGIO);
- n= pthread_sigmask(SIG_BLOCK,&set, &oldset);
- if(n==0) qDebug()<<"pthread success! "<< endl;
- }
- void open_adxl345()
- {
- int flags = 0;
- char const *filename="/dev/adxl345";
- adxl345_fd=open(filename, O_RDWR);
- if(adxl345_fd < 0){
- return ;
- }
- adxl345_fd_flag=1; /*打开文件标志 */
- signal(SIGIO, sigio_signal_func);
- fcntl(adxl345_fd,__F_SETOWN, getpid()); /*将当前进程的进程号告内核*/
- flags = fcntl(adxl345_fd, F_GETFD); /*获取当前的进程状态*/
- fcntl(adxl345_fd, F_SETFL, flags | 00020000); /*设置进程启用异步通知功能*/
- // 设置输出轴
- write(adxl345_fd,direction_buf,sizeof(direction_buf));
- }
数据读取利用queqe容器:
- void sigio_signal_func(int signum)
- {
- int retvalue;
- retvalue= read(adxl345_fd,data,sizeof(data));
- if(retvalue<0)
- qDebug()<<"read's retvalue:"<<retvalue<<endl;
- for(int i=0;i<32;i++)
- queue_data.push(data[i]); //入队操作
- }
子线程轮询处理数据:
- void deal_data_class::working(bool flag){
- this->is_can_run=flag;
- if(!this->is_can_run)
- return ;
- short st_tamp;
- double de_tamp;
- while(this->is_can_run){
- if((queue_data.size()>32)){ // 默认有32个数据才开始出队
- st_tamp=queue_data.front();
- queue_data.pop();
- de_tamp=double(st_tamp*scale_select*9.8);
-
- x_all_count+=x_step; // 绘图横坐标累加
- _y.push_back(de_tamp);
- _x.push_back(double(x_all_count));
- data_net[read_count]=st_tamp;
-
- read_count_plot++;
- read_count++;
-
- if(read_count_plot%32==0){
- // 赋值给plot数组以绘图
- time_data_and_x_lock.lock();
- size_32_time_data.clear();
- size_32_time_x.clear();
- size_32_time_data=_y;
- size_32_time_x=_x;
- time_data_and_x_lock.unlock();
- emit this->data_can_plot();
- _x.clear();
- _y.clear();
- }
- if(read_count%1024==0){
- // qDebug()<< read_count_plot<<":readcount"<<endl;
- if(network_status==1){ //网络数据数组复制
- memcpy(send_data_net.data(),&data_net,sizeof(data_net));
- net_flag=true; // 数据准备完善标志
- }
- read_count=0;
- }
- }
- else {
- QThread::sleep(1);continue;
- }
- }
- }
- void read_data_class::working(bool flag)
- {
- if(flag){
- if(adxl345_fd_flag==-1 || adxl345_fd_flag==0){
- open_adxl345();
- }
- }
- else{
- // 关闭文件描述符与关闭异步通知
- if(adxl345_fd_flag==1){
- close_adxl345();
- }
- }
- }
绘图用的是customplot,qt的qchart在数据量大的情况下就会特别卡顿。
数据发送由主线层将套接字传递给子线程,由子线程发送。
主要用于数据分割,后期处理,包括自定义的傅里叶变换长度,局部、全局数据分析。
2.2.1 模型训练与模型加载
模型训练使用的pytorch框架,其数据预处理、模型构建,以及示范的LSTM1DCNN网络模型与作者该篇博客类似:基于LSTM的故障诊断_基于lstm的轴承故障检测_旧日之歌的博客-CSDN博客。
模型的推理从pth模型保存为pt模型:
- import torch.nn.functional as F
- from torchvision import transforms
- from matplotlib import pyplot as plt
- from numpy import dtype
- from 数据划分 import *
- import torchvision
- from torch import nn, Tensor
- from torch.nn import Conv1d,MaxPool1d,Flatten,Linear
- import torch
- device = torch.device("cpu")
- class mylstm(nn.Module):
- def __init__(self):
- super(mylstm, self).__init__()
- self.modle1 = nn.LSTM(input_size=1,hidden_size=16,num_layers=2, batch_first=True,dropout=0.2)
- self.modle2 = nn.Sequential (
- nn.Conv1d(512,128,1,1),
- nn.MaxPool1d(2,2),
- nn.Conv1d(128,32,4,4),
- nn.Flatten(),
- nn.Linear(64, 32),
- nn.Dropout(0.1),
- nn.ReLU(),
- nn.Linear(32,4 ),
- )
- def forward(self, x):
- h_0 = torch.randn(2,128,16)
- c_0 = torch.randn(2,128, 16)
- h_0 = h_0.to(device)
- c_0 = c_0.to(device)
- x = x.to(torch.float32)
- x,(h_0,c_0)=self.modle1(x,(h_0,c_0))
- x=self.modle2(x)
- return x
- from LSTM_1DCNN import *
- model=mylstm()
- state_dict=torch.load("libtorchtest_dict_practice_200.pth")
- model.load_state_dict(state_dict)
- model.to(device)
- model.eval()
- x=torch.randn(128,cut_long,1).to(device)
- traced_script_module = torch.jit.trace(model, x)
- traced_script_module.save('libtorchtest_dict_practice_200.pt')#保存模型位置
- print("save is ok\n")
- mymodel=torch.jit.load('libtorchtest_dict_practice_200.pt')
- output=mymodel(x)
- print(output)
模型的部署主要采用的是libtorch框架,如何将libtorch框架移植在QT里可搜索网上其他教程,但一般要注意以下几个点:
- 1、-INCLUDE:?searchsorted_cuda@native@at@@YA?AVTensor@2@AEBV32@0_N1@Z -INCLUDE:?warp_size@cuda@at@@YAHXZ 一定要在qt链接动态的时候加上这串字符
- 2、一定要注意libtorch和pytorch的版本问题,两者要完全对应,推理用的是cuda,那么部署能用cpu/cuda,如果推理用cpu,部署只能用cpu。
- 3、环境变量
- //model_load 模型加载或者使用 model_select 选择模型 use_gpu gpu使用选择
- void model_load::working(bool model_load,const std::string module_path_, int model_select,bool use_gpu)
- {
- if(model_load){ // 模型加载
- this->module_path=module_path_;
- if(model_select==1){ // 加载lstm——1dcnn模型
- if(this->model_count[model_select]==1){
- emit this->to_log_print("lstm1dcnn已加载");
- return;
- }
- if (torch::cuda::is_available())
- emit this->to_log_print("支持GPU");
- if (torch::cuda::is_available() && use_gpu){
- this->device_=torch::kCUDA;
- emit this->to_log_print("the module for lstm1dcnn use cuda...");
- }
- else{
- emit this->to_log_print("the module for lstm1dcnn use cpu...");
- this->device_ = torch::kCPU;
- }
- try {
- this->module_lstm1dcnn=torch::jit::load(this->module_path);
- emit this->to_log_print("the module for lstm1dcnn load successed...");
- }
- catch (const c10::Error& e) {
- std::cerr << "Error loading the model!\n";
- emit this->to_log_print("the module for lstm1dcnn load failed...");
- }
- this->module_lstm1dcnn.to(this->device_);
- this->module_lstm1dcnn.eval();
- this->model_count[1]=1; // 模型lstm1dcnn加载标志
- }
- }
-
- else{ // 模型识别
- QString module_name;
- if (torch::cuda::is_available() && use_gpu){
- this->device_=torch::kCUDA;
- emit this->to_log_print("the module is using cuda...");
- }
- else{
- emit this->to_log_print("the module is using cpu...");
- this->device_ = torch::kCPU;
- }
- at::TensorOptions ops=at::TensorOptions().dtype(at::kDouble).device(this->device_);
- if(model_select==1 && this->model_count[1]==1){
- this->module_lstm1dcnn.to(this->device_);
- online_data.model_diagnosis_lock.lockForRead();
- data_scaler(online_data.model_diagnosis); //数据归一化
- this->input_tensor=torch::from_blob(online_data.model_diagnosis.data()
- , {128,512,1},ops).to(this->device_).clone(); // 深度拷贝
- online_data.model_diagnosis_lock.unlock();
- this->output=this->module_lstm1dcnn.forward(std::vector<torch::jit::IValue>{this->input_tensor}).toTensor();
- emit this->to_log_print("the module of lstm1dcnn'identify is OK...");
- module_name="LSTM1DCNN";
- // 结果输出
- at::Tensor outputarg= this->output.argmax(1);
- int output_result[4];
- memset(output_result,0,sizeof(output_result));
- for(int i=0;i<128;i++)
- output_result[outputarg[i].item().toInt()]++;
- emit this->diagnosis_result(output_result[0],output_result[1],output_result[2],output_result[3]);
- int max_result=0;
- int max_index=0;
- // 记录最大结果以及结果汇总
- for(int i=0;i<4;i++){
- emit this->to_log_print(QString("当前%1诊断结果统计:%2").arg(i).arg(output_result[i]));
- if(output_result[i]>max_result){
- max_result=output_result[i];
- max_index=i;
- }
- }
- // 发送给tabelwidget
- QDateTime Timedata = QDateTime::currentDateTime(); // 获取当前时间
- QString str = Timedata.toString("yyyy-MM-dd-hh:mm:ss");
- emit this->to_tablewidget("NULL", // 序号
- str, // 时间
- module_name, //模型
- online_data.direction_sample, // 采样轴
- QString("%1[%2/128]").arg(label_fault[max_index]).arg(max_result),// 诊断结果
- QString("%1Hz").arg(online_data.hz_for_select)); //采样频率
- }
- }
- // else{
- // emit this->to_log_print("the module of lstm1dcnn unloaded......");
- // return ;
- // }
- // if(model_select==2 && this->model_count[1]==2){
- // // restnet模型识别
- // }
- // else{
- // return ;
- // }
- // if(model_select==3 && this->model_count[3]==3){
- // // cnn模型识别
- // }
- // else{
- // return ;
- // }
-
- }
傅里叶变换采用的是fftw库,号称是世界上最快的傅里叶变换。测试结果与matlab所输出的结果一致。
- void fft_function(vector<double>& after_fft_data,vector<double> before_fft_data,int fft_length)
- {
- fftw_complex *out;
- fftw_complex *in;
- fftw_plan p;
- in =(fftw_complex*)fftw_malloc(sizeof(fftw_complex) * fft_length);
- out=(fftw_complex*)fftw_malloc(sizeof(fftw_complex) * fft_length);
- p=fftw_plan_dft_1d(fft_length,in,out,FFTW_FORWARD,FFTW_ESTIMATE);
- for(int i=0;i<fft_length;i++){
- in[i][0]=before_fft_data.at(i);
- in[i][1]=0;
- }
- fftw_execute(p);
- after_fft_data.clear();
- after_fft_data.push_back(0); // 去除直流分量
- for(int j=1;j<fft_length/2+1;j++){
- after_fft_data.push_back(sqrt(out[j][0]*out[j][0]+out[j][1]*out[j][1])/fft_length*2);
- }
-
- fftw_destroy_plan(p);
- if(in!=NULL)
- {
- fftw_free(in);
- }
- if(out!=NULL){
- fftw_free(out);
- }
- }
2.2.5 网络数据读取
- void Online_data_analysis::network_set_init()
- {
- // 创建通信的套接字
- this->m_tcp =new QTcpSocket(this);
- connect(this->m_tcp,&QTcpSocket::readyRead,this,[=](){
- // qDebug()<<"it can ready"<<endl;
- QByteArray array = this->m_tcp->readAll();
- if(array.size()==(sizeof(short)*1024)){ // 读取的数据
- short* p=(short*)array.data();
- online_data.fft_and_datapara_lock.lockForWrite();
- for(int i=0;i<1024;i++)
- online_data.fft_and_datapara.push_back((online_data.scale_sample*(double)p[i]*9.8));
- online_data.fft_and_datapara_lock.unlock();
- online_data.net_read_count++;
- //发送给fft时域状态参数计算变换等子线程
- this->fft_and_data_deal_thread->working();
- ui->output_log_plainTextEdit->appendPlainText(QString("从网络中读取次数为(2048字节/次):%1......").arg(online_data.net_read_count));
- }
- if(array.size()==5){ // 读取的初始化信息
- unsigned char *p= (unsigned char*)array.data();
- // online_data_struct online_dat在此函数内部初始化
- // 每检测到初始化信息,所有数据都重新清除
- ui->output_log_plainTextEdit->appendPlainText("状态重置>>>......");
- this->parameter_show_of_come_net(p[0],p[1],p[2],p[3],p[4]);
- }
- });
- // 服务器端断开连接
- connect(this->m_tcp,&QTcpSocket::disconnected ,this,[=](){
- ui->server_ip_lineEdit->setText("服务器连接已断开....");
- ui->server_port_lineEdit->setText("服务器连接已断开....");
- this->information_print_net_flag(0,"服务器连接已断开....");
- this->m_tcp->close();
- ui->pushButton_net_start->setDisabled(false);
- ui->pushButton_net_end->setDisabled(true);
- qDebug()<<"the net is disconnected"<<endl;
- });
- // 检测连接
- connect(this->m_tcp,&QTcpSocket::connected ,this,[=](){
- // 显示服务器IP和端口
- ui->server_ip_lineEdit->setText(this->m_tcp->peerAddress().toString());
- ui->server_port_lineEdit->setText( QString::number(this->m_tcp->peerPort()));
- this->information_print_net_flag(1,"已连接服务器....");
- ui->pushButton_net_start->setDisabled(true);
- ui->pushButton_net_end->setDisabled(false);
- QString str="the connect is init for client ,if recieved,server's read is ok";
- this->m_tcp->write(str.toUtf8().data());
- });
- }
频域折线图、柱状图、监测数据显示(导入与导出)、数据保存、界面状态参数更新等。
该系统还存在许多不足之处与改进之处,最开始只想做个数据实时绘制界面,就没考windows端的上位机,导致许多数据处理逻辑都还在下位机,这对下位机的性能是个挑战,最好是把所有数据处理逻辑全都移交给上位机,下位机只做数据读取和发送。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。