基于深度学习的轴承故障诊断系统

编程入门 行业动态 更新时间:2024-10-07 22:22:25

基于深度学习的<a href=https://www.elefans.com/category/jswz/34/1762810.html style=轴承故障诊断系统"/>

基于深度学习的轴承故障诊断系统

0 引言

        一种基于深度学习的轴承故障诊断系统,对振动信号进行监测,实现从数据采集到特征识别的端到端智能故障诊断。

          基于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 客户端)

数据接收与发送/客户端

故障诊断(特征识别)

接受下位机的状态信息进行参数更新

实时傅里叶变换、时域特征参数计算并显示

其他(模型加载、数据保存、绘图、数据导入导出等)

离线数据分析系统(全局、局部数据处理等)

    界面示意图如下图等所示:

        上位机登录界面:

        上位机(在线监测界面与离线分析界面):

         下位机示意图:

         诊断结果显示:

 1 下位机数据采集

1.1 传感器驱动

        传感器采用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 = &reg;					/* 读取的寄存器首地址 */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;elseret=-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;elseret=-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);}elseadxl345_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;elsereturn -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);/*申请中断*/// 初始化gpiodev->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[] = {{ patible = "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");

1.2 应用层数据读取

         应用层采用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;
}

 1.2.1 打开文件

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));
}

1.2.2 数据读取

        数据读取利用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;}}
}

1.3 文件打开与关闭

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();}}
}

1.4 绘图

        绘图用的是customplot,qt的qchart在数据量大的情况下就会特别卡顿。

1.5 数据发送

        数据发送由主线层将套接字传递给子线程,由子线程发送。

2 上位机数据处理

2.1 离线数据系统

        主要用于数据分割,后期处理,包括自定义的傅里叶变换长度,局部、全局数据分析。

2.2 在线监测系统

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)

2.2.2 模型的部署

        模型的部署主要采用的是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、环境变量

2.2.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;}}// 发送给tabelwidgetQDateTime 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 ;
//        }}

2.2.4 傅里叶变换

        傅里叶变换采用的是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_read_count++;//发送给fft时域状态参数计算变换等子线程this->fft_and_data_deal_thread->working();ui->output_log_plainTextEdit->appendPlainText(QString("从网络中读取次数为(2048字节/次):%1......").arg(online_data_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());});
}

2.2.6 其他

        频域折线图、柱状图、监测数据显示(导入与导出)、数据保存、界面状态参数更新等。

3 总结

        该系统还存在许多不足之处与改进之处,最开始只想做个数据实时绘制界面,就没考windows端的上位机,导致许多数据处理逻辑都还在下位机,这对下位机的性能是个挑战,最好是把所有数据处理逻辑全都移交给上位机,下位机只做数据读取和发送。

更多推荐

基于深度学习的轴承故障诊断系统

本文发布于:2024-03-08 23:19:13,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1722803.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:轴承   故障诊断   深度   系统

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!