Projekt 6
Budowa
sensorów programowych przy wykorzystaniu sieci neuronowych
5. Przykłady
W przykładzie wykorzystano sieć neuronową typu Sequencial z biblioteki Keras, o strukturze podobnej jak w projekcie 4.
Do wygenerowania danych uczących wykorzystano generator SP – skoki sygnału SP
w całym zakresie zmian. Testy przeprowadzono dla skoków wokół punktu pracy. Dane przygotowano w ten sposób, że dodano do nich dodatkowe kolumny dla sygnałów wejść i wyjścia z modelu opóźnionych o 1 krok (k-1):
dataset_train['L2_1'] = dataset_train['L2'].shift(1)
dataset_train.loc[0, "L2_1"] = dataset_train['L2_1'][1]
dataset_train['L3_1'] = dataset_train['L3'].shift(1)
dataset_train.loc[0, "L3_1"] = dataset_train['L3_1'][1]
dataset_test['L2_1'] = dataset_test['L2'].shift(1)
dataset_test.loc[0, "L2_1"] = dataset_test['L2_1'][1]
dataset_test['L3_1'] = dataset_test['L3'].shift(1)
dataset_test.loc[0, "L3_1"] = dataset_test['L3_1'][1]




Do modelowania wybrano sieć typu Sequential z biblioteki Keras. Wybrano bardzo prostą strukturę sieci, z jedną warstwą ukrytą i funkcjami aktywacji w postaci tanh oraz optymalizatorem Adam. Wykorzystano normalizację sygnałów wejściowych.
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
# Narmalizacja
normalizer = tf.keras.layers.Normalization(axis=-1)
normalizer.adapt(np.array(train_features))
# Warstwa wejść i konstrukcja modelu
input_layer = keras.layers.Input(train_features.shape[1:])
model = keras.Sequential([
input_layer,
layers.Dense(2, activation='tanh'),
layers.Dense(1)
])
# Kompilacja modelu
model.compile(loss='mean_absolute_error',
optimizer=tf.keras.optimizers.Adam(0.02))
Na rysunkach poniżej przedstawiono podsumowanie jakości modelowania, zarówno w trybie STD jak i MRO. Jest ona zadawalająca, chociaż widać, że w trybie MRO dokładność modelu spada.

L3
na podstawie modelu neuronowego w trybie STD
L3
na podstawie modelu neuronowego w trybie MROAby osadzić model neuronowy w symulatorze przygotowano dedykowany blok funkcyjny KerasModelFirstOrder
(modelling.py
), przygotowany do przetwarzania opóźnionych o 1 krok sygnałów we/wy.
from tensorflow import keras
from common.block import Block
from numpy import array
class KerasModelFirstOrder(Block):
def __init__(self, _name, _model, _mode):
super().__init__(_name)
self.add_output(None, 1, 0.018)
self.mode = _mode
self.model = keras.models.load_model('../output/'+_model+'.keras')
def add_input(self, _input, _del=1):
super().add_input(_input, _del)
def calculate(self):
match self.mode:
case 'std':
features = array([[self.input_val_del(0, 1), self.input_val_del(1, 1)]])
case 'mro':
features = array([[self.input_val_del(0, 1), self.output_val_del(0, 1)]])
case _:
features = 0
self.output_val(0, self.model.predict(features, verbose=None)[0][0])
W ostatnim kroku dodano „czujnik programowy” do konfiguracji głównej symulatora (konstruktor klasy TtsPidSim
). Dodano także dodatkowy parametr konstruktora _use_L3_modelling
, według którego model wykorzystywany jest tylko do odtwarzania wielkości L3
(monit
) lub jest także wykorzystywany w pętli sprzężenia zwrotnego jako wielkość regulowana (control
):
# Process-Controller feadback: use L3 measurement
if _use_L3_modelling!='control':
pv_measure.add_input(three_tanks.output(2))
# Soft-sensor: modelling L3
if _use_L3_modelling=='monit' or _use_L3_modelling=='control' :
l3_pred = monit_sim.add_block( KerasModelFirstOrder('l3_pred', 'L3_L2_1_L3_1', 'std'))
l3_pred.add_input(l2_dist.output(0))
l3_pred.add_input(l3_dist.output(0))
# Process-Controller feadback: use L3 model output
if _use_L3_modelling=='control':
pv_measure.add_input(l3_pred.output(0))
Pojawiła się także nowa zmienna wyjściowa symulatora L3_pred
:
if _use_L3_modelling=='monit' or _use_L3_modelling=='control' :
self.add_out_var('L3_pred', l3_pred.output(0))
Jak widać na rysunkach poniżej osiągnięto zadowalającą, ale daleką od optymalnej, jakość regulacji, zarówno w trybie STD jak i MRO.

