Added data processor code
This commit is contained in:
parent
6c3891aa81
commit
a6d4b1192c
@ -0,0 +1,147 @@
|
|||||||
|
#!/usr/local/bin/python
|
||||||
|
import logging
|
||||||
|
import time
|
||||||
|
|
||||||
|
import firebase_admin
|
||||||
|
import numpy as np
|
||||||
|
import pandas as pd
|
||||||
|
import tensorflow as tf
|
||||||
|
from firebase_admin import credentials, firestore
|
||||||
|
from Flask import Flask
|
||||||
|
from tensorflow import keras
|
||||||
|
|
||||||
|
|
||||||
|
class Firebase:
|
||||||
|
def __init__(self):
|
||||||
|
self.creds = credentials.Certificate(
|
||||||
|
'secrets/icl-iot-weather-firebase-adminsdk.json')
|
||||||
|
firebase_admin.initialize_app(self.creds)
|
||||||
|
self.db = firestore.client()
|
||||||
|
logging.debug('Initialized firebase instance')
|
||||||
|
|
||||||
|
def pull_from_db(self, last_n=24, orderby=u'timestamp'):
|
||||||
|
doc_ref = self.db.collection('weather_data')
|
||||||
|
query = doc_ref.order_by(
|
||||||
|
orderby, direction=firestore.Query.DESCENDING).limit(last_n)
|
||||||
|
doc = query.stream()
|
||||||
|
logging.debug('Got doc file from firestore')
|
||||||
|
return doc
|
||||||
|
|
||||||
|
def convert_to_df(self, data):
|
||||||
|
doc_elements = []
|
||||||
|
for element in data:
|
||||||
|
doc_elements.append(element)
|
||||||
|
dict_dataset = []
|
||||||
|
for element in doc_elements:
|
||||||
|
dict_dataset.append(element.to_dict())
|
||||||
|
df = pd.DataFrame(dict_dataset)
|
||||||
|
logging.debug(f'Acquired dataframe, length: {len(df)}')
|
||||||
|
return df
|
||||||
|
|
||||||
|
def get_day_df(self):
|
||||||
|
doc = self.pull_from_db()
|
||||||
|
df = self.convert_to_df(doc)
|
||||||
|
return df
|
||||||
|
|
||||||
|
|
||||||
|
class DataProcessor:
|
||||||
|
def __init__(self):
|
||||||
|
self.dataset_mean = [82.02044198895028, 10.402061304914362,
|
||||||
|
8.634944751381251, 67.74198895027624,
|
||||||
|
3.7384419889503326, 0.1401436464088398]
|
||||||
|
self.dataset_std = [9.125022051705823, 2.9134906109549754,
|
||||||
|
4.024228079173571, 32.40531138722004,
|
||||||
|
1.9577510487361403, 0.7008829473121821]
|
||||||
|
self.arranged_columns = [
|
||||||
|
'humidity', 'local_soil_temperature',
|
||||||
|
'temp', 'cloud', 'wind', 'rain_1h']
|
||||||
|
|
||||||
|
def drop_useless(self, df):
|
||||||
|
clean_df = df.drop(columns=['datetime', 'is_test',
|
||||||
|
'local_soil_humidity', 'timestamp'])
|
||||||
|
return clean_df
|
||||||
|
|
||||||
|
def normalize(self, df):
|
||||||
|
df = df[self.arranged_columns]
|
||||||
|
normalized_df = (df - self.dataset_mean)/self.dataset_std
|
||||||
|
return normalized_df
|
||||||
|
|
||||||
|
def correct(self, df, orig_df):
|
||||||
|
df['rain_1h'] = orig_df['rain_1h']
|
||||||
|
df['cloud'] = orig_df['cloud']/100 - 0.5
|
||||||
|
return df
|
||||||
|
|
||||||
|
def calculate_avg(self, df):
|
||||||
|
total_daily_rain = df['rain_1h'].sum()
|
||||||
|
df = df.drop(columns=['rain_1h'])
|
||||||
|
day_avg = df.mean()
|
||||||
|
day_avg['rain_24h'] = total_daily_rain
|
||||||
|
day_avg_dict = day_avg.to_dict()
|
||||||
|
columns = day_avg_dict.keys()
|
||||||
|
rows = [day_avg_dict.values()]
|
||||||
|
day_avg_df = pd.DataFrame(data=rows, columns=columns)
|
||||||
|
return day_avg_df
|
||||||
|
|
||||||
|
def prepare_for_prediction(self, data):
|
||||||
|
clean_df = self.drop_useless(data)
|
||||||
|
normalized_df = self.normalize(clean_df)
|
||||||
|
corrected_df = self.correct(normalized_df, clean_df)
|
||||||
|
daily_avg_df = self.calculate_avg(corrected_df)
|
||||||
|
return daily_avg_df
|
||||||
|
|
||||||
|
|
||||||
|
class WaterPredictor:
|
||||||
|
__PLANT_AREA_M2 = 1
|
||||||
|
__WATER_ML_PER_M2 = 500
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.firebase = Firebase()
|
||||||
|
self.data_processor = DataProcessor()
|
||||||
|
self.model = keras.models.load_model('watering_model.model')
|
||||||
|
logging.debug('Loaded ML model')
|
||||||
|
|
||||||
|
def bias_to_pct(self, bias):
|
||||||
|
offset_pct = bias[0][0]*100
|
||||||
|
if offset_pct > 100:
|
||||||
|
offset_pct = 100
|
||||||
|
if offset_pct < -100:
|
||||||
|
offset_pct = -100
|
||||||
|
return offset_pct
|
||||||
|
|
||||||
|
def calculate_predicted_volume(self, offset_pct):
|
||||||
|
baseline_volume = self.__PLANT_AREA_M2*self.__WATER_ML_PER_M2
|
||||||
|
total_water_ml_day = baseline_volume + offset_pct*(baseline_volume/100)
|
||||||
|
return total_water_ml_day
|
||||||
|
|
||||||
|
def predict_water_ml(self):
|
||||||
|
df = self.firebase.get_day_df()
|
||||||
|
feature_df = self.data_processor.prepare_for_prediction(df)
|
||||||
|
features = {name: np.array(value)
|
||||||
|
for name, value in feature_df.items()}
|
||||||
|
day_pred_bias = self.model.predict(features)
|
||||||
|
offset_pct = self.bias_to_pct(day_pred_bias)
|
||||||
|
predicted_watering_vol = self.calculate_predicted_volume(offset_pct)
|
||||||
|
logging.debug(f'Predicted watering volume: {predicted_watering_vol}')
|
||||||
|
return predicted_watering_vol
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
logging.root.setLevel(logging.DEBUG)
|
||||||
|
predictor = WaterPredictor()
|
||||||
|
|
||||||
|
server = Flask(__name__)
|
||||||
|
|
||||||
|
@server.route("/")
|
||||||
|
def root():
|
||||||
|
return "<h1>IoT-ICL DE Watering Predictor running...</h1>"
|
||||||
|
|
||||||
|
@server.route("/water")
|
||||||
|
def get_water_vol():
|
||||||
|
try:
|
||||||
|
vol = predictor.predict_water_ml()
|
||||||
|
return {'success': True, 'value': vol}
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f'Encountered error: {e}')
|
||||||
|
return {'success': False, 'error': e}
|
||||||
|
|
||||||
|
server.run(host='0.0.0.0', port='3535', use_reloader=False)
|
||||||
BIN
data_processor/watering_model.model/saved_model.pb
Normal file
BIN
data_processor/watering_model.model/saved_model.pb
Normal file
Binary file not shown.
Binary file not shown.
BIN
data_processor/watering_model.model/variables/variables.index
Normal file
BIN
data_processor/watering_model.model/variables/variables.index
Normal file
Binary file not shown.
@ -64,7 +64,7 @@ class LoRa:
|
|||||||
|
|
||||||
|
|
||||||
class Irrigator:
|
class Irrigator:
|
||||||
__DATA_ENDPOINT = "http://todo.maxhunt/design/water"
|
__DATA_ENDPOINT = "http://api.maxhunt.design/water"
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.com = LoRa()
|
self.com = LoRa()
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user