当前位置:   article > 正文

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

基于深度学习的轴承故障检测系统

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框架也没有删除,利用互斥锁机制来对数据读取和写入进行保护)。

  1. #include <linux/types.h>
  2. #include <linux/kernel.h>
  3. #include <linux/delay.h>
  4. #include <linux/ide.h>
  5. #include <linux/init.h>
  6. #include <linux/module.h>
  7. #include <linux/errno.h>
  8. #include <linux/gpio.h>
  9. #include <linux/cdev.h>
  10. #include <linux/device.h>
  11. #include <linux/of_gpio.h>
  12. #include <linux/semaphore.h>
  13. #include <linux/timer.h>
  14. #include <linux/i2c.h>
  15. #include <asm/mach/map.h>
  16. #include <asm/uaccess.h>
  17. #include <asm/io.h>
  18. #include <linux/of_irq.h>
  19. #include <linux/irq.h>
  20. #include <linux/regmap.h>
  21. #include <linux/iio/iio.h>
  22. #include <linux/iio/sysfs.h>
  23. #include <linux/iio/buffer.h>
  24. #include <linux/iio/trigger.h>
  25. #include <linux/iio/triggered_buffer.h>
  26. #include <linux/iio/trigger_consumer.h>
  27. #include <linux/types.h>
  28. #include <linux/unaligned/be_byteshift.h>
  29. #include "adxl345.h"
  30. #define DATA_SIZE 32
  31. #define ADXL345_CNT 1
  32. #define ADXL345_NAME "adxl345"
  33. #define ADXL345_INTERRUPT_NAME "ADXL345"
  34. #define ADXL345_CHAN(_type, _channel2, _index) \
  35. { \
  36. .type = _type, \
  37. .modified = 1, \
  38. .channel2 = _channel2, \
  39. .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_FREQUENCY), \
  40. .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
  41. BIT(IIO_CHAN_INFO_CALIBBIAS), \
  42. .scan_index = _index, \
  43. .scan_type = { \
  44. .sign = 's', \
  45. .realbits = 16, \
  46. .storagebits = 16, \
  47. .shift = 0, \
  48. .endianness = IIO_BE, \
  49. }, \
  50. }
  51. static short BUF_DATA_X_Y_Z[DATA_SIZE];
  52. unsigned char X_Y_Z_FLAG=2; /*采样方向,默认Z轴*/
  53. static int interrupt_conter=0; /*中断计数*/
  54. /* adxl345的扫描元素,三路振动数据*/
  55. enum inv_adxl345_scan {
  56. INV_ADXL345_SCAN_ACCL_X,
  57. INV_ADXL345_SCAN_ACCL_Y,
  58. INV_ADXL345_SCAN_ACCL_Z,
  59. };
  60. /*
  61. * 扫描掩码,两种情况,全启动0X111,或者都不启动0X0
  62. */
  63. static const unsigned long adxl345_scan_masks[]={
  64. BIT(INV_ADXL345_SCAN_ACCL_X)|
  65. BIT(INV_ADXL345_SCAN_ACCL_Y) |
  66. BIT(INV_ADXL345_SCAN_ACCL_Z) ,
  67. 0,
  68. };
  69. static const struct iio_chan_spec adxl345_channels[] =
  70. {
  71. ADXL345_CHAN(IIO_ACCEL,IIO_MOD_X,INV_ADXL345_SCAN_ACCL_X),
  72. ADXL345_CHAN(IIO_ACCEL,IIO_MOD_Y,INV_ADXL345_SCAN_ACCL_Y),
  73. ADXL345_CHAN(IIO_ACCEL,IIO_MOD_Z,INV_ADXL345_SCAN_ACCL_Z),
  74. };
  75. /*设备结构体*/
  76. struct adxl345_dev
  77. {
  78. dev_t devid; /* 设备号 */
  79. struct cdev cdev; /* cdev */
  80. struct class *class; /**/
  81. struct device *device; /* 设备 */
  82. int major; /* 主设备号 */
  83. struct i2c_client *client; /* i2c 设备 */
  84. struct mutex lock;
  85. int irqnum;
  86. irqreturn_t (*handler)(int ,void *);
  87. struct work_struct adxl345_irq_work;
  88. struct device_node *nd;
  89. int gpio;
  90. struct fasync_struct *async_queue; /* 异步相关结构体 */
  91. };
  92. static struct adxl345_dev *myprivate_data; /* 私有数据 */
  93. /* 以正负2g量程为例,4/2^13=,扩大10^9倍,就是488280加速度计分辨率*/
  94. static const int accel_scale_adxl345[] =
  95. {488280,976560,1953120,3906250};
  96. /* 输出速率*/
  97. static const int accel_frequency_adxl345[]=
  98. {0.10,0.20,0.39,0.78,1.56,3.13,6.25,12.5,25,50,100,200,400,800,1600,3200};
  99. /*
  100. * @description: iic从adxl345读取寄存器的值
  101. * @para-dev adxl345设备 reg 要读取的寄存器 val 读取数据缓冲区 len 要读取的数据长度
  102. */
  103. static int adxl345_read_regs(struct adxl345_dev *dev, u8 reg, void *val, int len)
  104. {
  105. int ret=0;
  106. struct i2c_msg msg[2];
  107. struct i2c_client *client = dev->client;
  108. /* msg[0]为发送要读取的首地址 */
  109. msg[0].addr = client->addr; /* adxl345地址 */
  110. msg[0].flags = 0; /* 标记为发送数据 */
  111. msg[0].buf = &reg; /* 读取的寄存器首地址 */
  112. msg[0].len = 1; /* reg长度*/
  113. /* msg[1]读取数据 */
  114. msg[1].addr = client->addr; /* adxl345地址 */
  115. msg[1].flags = I2C_M_RD; /* 标记为读取数据*/
  116. msg[1].buf = val; /* 读取数据缓冲区 */
  117. msg[1].len = len; /* 要读取的数据长度*/
  118. ret=i2c_transfer(client->adapter, msg, 2);
  119. if(ret==2)
  120. ret=0;
  121. else
  122. ret=-EREMOTEIO;
  123. return ret;
  124. }
  125. /*
  126. * @description: iic从adxl345写值
  127. * @para-dev adxl345设备 reg 要写的寄存器 buf 写数据缓冲区 len 要写的数据长度
  128. */
  129. static int adxl345_write_regs(struct adxl345_dev *dev, u8 reg, u8 *buf, u8 len)
  130. {
  131. int ret;
  132. u8 b[256];
  133. struct i2c_msg msg;
  134. struct i2c_client *client =dev->client;
  135. b[0] = reg; /* 寄存器首地址 */
  136. memcpy(&b[1],buf,len); /* 将要写入的数据拷贝到数组b里面 */
  137. msg.addr = client->addr; /* adxl345地址 */
  138. msg.flags = 0; /* 标记为写数据 */
  139. msg.buf = b; /* 要写入的数据缓冲区 */ /*根据iic时序 adxl345*/
  140. msg.len = len + 1; /* 要写入的数据长度,加上寄存器地址的长度*/
  141. ret= i2c_transfer(client->adapter, &msg, 1);
  142. if(ret==1)
  143. ret=0;
  144. else
  145. ret=-EREMOTEIO;
  146. return ret;
  147. }
  148. /*
  149. * @description: 向adxl345指定寄存器读一个寄存器的值
  150. * @param -dev: adxl345设备
  151. * @param - reg: 要读的寄存器
  152. * @return
  153. */
  154. static u8 adxl345_read_reg(struct adxl345_dev *dev, u8 reg)
  155. {
  156. u8 data = 0;
  157. adxl345_read_regs(dev, reg, &data, 1);
  158. return data;
  159. }
  160. /*
  161. * @description : 向adxl345指定寄存器写入指定的值,写一个寄存器
  162. * @param - dev: adxl345设备
  163. * @param - reg: 要写的寄存器
  164. * @param - data: 要写入的值
  165. * @return : 无
  166. */
  167. static void adxl345_write_reg(struct adxl345_dev *dev, u8 reg, u8 data)
  168. {
  169. u8 buf = 0;
  170. buf = data;
  171. adxl345_write_regs(dev, reg, &buf, 1);
  172. }
  173. /*利用IIO框架读取adxl345寄存器的数据*/
  174. static int adxl345_read_channel_data(struct iio_dev *indio_dev,
  175. struct iio_chan_spec const *chan,
  176. int *val)
  177. {
  178. int ind;
  179. __be16 d ;
  180. int axis=chan->channel2;
  181. struct adxl345_dev *dev=iio_priv(indio_dev);
  182. ind = (axis - IIO_MOD_X) * 2;
  183. adxl345_read_regs(dev,DATAX0+ind,(u8*)&d,2);// 大端:d[0]低地址 d[1]高地址
  184. *val=(short)d;
  185. return IIO_VAL_INT;
  186. }
  187. /*
  188. * @description : 读函数,当读取 sysfs 中的文件的时候最终此函数会执,
  189. 此函数里面会从传感器里面读取各种数据,然后上传给应用。
  190. * @param - indio_dev : iio_dev
  191. * @param - chan : 通道
  192. * @param - val : 读取的值,如果是小数值的话,val 是整数部分。
  193. * @param - val2 : 读取的值,如果是小数值的话,val2 是小数部分。
  194. * @param - mask : 掩码。
  195. */
  196. static int adxl345_read_raw(struct iio_dev *indio_dev,
  197. struct iio_chan_spec const *chan,
  198. int *val, int *val2, long mask)
  199. {
  200. struct adxl345_dev *dev=iio_priv(indio_dev);
  201. int ret = 0;
  202. unsigned char regdata = 0;
  203. switch (mask){
  204. case IIO_CHAN_INFO_RAW:
  205. mutex_lock(&dev->lock);
  206. ret = adxl345_read_channel_data(indio_dev, chan, val); /* 读取通道值 */
  207. mutex_unlock(&dev->lock);
  208. return ret;
  209. break;
  210. case IIO_CHAN_INFO_SCALE:
  211. mutex_lock(&dev->lock);
  212. regdata=(adxl345_read_reg(dev,DATA_FORMAT)&0x3);
  213. mutex_unlock(&dev->lock);
  214. *val=0;
  215. *val2=accel_scale_adxl345[regdata];
  216. return IIO_VAL_INT_PLUS_NANO;/*组合宏, 值为val+val2/1000000000 */
  217. break;
  218. case IIO_CHAN_INFO_CALIBBIAS: //偏移量
  219. switch (chan->channel2)
  220. {
  221. case IIO_MOD_X:
  222. *val=(int)adxl345_read_reg(dev,OFSX);
  223. return IIO_VAL_INT;
  224. break;
  225. case IIO_MOD_Y:
  226. *val=(int)adxl345_read_reg(dev,OFSY);
  227. return IIO_VAL_INT;
  228. break;
  229. case IIO_MOD_Z:
  230. *val=(int)adxl345_read_reg(dev,OFSZ);
  231. return IIO_VAL_INT;
  232. break;
  233. default:
  234. break;
  235. }
  236. break;
  237. case IIO_CHAN_INFO_FREQUENCY:
  238. *val=accel_frequency_adxl345[adxl345_read_reg(dev,BW_RATE)];
  239. return IIO_VAL_INT;
  240. break;
  241. default:
  242. break;
  243. }
  244. return 0;
  245. }
  246. /*
  247. * @description : 写函数,向 sysfs 中的文件写数据的时候最终此函数会
  248. 执行,一般在此函数里面设置传感器,比如量程等。
  249. * @param - indio_dev : iio_dev
  250. * @param - chan : 通道
  251. * @param - val : 读取的值,如果是小数值的话,val 是整数部分。
  252. * @param - val2 : 读取的值,如果是小数值的话,val2 是小数部分。
  253. * @param - mask : 掩码。
  254. */
  255. static int adxl345_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
  256. int val, int val2, long mask)
  257. {
  258. struct adxl345_dev *dev = iio_priv(indio_dev);
  259. int ret = 0;
  260. int i=0;
  261. switch (mask){
  262. case IIO_CHAN_INFO_SCALE:
  263. for(i=0;i<ARRAY_SIZE(accel_scale_adxl345);i++){
  264. if(accel_scale_adxl345[i]==val2){
  265. switch (i){
  266. case 0:
  267. adxl345_write_reg(dev,DATA_FORMAT ,0X08);
  268. break;
  269. case 1:
  270. adxl345_write_reg(dev,DATA_FORMAT ,0X09);
  271. break;
  272. case 2:
  273. adxl345_write_reg(dev,DATA_FORMAT ,0X0A);
  274. break;
  275. case 3:
  276. adxl345_write_reg(dev,DATA_FORMAT ,0X0B);
  277. break;
  278. default:
  279. break;
  280. }
  281. }
  282. }
  283. break;
  284. case IIO_CHAN_INFO_CALIBBIAS:
  285. break;
  286. case IIO_CHAN_INFO_FREQUENCY:
  287. for(i=0;i<ARRAY_SIZE(accel_frequency_adxl345);i++){
  288. if (accel_frequency_adxl345[i]==val){
  289. adxl345_write_reg(dev,BW_RATE,i);
  290. mdelay(50); // 等待更新
  291. }
  292. }
  293. break;
  294. default:
  295. ret = -EINVAL;
  296. break;
  297. }
  298. return ret;
  299. }
  300. /*
  301. * @description : 用户空间写数据格式,比如我们在用户空间操作 sysfs 来设置传
  302. * :感器的分辨率,如果分辨率带小数,那么这个小数传递到内核空间
  303. * : 应该扩大多少倍,此函数就是用来设置这个的。
  304. * @param - indio_dev : iio_dev
  305. * @param - chan : 通道
  306. * @param - mask : 掩码
  307. */
  308. static int adxl345_write_raw_get_fmt(struct iio_dev *indio_dev,
  309. struct iio_chan_spec const *chan, long mask)
  310. {
  311. switch (mask) {
  312. case IIO_CHAN_INFO_SCALE: /* 用户空间写的加速度计分辨率数据要乘以1000000000 */
  313. return IIO_VAL_INT_PLUS_NANO;
  314. break;
  315. case IIO_CHAN_INFO_CALIBBIAS:
  316. return IIO_VAL_INT_PLUS_NANO;
  317. // return IIO_VAL_INT; //这个宏会报错
  318. break;
  319. case IIO_CHAN_INFO_FREQUENCY:
  320. return IIO_VAL_INT_PLUS_NANO;
  321. // return IIO_VAL_INT;
  322. break;
  323. }
  324. return -EINVAL;
  325. }
  326. /*
  327. * iio_info结构体变量
  328. */
  329. static const struct iio_info adxl345_info =
  330. {
  331. .read_raw = adxl345_read_raw,
  332. .write_raw = adxl345_write_raw,
  333. .write_raw_get_fmt = &adxl345_write_raw_get_fmt,
  334. };
  335. /**
  336. * @description: adxl345寄存器初始化函数
  337. * @return {*}
  338. */
  339. static int adxl345reg_init(struct adxl345_dev *dev)
  340. {
  341. u8 value;
  342. /* 初始化adxl345*/
  343. adxl345_write_reg(dev,INTENABLE,0X00); //使能中断
  344. adxl345_write_reg(dev,DATA_FORMAT ,0X0B); //+-16g 中断高电平有效
  345. adxl345_write_reg(dev,BW_RATE,0XF); //输出速率
  346. adxl345_write_reg(dev,POWER_CTL,0X08);
  347. adxl345_write_reg(dev,OFSX,0X00);
  348. adxl345_write_reg(dev,OFSY,0X00);
  349. adxl345_write_reg(dev,OFSZ,0X00);
  350. // 中断配置
  351. adxl345_write_reg(dev,INT_MAP,0X02);
  352. adxl345_write_reg(dev,FIFO_CTL,0X9F);
  353. adxl345_write_reg(dev,INTENABLE,0X83); //使能中断
  354. value=(unsigned char)adxl345_read_reg(dev,DEVICEID);
  355. printk("device id=%#x\r\n",value);
  356. mdelay(5);
  357. return 0;
  358. }
  359. /*中断下半步处理函数*/
  360. void adxl345_irqwork_func(struct work_struct *work)
  361. {
  362. __be16 d ;
  363. int i;
  364. struct adxl345_dev *dev=container_of(work,struct adxl345_dev,adxl345_irq_work);
  365. if(adxl345_read_reg(dev,INT_SORCE)&0X02){
  366. mutex_lock(&dev->lock);
  367. if(X_Y_Z_FLAG==0){
  368. for(i=0;i<DATA_SIZE;i++) {
  369. adxl345_read_regs(dev,DATAX0,(u8*)&d,2);
  370. BUF_DATA_X_Y_Z[i]=(short)d;
  371. }
  372. }
  373. else if(X_Y_Z_FLAG==1){
  374. for(i=0;i<DATA_SIZE;i++) {
  375. adxl345_read_regs(dev,DATAY0,(u8*)&d,2);
  376. BUF_DATA_X_Y_Z[i]=(short)d;
  377. }
  378. }
  379. else if(X_Y_Z_FLAG==2){
  380. for(i=0;i<DATA_SIZE;i++) {
  381. adxl345_read_regs(dev,DATAZ0,(u8*)&d,2);
  382. BUF_DATA_X_Y_Z[i]=(short)d;
  383. }
  384. }
  385. mutex_unlock(&dev->lock);
  386. }
  387. if(interrupt_conter%100==0)
  388. printk("this is interuupt_%d\n",interrupt_conter);
  389. interrupt_conter++;
  390. kill_fasync(&dev->async_queue,SIGIO,POLL_IN);
  391. adxl345_read_reg(dev,INT_SORCE); // 清除中断标志位
  392. }
  393. /*adxl345中断使能与关闭*/
  394. static void interrupt_enbale(struct adxl345_dev *dev,bool this_flag)
  395. {
  396. mutex_lock(&dev->lock);
  397. if(this_flag){
  398. adxl345_write_reg(dev,INTENABLE,0X00);
  399. adxl345_write_reg(dev,INT_MAP,0X02);
  400. adxl345_write_reg(dev,FIFO_CTL,0X9F);
  401. adxl345_write_reg(dev,INTENABLE,0X83);
  402. }
  403. else
  404. adxl345_write_reg(dev,INTENABLE,0X00);
  405. mutex_unlock(&dev->lock);
  406. }
  407. /*使用工作队列处理下半部*/
  408. static irqreturn_t adxl345_handler(int irq, void *p)
  409. { struct iio_dev *in_dev=(struct iio_dev*) p;
  410. struct adxl345_dev *dev=iio_priv(in_dev);
  411. schedule_work(&dev->adxl345_irq_work);
  412. return IRQ_RETVAL(IRQ_HANDLED);
  413. }
  414. /*打开设备,然后打开中断*/
  415. static int adxl345_open(struct inode *inode, struct file *filp)
  416. {
  417. struct adxl345_dev *dev;
  418. filp->private_data = myprivate_data;
  419. dev =(struct adxl345_dev *)filp->private_data;
  420. interrupt_enbale(dev,true);
  421. return 0;
  422. }
  423. /*fasync函数,用于处理异步通知*/
  424. static int adxl345_fasync(int fd, struct file *filp, int on)
  425. {
  426. struct adxl345_dev *dev =(struct adxl345_dev *)filp->private_data;
  427. return fasync_helper(fd, filp, on, &dev->async_queue);
  428. }
  429. /*数据读取*/
  430. static ssize_t adxl345_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
  431. {
  432. int err=0;
  433. struct adxl345_dev *dev =(struct adxl345_dev *)filp->private_data;
  434. interrupt_enbale(dev,false);
  435. mutex_lock(&dev->lock);
  436. err=copy_to_user(buf,BUF_DATA_X_Y_Z,sizeof(BUF_DATA_X_Y_Z));
  437. mutex_unlock(&dev->lock);
  438. interrupt_enbale(dev,true);
  439. if(err==0)
  440. return DATA_SIZE;
  441. else
  442. return -1;
  443. }
  444. /*数据写入*/
  445. /*应该重新定义write函数的读写格式,buf[0]为寄存器的地址,buf[1]为要写入的值*/
  446. static ssize_t adxl345_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
  447. {
  448. int retvalue=0;
  449. unsigned char databuf[1];
  450. struct adxl345_dev *dev =(struct adxl345_dev *)filp->private_data;
  451. retvalue = copy_from_user(databuf, buf, cnt);
  452. if(retvalue < 0) {
  453. printk(KERN_ERR"kernel write failed!\n");
  454. return -EFAULT;
  455. }
  456. mutex_lock(&dev->lock);
  457. X_Y_Z_FLAG=databuf[0];
  458. mutex_unlock(&dev->lock);
  459. return retvalue;
  460. }
  461. static int adxl345_release(struct inode *inode, struct file *filp)
  462. {
  463. struct adxl345_dev *dev =(struct adxl345_dev *)filp->private_data;
  464. interrupt_enbale(dev,false);
  465. return adxl345_fasync(-1, filp, 0);
  466. }
  467. /* adxl345操作函数 */
  468. static const struct file_operations adxl345_ops =
  469. {
  470. .owner = THIS_MODULE,
  471. .open = adxl345_open,
  472. .read = adxl345_read,
  473. .release = adxl345_release,
  474. .write =adxl345_write,
  475. .fasync =adxl345_fasync,
  476. };
  477. static int adxl345_probe(struct i2c_client *client, const struct i2c_device_id *id)
  478. {
  479. int ret;
  480. struct adxl345_dev *dev;
  481. struct iio_dev *indio_dev;
  482. /* 1、申请iio_dev内存 */
  483. indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*dev));//使用 iio_device_alloc 函数来申请 iio_dev,并且一起申请了 adxl345_dev 的内存。
  484. if (!indio_dev)
  485. return -ENOMEM;
  486. /* 2、获取adxl345_dev结构体地址 */
  487. dev = iio_priv(indio_dev);
  488. myprivate_data=dev;
  489. dev->client = client;
  490. i2c_set_clientdata(client, indio_dev); /* 保存indio_dev */
  491. mutex_init(&dev->lock);
  492. /*注册设备*/
  493. /* 1、构建设备号 */
  494. dev->major=0;
  495. ret=alloc_chrdev_region(&dev->devid, 0, ADXL345_CNT, ADXL345_NAME);
  496. dev->major = MAJOR(dev->devid);
  497. if(ret<0) printk(KERN_ERR"adxl345 chardev err!\n");
  498. printk("adxl345 major=%d,minor=%d\n",dev->major,MINOR(dev->devid));
  499. /* 2、注册设备 */
  500. dev->cdev.owner=THIS_MODULE;
  501. cdev_init(&dev->cdev, &adxl345_ops);
  502. ret=cdev_add(&dev->cdev, dev->devid, ADXL345_CNT);
  503. if(ret<0) printk(KERN_ERR"adxl345 cedvadd err!\n");
  504. /* 3、创建类 */
  505. dev->class = class_create(THIS_MODULE, ADXL345_NAME);
  506. if (IS_ERR(dev->class)) {
  507. printk(KERN_ERR"adxl345 class creat failed!\n");
  508. return PTR_ERR(dev->class);
  509. }
  510. /* 4、创建设备 */
  511. dev->device = device_create(dev->class, NULL, dev->devid, NULL, ADXL345_NAME);
  512. if (IS_ERR(dev->device))
  513. return PTR_ERR(dev->device);
  514. /*申请中断*/
  515. // 初始化gpio
  516. dev->nd=of_find_node_by_path("/soc/aips-bus@02100000/i2c@021a0000/adxl345@53");
  517. if (dev->nd== NULL)
  518. printk(KERN_INFO"adxl345interrupt-gpios node not find!\n");
  519. dev->gpio=of_get_named_gpio(dev->nd,"adxl345_interrupt_gpios",0);
  520. if(dev->gpio<0)
  521. printk(KERN_INFO"can't get gpio\n");
  522. /* 初始化 IO,并且设置成中断模式 */
  523. gpio_request(dev->gpio,ADXL345_INTERRUPT_NAME);
  524. gpio_direction_input(dev->gpio);
  525. dev->irqnum=irq_of_parse_and_map(dev->nd,0);
  526. printk("adxl345_gpio=[%d],irq_num=[%d]\n",dev->gpio,dev->irqnum);
  527. dev->handler=adxl345_handler;
  528. ret=request_irq(dev->irqnum,dev->handler,IRQF_TRIGGER_RISING,ADXL345_INTERRUPT_NAME,indio_dev);
  529. if(ret < 0)
  530. printk(KERN_INFO"irq %d request failed!\n",dev->irqnum);
  531. /* 初始化 work */
  532. INIT_WORK(&dev->adxl345_irq_work,adxl345_irqwork_func);
  533. /* 3、iio_dev的其他成员变量 */
  534. indio_dev->dev.parent = &client->dev;
  535. indio_dev->info = &adxl345_info;
  536. indio_dev->name = ADXL345_NAME;
  537. indio_dev->modes = INDIO_DIRECT_MODE; /* 直接模式,提供sysfs接口 */
  538. indio_dev->channels = adxl345_channels;
  539. indio_dev->num_channels =ARRAY_SIZE(adxl345_channels);
  540. indio_dev->available_scan_masks = adxl345_scan_masks;
  541. /* 4、注册iio_dev */
  542. ret = iio_device_register(indio_dev);
  543. if (ret < 0) {
  544. dev_err(&client->dev, "iio_device_register failed\n");
  545. return ret;
  546. }
  547. adxl345reg_init(dev);
  548. interrupt_enbale(dev,false);
  549. return 0;
  550. }
  551. static int adxl345_remove(struct i2c_client *client)
  552. {
  553. struct iio_dev *indio_dev = i2c_get_clientdata(client);
  554. struct adxl345_dev *dev=iio_priv(indio_dev);
  555. /* 删除设备 */
  556. cdev_del(&dev->cdev);
  557. unregister_chrdev_region(dev->devid, ADXL345_CNT);
  558. /* 注销掉类和设备 */
  559. device_destroy(dev->class, dev->devid);
  560. class_destroy(dev->class);
  561. free_irq(dev->irqnum,indio_dev);
  562. /*注销IIO */
  563. iio_device_unregister(indio_dev);
  564. return 0;
  565. }
  566. /* 传统匹配方式ID列表 */
  567. static const struct i2c_device_id adxl345_id[] = {
  568. {"adxl345", 0},
  569. {}
  570. };
  571. /* 设备树匹配列表 */
  572. static const struct of_device_id adxl345_of_match[] = {
  573. { .compatible = "adxl345" },
  574. { /* Sentinel */ }
  575. };
  576. /* i2c驱动结构体 */
  577. static struct i2c_driver adxl345_driver = {
  578. .probe = adxl345_probe,
  579. .remove = adxl345_remove,
  580. .driver = {
  581. .owner = THIS_MODULE,
  582. .name = "adxl345_driver",
  583. .of_match_table = adxl345_of_match,
  584. },
  585. .id_table = adxl345_id,
  586. };
  587. static int __init adxl345_init(void)
  588. {
  589. int ret = 0;
  590. ret = i2c_add_driver(&adxl345_driver);
  591. return ret;
  592. }
  593. static void __exit adxl345_exit(void)
  594. {
  595. i2c_del_driver(&adxl345_driver);
  596. }
  597. module_init(adxl345_init);
  598. module_exit(adxl345_exit);
  599. MODULE_LICENSE("GPL");
  600. MODULE_AUTHOR("ZHW-SYSTEAM-FAULT");
  601. MODULE_INFO(intree, "Y");

1.2 应用层数据读取

         应用层采用Qt 多线程同步处理异步通知信号,因为线程共享信号屏蔽字,主线程在创建子线程后,调用该函数,屏蔽信号,主线程只做界面绘制工作,空闲的子线程拿到信号就进行数据的读取。

  1. void pthread_myset()
  2. {
  3. int n;
  4. sigset_t set, oldset;
  5. sigemptyset(&set);
  6. sigemptyset(&oldset);
  7. sigaddset(&set, SIGIO);
  8. n= pthread_sigmask(SIG_BLOCK,&set, &oldset);
  9. if(n==0) qDebug()<<"pthread success! "<< endl;
  10. }

 1.2.1 打开文件

  1. void open_adxl345()
  2. {
  3. int flags = 0;
  4. char const *filename="/dev/adxl345";
  5. adxl345_fd=open(filename, O_RDWR);
  6. if(adxl345_fd < 0){
  7. return ;
  8. }
  9. adxl345_fd_flag=1; /*打开文件标志 */
  10. signal(SIGIO, sigio_signal_func);
  11. fcntl(adxl345_fd,__F_SETOWN, getpid()); /*将当前进程的进程号告内核*/
  12. flags = fcntl(adxl345_fd, F_GETFD); /*获取当前的进程状态*/
  13. fcntl(adxl345_fd, F_SETFL, flags | 00020000); /*设置进程启用异步通知功能*/
  14. // 设置输出轴
  15. write(adxl345_fd,direction_buf,sizeof(direction_buf));
  16. }

1.2.2 数据读取

        数据读取利用queqe容器:

  1. void sigio_signal_func(int signum)
  2. {
  3. int retvalue;
  4. retvalue= read(adxl345_fd,data,sizeof(data));
  5. if(retvalue<0)
  6. qDebug()<<"read's retvalue:"<<retvalue<<endl;
  7. for(int i=0;i<32;i++)
  8. queue_data.push(data[i]); //入队操作
  9. }

子线程轮询处理数据:

  1. void deal_data_class::working(bool flag){
  2. this->is_can_run=flag;
  3. if(!this->is_can_run)
  4. return ;
  5. short st_tamp;
  6. double de_tamp;
  7. while(this->is_can_run){
  8. if((queue_data.size()>32)){ // 默认有32个数据才开始出队
  9. st_tamp=queue_data.front();
  10. queue_data.pop();
  11. de_tamp=double(st_tamp*scale_select*9.8);
  12. x_all_count+=x_step; // 绘图横坐标累加
  13. _y.push_back(de_tamp);
  14. _x.push_back(double(x_all_count));
  15. data_net[read_count]=st_tamp;
  16. read_count_plot++;
  17. read_count++;
  18. if(read_count_plot%32==0){
  19. // 赋值给plot数组以绘图
  20. time_data_and_x_lock.lock();
  21. size_32_time_data.clear();
  22. size_32_time_x.clear();
  23. size_32_time_data=_y;
  24. size_32_time_x=_x;
  25. time_data_and_x_lock.unlock();
  26. emit this->data_can_plot();
  27. _x.clear();
  28. _y.clear();
  29. }
  30. if(read_count%1024==0){
  31. // qDebug()<< read_count_plot<<":readcount"<<endl;
  32. if(network_status==1){ //网络数据数组复制
  33. memcpy(send_data_net.data(),&data_net,sizeof(data_net));
  34. net_flag=true; // 数据准备完善标志
  35. }
  36. read_count=0;
  37. }
  38. }
  39. else {
  40. QThread::sleep(1);continue;
  41. }
  42. }
  43. }

1.3 文件打开与关闭

  1. void read_data_class::working(bool flag)
  2. {
  3. if(flag){
  4. if(adxl345_fd_flag==-1 || adxl345_fd_flag==0){
  5. open_adxl345();
  6. }
  7. }
  8. else{
  9. // 关闭文件描述符与关闭异步通知
  10. if(adxl345_fd_flag==1){
  11. close_adxl345();
  12. }
  13. }
  14. }

1.4 绘图

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

1.5 数据发送

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

2 上位机数据处理

2.1 离线数据系统

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

2.2 在线监测系统

2.2.1 模型训练与模型加载

模型训练使用的pytorch框架,其数据预处理、模型构建,以及示范的LSTM1DCNN网络模型与作者该篇博客类似:基于LSTM的故障诊断_基于lstm的轴承故障检测_旧日之歌的博客-CSDN博客

模型的推理从pth模型保存为pt模型:

  1. import torch.nn.functional as F
  2. from torchvision import transforms
  3. from matplotlib import pyplot as plt
  4. from numpy import dtype
  5. from 数据划分 import *
  6. import torchvision
  7. from torch import nn, Tensor
  8. from torch.nn import Conv1d,MaxPool1d,Flatten,Linear
  9. import torch
  10. device = torch.device("cpu")
  11. class mylstm(nn.Module):
  12. def __init__(self):
  13. super(mylstm, self).__init__()
  14. self.modle1 = nn.LSTM(input_size=1,hidden_size=16,num_layers=2, batch_first=True,dropout=0.2)
  15. self.modle2 = nn.Sequential (
  16. nn.Conv1d(512,128,1,1),
  17. nn.MaxPool1d(2,2),
  18. nn.Conv1d(128,32,4,4),
  19. nn.Flatten(),
  20. nn.Linear(64, 32),
  21. nn.Dropout(0.1),
  22. nn.ReLU(),
  23. nn.Linear(32,4 ),
  24. )
  25. def forward(self, x):
  26. h_0 = torch.randn(2,128,16)
  27. c_0 = torch.randn(2,128, 16)
  28. h_0 = h_0.to(device)
  29. c_0 = c_0.to(device)
  30. x = x.to(torch.float32)
  31. x,(h_0,c_0)=self.modle1(x,(h_0,c_0))
  32. x=self.modle2(x)
  33. return x
  34. from LSTM_1DCNN import *
  35. model=mylstm()
  36. state_dict=torch.load("libtorchtest_dict_practice_200.pth")
  37. model.load_state_dict(state_dict)
  38. model.to(device)
  39. model.eval()
  40. x=torch.randn(128,cut_long,1).to(device)
  41. traced_script_module = torch.jit.trace(model, x)
  42. traced_script_module.save('libtorchtest_dict_practice_200.pt')#保存模型位置
  43. print("save is ok\n")
  44. mymodel=torch.jit.load('libtorchtest_dict_practice_200.pt')
  45. output=mymodel(x)
  46. print(output)

2.2.2 模型的部署

        模型的部署主要采用的是libtorch框架,如何将libtorch框架移植在QT里可搜索网上其他教程,但一般要注意以下几个点:

  1. 1、-INCLUDE:?searchsorted_cuda@native@at@@YA?AVTensor@2@AEBV32@0_N1@Z -INCLUDE:?warp_size@cuda@at@@YAHXZ 一定要在qt链接动态的时候加上这串字符
  2. 2、一定要注意libtorch和pytorch的版本问题,两者要完全对应,推理用的是cuda,那么部署能用cpu/cuda,如果推理用cpu,部署只能用cpu。
  3. 3、环境变量

2.2.3 模型加载与使用

        

  1. //model_load 模型加载或者使用 model_select 选择模型 use_gpu gpu使用选择
  2. void model_load::working(bool model_load,const std::string module_path_, int model_select,bool use_gpu)
  3. {
  4. if(model_load){ // 模型加载
  5. this->module_path=module_path_;
  6. if(model_select==1){ // 加载lstm——1dcnn模型
  7. if(this->model_count[model_select]==1){
  8. emit this->to_log_print("lstm1dcnn已加载");
  9. return;
  10. }
  11. if (torch::cuda::is_available())
  12. emit this->to_log_print("支持GPU");
  13. if (torch::cuda::is_available() && use_gpu){
  14. this->device_=torch::kCUDA;
  15. emit this->to_log_print("the module for lstm1dcnn use cuda...");
  16. }
  17. else{
  18. emit this->to_log_print("the module for lstm1dcnn use cpu...");
  19. this->device_ = torch::kCPU;
  20. }
  21. try {
  22. this->module_lstm1dcnn=torch::jit::load(this->module_path);
  23. emit this->to_log_print("the module for lstm1dcnn load successed...");
  24. }
  25. catch (const c10::Error& e) {
  26. std::cerr << "Error loading the model!\n";
  27. emit this->to_log_print("the module for lstm1dcnn load failed...");
  28. }
  29. this->module_lstm1dcnn.to(this->device_);
  30. this->module_lstm1dcnn.eval();
  31. this->model_count[1]=1; // 模型lstm1dcnn加载标志
  32. }
  33. }
  34. else{ // 模型识别
  35. QString module_name;
  36. if (torch::cuda::is_available() && use_gpu){
  37. this->device_=torch::kCUDA;
  38. emit this->to_log_print("the module is using cuda...");
  39. }
  40. else{
  41. emit this->to_log_print("the module is using cpu...");
  42. this->device_ = torch::kCPU;
  43. }
  44. at::TensorOptions ops=at::TensorOptions().dtype(at::kDouble).device(this->device_);
  45. if(model_select==1 && this->model_count[1]==1){
  46. this->module_lstm1dcnn.to(this->device_);
  47. online_data.model_diagnosis_lock.lockForRead();
  48. data_scaler(online_data.model_diagnosis); //数据归一化
  49. this->input_tensor=torch::from_blob(online_data.model_diagnosis.data()
  50. , {128,512,1},ops).to(this->device_).clone(); // 深度拷贝
  51. online_data.model_diagnosis_lock.unlock();
  52. this->output=this->module_lstm1dcnn.forward(std::vector<torch::jit::IValue>{this->input_tensor}).toTensor();
  53. emit this->to_log_print("the module of lstm1dcnn'identify is OK...");
  54. module_name="LSTM1DCNN";
  55. // 结果输出
  56. at::Tensor outputarg= this->output.argmax(1);
  57. int output_result[4];
  58. memset(output_result,0,sizeof(output_result));
  59. for(int i=0;i<128;i++)
  60. output_result[outputarg[i].item().toInt()]++;
  61. emit this->diagnosis_result(output_result[0],output_result[1],output_result[2],output_result[3]);
  62. int max_result=0;
  63. int max_index=0;
  64. // 记录最大结果以及结果汇总
  65. for(int i=0;i<4;i++){
  66. emit this->to_log_print(QString("当前%1诊断结果统计:%2").arg(i).arg(output_result[i]));
  67. if(output_result[i]>max_result){
  68. max_result=output_result[i];
  69. max_index=i;
  70. }
  71. }
  72. // 发送给tabelwidget
  73. QDateTime Timedata = QDateTime::currentDateTime(); // 获取当前时间
  74. QString str = Timedata.toString("yyyy-MM-dd-hh:mm:ss");
  75. emit this->to_tablewidget("NULL", // 序号
  76. str, // 时间
  77. module_name, //模型
  78. online_data.direction_sample, // 采样轴
  79. QString("%1[%2/128]").arg(label_fault[max_index]).arg(max_result),// 诊断结果
  80. QString("%1Hz").arg(online_data.hz_for_select)); //采样频率
  81. }
  82. }
  83. // else{
  84. // emit this->to_log_print("the module of lstm1dcnn unloaded......");
  85. // return ;
  86. // }
  87. // if(model_select==2 && this->model_count[1]==2){
  88. // // restnet模型识别
  89. // }
  90. // else{
  91. // return ;
  92. // }
  93. // if(model_select==3 && this->model_count[3]==3){
  94. // // cnn模型识别
  95. // }
  96. // else{
  97. // return ;
  98. // }
  99. }

2.2.4 傅里叶变换

        傅里叶变换采用的是fftw库,号称是世界上最快的傅里叶变换。测试结果与matlab所输出的结果一致。

  1. void fft_function(vector<double>& after_fft_data,vector<double> before_fft_data,int fft_length)
  2. {
  3. fftw_complex *out;
  4. fftw_complex *in;
  5. fftw_plan p;
  6. in =(fftw_complex*)fftw_malloc(sizeof(fftw_complex) * fft_length);
  7. out=(fftw_complex*)fftw_malloc(sizeof(fftw_complex) * fft_length);
  8. p=fftw_plan_dft_1d(fft_length,in,out,FFTW_FORWARD,FFTW_ESTIMATE);
  9. for(int i=0;i<fft_length;i++){
  10. in[i][0]=before_fft_data.at(i);
  11. in[i][1]=0;
  12. }
  13. fftw_execute(p);
  14. after_fft_data.clear();
  15. after_fft_data.push_back(0); // 去除直流分量
  16. for(int j=1;j<fft_length/2+1;j++){
  17. after_fft_data.push_back(sqrt(out[j][0]*out[j][0]+out[j][1]*out[j][1])/fft_length*2);
  18. }
  19. fftw_destroy_plan(p);
  20. if(in!=NULL)
  21. {
  22. fftw_free(in);
  23. }
  24. if(out!=NULL){
  25. fftw_free(out);
  26. }
  27. }

2.2.5 网络数据读取

  1. void Online_data_analysis::network_set_init()
  2. {
  3. // 创建通信的套接字
  4. this->m_tcp =new QTcpSocket(this);
  5. connect(this->m_tcp,&QTcpSocket::readyRead,this,[=](){
  6. // qDebug()<<"it can ready"<<endl;
  7. QByteArray array = this->m_tcp->readAll();
  8. if(array.size()==(sizeof(short)*1024)){ // 读取的数据
  9. short* p=(short*)array.data();
  10. online_data.fft_and_datapara_lock.lockForWrite();
  11. for(int i=0;i<1024;i++)
  12. online_data.fft_and_datapara.push_back((online_data.scale_sample*(double)p[i]*9.8));
  13. online_data.fft_and_datapara_lock.unlock();
  14. online_data.net_read_count++;
  15. //发送给fft时域状态参数计算变换等子线程
  16. this->fft_and_data_deal_thread->working();
  17. ui->output_log_plainTextEdit->appendPlainText(QString("从网络中读取次数为(2048字节/次):%1......").arg(online_data.net_read_count));
  18. }
  19. if(array.size()==5){ // 读取的初始化信息
  20. unsigned char *p= (unsigned char*)array.data();
  21. // online_data_struct online_dat在此函数内部初始化
  22. // 每检测到初始化信息,所有数据都重新清除
  23. ui->output_log_plainTextEdit->appendPlainText("状态重置>>>......");
  24. this->parameter_show_of_come_net(p[0],p[1],p[2],p[3],p[4]);
  25. }
  26. });
  27. // 服务器端断开连接
  28. connect(this->m_tcp,&QTcpSocket::disconnected ,this,[=](){
  29. ui->server_ip_lineEdit->setText("服务器连接已断开....");
  30. ui->server_port_lineEdit->setText("服务器连接已断开....");
  31. this->information_print_net_flag(0,"服务器连接已断开....");
  32. this->m_tcp->close();
  33. ui->pushButton_net_start->setDisabled(false);
  34. ui->pushButton_net_end->setDisabled(true);
  35. qDebug()<<"the net is disconnected"<<endl;
  36. });
  37. // 检测连接
  38. connect(this->m_tcp,&QTcpSocket::connected ,this,[=](){
  39. // 显示服务器IP和端口
  40. ui->server_ip_lineEdit->setText(this->m_tcp->peerAddress().toString());
  41. ui->server_port_lineEdit->setText( QString::number(this->m_tcp->peerPort()));
  42. this->information_print_net_flag(1,"已连接服务器....");
  43. ui->pushButton_net_start->setDisabled(true);
  44. ui->pushButton_net_end->setDisabled(false);
  45. QString str="the connect is init for client ,if recieved,server's read is ok";
  46. this->m_tcp->write(str.toUtf8().data());
  47. });
  48. }

2.2.6 其他

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

3 总结

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/煮酒与君饮/article/detail/868351
推荐阅读
相关标签
  

闽ICP备14008679号