轴承故障诊断系统"/>
基于深度学习的轴承故障诊断系统
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 = ® /* 读取的寄存器首地址 */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端的上位机,导致许多数据处理逻辑都还在下位机,这对下位机的性能是个挑战,最好是把所有数据处理逻辑全都移交给上位机,下位机只做数据读取和发送。
更多推荐
基于深度学习的轴承故障诊断系统
发布评论