"Kitaplar, size hiçbir zaman yalan söylemeyen tek dostunuzdur. Tabii yazarı siz değilseniz." - Oscar Wilde"

Buğday Kesi̇ksi̇neği̇ Mücadelesi̇nde Veri̇ Anali̇zi̇ Ve Yapay Zekâ Destekli̇ Erken Uyari Si̇stemleri̇

yazı resim

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ı

  1. 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
  2. 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

KİTAP İZLERİ

Dert Dinleme Uzmanı

Adalet Ağaoğlu

Adalet Ağaoğlu’nun ‘Dert Dinleme Uzmanı’: Toplumsal Bir Stetoskop Türk edebiyatının büyük ustalarından Adalet Ağaoğlu, uzun bir aradan sonra, yankıları bugün dahi süren "Dar Zamanlar" serisine
İncelemeyi Oku

Yorumlar

Başa Dön