Buğday kesiksineği (Mayetiola destructor), Türkiye'de özellikle Trakya bölgesinde önemli ekonomik kayıplara neden olan bir zararlıdır. Geleneksel mücadele yöntemleri genellikle reaktif ve takvim bazlı ilaçlamaya dayanmakta, bu da gereksiz kimyasal kullanımı, maliyet artışı ve çevresel sorunlara yol açmaktadır. Oysa zararlının biyolojisi ve fenolojisi sıcaklık gibi çevresel faktörlerle yakından ilişkilidir ve bu durum, veri analizi ve yapay zekâ teknolojilerinin tarımsal karar destek sistemlerinde kullanılması için büyük bir fırsat sunmaktadır.
ZARARLININ BİYOLOJİSİ VE EKONOMİK ÖNEMİ
Morfolojik Özellikler
Ergin birey: Ortalama 4 mm boyunda, koyu gri veya siyah renkli, abdomen kırmızımsı kahverengidir. Sivrisineği andıran görünümüyle dikkat çeker. Antenler dişilerde vücut uzunluğunun 1/3'ü, erkeklerde ise yarısı kadardır.
Yumurta: Silindir şeklinde, kırmızı renkte, 0.5 mm boyundadır.
Larva: Bacaksız, iğ şeklinde, sarımsı beyazdan soluk yeşile kadar değişen renklerde, 4-5 mm boyundadır.
Pupa: 9-10 mm boyunda, koyu kahverengi, iğ şeklinde olup, bulaşık bitkilerde kolayca görülebilir.
Yaşam Döngüsü ve Fenoloji
Buğday kesiksineği kışı pupa döneminde geçirir. İlkbaharda pupadan çıkan erginler aktif değildir, beslenmez ve yalnızca 4-5 gün yaşarlar. Bir dişi ortalama 300 yumurta bırakabilir. Yumurtalar 3-7 günde açılır, larvalar 25-30 günde üç larva dönemini tamamlayarak pupa dönemine geçer. Zararlı yaz aylarını tarlada kalan sap ve artıklar üzerinde pupa halinde geçirir. İlkbahar ve sonbaharda aktif olup, yılda altıdan fazla döl verebilir.
Zarar Şekli
Asıl zarar larvalar tarafından iki kritik dönemde meydana gelir:
Sonbahar zararı: Tarlada kalan bitkilerdeki pupalardan çıkan erginlerin bıraktığı yumurtalardan çıkan larvalar, gövdenin kaide kısmında emmek suretiyle beslenir. Bu şekilde soğana benzer şişlikler oluşur, büyüme noktası tahrip olabilir ve bitki ölüme gidebilir. Kardeşlerde cüceleşme ve tarlada seyrekleşmeler görülür.
İlkbahar zararı: Kışı geçiren pupalardan çıkan ikinci nesil erginlerin yumurta bırakmasıyla başlar. Larvalar sapa kalkma döneminde, toprak yüzeyine yakın ilk boğumun hemen üstünde yaprak kını ile sap arasına girerek bitki özsuyunu emer. Bu durum başağa besin naklini engeller, başak oluşumu ve dane kalitesini olumsuz etkiler. Zarar görmüş saplar zamanla kırılarak dane kaybına yol açar.
Konukçular ve Yayılış
Buğday, arpa, çavdar, yulaf ve yabani buğdaygiller konukçularıdır. Ülkemizde Trakya bölgesinde tespit edilmiştir.
GELENEKSEL MÜCADELE YÖNTEMLERİ VE SINIRLAMALARI
Kültürel Önlemler
- Sonbahar zararından korunmak için geç ekim
- Hasattan arta kalan sap ve artıkların derin sürümle yok edilmesi
- Ekim nöbeti uygulanması
- Hasadın gecikmemesi ve dane kaybının önlenmesi
Biyolojik Mücadele
Dünyada en iyi bilinen biyolojik kontrol ajanları Hymenoptera takımına ait parazitoitlerdir:
- Platygaster hiemalis (Platygastridae): Larva-pupa parazitoididir ve popülasyonu baskılayabilen en önemli doğal düşmandır.
- Platygaster herrickii
- Eupelmus spp.
- Tetrastichus spp.
Doğal koşullarda %30-70'e varan parazitlenme oranları bildirilmiştir. Ancak monokültür alanlarda ve yoğun kimyasal kullanımında etkinlik düşmektedir. Türkiye'de doğal popülasyonları sınırlıdır ve kitle üretimi ile salımı rutin değildir.
Geleneksel Yaklaşımın Sınırlamaları
Geleneksel mücadele yaklaşımının en büyük sorunu, "her yıl aynı şeyi yapma" alışkanlığıdır. Oysa:
- Her yıl aynı risk seviyesinde olmaz
- Bazı yıllar zararlı baskısı doğal olarak düşüktür
- Geç ekim bazı yıllarda verimi düşürebilir
- Kimyasal kullanımı çoğu zaman gereksizdir
- Zarar, larva bitki içine girdikten sonra fark edilir; bu aşamada müdahale çok geçtir
Bu noktada veri analizi devreye girer ve zarar oluşmadan önce şu kritik sorulara yanıt üretir:
- Ergin çıkışı ne zaman başlayacak?
- Yumurta bırakma için en riskli günler hangileri?
- Sonbahar mı, ilkbahar mı daha tehlikeli?
- Geç ekim gerçekten bu yıl işe yarayacak mı?
VERİ ANALİZİ VE YAPAY ZEKÂNIN ROLÜ
Kullanılan Veri Türleri
Meteorolojik Veriler:
- Günlük ortalama sıcaklık
- Toprak sıcaklığı (0-5 cm)
- Bağıl nem
- Yağış
Ergin çıkışı ve yumurta bırakma sıcaklık eşiğine bağlıdır. Genellikle 8-10°C üstünde ergin aktivitesi başlar, 15-20°C'de maksimum yumurtlama gerçekleşir.
Fenolojik Veriler:
- Ekim tarihi
- Bitkinin gelişim dönemi (çıkış, kardeşlenme, sapa kalkma)
Zararlı ile bitkinin hassas dönemi çakışıyorsa zarar maksimum olur.
Geçmiş Popülasyon ve Zarar Verileri:
- Önceki yılların zarar yoğunluğu
- Aynı tarladaki tekrar sıklığı
Bu veri olmadan yapılan mücadele "kör atış" olur.
Derece-Gün (Degree-Day) Modeli
Derece-gün modeli, böceklerin gelişiminin sıcaklığa bağlı olduğu prensibine dayanır.
Temel Parametreler:
- Alt gelişim eşiği (Tbase): 8°C
- Optimum aktivite sıcaklığı: 15-20°C
- Ergin çıkışı için gereken DD: ~180-220
- Yumurtlama zirvesi: ~250 DD
Günlük DD Hesabı:
DD = ((Tmax + Tmin) / 2) - Tbase
Eğer sonuç negatifse, 0 kabul edilir.
Birikimli DD:
Toplam_DD = Σ Günlük_DD
Excel'de Uygulama:
| Tarih | Tmin | Tmax | Ortalama | Günlük DD | Birikimli DD | Risk Durumu |
|-------|------|------|----------|-----------|--------------|-------------|
| ... | ... | ... | =ORTALAMA(B2:C2) | =MAX(0,D2-8) | =E2+F1 | =EĞER(F2<180,"Düşük",EĞER(F2<=250,"Yüksek","Çok Geç")) |
Yapay Zekânın Katkısı
Derece-gün modeli "ne zaman olabilir?" sorusunu yanıtlarken, yapay zekâ "olur mu, olmaz mı?" sorusunu yanıtlar.
Klasik Model vs AI:
| Klasik Model | AI Model |
|--------------|----------|
| Sadece sıcaklığa bakar | Sıcaklık + nem + yağış + ekim tarihi + önceki yıl zararı + toprak tipi + bitki gelişim hızı |
| Tek boyutlu | Çok değişkenli |
| Biyolojiye dayanır | İstatistiksel öğrenme |
AI'nin Güçlü Olduğu Alanlar:
- "Bu yıl risk yılı mı, risksiz mi?"
- Sonbahar mı, ilkbahar mı daha tehlikeli olacak?
- Geç ekim bu yıl işe yarar mı, verimi düşürür mü?
- Tohum ilaçlaması gerçekten gerekli mi?
Kullanılan AI Algoritmaları
Random Forest (Rastgele Orman):
- Küçük veriyle çalışır
- Aşırı öğrenme riski düşüktür
- Tarımda yaygın olarak kullanılır
- Değişken önem sıralaması yapabilir
XGBoost (Extreme Gradient Boosting):
- Daha hassas tahmin
- Risk skoru üretir
- Karmaşık ilişkileri yakalayabilir
Model Girdileri ve Çıktıları
Girdiler (X):
| Değişken | Açıklama |
|----------|----------|
| avg_temp | Sezon ortalama sıcaklık |
| total_dd | Toplam derece-gün |
| soil_temp | Toprak sıcaklığı |
| rainfall | Sezon yağışı |
| sowing_day | Ekim günü (yılın kaçıncı günü) |
| prev_damage | Önceki yıl zarar (0/1) |
Çıktı (y):
- risk: 0 = Müdahale gerekmez, 1 = Müdahale gerekir
Örnek Veri Seti:
csv
avg_temp,total_dd,soil_temp,rainfall,sowing_day,prev_damage,risk
12.4,210,10.1,320,295,1,1
10.8,160,8.5,280,305,0,0
13.2,245,11.0,340,290,1,1
9.9,140,7.8,260,310,0,0
Python ile Model Eğitimi
python
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score, classification_report
import joblib
Veri yükleme
data = pd.read_csv("kesiksinegi.csv")
X = data.drop("risk", axis=1)
y = data["risk"]
Eğitim / Test bölme
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.25, random_state=42
)
Model oluşturma ve eğitim
model = RandomForestClassifier(
n_estimators=200,
max_depth=6,
random_state=42
)
model.fit(X_train, y_train)
Test ve değerlendirme
pred = model.predict(X_test)
print("Doğruluk:", accuracy_score(y_test, pred))
print(classification_report(y_test, pred))
Model kaydetme
joblib.dump(model, "kesiksinegi_ai.pkl")
Gerçek Zamanlı Tahmin
python
Yeni tarla verisi
new_field = [[
12.1, # avg_temp
215, # total_dd
10.3, # soil_temp
310, # rainfall
298, # sowing_day
1 # prev_damage
]]
Risk tahmini
risk = model.predict(new_field)
risk_prob = model.predict_proba(new_field)
print("Risk:", risk[0])
print("Olasılık:", risk_prob)
Beklenen Faydalar
Doğru kurulmuş bir AI sistemiyle:
- Gereksiz tohum ilaçlaması %40-60 azalır
- Riskli yıllar önceden yakalanır
- Kimyasal sadece gerektiğinde kullanılır
- Uzun vadede direnç ve maliyet düşer
- "Her yıl aynı şeyi yapma" alışkanlığı kırılır
IoT TABANLI ERKEN UYARI SİSTEMİ TASARIMI
Sistem Mimarisi
Tarla Sensörleri (ESP32)
↓
Gerçek Zamanlı Veri Toplama
↓
Bulut Sunucu (API)
↓
Veritabanı (PostgreSQL/SQLite)
↓
DD Hesaplama + AI Model
↓
Risk Analizi
↓
Uyarı Sistemi (SMS/WhatsApp/Uygulama)
↓
Çiftçi
Donanım Bileşenleri
Ana Kontrol Ünitesi:
- ESP32 DevKit v1: Wi-Fi/Bluetooth destekli, düşük güç tüketimli mikrodenetleyici
Sensörler:
- DS18B20: Su geçirmez toprak sıcaklığı sensörü (0-5 cm derinlik)
- DHT22: Hava sıcaklığı ve nem sensörü
- DS3231 RTC (Opsiyonel): Gerçek zamanlı saat modülü
Destek Bileşenleri:
- 4.7 kΩ direnç (DS18B20 data hattı için)
- Güneş paneli + batarya (15-30 gün kesintisiz çalışma)
- IP65 koruma kutsu (dış ortam dayanıklılığı)
İletişim (Alternatifler):
- Wi-Fi (ESP32 dahili)
- GSM modül
- LoRa modül
Pin Bağlantıları
DS18B20 (Toprak Sıcaklığı):
- VCC → 3.3V
- GND → GND
- DATA → GPIO 4
- DATA ↔ VCC arasına 4.7 kΩ direnç (pull-up)
DHT22 (Hava Sıcaklığı + Nem):
- VCC → 3.3V
- GND → GND
- DATA → GPIO 15
DS3231 RTC (Opsiyonel):
- VCC → 3.3V
- GND → GND
- SDA → GPIO 21
- SCL → GPIO 22
ESP32 Yazılımı
cpp
#include
#include
#include
#include
#include
// ====== WiFi Ayarları ======
const char* ssid = "WIFI_ADI";
const char* password = "WIFI_SIFRE";
// ====== Sensör Tanımlamaları ======
#define ONE_WIRE_BUS 4
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature soilSensor(&oneWire);
#define DHTPIN 15
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
// ====== Derece-Gün Parametreleri ======
float Tbase = 8.0; // Alt gelişim eşiği (°C)
float dailyDD = 0.0;
float cumulativeDD = 0.0;
// ====== Sunucu Adresi ======
String serverURL = "https://SIZIN_API_ADRESINIZ/api/data";
void setup() {
Serial.begin(115200);
// WiFi bağlantısı
WiFi.begin(ssid, password);
Serial.print("WiFi'ye bağlanılıyor");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi bağlandı!");
Serial.print("IP Adresi: ");
Serial.println(WiFi.localIP());
// Sensörleri başlat
soilSensor.begin();
dht.begin();
Serial.println("Sistem hazır. Ölçümler başlıyor...");
}
void loop() {
// ====== Sensör Okuma ======
soilSensor.requestTemperatures();
float soilTemp = soilSensor.getTempCByIndex(0);
float airTemp = dht.readTemperature();
float humidity = dht.readHumidity();
// Hata kontrolü
if (isnan(airTemp) || isnan(humidity)) {
Serial.println("Sensör okunamadı! Tekrar deneniyor...");
delay(60000);
return;
}
if (soilTemp == -127.0) {
Serial.println("Toprak sıcaklık sensörü okunamadı!");
delay(60000);
return;
}
// ====== Derece-Gün Hesaplama ======
// Saatlik ortalama sıcaklığa göre DD hesabı
float hourlyDD = airTemp - Tbase;
if (hourlyDD < 0) hourlyDD = 0;
// Günlük DD biriktirme (24 saatlik ortalama)
dailyDD += hourlyDD / 24.0;
// ====== Gün Sonu İşlemleri ======
static int hourCount = 0;
hourCount++;
// Konsol çıktısı
Serial.println("========================================");
Serial.print("Hava Sıcaklığı: "); Serial.print(airTemp); Serial.println(" °C");
Serial.print("Toprak Sıcaklığı: "); Serial.print(soilTemp); Serial.println(" °C");
Serial.print("Nem: "); Serial.print(humidity); Serial.println(" %");
Serial.print("Saatlik DD: "); Serial.println(hourlyDD);
Serial.print("Günlük DD (kümülatif): "); Serial.println(dailyDD);
Serial.print("Toplam DD: "); Serial.println(cumulativeDD);
Serial.println("========================================");
// Her 24 saatte bir sunucuya gönder
if (hourCount >= 24) {
cumulativeDD += dailyDD;
Serial.println("\n GÜN SONU - VERİLER GÖNDERİLİYOR ");
sendToServer(airTemp, soilTemp, humidity, dailyDD, cumulativeDD);
// Günlük değerleri sıfırla
dailyDD = 0;
hourCount = 0;
}
// 1 saat bekle (3600000 ms)
delay(3600000);
}
void sendToServer(float airT, float soilT, float hum, float dd, float cdd) {
if (WiFi.status() == WL_CONNECTED) {
HTTPClient http;
http.begin(serverURL);
http.addHeader("Content-Type", "application/json");
// JSON payload oluşturma
String payload = "{";
payload += "\"air_temp\":" + String(airT, 2) + ",";
payload += "\"soil_temp\":" + String(soilT, 2) + ",";
payload += "\"humidity\":" + String(hum, 2) + ",";
payload += "\"daily_dd\":" + String(dd, 2) + ",";
payload += "\"cumulative_dd\":" + String(cdd, 2);
payload += "}";
Serial.println("Gönderilen veri:");
Serial.println(payload);
// POST isteği
int httpResponseCode = http.POST(payload);
if (httpResponseCode > 0) {
String response = http.getString();
Serial.print("Sunucu yanıtı (Kod: ");
Serial.print(httpResponseCode);
Serial.println("):");
Serial.println(response);
} else {
Serial.print("Hata! HTTP Kodu: ");
Serial.println(httpResponseCode);
}
http.end();
} else {
Serial.println("WiFi bağlantısı yok!");
}
}
Sistem Özellikleri ve Çalışma Prensibi
Gerçek Zamanlı Ölçüm:
- Saatlik hava sıcaklığı
- Toprak sıcaklığı (0-5 cm derinlik)
- Bağıl nem
Otomatik Derece-Gün Hesabı:
- Saatlik DD hesaplama
- Günlük DD birikimi
- Sezon boyunca kümülatif DD
Eşik Tabanlı Uyarı:
- 180 DD → "Risk başlıyor"
- 220 DD → "Müdahale penceresi"
- 250 DD → "Geç kalındı"
Enerji Yönetimi:
- Güneş paneli + batarya
- Deep sleep modu (isteğe bağlı)
- 15-30 gün bakım gerektirmez
BULUT VE YAPAY ZEKÂ ENTEGRASYONU
API Geliştirme (FastAPI)
python
from fastapi import FastAPI
from pydantic import BaseModel
import joblib
import numpy as np
from datetime import datetime
import sqlite3
app = FastAPI()
AI Modeli yükleme
model = joblib.load("kesiksinegi_ai.pkl")
Veri modeli
class SensorData(BaseModel):
air_temp: float
soil_temp: float
humidity: float
daily_dd: float
cumulative_dd: float
Veritabanı bağlantısı
def get_db_connection():
conn = sqlite3.connect('kesiksinegi.db')
conn.row_factory = sqlite3.Row
return conn
Veritabanı tablosu oluşturma
def init_db():
conn = get_db_connection()
conn.execute('''
CREATE TABLE IF NOT EXISTS sensor_data (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp TEXT NOT NULL,
air_temp REAL,
soil_temp REAL,
humidity REAL,
daily_dd REAL,
cumulative_dd REAL,
risk INTEGER,
risk_probability REAL
)
''')
conn.commit()
conn.close()
init_db()
@app.post("/api/data")
def receive_data(data: SensorData):
AI modeli için girdi hazırlama
X = np.array([[
data.air_temp,
data.soil_temp,
data.humidity,
data.cumulative_dd
]])
Risk tahmini
risk = model.predict(X)[0]
risk_prob = model.predict_proba(X)[0][1]
Veritabanına kaydetme
conn = get_db_connection()
conn.execute('''
INSERT INTO sensor_data
(timestamp, air_temp, soil_temp, humidity, daily_dd, cumulative_dd, risk, risk_probability)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
''', (
datetime.now().isoformat(),
data.air_temp,
data.soil_temp,
data.humidity,
data.daily_dd,
data.cumulative_dd,
int(risk),
round(risk_prob, 4)
))
conn.commit()
conn.close()
Uyarı kontrolü
alert_message = None
if data.cumulative_dd >= 180 and risk == 1 and risk_prob >= 0.65:
alert_message = f"YÜKSEK RİSK! DD: {data.cumulative_dd:.1f}, Olasılık: %{risk_prob*100:.1f}"
Burada SMS/WhatsApp/Email gönderimi yapılabilir
return {
"status": "success",
"risk": int(risk),
"risk_probability": round(risk_prob, 4),
"cumulative_dd": data.cumulative_dd,
"alert": alert_message
}
@app.get("/api/history")
def get_history(limit: int = 100):
conn = get_db_connection()
data = conn.execute('''
SELECT * FROM sensor_data
ORDER BY timestamp DESC
LIMIT ?
''', (limit,)).fetchall()
conn.close()
return [dict(row) for row in data]
@app.get("/")
def root():
return {"message": "Buğday Kesiksineği Erken Uyarı Sistemi API"}
AI Modeli Eğitimi
python
import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score
import joblib
Eğitim verisi yükleme
data = pd.read_csv("egitim_verisi.csv")
Özellikler ve hedef değişken
X = data[["air_temp", "soil_temp", "humidity", "cumulative_dd"]]
y = data["risk"]
Model oluşturma
model = RandomForestClassifier(
n_estimators=200,
max_depth=6,
min_samples_split=5,
random_state=42
)
Cross-validation ile doğruluk testi
scores = cross_val_score(model, X, y, cv=5, scoring='accuracy')
print(f"Ortalama Doğruluk: {scores.mean():.2f} (+/- {scores.std():.2f})")
Modeli eğit
model.fit(X, y)
Özellik önem sıralaması
feature_importance = pd.DataFrame({
'feature': X.columns,
'importance': model.feature_importances_
}).sort_values('importance', ascending=False)
print("\nÖzellik Önem Sıralaması:")
print(feature_importance)
Modeli kaydet
joblib.dump(model, "kesiksinegi_ai.pkl")
print("\nModel kaydedildi: kesiksinegi_ai.pkl")
Karar Mantığı ve Risk Sınıflandırması
Otomatik Karar Kuralları:
python
def risk_decision(cumulative_dd, ai_risk, ai_probability):
"""
Derece-gün ve AI tahminini birleştirerek karar verir
"""
DD < 180: Henüz risk yok
if cumulative_dd < 180:
return {
"decision": "NO_ACTION",
"message": "Henüz risk eşiği aşılmadı. İzleme devam ediyor.",
"action": None
}
DD 180-220: AI karar verir
elif 180 <= cumulative_dd <= 220:
if ai_risk == 1 and ai_probability >= 0.65:
return {
"decision": "HIGH_RISK",
"message": f"YÜKSEK RİSK! DD: {cumulative_dd:.1f}, AI Olasılık: %{ai_probability*100:.1f}",
"action": "IMMEDIATE_INTERVENTION"
}
elif ai_risk == 1 and ai_probability >= 0.50:
return {
"decision": "MODERATE_RISK",
"message": f"ORTA RİSK. DD: {cumulative_dd:.1f}, AI Olasılık: %{ai_probability*100:.1f}",
"action": "MONITOR_CLOSELY"
}
else:
return {
"decision": "LOW_RISK",
"message": "Risk düşük. Normal izleme devam ediyor.",
"action": "CONTINUE_MONITORING"
}
DD 220-250: Kritik müdahale penceresi
elif 220 < cumulative_dd <= 250:
if ai_risk == 1:
return {
"decision": "CRITICAL",
"message": f"KRİTİK! Müdahale penceresi. DD: {cumulative_dd:.1f}",
"action": "URGENT_INTERVENTION"
}
else:
return {
"decision": "WATCH",
"message": "Kritik dönem ancak AI riski düşük gösteriyor. Dikkatli izleyin.",
"action": "INTENSIVE_MONITORING"
}
DD > 250: Müdahale için geç kalınmış
else:
return {
"decision": "TOO_LATE",
"message": f"Müdahale penceresi kapandı. DD: {cumulative_dd:.1f}",
"action": "PLAN_NEXT_SEASON"
}
Uyarı Sistemi Entegrasyonu
SMS/WhatsApp Uyarı Gönderimi:
python
from twilio.rest import Client
import os
class AlertSystem:
def __init__(self):
self.twilio_account_sid = os.getenv('TWILIO_ACCOUNT_SID')
self.twilio_auth_token = os.getenv('TWILIO_AUTH_TOKEN')
self.twilio_phone = os.getenv('TWILIO_PHONE')
self.client = Client(self.twilio_account_sid, self.twilio_auth_token)
def send_sms(self, to_phone, message):
"""SMS gönderimi"""
try:
message = self.client.messages.create(
body=message,
from_=self.twilio_phone,
to=to_phone
)
return {"status": "success", "sid": message.sid}
except Exception as e:
return {"status": "error", "message": str(e)}
def send_whatsapp(self, to_phone, message):
"""WhatsApp gönderimi"""
try:
message = self.client.messages.create(
body=message,
from_=f'whatsapp:{self.twilio_phone}',
to=f'whatsapp:{to_phone}'
)
return {"status": "success", "sid": message.sid}
except Exception as e:
return {"status": "error", "message": str(e)}
def generate_alert_message(self, decision_data, farmer_name):
"""Çiftçiye özel mesaj oluşturma"""
messages = {
"HIGH_RISK": f"""
ACIL UYARI - {farmer_name}
Buğday kesiksineği riski YÜKSEK!
Birikimli Derece-Gün: {decision_data.get('dd', 0):.1f}
Risk Olasılığı: %{decision_data.get('probability', 0)*100:.1f}
ÖNERİLEN EYLEM:
- Tarlayı kontrol edin
- Gerekirse müdahale yapın
- Danışmanınıza başvurun
Detaylı bilgi: [LINK]
""",
"MODERATE_RISK": f"""
DİKKAT - {farmer_name}
Buğday kesiksineği riski ORTA seviyede.
Derece-Gün: {decision_data.get('dd', 0):.1f}
ÖNERİ:
- Yakın takip yapın
- 2-3 gün içinde kontrol edin
Detaylı bilgi: [LINK]
""",
"CRITICAL": f"""
KRİTİK UYARI - {farmer_name}
MÜDAHALENİN SON GÜNLERI!
Derece-Gün: {decision_data.get('dd', 0):.1f}
ACİL EYLEM GEREKLİ:
- Hemen tarlayı kontrol edin
- Müdahale için son fırsat
- Uzmanla görüşün
Tel: [DANIŞMAN NO]
"""
}
return messages.get(
decision_data.get('decision'),
f"Bilgilendirme - {farmer_name}: Sistem normal çalışıyor."
)
API'ye entegrasyon
alert_system = AlertSystem()
@app.post("/api/send_alert")
def send_alert(farmer_phone: str, decision_data: dict, farmer_name: str = "Çiftçi"):
message = alert_system.generate_alert_message(decision_data, farmer_name)
WhatsApp tercih edilir, başarısız olursa SMS
result = alert_system.send_whatsapp(farmer_phone, message)
if result['status'] != 'success':
result = alert_system.send_sms(farmer_phone, message)
return result
VERİ GÖRSELLEŞTİRME VE İZLEME PANELİ
Web Dashboard (Streamlit)
python
import streamlit as st
import pandas as pd
import plotly.graph_objects as go
import plotly.express as px
import sqlite3
from datetime import datetime, timedelta
st.set_page_config(
page_title="Buğday Kesiksineği İzleme Sistemi",
page_icon="🌾",
layout="wide"
)
Veritabanından veri çekme
@st.cache_data(ttl=600)
def load_data(days=30):
conn = sqlite3.connect('kesiksinegi.db')
query = f"""
SELECT * FROM sensor_data
WHERE timestamp >= datetime('now', '-{days} days')
ORDER BY timestamp DESC
"""
df = pd.read_sql_query(query, conn)
conn.close()
df['timestamp'] = pd.to_datetime(df['timestamp'])
return df
Başlık
st.title("🌾 Buğday Kesiksineği Erken Uyarı Sistemi")
st.markdown("---")
Sidebar
st.sidebar.header("Filtreler")
days_to_show = st.sidebar.slider("Gösterilecek Gün Sayısı", 7, 90, 30)
data = load_data(days_to_show)
if data.empty:
st.warning("Henüz veri bulunmuyor.")
st.stop()
Ana metrikler
col1, col2, col3, col4 = st.columns(4)
with col1:
latest_temp = data.iloc[0]['air_temp']
st.metric(
"Güncel Sıcaklık",
f"{latest_temp:.1f} °C",
delta=f"{latest_temp - data.iloc[1]['air_temp']:.1f} °C" if len(data) > 1 else None
)
with col2:
latest_dd = data.iloc[0]['cumulative_dd']
st.metric(
"Toplam Derece-Gün",
f"{latest_dd:.1f}",
delta=f"+{data.iloc[0]['daily_dd']:.1f}" if data.iloc[0]['daily_dd'] > 0 else None
)
with col3:
latest_risk_prob = data.iloc[0]['risk_probability']
st.metric(
"Risk Olasılığı",
f"%{latest_risk_prob*100:.1f}",
delta=f"{(latest_risk_prob - data.iloc[1]['risk_probability'])*100:.1f}%" if len(data) > 1 else None
)
with col4:
risk_status = "🔴 YÜKSEK" if latest_risk_prob > 0.65 else "🟡 ORTA" if latest_risk_prob > 0.35 else "🟢 DÜŞÜK"
st.metric("Risk Durumu", risk_status)
st.markdown("---")
Grafikler
col1, col2 = st.columns(2)
with col1:
Derece-Gün Grafiği
fig_dd = go.Figure()
fig_dd.add_trace(go.Scatter(
x=data['timestamp'],
y=data['cumulative_dd'],
mode='lines+markers',
name='Birikimli DD',
line=dict(color='#1f77b4', width=2)
))
Eşik çizgileri
fig_dd.add_hline(y=180, line_dash="dash", line_color="orange",
annotation_text="Risk Başlangıcı (180)")
fig_dd.add_hline(y=220, line_dash="dash", line_color="red",
annotation_text="Müdahale Penceresi (220)")
fig_dd.add_hline(y=250, line_dash="dash", line_color="darkred",
annotation_text="Kritik Eşik (250)")
fig_dd.update_layout(
title="Birikimli Derece-Gün Takibi",
xaxis_title="Tarih",
yaxis_title="Derece-Gün",
height=400
)
st.plotly_chart(fig_dd, use_container_width=True)
with col2:
Risk Olasılığı Grafiği
fig_risk = go.Figure()
fig_risk.add_trace(go.Scatter(
x=data['timestamp'],
y=data['risk_probability'] * 100,
mode='lines+markers',
name='Risk Olasılığı',
line=dict(color='#d62728', width=2),
fill='tozeroy',
fillcolor='rgba(214, 39, 40, 0.2)'
))
Risk eşikleri
fig_risk.add_hline(y=65, line_dash="dash", line_color="red",
annotation_text="Yüksek Risk (%65)")
fig_risk.add_hline(y=35, line_dash="dash", line_color="orange",
annotation_text="Orta Risk (%35)")
fig_risk.update_layout(
title="AI Risk Tahmini",
xaxis_title="Tarih",
yaxis_title="Risk Olasılığı (%)",
height=400
)
st.plotly_chart(fig_risk, use_container_width=True)
Sıcaklık ve Nem Grafiği
st.markdown("### 🌡️ Çevresel Koşullar")
fig_env = go.Figure()
fig_env.add_trace(go.Scatter(
x=data['timestamp'],
y=data['air_temp'],
mode='lines',
name='Hava Sıcaklığı (°C)',
yaxis='y1',
line=dict(color='#ff7f0e', width=2)
))
fig_env.add_trace(go.Scatter(
x=data['timestamp'],
y=data['soil_temp'],
mode='lines',
name='Toprak Sıcaklığı (°C)',
yaxis='y1',
line=dict(color='#8b4513', width=2)
))
fig_env.add_trace(go.Scatter(
x=data['timestamp'],
y=data['humidity'],
mode='lines',
name='Nem (%)',
yaxis='y2',
line=dict(color='#2ca02c', width=2, dash='dot')
))
fig_env.update_layout(
xaxis_title="Tarih",
yaxis=dict(
title="Sıcaklık (°C)",
side='left'
),
yaxis2=dict(
title="Nem (%)",
overlaying='y',
side='right'
),
height=400,
hovermode='x unified'
)
st.plotly_chart(fig_env, use_container_width=True)
Uyarı Geçmişi Tablosu
st.markdown("### 📋 Son Uyarılar")
alert_data = data[data['risk'] == 1].head(10)
if not alert_data.empty:
alert_display = alert_data[['timestamp', 'cumulative_dd', 'risk_probability', 'air_temp']].copy()
alert_display.columns = ['Tarih', 'Toplam DD', 'Risk Olasılığı', 'Sıcaklık (°C)']
alert_display['Risk Olasılığı'] = alert_display['Risk Olasılığı'].apply(lambda x: f"%{x*100:.1f}")
st.dataframe(alert_display, use_container_width=True)
else:
st.info("Henüz uyarı kaydı bulunmuyor.")
İstatistikler
st.markdown("### 📊 Sezon İstatistikleri")
col1, col2, col3 = st.columns(3)
with col1:
avg_temp = data['air_temp'].mean()
st.info(f"**Ortalama Sıcaklık:** {avg_temp:.1f} °C")
with col2:
total_risk_days = (data['risk'] == 1).sum()
st.warning(f"**Riskli Gün Sayısı:** {total_risk_days}")
with col3:
max_dd = data['cumulative_dd'].max()
st.error(f"**Maksimum DD:** {max_dd:.1f}")
Mobil Uygulama Konsepti (React Native - Temel Yapı)
javascript
import React, { useState, useEffect } from 'react';
import { View, Text, ScrollView, StyleSheet, RefreshControl } from 'react-native';
import { LineChart } from 'react-native-chart-kit';
import axios from 'axios';
const API_URL = 'https://YOUR_API_URL/api';
export default function DashboardScreen() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const fetchData = async () => {
try {
const response = await axios.get(${API\_URL}/history?limit=30);
setData(response.data);
setLoading(false);
} catch (error) {
console.error('Veri yüklenemedi:', error);
setLoading(false);
}
};
useEffect(() => {
fetchData();
const interval = setInterval(fetchData, 300000); // 5 dakikada bir güncelle
return () => clearInterval(interval);
}, []);
if (loading || !data) {
return (
Yükleniyor...
);
}
const latestData = data[0];
const riskColor = latestData.risk_probability > 0.65 ? '#d32f2f' :
latestData.risk_probability > 0.35 ? '#f57c00' : '#388e3c';
return (
style={styles.container}
refreshControl={
}
>
Buğday Kesiksineği İzleme
{/ Metrik Kartları /}
Sıcaklık
{latestData.air_temp.toFixed(1)}°C
Toplam DD
{latestData.cumulative_dd.toFixed(1)}
Risk
%{(latestData.risk_probability * 100).toFixed(0)}
{/ Uyarı Mesajı /}
{latestData.risk_probability > 0.65 && (
YÜKSEK RİSK! Tarlayı kontrol edin ve gerekirse müdahale yapın.
)}
{/ Grafik /}
Derece-Gün Takibi
data={{
labels: data.slice(0, 7).reverse().map(d =>
new Date(d.timestamp).getDate() + '/' + (new Date(d.timestamp).getMonth()
+ 1)
),
datasets: [{
data: data.slice(0, 7).reverse().map(d => d.cumulative_dd)
}]
}}
width={350}
height={220}
chartConfig={{
backgroundColor: '#fff',
backgroundGradientFrom: '#fff',
backgroundGradientTo: '#fff',
decimalPlaces: 0,
color: (opacity = 1) => rgba(33, 150, 243, ${opacity}),
style: {
borderRadius: 16
}
}}
bezier
style={styles.chart}
/>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
},
header: {
backgroundColor: '#2e7d32',
padding: 20,
paddingTop: 50,
},
title: {
fontSize: 24,
fontWeight: 'bold',
color: '#fff',
},
metricsContainer: {
flexDirection: 'row',
justifyContent: 'space-around',
padding: 15,
},
metricCard: {
backgroundColor: '#fff',
padding: 15,
borderRadius: 10,
alignItems: 'center',
flex: 1,
marginHorizontal: 5,
elevation: 3,
},
metricLabel: {
fontSize: 12,
color: '#666',
marginBottom: 5,
},
metricValue: {
fontSize: 24,
fontWeight: 'bold',
color: '#333',
},
alertBox: {
backgroundColor: '#ffebee',
margin: 15,
padding: 15,
borderRadius: 10,
borderLeftWidth: 5,
borderLeftColor: '#d32f2f',
},
alertText: {
color: '#c62828',
fontWeight: '600',
},
chartContainer: {
backgroundColor: '#fff',
margin: 15,
padding: 15,
borderRadius: 10,
elevation: 3,
},
chartTitle: {
fontSize: 16,
fontWeight: 'bold',
marginBottom: 10,
color: '#333',
},
chart: {
marginVertical: 8,
borderRadius: 16,
},
});
SAHA UYGULAMASI VE KALIBRASYON
Kurulum Adımları
- Donanım Kurulumu:
- ESP32'yi IP65 kutusuna yerleştirin
- DS18B20'yi su geçirmez kılıf içinde 0-5 cm toprak derinliğine göm Human: devam et
- DHT22'yi gölgeli konumda, toprak seviyesinden 30-50 cm yüksekliğe monte edin
- Güneş panelini güneye bakacak şekilde 30-45° açıyla yerleştirin
- Bataryayı kutunun içine güvenli şekilde bağlayın
- Tüm bağlantıları su ve nem geçirmez şekilde yalıtın - Yazılım Yükleme:
- Arduino IDE'yi kurun ve ESP32 board desteğini ekleyin
- Gerekli kütüphaneleri yükleyin (WiFi, HTTPClient, OneWire, DallasTemperature, DHT)
- WiFi bilgilerini ve API adresini koda girin
- Kodu ESP32'ye yükleyin
- Seri monitörden çalışmayı kontrol edin
. Saha Yerleşimi:
- Cihazı tarlanın temsili bir noktasına yerleştirin
- Kenar etkilerinden kaçının (en az 10 m içeride)
- Cihaz çevresini ezmeyin, doğal bitki örtüsünü koruyun
- GPS koordinatlarını kaydedin
Kalibrasyon Protokolü
İlk Kurulum (Sezon Başı):
python
Kalibrasyon veri toplama scripti
import serial
import csv
from datetime import datetime
Seri port bağlantısı
ser = serial.Serial('COM3', 115200, timeout=1)
CSV dosyası oluştur
with open('calibration_data.csv', 'w', newline='') as file:
writer = csv.writer(file)
writer.writerow(['timestamp', 'air_temp', 'soil_temp', 'humidity'])
print("Kalibrasyon başladı. 24 saat boyunca veri toplanacak...")
print("Durdurmak için Ctrl+C")
try:
while True:
if ser.in_waiting > 0:
line = ser.readline().decode('utf-8').strip()
Veriyi parse et
if "Hava Sıcaklığı:" in line:
air_temp = float(line.split(":")[1].split("°C")[0].strip())
elif "Toprak Sıcaklığı:" in line:
soil_temp = float(line.split(":")[1].split("°C")[0].strip())
elif "Nem:" in line:
humidity = float(line.split(":")[1].split("%")[0].strip())
Tam veri seti toplandı, kaydet
timestamp = datetime.now().isoformat()
writer.writerow([timestamp, air_temp, soil_temp, humidity])
file.flush()
print(f"Kaydedildi: {timestamp} | Hava: {air_temp}°C | Toprak: {soil_temp}°C | Nem: {humidity}%")
except KeyboardInterrupt:
print("\nKalibrasyon tamamlandı.")
ser.close()
Sensör Doğruluk Kontrolü:
Kurulumdan sonra ilk 48 saat içinde:
- Manuel termometre ile sıcaklık karşılaştırması yapın
- Toprak nemi sensörü varsa çapraz kontrol edin
- Verinin tutarlılığını kontrol edin (ani sıçramalar olmamalı)
Periyodik Bakım (15 günde bir):
- Sensörleri temizleyin (toz, toprak, böcek gibi yabancı maddelerden)
- Güneş panelini temizleyin
- Batarya voltajını kontrol edin
- Bağlantıları kontrol edin
Veri Kalitesi Kontrol Algoritması
python
import pandas as pd
import numpy as np
class DataQualityChecker:
"""Sensör verisi kalite kontrol sınıfı"""
def __init__(self, data):
self.data = data
self.errors = []
self.warnings = []
def check_missing_values(self):
"""Eksik veri kontrolü"""
missing = self.data.isnull().sum()
if missing.any():
self.errors.append(f"Eksik veri tespit edildi: {missing[missing > 0].to_dict()}")
def check_value_ranges(self):
"""Değer aralığı kontrolü"""
Hava sıcaklığı kontrolü
if (self.data['air_temp'] < -20).any() or (self.data['air_temp'] > 50).any():
self.errors.append("Hava sıcaklığı makul aralık dışında (-20 ile 50°C arası olmalı)")
Toprak sıcaklığı kontrolü
if (self.data['soil_temp'] < -10).any() or (self.data['soil_temp'] > 40).any():
self.errors.append("Toprak sıcaklığı makul aralık dışında (-10 ile 40°C arası olmalı)")
Nem kontrolü
if (self.data['humidity'] < 0).any() or (self.data['humidity'] > 100).any():
self.errors.append("Nem değeri %0-100 arasında olmalı")
def check_sudden_changes(self, threshold_temp=5, threshold_humidity=20):
"""Ani değişim kontrolü"""
Sıcaklık ani değişimi
temp_diff = self.data['air_temp'].diff().abs()
if (temp_diff > threshold_temp).any():
indices = temp_diff[temp_diff > threshold_temp].index
self.warnings.append(f"Ani sıcaklık değişimi tespit edildi (>{threshold_temp}°C): {list(indices)}")
Nem ani değişimi
humidity_diff = self.data['humidity'].diff().abs()
if (humidity_diff > threshold_humidity).any():
indices = humidity_diff[humidity_diff > threshold_humidity].index
self.warnings.append(f"Ani nem değişimi tespit edildi (>%{threshold_humidity}): {list(indices)}")
def check_stuck_values(self, window=10):
"""Takılı sensör kontrolü (aynı değer tekrarı)"""
for col in ['air_temp', 'soil_temp', 'humidity']:
rolling = self.data[col].rolling(window=window)
stuck = rolling.apply(lambda x: x.nunique() == 1, raw=False)
if stuck.any():
self.warnings.append(f"{col} sensörü {window} ölçümde aynı değeri veriyor - sensör arızası olabilir")
def check_correlation(self):
"""Hava ve toprak sıcaklığı korelasyonu"""
corr = self.data['air_temp'].corr(self.data['soil_temp'])
if corr < 0.5:
self.warnings.append(f"Hava ve toprak sıcaklığı arasında düşük korelasyon ({corr:.2f}) - sensör yerleşimi kontrol edilmeli")
def generate_report(self):
"""Kalite kontrol raporu"""
report = {
'timestamp': pd.Timestamp.now().isoformat(),
'total_records': len(self.data),
'errors': self.errors,
'warnings': self.warnings,
'status': 'FAIL' if self.errors else 'PASS' if self.warnings else 'OK'
}
return report
def run_all_checks(self):
"""Tüm kontrolleri çalıştır"""
self.check_missing_values()
self.check_value_ranges()
self.check_sudden_changes()
self.check_stuck_values()
self.check_correlation()
return self.generate_report()
Kullanım
data = pd.read_csv('sensor_data.csv')
checker = DataQualityChecker(data)
report = checker.run_all_checks()
print("VERİ KALİTE KONTROL RAPORU")
print("="*50)
print(f"Durum: {report['status']}")
print(f"Toplam Kayıt: {report['total_records']}")
print(f"\nHatalar ({len(report['errors'])}):")
for error in report['errors']:
print(f" ❌ {error}")
print(f"\nUyarılar ({len(report['warnings'])}):")
for warning in report['warnings']:
print(f" ⚠️ {warning}")
Çoklu Tarla Yönetimi
Birden fazla tarlada sistem kurulumu için:
Python
class MultiFieldManager:
"""Çoklu tarla yönetim sistemi"""
def __init__(self, db_connection):
self.conn = db_connection
self.fields = self.load_fields()
def load_fields(self):
"""Kayıtlı tarlaları yükle"""
query = """
SELECT field_id, field_name, location_lat, location_lon,
crop_type, sowing_date, area_hectares
FROM fields
WHERE active = 1
"""
return pd.read_sql_query(query, self.conn)
def get_field_status(self, field_id):
"""Tarlanın güncel durumunu getir"""
query = f"""
SELECT * FROM sensor_data
WHERE field_id = '{field_id}'
ORDER BY timestamp DESC
LIMIT 1
"""
latest = pd.read_sql_query(query, self.conn).iloc[0]
Risk hesaplama
decision = risk_decision(
latest['cumulative_dd'],
latest['risk'],
latest['risk_probability']
)
return {
'field_id': field_id,
'field_name': self.fields[self.fields['field_id'] == field_id]['field_name'].values[0],
'last_update': latest['timestamp'],
'cumulative_dd': latest['cumulative_dd'],
'risk_level': decision['decision'],
'action_required': decision['action'],
'temperature': latest['air_temp'],
'humidity': latest['humidity']
}
def get_all_fields_summary(self):
"""Tüm tarlaların özet durumu"""
summary = []
for field_id in self.fields['field_id']:
try:
status = self.get_field_status(field_id)
summary.append(status)
except Exception as e:
print(f"Tarla {field_id} için veri alınamadı: {e}")
return pd.DataFrame(summary)
def get_priority_fields(self):
"""Öncelikli müdahale gereken tarlaları listele"""
summary = self.get_all_fields_summary()
priority_map = {
'CRITICAL': 1,
'HIGH_RISK': 2,
'MODERATE_RISK': 3,
'LOW_RISK': 4,
'NO_ACTION': 5
}
summary['priority'] = summary['risk_level'].map(priority_map)
summary = summary.sort_values('priority')
return summary[summary['priority'] <= 3] # Sadece kritik, yüksek ve orta risk
def generate_daily_report(self):
"""Günlük rapor oluştur"""
summary = self.get_all_fields_summary()
priority_fields = self.get_priority_fields()
report = f"""
GÜNLÜK TARLA RAPORU
Tarih: {datetime.now().strftime('%d.%m.%Y %H:%M')}
{'='*60}
GENEL DURUM:
- Toplam Tarla: {len(summary)}
- Öncelikli Müdahale Gerekli: {len(priority_fields)}
- Kritik Durum: {len(summary[summary['risk_level'] == 'CRITICAL'])}
- Yüksek Risk: {len(summary[summary['risk_level'] == 'HIGH_RISK'])}
- Orta Risk: {len(summary[summary['risk_level'] == 'MODERATE_RISK'])}
ÖNCELİKLİ TARLALAR:
if len(priority_fields) > 0:
for _, field in priority_fields.iterrows():
report += f"""
📍 {field['field_name']} (ID: {field['field_id']})
Durum: {field['risk_level']}
Eylem: {field['action_required']}
DD: {field['cumulative_dd']:.1f}
Sıcaklık: {field['temperature']:.1f}°C
Son Güncelleme: {field['last_update']}
"""
else:
report += "\n✅ Öncelikli müdahale gerektiren tarla yok.\n"
report += "\n" + "="*60
return report
Kullanım
import sqlite3
conn = sqlite3.connect('kesiksinegi.db')
manager = MultiFieldManager(conn)
Günlük rapor
daily_report = manager.generate_daily_report()
print(daily_report)
Öncelikli tarlaları çiftçilere bildir
priority_fields = manager.get_priority_fields()
for _, field in priority_fields.iterrows():
SMS/WhatsApp gönderimi
pass


