Разработка AI для сельского хозяйства (анализ снимков посевов)
Precision agriculture — точное земледелие с использованием данных: спутниковые снимки, аэрофотосъёмка с дронов, наземные датчики. CV-компонент: анализ состояния посевов по изображениям для выявления стресса, болезней, вредителей, оценки урожайности. Результат — зональные карты полей для дифференцированного применения удобрений и средств защиты.
Индексы вегетации из мультиспектральных снимков
Мультиспектральные камеры на дронах захватывают NIR (near-infrared) диапазон, недоступный RGB-камерам. NDVI (Normalized Difference Vegetation Index) — стандартный показатель здоровья растений:
import numpy as np
import rasterio
import matplotlib.pyplot as plt
import matplotlib.colors as colors
class CropHealthAnalyzer:
def calculate_ndvi(self, red_band: np.ndarray,
nir_band: np.ndarray) -> np.ndarray:
"""NDVI = (NIR - Red) / (NIR + Red)"""
red = red_band.astype(np.float32)
nir = nir_band.astype(np.float32)
ndvi = np.where(
(nir + red) > 0,
(nir - red) / (nir + red),
0
)
return np.clip(ndvi, -1, 1)
def calculate_ndre(self, red_edge: np.ndarray,
nir: np.ndarray) -> np.ndarray:
"""NDRE — чувствительнее NDVI для раннего стресса"""
return (nir - red_edge) / (nir + red_edge + 1e-8)
def calculate_indices_batch(self, multispectral_path: str) -> dict:
"""Расчёт всех индексов из мультиспектрального GeoTIFF"""
with rasterio.open(multispectral_path) as src:
# Предполагаем порядок: Blue, Green, Red, RedEdge, NIR
blue = src.read(1).astype(np.float32)
green = src.read(2).astype(np.float32)
red = src.read(3).astype(np.float32)
red_edge = src.read(4).astype(np.float32)
nir = src.read(5).astype(np.float32)
transform = src.transform
crs = src.crs
indices = {
'ndvi': self.calculate_ndvi(red, nir),
'ndre': self.calculate_ndre(red_edge, nir),
'gndvi': (nir - green) / (nir + green + 1e-8), # Green NDVI
'evi': 2.5 * (nir - red) / (nir + 6*red - 7.5*blue + 1 + 1e-8),
}
return indices, transform, crs
def classify_crop_health(self, ndvi: np.ndarray) -> np.ndarray:
"""Классификация здоровья посевов по NDVI"""
health_map = np.zeros_like(ndvi, dtype=np.uint8)
health_map[ndvi < 0.1] = 0 # Почва/нет растительности
health_map[(ndvi >= 0.1) & (ndvi < 0.3)] = 1 # Стресс/разреженный
health_map[(ndvi >= 0.3) & (ndvi < 0.5)] = 2 # Умеренный
health_map[(ndvi >= 0.5) & (ndvi < 0.7)] = 3 # Хорошее
health_map[ndvi >= 0.7] = 4 # Отличное
return health_map
Детекция болезней и вредителей по RGB-снимкам
from ultralytics import YOLO
import cv2
class CropDiseaseDetector:
def __init__(self, model_path: str):
# YOLOv8 дообученный на PlantVillage Dataset + кастомные данные
self.model = YOLO(model_path)
self.disease_classes = [
'healthy', 'early_blight', 'late_blight', 'leaf_mold',
'septoria_leaf_spot', 'spider_mites', 'target_spot',
'yellow_leaf_curl', 'mosaic_virus', 'bacterial_spot'
]
def analyze_leaf(self, leaf_image: np.ndarray) -> dict:
results = self.model(leaf_image, conf=0.4)
detections = []
for box in results[0].boxes:
disease = self.disease_classes[int(box.cls)]
detections.append({
'disease': disease,
'confidence': float(box.conf),
'bbox': box.xyxy[0].tolist(),
'severity': self._estimate_severity(leaf_image, box)
})
# Общий балл здоровья
if not detections:
health_score = 1.0
else:
max_disease_conf = max(d['confidence'] for d in detections
if d['disease'] != 'healthy')
health_score = 1.0 - max_disease_conf
return {
'health_score': health_score,
'detections': detections,
'needs_treatment': health_score < 0.6
}
Зональные карты для дифференцированного внесения
import geopandas as gpd
from shapely.geometry import shape
import json
def create_prescription_map(ndvi_array: np.ndarray,
transform,
field_boundary: gpd.GeoDataFrame) -> dict:
"""
Создание карты рекомендаций по зонам (prescription map)
для переменной нормы внесения удобрений
"""
health_zones = classify_into_zones(ndvi_array, n_zones=4)
# Векторизация растровых зон
from rasterio.features import shapes
zone_geometries = list(shapes(health_zones.astype('uint8'), transform=transform))
prescription = []
for geom, zone_value in zone_geometries:
# Норма внесения N по зонам (кг/га)
n_rate = {1: 120, 2: 90, 3: 60, 4: 30}[int(zone_value)] if zone_value > 0 else 0
prescription.append({
'geometry': geom,
'zone': int(zone_value),
'n_rate_kg_ha': n_rate,
'action': 'apply_fertilizer' if n_rate > 0 else 'skip'
})
return prescription
Integration с агротехническим ПО
- John Deere Operations Center: импорт prescription maps в формате shapefile/geojson
- ISOBUS: стандарт передачи задания на технику
- FMIS (Farm Management Information System): хранение полевых данных
| Применение | Данные | Точность |
|---|---|---|
| NDVI мониторинг | Мультиспектральный дрон | Корреляция с урожайностью 0.82 |
| Детекция болезней листьев | RGB фото | 87–93% accuracy |
| Оценка урожайности | RGB + NIR | MAE 8–15% от реальной |
| Масштаб | Срок |
|---|---|
| Пилот: 1 культура, 1 поле | 4–6 недель |
| Хозяйство: 5–10 культур | 8–14 недель |
| Региональная платформа | 16–24 недели |







