風(fēng)機(jī)葉片葉根螺栓斷裂預(yù)測(cè)

關(guān)鍵詞

葉根螺栓 時(shí)間序列 LSTM

項(xiàng)目背景

以下直接引用了某些論文的內(nèi)容,幫助大家了解背景。

螺栓連接是風(fēng)力發(fā)電機(jī)組裝配中的重要裝配方式,幾乎涉及到風(fēng)力發(fā)電機(jī)組的所有部件。因此,螺栓的選用和強(qiáng)度校核是風(fēng)力發(fā)電機(jī)組可靠性的重要保證。隨著我國(guó)風(fēng)電事業(yè)的跨越式發(fā)展,伴隨著風(fēng)力發(fā)電成本不斷下降,風(fēng)電機(jī)組的價(jià)格也越來(lái)越低,各大風(fēng)電設(shè)備總裝企業(yè)的價(jià)格戰(zhàn)已經(jīng)進(jìn)行到了白熱化階段。如何在降低成本的情況下,保證風(fēng)電機(jī)組的質(zhì)量,成為各大風(fēng)電企業(yè)面臨的重要問(wèn)題。
--
現(xiàn)階段,我國(guó)風(fēng)電機(jī)組的螺栓失效問(wèn)題已經(jīng)在連接塔筒法蘭的高強(qiáng)度螺栓上有所體現(xiàn)。主要失效形式為:安裝麥搶帶發(fā)生滑絲、扭斷、屈服、甚至拉斷等現(xiàn)象;設(shè)備運(yùn)行過(guò)程中發(fā)生螺栓斷裂,威脅機(jī)組運(yùn)行,嚴(yán)重者甚至造成風(fēng)力發(fā)電機(jī)組倒塌。
--
螺栓斷裂與以下幾種因素有關(guān):

  • 螺栓的質(zhì)量
  • 螺栓的預(yù)緊力矩
  • 螺栓的強(qiáng)度
  • 螺栓的疲勞強(qiáng)度

數(shù)據(jù)及問(wèn)題描述

該項(xiàng)目是想基于風(fēng)機(jī)scada系統(tǒng)數(shù)據(jù)通過(guò)機(jī)器學(xué)習(xí)的方式對(duì)葉根螺栓的斷裂進(jìn)行提前預(yù)測(cè),從而避免重大事故和經(jīng)濟(jì)損失。
拿到的樣本數(shù)據(jù)中包括33組風(fēng)機(jī)3月、4月、5月、6月共四個(gè)月的數(shù)據(jù),其中部分?jǐn)?shù)據(jù)有少量缺失。故障風(fēng)機(jī)為17#、12#、6#,其他均為正常風(fēng)機(jī)。根據(jù)實(shí)際數(shù)據(jù)情況與建模需求選取全部故障風(fēng)機(jī)數(shù)據(jù)、隨機(jī)選取適量的正常數(shù)據(jù)進(jìn)行建模。具體數(shù)據(jù)使用情況如下(參與分析風(fēng)機(jī)均已去除停機(jī)數(shù)據(jù)):


數(shù)據(jù)使用情況

解決方案

  • 經(jīng)過(guò)對(duì)數(shù)據(jù)的深入考察發(fā)現(xiàn),所給數(shù)據(jù)中,與變槳系統(tǒng)相關(guān)的變量及部分溫度特征與葉根螺栓故障有明顯的相關(guān)性,如平均槳葉角度、最高Topbox溫度、最高控制柜溫度等。理論上基于足夠的數(shù)據(jù)可以訓(xùn)練識(shí)別故障的機(jī)器學(xué)習(xí)模型。
  • 考慮到葉根螺栓斷裂在時(shí)間上應(yīng)該存在某種應(yīng)力累積的過(guò)程,達(dá)到一定程度時(shí)則會(huì)發(fā)生斷裂。因此嘗試基于時(shí)間序列的LSTM深度學(xué)習(xí)模型。為了預(yù)測(cè)可能發(fā)生葉根螺栓斷裂的風(fēng)機(jī)大概的斷裂時(shí)間,考慮搭建兩個(gè)模型,第一個(gè)模型識(shí)別是否為可能發(fā)生故障的風(fēng)機(jī)并標(biāo)識(shí)蘊(yùn)含故障模式的數(shù)據(jù)與正常數(shù)據(jù),第二個(gè)模型對(duì)故障風(fēng)機(jī)預(yù)測(cè)發(fā)生斷裂時(shí)間。具體思路如下:
  1. 首先去除停機(jī)部分?jǐn)?shù)據(jù),僅考察風(fēng)機(jī)運(yùn)行數(shù)據(jù);
  2. 然后針對(duì)第一階段的模型,對(duì)故障風(fēng)機(jī)停機(jī)時(shí)間點(diǎn)之前的數(shù)據(jù)逐條貼標(biāo)簽1,取時(shí)間窗口為10天,對(duì)故障點(diǎn)t0后的正常運(yùn)行數(shù)據(jù)和隨機(jī)選取的正常風(fēng)機(jī)數(shù)據(jù)貼標(biāo)簽0,分別表示葉根螺栓故障發(fā)生和未發(fā)生。
  3. 對(duì)上述數(shù)據(jù)進(jìn)行滑窗處理,為使得參與訓(xùn)練數(shù)據(jù)中正負(fù)樣本均衡,令正常數(shù)據(jù)的滑窗步長(zhǎng)大于故障數(shù)據(jù)的滑窗步長(zhǎng)?;谏鲜鰯?shù)據(jù)訓(xùn)練一個(gè)LSTM分類(lèi)模型。
  4. 針對(duì)第二階段的故障模型,對(duì)故障風(fēng)機(jī)停機(jī)時(shí)間點(diǎn)之前的數(shù)據(jù)逐條貼標(biāo)簽,以遞增的整數(shù)表示距離該時(shí)間點(diǎn)的由近及遠(yuǎn),取時(shí)間窗口為10天,為提高模型預(yù)測(cè)效果,相鄰數(shù)據(jù)之間的標(biāo)簽為間隔為10的整數(shù)。例如對(duì)于10分鐘級(jí)的數(shù)據(jù),某條數(shù)據(jù)的標(biāo)簽為60則表示距離故障發(fā)生時(shí)間t0為1小時(shí)左右。
  5. 同樣進(jìn)行滑窗處理,一個(gè)滑窗內(nèi)有144條數(shù)據(jù)。訓(xùn)練LSTM回歸模型。

實(shí)施過(guò)程

特征處理

考察樣本數(shù)據(jù),剔除無(wú)關(guān)變量,選取與葉根螺栓斷裂故障相關(guān)的特征。經(jīng)過(guò)分析發(fā)現(xiàn)如下特征與葉根螺栓斷裂故障有一定相關(guān)性:平均槳葉角度(deg)、最小槳葉角度(deg)、最大槳葉角度(deg)、最大機(jī)艙加速度(g)、最高電機(jī)繞組溫度(℃)、最高Topbox溫度(℃)、最高機(jī)艙溫度(℃)、最高控制柜溫度(℃)、最高變槳電機(jī)1溫度(℃)、最高變槳電機(jī)2溫度(℃)、最高變槳電機(jī)3溫度(℃)、最高變槳柜1的柜體溫度(℃)、最高變槳柜2的柜體溫度(℃)、最高變槳柜3的柜體溫度(℃)、平均變流器功率(kW)、最高變槳柜1備電柜溫度(℃)、最高變槳柜2備電柜溫度(℃)、最高變槳柜3備電柜溫度(℃)。
為便于觀察,以下繪制了部分特征在一定時(shí)間范圍內(nèi)的變化趨勢(shì)對(duì)比:


topbox溫度變化

平均槳葉角度

最高機(jī)艙溫度

變槳柜1柜體溫度

經(jīng)過(guò)對(duì)所有特征的遍歷分析,選取上述特征或其組合參與模型訓(xùn)練,對(duì)最高變槳電機(jī)1溫度(℃)、最高變槳電機(jī)2溫度(℃)、最高變槳電機(jī)3溫度(℃),最高變槳柜1的柜體溫度(℃)、最高變槳柜2的柜體溫度(℃)、最高變槳柜3的柜體溫度(℃),最高變槳柜1備電柜溫度(℃)、最高變槳柜2備電柜溫度(℃)、最高變槳柜3備電柜溫度(℃)等特征進(jìn)行合并(分別取其均值作為新變量代替三個(gè)近似特征)。

基于LSTM的故障分類(lèi)模型

  • 模型的結(jié)構(gòu)上,LSTM層設(shè)置cell_size 為13,其中有dropout機(jī)制,會(huì)隨機(jī)忘記前幾層的Cell中神經(jīng)元,該層設(shè)置共13個(gè)神經(jīng)元作為L(zhǎng)STM的輸出。DeepLearnning全連接層共設(shè)置3層,其中第一層有18個(gè)神經(jīng)元,激活函數(shù)為ReLU;第二層有15個(gè)神經(jīng)元,激活函數(shù)為ReLU;第三層有12個(gè)神經(jīng)元,激活函數(shù)也為ReLU。最終隱層的輸出進(jìn)入輸出層,輸出層共有1個(gè)神經(jīng)元,輸出0/1數(shù)值標(biāo)簽。
  • 訓(xùn)練方面,采取128Batch Size大小的數(shù)據(jù)分批投入。由于已在數(shù)據(jù)滑窗時(shí)均衡了正負(fù)樣本比例,這里直接按4:1的比例隨機(jī)劃分訓(xùn)練集和測(cè)試集。
    訓(xùn)練過(guò)程中的loss下降趨勢(shì)如下圖所示:


    損失函數(shù)

    經(jīng)過(guò)測(cè)試模型最終的綜合準(zhǔn)確率為96%,故障召回率95%。如下圖:


    report

基于LSTM的故障時(shí)間預(yù)測(cè)模型

  • 該模型為回歸模型,模型的結(jié)構(gòu)上,LSTM層設(shè)置cell_size 為20,num_size為3,DeepLearnning全連接層,同樣設(shè)置3層,其中第一層有20個(gè)神經(jīng)元,激活函數(shù)為ReLU;第二層有15個(gè)神經(jīng)元,激活函數(shù)為ReLU;第三層有11個(gè)神經(jīng)元,激活函數(shù)也為ReLU。學(xué)習(xí)率為0.01。
  • 最終隱層的輸出進(jìn)入輸出層,輸出層共有1個(gè)神經(jīng)元,輸出距離故障停機(jī)時(shí)間點(diǎn)的整數(shù)值標(biāo)簽,激活函數(shù)為L(zhǎng)inear線性激活函數(shù),構(gòu)造出基于LSTM神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)的回歸模型。
  • 訓(xùn)練方面,采取64Batch Size大小的數(shù)據(jù)分批投入。Loss的計(jì)算采用均方誤差。其模型架構(gòu)如下圖所示:


    模型結(jié)構(gòu)

Loss值變化如下,模型loss最終收斂于232.86,對(duì)于一個(gè)Batch Size(10080條數(shù)據(jù))來(lái)說(shuō)已經(jīng)很低,訓(xùn)練效果比較理想。


loss

將實(shí)際數(shù)據(jù)與預(yù)測(cè)數(shù)據(jù)對(duì)比如下圖,對(duì)于葉根螺栓故障來(lái)說(shuō)該預(yù)測(cè)誤差是可以接受的。


result

核心代碼

這里貼出部分代碼(關(guān)鍵代碼)

  • lstm網(wǎng)絡(luò)實(shí)現(xiàn)(基于tensorflow1.2.0)
# -*- coding: utf-8 -*-
"""
Created on Wed Sep 13 13:20:45 2018

@author: xuanlei
"""

import pandas as pd
import tensorflow as tf
import numpy as np

#==============================================================================
# Batch Normalization
#==============================================================================
def batch_norm_layer(x, train_phase, scope_bn):
    with tf.variable_scope(scope_bn):
        beta = tf.Variable(tf.constant(0.0, shape=[x.shape[-1]]), name='beta', trainable=True)
        gamma = tf.Variable(tf.constant(1.0, shape=[x.shape[-1]]), name='gamma', trainable=True)
        axises = np.arange(len(x.shape) - 1)
        batch_mean, batch_var = tf.nn.moments(x, axises, name='moments')
        ema = tf.train.ExponentialMovingAverage(decay=0.5)

        def mean_var_with_update():
            ema_apply_op = ema.apply([batch_mean, batch_var])
            with tf.control_dependencies([ema_apply_op]):
                return tf.identity(batch_mean), tf.identity(batch_var)

        mean, var = tf.cond(train_phase, mean_var_with_update,
                            lambda: (ema.average(batch_mean), ema.average(batch_var)))
        normed = tf.nn.batch_normalization(x, mean, var, beta, gamma, 1e-3)
    return normed

#==============================================================================
# RNN Structure
#==============================================================================
class LSTMRNN():
    
    #initial setting
    def __init__(self, n_steps, input_size, output_size, cell_size, h1_size, h2_size, h3_size, LR,num_size, batch_size):
        self.n_steps = n_steps
        self.input_size = input_size
        self.output_size = output_size
        self.cell_size = cell_size
        self.num_size = num_size
        self.h1_size = h1_size
        self.h2_size = h2_size
        self.h3_size = h3_size
        self.batch_size = batch_size
        self.LR = LR
        self.num_size = num_size
        
        with tf.name_scope('inputs'):
            self.xs = tf.placeholder(tf.float32, [None, n_steps, input_size], name='xs')
            self.ys = tf.placeholder(tf.float32, [None, output_size], name='ys')
            self.keep_prob = tf.placeholder(tf.float32, name='keep_prob')
            self.train_phase = tf.placeholder(tf.bool, name='train_phase')

            
        with tf.name_scope('in_hidden'):
            self.add_input_layer()
                
        with tf.name_scope('LSTM_Cell'):
            self.add_cell_layer()
            
        with tf.name_scope('hidden_1'):
            self.add_h1_layer()
            
        with tf.name_scope('hidden_2'):
            self.add_h2_layer()

        with tf.name_scope('hidden_3'):
            self.add_h3_layer()
                    
        with tf.name_scope('out_hidden'):
            self.add_output_layer()
    
        with tf.name_scope('cost'):
            self.compute_cost()

        with tf.name_scope('train'):
            self.train_op = tf.train.AdamOptimizer(learning_rate=self.LR).minimize(self.cost)

    def add_input_layer(self):
        with tf.name_scope('input_layer'):
            l_in_x = tf.reshape(self.xs,[-1,self.input_size], name='x_input')
            #Ws_in = tf.Variable(tf.truncated_normal([self.input_size, self.cell_size], mean=1, stddev=0.5))
            Ws_in = tf.get_variable("W", shape=[self.input_size, self.cell_size],initializer=tf.contrib.layers.xavier_initializer())
            bs_in = tf.Variable(tf.zeros([self.cell_size,])+0.01)
            l_in_y = tf.matmul(l_in_x,Ws_in)+bs_in
            self.l_in_y = tf.reshape(l_in_y,[-1,self.n_steps,self.cell_size],name='cell_input')


    def add_cell_layer(self):
        with tf.name_scope('LSTM_layer'):
            lstm_cell = tf.contrib.rnn.DropoutWrapper(tf.contrib.rnn.BasicLSTMCell(self.cell_size, forget_bias=1.0, state_is_tuple=True), output_keep_prob=self.keep_prob)
            lstm_cells = tf.contrib.rnn.MultiRNNCell([lstm_cell]*self.num_size, state_is_tuple=True)
            self.cells_init_state = lstm_cells.zero_state(self.batch_size,dtype=tf.float32)
            self.cells_outputs, self.cells_final_state = tf.nn.dynamic_rnn(lstm_cells, self.l_in_y, initial_state=self.cells_init_state, time_major=False)
            tf.summary.histogram('cells_outputs',self.cells_outputs)
            tf.summary.histogram('cells_final_state',self.cells_final_state)
    def add_h1_layer(self):
        with tf.name_scope('h1_layer'):   
            h1_x = tf.reshape(self.cells_outputs, [-1,self.cell_size])
            #Ws_h1 = tf.Variable(tf.truncated_normal([self.cell_size, self.h1_size], mean=3, stddev=1))
            Ws_h1 = tf.get_variable("W1", shape=[self.cell_size, self.h1_size],initializer=tf.contrib.layers.xavier_initializer())
            bs_h1 = tf.Variable(tf.zeros([self.h1_size,])+0.01)
            non_bn_h1 = tf.nn.relu(tf.matmul(h1_x,Ws_h1)+bs_h1)
            non_bn_h1 = tf.matmul(h1_x,Ws_h1)+bs_h1
            self.h1_y = batch_norm_layer(non_bn_h1, train_phase=self.train_phase, scope_bn='bn_h1')
        

    def add_h2_layer(self):
        with tf.name_scope('h2_layer'):
            h2_x = tf.reshape(self.h1_y, [-1,self.h1_size])
            #Ws_h2 = tf.Variable(tf.truncated_normal([self.h1_size, self.h2_size], mean=3, stddev=2))
            Ws_h2 = tf.get_variable("W2", shape=[self.h1_size, self.h2_size],initializer=tf.contrib.layers.xavier_initializer())
            bs_h2 = tf.Variable(tf.zeros([self.h2_size,])+0.01)
            non_bn_h2 = tf.nn.relu(tf.matmul(h2_x,Ws_h2)+bs_h2)
            non_bn_h2 = tf.matmul(h2_x,Ws_h2)+bs_h2
            self.h2_y = batch_norm_layer(non_bn_h2, train_phase=self.train_phase, scope_bn='bn_h2')

    def add_h3_layer(self):
        with tf.name_scope('h3_layer'):
            h3_x = tf.reshape(self.h2_y, [-1,self.h2_size])
            #Ws_h3 = tf.Variable(tf.truncated_normal([self.h2_size, self.h3_size], mean=3, stddev=2))
            Ws_h3 = tf.get_variable("W3", shape=[self.h2_size, self.h3_size],initializer=tf.contrib.layers.xavier_initializer())
            bs_h3 = tf.Variable(tf.zeros([self.h3_size,])+0.01)
            non_bn_h3 = tf.nn.relu(tf.matmul(h3_x,Ws_h3)+bs_h3)
            non_bn_h3 = tf.matmul(h3_x,Ws_h3)+bs_h3
            self.h3_y = batch_norm_layer(non_bn_h3, train_phase=self.train_phase, scope_bn='bn_h3')

    def add_output_layer(self):
        layer_name='output_layer'
        with tf.name_scope('output_layer'):
            l_out_x = tf.reshape(self.h3_y,[-1,self.h3_size],name = 'y_input')
            #Ws_out = tf.Variable(tf.truncated_normal([self.h3_size, self.output_size], mean=3, stddev=1))
            Ws_out = tf.get_variable("W4", shape=[self.h3_size, self.output_size],initializer=tf.contrib.layers.xavier_initializer())
            bs_out = tf.Variable(tf.zeros([self.output_size,]))
            #self.pred = tf.matmul(l_out_x,Ws_out)+bs_out
            self.pred = tf.nn.sigmoid(tf.matmul(l_out_x,Ws_out)+bs_out)
            #self.pred = tf.matmul(l_out_x,Ws_out)+bs_out
            #self.pred = tf.nn.softmax(tf.matmul(l_out_x,Ws_out)+bs_out)
            tf.summary.histogram('w', Ws_out)
            tf.summary.histogram('b', bs_out)
            tf.summary.histogram('out', self.pred)

#===============================================================================
# 交叉熵
#===============================================================================
    def compute_cost(self):
        with tf.name_scope('loss'):
            self.cost = -tf.reduce_sum(self.ys*tf.log(self.pred+0.001)+(1-self.ys)*tf.log(1-self.pred+0.001))#可以調(diào)節(jié)權(quán)重控制樣本不平衡的數(shù)據(jù)訓(xùn)練
            tf.summary.scalar('result_cost', self.cost)

小結(jié)

  • 值得指出的是故障記錄中的故障發(fā)生時(shí)間并非實(shí)際的葉根螺栓斷裂時(shí)間而是發(fā)現(xiàn)時(shí)間,盡管真正的斷裂時(shí)間對(duì)模型更有意義但實(shí)際中難以捕捉,因此若進(jìn)一步提升模型的實(shí)際效果,可將貼標(biāo)簽的基準(zhǔn)時(shí)間往前順延。
  • 盡管樣本數(shù)據(jù)中存在與葉根螺栓斷裂故障相關(guān)的特征,模型效果較為理想,但并不能排除其他故障或風(fēng)機(jī)自身特性的影響。要訓(xùn)練一個(gè)穩(wěn)健的、高魯棒性的模型還需要更多的故障數(shù)據(jù)。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 專業(yè)考題類(lèi)型管理運(yùn)行工作負(fù)責(zé)人一般作業(yè)考題內(nèi)容選項(xiàng)A選項(xiàng)B選項(xiàng)C選項(xiàng)D選項(xiàng)E選項(xiàng)F正確答案 變電單選GYSZ本規(guī)程...
    小白兔去釣魚(yú)閱讀 10,508評(píng)論 0 13
  • A. 問(wèn)答題 1. 簡(jiǎn)述運(yùn)維工作中專業(yè)巡視的內(nèi)容及周期。 答:運(yùn)維工作中專業(yè)巡視的內(nèi)容及周期如下: 1) 專業(yè)巡視...
    tomding閱讀 3,766評(píng)論 0 2
  • 在閱讀JTA源碼的時(shí)候,看到事務(wù)管理器的是有,遇到這樣一段代碼。 其中的TheadLocal引起了我的注意。 通過(guò)...
    self_vc閱讀 463評(píng)論 1 0
  • 使用AndroidStudio開(kāi)發(fā)的時(shí)候,編譯出錯(cuò),會(huì)提示: 但是去看不到詳細(xì)的錯(cuò)誤地方。其實(shí)只要切換下視圖模式就...
    見(jiàn)字如晤一閱讀 16,596評(píng)論 8 2
  • 貪名圖利是非多, 爭(zhēng)強(qiáng)好勝煩惱隨。 淡泊名利人心靜, 清心寡欲天地寬。
    張旭鋒閱讀 468評(píng)論 2 4

友情鏈接更多精彩內(nèi)容