303 lines
12 KiB
Python
303 lines
12 KiB
Python
import numpy as np
|
|
import joblib
|
|
import pickle
|
|
from datetime import datetime, timedelta
|
|
import random
|
|
|
|
class PlantGrowthModel:
|
|
def __init__(self):
|
|
self.plant_characteristics = {
|
|
'tomato': {
|
|
'max_height': 150,
|
|
'growth_rate_base': 2.5,
|
|
'optimal_temp': (20, 25),
|
|
'optimal_humidity': (60, 70),
|
|
'optimal_ph': (6.0, 6.8),
|
|
'phases': [
|
|
{'name': 'Germination', 'duration': 7, 'growth_factor': 0.1},
|
|
{'name': 'Seedling', 'duration': 14, 'growth_factor': 0.3},
|
|
{'name': 'Vegetative', 'duration': 30, 'growth_factor': 1.0},
|
|
{'name': 'Flowering', 'duration': 21, 'growth_factor': 0.7},
|
|
{'name': 'Fruiting', 'duration': 28, 'growth_factor': 0.4}
|
|
]
|
|
},
|
|
'basil': {
|
|
'max_height': 60,
|
|
'growth_rate_base': 1.8,
|
|
'optimal_temp': (18, 24),
|
|
'optimal_humidity': (50, 65),
|
|
'optimal_ph': (6.0, 7.0),
|
|
'phases': [
|
|
{'name': 'Germination', 'duration': 5, 'growth_factor': 0.1},
|
|
{'name': 'Seedling', 'duration': 10, 'growth_factor': 0.4},
|
|
{'name': 'Vegetative', 'duration': 25, 'growth_factor': 1.0},
|
|
{'name': 'Mature', 'duration': 30, 'growth_factor': 0.6}
|
|
]
|
|
},
|
|
'mint': {
|
|
'max_height': 40,
|
|
'growth_rate_base': 2.0,
|
|
'optimal_temp': (15, 22),
|
|
'optimal_humidity': (65, 75),
|
|
'optimal_ph': (6.0, 7.0),
|
|
'phases': [
|
|
{'name': 'Germination', 'duration': 7, 'growth_factor': 0.1},
|
|
{'name': 'Seedling', 'duration': 12, 'growth_factor': 0.3},
|
|
{'name': 'Vegetative', 'duration': 20, 'growth_factor': 1.0},
|
|
{'name': 'Mature', 'duration': 35, 'growth_factor': 0.5}
|
|
]
|
|
},
|
|
'lettuce': {
|
|
'max_height': 25,
|
|
'growth_rate_base': 1.2,
|
|
'optimal_temp': (16, 20),
|
|
'optimal_humidity': (70, 80),
|
|
'optimal_ph': (6.0, 7.0),
|
|
'phases': [
|
|
{'name': 'Germination', 'duration': 4, 'growth_factor': 0.1},
|
|
{'name': 'Seedling', 'duration': 8, 'growth_factor': 0.4},
|
|
{'name': 'Vegetative', 'duration': 20, 'growth_factor': 1.0},
|
|
{'name': 'Mature', 'duration': 18, 'growth_factor': 0.3}
|
|
]
|
|
},
|
|
'rosemary': {
|
|
'max_height': 120,
|
|
'growth_rate_base': 0.8,
|
|
'optimal_temp': (18, 25),
|
|
'optimal_humidity': (40, 55),
|
|
'optimal_ph': (6.0, 7.5),
|
|
'phases': [
|
|
{'name': 'Germination', 'duration': 14, 'growth_factor': 0.05},
|
|
{'name': 'Seedling', 'duration': 21, 'growth_factor': 0.2},
|
|
{'name': 'Vegetative', 'duration': 60, 'growth_factor': 1.0},
|
|
{'name': 'Mature', 'duration': 90, 'growth_factor': 0.4}
|
|
]
|
|
},
|
|
'strawberry': {
|
|
'max_height': 30,
|
|
'growth_rate_base': 1.5,
|
|
'optimal_temp': (18, 24),
|
|
'optimal_humidity': (60, 70),
|
|
'optimal_ph': (5.5, 6.5),
|
|
'phases': [
|
|
{'name': 'Germination', 'duration': 10, 'growth_factor': 0.1},
|
|
{'name': 'Seedling', 'duration': 15, 'growth_factor': 0.3},
|
|
{'name': 'Vegetative', 'duration': 25, 'growth_factor': 1.0},
|
|
{'name': 'Flowering', 'duration': 20, 'growth_factor': 0.6},
|
|
{'name': 'Fruiting', 'duration': 30, 'growth_factor': 0.4}
|
|
]
|
|
}
|
|
}
|
|
|
|
def predict_growth(self, parameters):
|
|
"""Predict plant growth based on environmental parameters"""
|
|
plant_type = parameters.get('plant_type', 'tomato')
|
|
ambient_mode = parameters.get('ambient_mode', 'controlled')
|
|
|
|
if plant_type not in self.plant_characteristics:
|
|
plant_type = 'tomato'
|
|
|
|
plant_info = self.plant_characteristics[plant_type]
|
|
|
|
# Calculate environmental stress factors
|
|
stress_factors = self._calculate_stress_factors(parameters, plant_info)
|
|
|
|
# Apply ambient mode effects
|
|
if ambient_mode == 'open':
|
|
# Add random variations for open environment
|
|
for factor in stress_factors:
|
|
stress_factors[factor] *= (0.8 + random.random() * 0.4)
|
|
elif ambient_mode == 'semi-controlled':
|
|
# Moderate variations
|
|
for factor in stress_factors:
|
|
stress_factors[factor] *= (0.9 + random.random() * 0.2)
|
|
|
|
# Calculate overall health score
|
|
health_score = np.mean(list(stress_factors.values())) * 100
|
|
|
|
# Generate growth stages
|
|
growth_stages = self._simulate_growth_stages(plant_info, stress_factors)
|
|
|
|
# Calculate final metrics
|
|
final_height = growth_stages[-1] if growth_stages else 0
|
|
growth_rate = final_height / len(growth_stages) if growth_stages else 0
|
|
|
|
# Calculate optimal conditions percentage
|
|
optimal_conditions = self._calculate_optimal_conditions(parameters, plant_info)
|
|
|
|
# Estimate yield
|
|
yield_estimate = self._estimate_yield(plant_type, health_score, final_height)
|
|
|
|
# Generate phase information
|
|
phases = self._generate_phase_info(plant_info)
|
|
|
|
return {
|
|
'growth_stages': growth_stages,
|
|
'final_height': final_height,
|
|
'growth_rate': growth_rate,
|
|
'health_score': health_score,
|
|
'optimal_conditions': optimal_conditions,
|
|
'yield': yield_estimate,
|
|
'phases': phases,
|
|
'stress_factors': stress_factors
|
|
}
|
|
|
|
def _calculate_stress_factors(self, params, plant_info):
|
|
"""Calculate stress factors for each environmental parameter"""
|
|
factors = {}
|
|
|
|
# Temperature stress
|
|
temp = params.get('temperature', 20)
|
|
opt_temp = plant_info['optimal_temp']
|
|
if opt_temp[0] <= temp <= opt_temp[1]:
|
|
factors['temperature'] = 1.0
|
|
else:
|
|
deviation = min(abs(temp - opt_temp[0]), abs(temp - opt_temp[1]))
|
|
factors['temperature'] = max(0.1, 1.0 - deviation / 20.0)
|
|
|
|
# Humidity stress
|
|
humidity = params.get('humidity', 60)
|
|
opt_humidity = plant_info['optimal_humidity']
|
|
if opt_humidity[0] <= humidity <= opt_humidity[1]:
|
|
factors['humidity'] = 1.0
|
|
else:
|
|
deviation = min(abs(humidity - opt_humidity[0]), abs(humidity - opt_humidity[1]))
|
|
factors['humidity'] = max(0.1, 1.0 - deviation / 40.0)
|
|
|
|
# pH stress
|
|
ph = params.get('soil_acidity', 6.5)
|
|
opt_ph = plant_info['optimal_ph']
|
|
if opt_ph[0] <= ph <= opt_ph[1]:
|
|
factors['ph'] = 1.0
|
|
else:
|
|
deviation = min(abs(ph - opt_ph[0]), abs(ph - opt_ph[1]))
|
|
factors['ph'] = max(0.1, 1.0 - deviation / 2.0)
|
|
|
|
# Water stress
|
|
water = params.get('water', 70) / 100.0
|
|
factors['water'] = min(1.0, max(0.1, water))
|
|
|
|
# Nutrient stress
|
|
nutrients = params.get('nutrients', 70) / 100.0
|
|
factors['nutrients'] = min(1.0, max(0.1, nutrients))
|
|
|
|
# Light stress
|
|
brightness = params.get('brightness', 30000)
|
|
optimal_light = 40000 # Optimal light in lux
|
|
light_factor = min(1.0, brightness / optimal_light)
|
|
factors['light'] = max(0.1, light_factor)
|
|
|
|
# CO2 stress
|
|
co2 = params.get('co2', 400)
|
|
optimal_co2 = 600 # Optimal CO2 in ppm
|
|
co2_factor = min(1.0, co2 / optimal_co2)
|
|
factors['co2'] = max(0.3, co2_factor)
|
|
|
|
return factors
|
|
|
|
def _simulate_growth_stages(self, plant_info, stress_factors):
|
|
"""Simulate daily growth stages"""
|
|
base_rate = plant_info['growth_rate_base']
|
|
max_height = plant_info['max_height']
|
|
phases = plant_info['phases']
|
|
|
|
overall_stress = np.mean(list(stress_factors.values()))
|
|
adjusted_rate = base_rate * overall_stress
|
|
|
|
growth_stages = []
|
|
current_height = 0
|
|
day = 0
|
|
|
|
for phase in phases:
|
|
phase_duration = phase['duration']
|
|
growth_factor = phase['growth_factor']
|
|
|
|
for _ in range(phase_duration):
|
|
daily_growth = adjusted_rate * growth_factor
|
|
# Add some randomness
|
|
daily_growth *= (0.8 + random.random() * 0.4)
|
|
|
|
current_height += daily_growth
|
|
current_height = min(current_height, max_height)
|
|
growth_stages.append(current_height)
|
|
day += 1
|
|
|
|
return growth_stages
|
|
|
|
def _calculate_optimal_conditions(self, params, plant_info):
|
|
"""Calculate percentage of optimal conditions met"""
|
|
optimal_count = 0
|
|
total_conditions = 3 # temp, humidity, pH
|
|
|
|
temp = params.get('temperature', 20)
|
|
if plant_info['optimal_temp'][0] <= temp <= plant_info['optimal_temp'][1]:
|
|
optimal_count += 1
|
|
|
|
humidity = params.get('humidity', 60)
|
|
if plant_info['optimal_humidity'][0] <= humidity <= plant_info['optimal_humidity'][1]:
|
|
optimal_count += 1
|
|
|
|
ph = params.get('soil_acidity', 6.5)
|
|
if plant_info['optimal_ph'][0] <= ph <= plant_info['optimal_ph'][1]:
|
|
optimal_count += 1
|
|
|
|
return (optimal_count / total_conditions) * 100
|
|
|
|
def _estimate_yield(self, plant_type, health_score, final_height):
|
|
"""Estimate plant yield based on health and growth"""
|
|
yield_factors = {
|
|
'tomato': {'base': 2.0, 'unit': 'kg'},
|
|
'basil': {'base': 0.3, 'unit': 'kg'},
|
|
'mint': {'base': 0.2, 'unit': 'kg'},
|
|
'lettuce': {'base': 0.5, 'unit': 'kg'},
|
|
'rosemary': {'base': 0.1, 'unit': 'kg'},
|
|
'strawberry': {'base': 0.8, 'unit': 'kg'}
|
|
}
|
|
|
|
if plant_type in yield_factors:
|
|
base_yield = yield_factors[plant_type]['base']
|
|
unit = yield_factors[plant_type]['unit']
|
|
|
|
# Adjust yield based on health and height
|
|
health_factor = health_score / 100.0
|
|
height_factor = min(1.0, final_height / 50.0) # Normalize height
|
|
|
|
estimated_yield = base_yield * health_factor * height_factor
|
|
return f"{estimated_yield:.2f} {unit}"
|
|
|
|
return "N/A"
|
|
|
|
def _generate_phase_info(self, plant_info):
|
|
"""Generate phase information with start/end days"""
|
|
phases = []
|
|
current_day = 0
|
|
|
|
for phase in plant_info['phases']:
|
|
phases.append({
|
|
'name': phase['name'],
|
|
'start': current_day,
|
|
'end': current_day + phase['duration'] - 1
|
|
})
|
|
current_day += phase['duration']
|
|
|
|
return phases
|
|
|
|
def save_model(self, filename):
|
|
"""Save the model to a file"""
|
|
try:
|
|
with open(filename, 'wb') as f:
|
|
pickle.dump(self.plant_characteristics, f)
|
|
return True
|
|
except Exception as e:
|
|
print(f"Error saving model: {e}")
|
|
return False
|
|
|
|
def load_model(self, filename):
|
|
"""Load the model from a file"""
|
|
try:
|
|
with open(filename, 'rb') as f:
|
|
self.plant_characteristics = pickle.load(f)
|
|
return True
|
|
except Exception as e:
|
|
print(f"Error loading model: {e}")
|
|
return False
|