Pionowa automatyzacja rolnictwa: sterowanie robotyczne za pośrednictwem API
Opuszczony magazyn przemysłowy w Cavenago di Brianza, na obrzeżach Mediolanu. Wewnątrz 9000 metrów kwadraty pionowych regałów, na których pod niebiesko-czerwonym oświetleniem LED rosną sałaty, bazylia i rukola nie widząc nigdy słońca. Stała temperatura 21 stopni, wilgotność 70%, CO2 1200 ppm. Każda roślina posiada spersonalizowaną recepturę światła, każda linia uprawy komunikuje się z systemem centralnym poprzez API, każdy robot wie, dokąd się udać w czasie rzeczywistym. To jest Farmy Planet, włoska farma pionowa który w 2025 roku stał się jednym ze światowych punktów odniesienia w zakresie zautomatyzowanego rolnictwa w pomieszczeniach zamkniętych.
Rolnictwo pionowe nie jest już eksperymentem laboratoryjnym. W 2025 roku rynek globalny jest wart 9,62 miliarda dolarów z prognozami na poziomie 39,2 miliarda do 2033 r. (CAGR 19,3%). Farmy pionowe nowej generacji zużywają 95% mniej wody szacunek do tradycyjnego rolnictwa, produkują przez cały rok niezależnie od klimatu, eliminują pestycydy i osiągaj plony z metra kwadratowego do 100 razy wyższe niż w otwartym polu. Ale te wyniki są one możliwe jedynie w przypadku oprogramowania klasy przemysłowej i infrastruktury robotyki.
W tym artykule budujemy całą architekturę oprogramowania farmy pionowej: z czujników środowiskowych do regulatora PID w Pythonie, od projektu API REST z FastAPI po integrację ROS2 dla robotów, od cyfrowego bliźniaka do procesu optymalizacji opartego na uczeniu się przez wzmacnianie. Działający kod, architektury sprawdzone w produkcji, liczby rzeczywiste z włoskiego ekosystemu.
Czego dowiesz się w tym artykule
- Kompletna architektura oprogramowania dla farm pionowych: czujniki, sterowniki, SCADA, chmura, AI
- Zarządzanie widmem LED: PAR, DLI, lekkie przepisy na sałatę, bazylię i truskawki
- Implementacja regulatora PID w Pythonie dla temperatury, CO2, wilgotności i nawadniania
- Projekt API REST z FastAPI do zarządzania recepturami upraw i sterowania siłownikami
- Integracja z ROS2 do automatycznego siewu, przesadzania i zbioru robotów
- Cyfrowy bliźniak gospodarstwa: symulacja wzrostu roślin i optymalizacja układu
- Uczenie się przez wzmacnianie w celu optymalizacji receptur świetlnych
- Infrastruktura IoT: Modbus RTU, MQTT, OPC-UA, bramy przemysłowe
- Analiza ekonomiczna: CAPEX, OPEX, próg rentowności i porównanie z rolnictwem plenerowym
- Studium przypadku: Planet Farms i Agricola Moderna, wiodące włoskie gospodarstwa pionowe
Seria FoodTech - Wszystkie artykuły
| # | Przedmiot | Poziom | Państwo |
|---|---|---|---|
| 1 | Rurociąg IoT dla rolnictwa precyzyjnego z Pythonem i MQTT | Zaawansowany | Opublikowany |
| 2 | ML Edge do monitorowania upraw: wizja komputerowa na polach | Zaawansowany | Opublikowany |
| 3 | Satelitarne API i wskaźniki roślinności: NDVI z Pythonem i Sentinel-2 | Mediator | Opublikowany |
| 4 | Identyfikowalność Blockchain w żywności: od pola do supermarketu | Mediator | Opublikowany |
| 5 | Wizja komputerowa w kontroli jakości w przemyśle spożywczym | Zaawansowany | Opublikowany |
| 6 | FSMA i zgodność cyfrowa: automatyzacja procesów regulacyjnych | Mediator | Opublikowany |
| 7 | Pionowa automatyzacja rolnictwa: Sterowanie robotyczne poprzez API (tutaj jesteś) | Zaawansowany | Aktualny |
| 8 | Prognozowanie popytu na sprzedaż detaliczną żywności za pomocą Prophet i LightGBM | Mediator | Już wkrótce |
| 9 | Pulpit nawigacyjny Farm Intelligence: analityka w czasie rzeczywistym za pomocą Grafany | Mediator | Już wkrótce |
| 10 | Optymalizacja żywności w łańcuchu dostaw: ML na rzecz redukcji odpadów | Mediator | Już wkrótce |
Rynek rolnictwa pionowego w 2025 r.: czynniki wzrostu i technologie
Rolnictwo pionowe przeszło fazę konsolidacji w latach 2023–2024 z udziałem kilku dużych graczy Firmy północnoamerykańskie (AeroFarms, AppHarvest, Bowery Farming), które pod ciężarem ogłosiły upadłość bardzo wysokich nakładów inwestycyjnych i eksplodujących kosztów energii. Ale rynek nie zatrzymał się: został zrestrukturyzowany wokół bardziej wydajnych modeli, z darwinowską selekcją, która nagradzała tych, którzy budowali solidna ekonomika jednostki przed skalowaniem.
W roku 2025 liczby świadczą o rosnącej dojrzałości: rynek globalny jest tego wart 9,62 miliarda dolarów i wzrośnie do 39,2 miliardów do 2033 r. Inne szacunki (Maximize Market Research) szacuje, że w 2025 r. będzie to 8 miliardów, a prognozy przewidują, że do roku 2025 będzie to 39,7 miliarda. 2032 r. przy CAGR na poziomie 25,7%. Rozbieżność szacunków odzwierciedla trudność w sklasyfikowaniu tego, co stanowi dokładnie „rolnictwo pionowe” (tylko tace układane w pomieszczeniach? także zaawansowane szklarnie?), ale trend i jednoznaczne. Wartość połączonego rynku robotyki rolniczej w 2025 r. będzie wynosić 10,23 miliarda wzrostu do 28,2 miliardów w roku 2030.
Rolnictwo pionowe a rolnictwo tradycyjne: porównanie wydajności
| Parametr | Rolnictwo na świeżym powietrzu | Szklarnia | Farma pionowa |
|---|---|---|---|
| Zużycie wody (względne) | 100% | 30-40% | 5-10% |
| Wydajność z m² (sałata) | ~2 kg/m²/rok | ~15 kg/m²/rok | ~150-200 kg/m²/rok |
| Cykle produkcyjne/rok | 1-3 | 4-8 | 12-18 |
| Pestycydy | Wysoka konieczność | Zmniejszony | Zero |
| Zależność klimatyczna | Całkowity | Częściowy | Nic |
| Użytkowanie gruntów (grunty) | 1x | 1x | 0,01-0,05x |
| Wymagana energia | Niski | Przeciętny | Wysoka (LED + HVAC) |
| Koszt produkcji (sałata) | 0,5-1 USD/kg | 1,5-3 USD/kg | 4-8 dolarów/kg |
Istnieją cztery czynniki wzrostu w 2025 r.: po pierwsze, spadł koszt wysokowydajnych diod LED o 70% w ciągu ostatniej dekady, dzięki czemu nakłady inwestycyjne na oświetlenie są znacznie bardziej dostępne. Po drugie, tj Systemy sterowania oparte na sztucznej inteligencji umożliwiają niewyobrażalną optymalizację zużycia energii przez okres do 3-4 lat temu. Po trzecie, stale rośnie popyt na produkty lokalne, świeże i wolne od pestycydów, zwłaszcza na obszarach miejskich. Po czwarte, moduły robotyczne do siewu, przesadzania i zbioru są teraz dostępne po cenach przemysłowych, a nie tylko laboratoryjnych.
Architektura oprogramowania dla farmy pionowej: kompletny stos
Nowoczesna farma pionowa to w istocie system cyberfizyczny: każda fizyczna decyzja (włącz dioda LED, otwórz zawór, przesuń robota) i wynik obliczeń oprogramowania. Architektura musi działać w czasie rzeczywistym, aby zapewnić kontrolę, być niezawodny pod względem bezpieczeństwa upraw i skalowalny zarządzaj setkami obszarów upraw.
Kompleksowy stos architektoniczny
+-----------------------------------------------------------------------+
| LAYER 1: FIELD DEVICES |
| [Sensori Temp/RH] [CO2 Sensor] [PAR Meter] [Nutrient EC/pH] |
| [Flow Sensor] [Camera RGB-D] [Weight Sensor] [RFID Tray] |
| | | | | |
| +------------------+-------------+--------------+ |
| Modbus RTU / RS-485 / I2C / SPI |
+-----------------------------------------------------------------------+
|
+-----------------------------------------------------------------------+
| LAYER 2: EDGE CONTROLLER |
| [PLC Siemens S7-1500 / Beckhoff CX / Raspberry] |
| - Loop PID per temperatura, CO2, umidita |
| - Scheduling ricette luminose (LED driver DMX/PWM) |
| - Gestione irrigazione NFT/DWC cicli |
| - Buffer locale offline-tolerant |
| - OPC-UA Server / MQTT Publisher |
+-----------------------------------------------------------------------+
|
+-----------------------------------------------------------------------+
| LAYER 3: SCADA / MES |
| [Ignition SCADA / custom Python SCADA] |
| - Supervisione multi-zona real-time |
| - Storicizzazione time-series (InfluxDB/TimescaleDB) |
| - Allarmi e notifiche (temperatura, EC, pH out-of-range) |
| - Ricette colturali e scheduling batch |
+-----------------------------------------------------------------------+
|
+-----------------------------------------------------------------------+
| LAYER 4: CLOUD PLATFORM |
| [FastAPI Backend] [Message Broker MQTT/Kafka] [PostgreSQL] |
| - REST API per integrazione ERP/WMS/retail |
| - Gestione ricette, batch, inventory, ordini |
| - Autenticazione OAuth2, RBAC, audit log |
+-----------------------------------------------------------------------+
|
+-----------------------------------------------------------------------+
| LAYER 5: AI / ANALYTICS |
| [ML Pipeline] [Digital Twin] [Computer Vision] [RL Optimizer] |
| - Ottimizzazione ricette luminose (RL) |
| - Previsione resa e time-to-harvest |
| - Rilevamento anomalie (sensori + visione) |
| - Simulazione crescita (digital twin) |
+-----------------------------------------------------------------------+
Wybór pomiędzy przemysłowym sterownikiem PLC (Siemens S7, Beckhoff) a komputerem jednopłytkowym (Raspberry Pi 4, BeagleBone) zależy od wymaganego poziomu niezawodności. Dla gospodarstw towarowych o wysokich plonach wartość, sterownik PLC z certyfikatem IEC 61131-3 z redundancją sprzętową i właściwym wyborem. Do prototypów i farmy eksperymentalne, rozwiązanie Python na sprzęcie wbudowanym, bardziej elastyczne i szybsze w rozwoju.
Systemy kontroli środowiska: LED, HVAC, CO2 i nawadnianie
Sterowanie środowiskiem jest sercem operacyjnym farmy pionowej. Dominują cztery parametry: widmo i intensywność światła, temperatura powietrza, stężenie CO2 i skład roztworu odżywczego. Każdy wymaga dedykowanej pętli sterującej.
Zarządzanie widmem LED: PAR, DLI i przepisy na światło
Rośliny nie wykorzystują jednakowo całego światła widzialnego. Zakres aktywny fotosyntetycznie idzie z 400 do 700 nm (PAR – promieniowanie aktywne fotosyntetycznie). Wewnątrz W tym zakresie kolor niebieski (400-500 nm) reguluje morfologię liści i syntezę związków aromaty; czerwony (600-700 nm) jest głównym motorem fotosyntezy; daleka czerwień (700-800 nm) wpływa na kwitnienie i geometrię roślin poprzez system fitochromów.
DLI (Daily Light Integral) mierzy całkowitą ilość fotonów PAR otrzymywanych przez roślinę w ciągu 24 godzin, wyrażone w mol/m²/dzień. I najważniejsza metryka w przepisach dotyczących wymiarowania jasny. Badania opublikowane w Nature Scientific Reports w 2025 r. pokazują, że diody LED zoptymalizowano w przypadku rolnictwa pionowego produkują do Wydajność większa o 32%. w porównaniu do widm standard, z większą masą świeżej sałaty i bazylii.
Parametry światła dla głównych upraw
| Kultura | PPFD (µmol/m²/s) | Docelowy DLI (mol/m²/dzień) | Optymalne spektrum | Fotoperiod |
|---|---|---|---|---|
| Sałata (liść) | 150-250 | 15:00-18:00 | R:B = 4:1, +daleka czerwień 5% | Światło 16-18h |
| Sałata (głowa) | 200-300 | 17-22 | R:B = 3:1, UV 380nm 2% | 16h światło |
| Bazylia | 200-300 | 14:00-17:00 | R:B = 3:1, niebieski 20-25% | 16h światło |
| Szpinak | 150-200 | 12-17 | R:B = 4:1 | Światło 14-16h |
| Truskawki (wegetatywne) | 200-300 | 15:00-20:00 | R:B = 3:1 | 16h światło |
| Truskawki (kwitnące) | 300-400 | 20-25 | R:B:FR = 3:1:0,5 | 12h światła |
| Mikrogreeny | 100-200 | 8-12 | Pełne spektrum bieli | 16h światło |
| Aromatyczne zioła | 200-250 | 14-16 | Niebieski 15%, dominuje R | 16h światło |
Kontroler zarządzający diodami LED musi przetłumaczyć te receptury na sygnały PWM dla sterowników LED. Każdy obszar uprawy może mieć inny przepis, który może zmieniać się w ciągu roku cykl uprawy (np. więcej koloru niebieskiego w ciągu ostatnich 48 godzin, aby zintensyfikować aromat bazylii). Oto implementacja kompletnego regulatora PID w języku Python:
"""
Vertical Farm Environmental Controller
Controller PID per gestione LED, CO2, temperatura e irrigazione
"""
import time
import asyncio
import logging
from dataclasses import dataclass, field
from typing import Optional
from enum import Enum
logger = logging.getLogger(__name__)
class CropStage(Enum):
GERMINATION = "germination"
SEEDLING = "seedling"
VEGETATIVE = "vegetative"
MATURATION = "maturation"
HARVEST_READY = "harvest_ready"
@dataclass
class LightRecipe:
"""Ricetta luminosa per una coltura in un determinato stadio"""
crop_name: str
stage: CropStage
ppfd_target: float # µmol/m²/s
dli_target: float # mol/m²/giorno
photoperiod_hours: float # ore di luce al giorno
spectrum_red_pct: float # % canale rosso (620-700nm)
spectrum_blue_pct: float # % canale blu (400-500nm)
spectrum_white_pct: float # % LED bianco full-spectrum
spectrum_farred_pct: float # % far-red (700-800nm)
spectrum_uv_pct: float = 0.0
def validate(self) -> bool:
total = (self.spectrum_red_pct + self.spectrum_blue_pct +
self.spectrum_white_pct + self.spectrum_farred_pct +
self.spectrum_uv_pct)
return abs(total - 100.0) < 0.1
def ppfd_from_dli(self) -> float:
"""Calcola PPFD target dalle ore di fotoperiodo e DLI"""
photoperiod_seconds = self.photoperiod_hours * 3600
return (self.dli_target * 1_000_000) / photoperiod_seconds
@dataclass
class PIDController:
"""
Controller PID generico per parametri ambientali.
Usa anti-windup per prevenire integrator saturation.
"""
kp: float
ki: float
kd: float
setpoint: float
output_min: float = 0.0
output_max: float = 100.0
_integral: float = field(default=0.0, init=False)
_last_error: float = field(default=0.0, init=False)
_last_time: float = field(default_factory=time.time, init=False)
def compute(self, measured_value: float) -> float:
current_time = time.time()
dt = current_time - self._last_time
if dt <= 0:
return self._last_output if hasattr(self, '_last_output') else 0.0
error = self.setpoint - measured_value
# Proporzionale
p_term = self.kp * error
# Integrale con anti-windup (clamping)
self._integral += error * dt
i_term = self.ki * self._integral
# Clamp integrale per prevenire windup
i_max = (self.output_max - self.output_min) / self.ki if self.ki != 0 else 1000
self._integral = max(-i_max, min(i_max, self._integral))
i_term = self.ki * self._integral
# Derivativo
d_term = self.kd * (error - self._last_error) / dt if dt > 0 else 0.0
output = p_term + i_term + d_term
output = max(self.output_min, min(self.output_max, output))
self._last_error = error
self._last_time = current_time
self._last_output = output
return output
class EnvironmentalController:
"""
Controller principale per una zona di coltivazione.
Gestisce temperatura, CO2, umidita e irrigazione via PID.
"""
def __init__(self, zone_id: str, recipe: LightRecipe):
self.zone_id = zone_id
self.recipe = recipe
# PID temperatura: setpoint 21°C, banda ±1°C
self.temp_pid = PIDController(
kp=2.0, ki=0.5, kd=0.1,
setpoint=21.0,
output_min=-100.0, # raffreddamento
output_max=100.0 # riscaldamento
)
# PID CO2: setpoint 1200 ppm per crescita accelerata
self.co2_pid = PIDController(
kp=0.5, ki=0.1, kd=0.05,
setpoint=1200.0,
output_min=0.0,
output_max=100.0 # % apertura valvola CO2
)
# PID umidita relativa: setpoint 70%
self.humidity_pid = PIDController(
kp=1.5, ki=0.3, kd=0.05,
setpoint=70.0,
output_min=0.0,
output_max=100.0
)
def compute_led_pwm(self, current_hour: float) -> dict:
"""
Calcola duty cycle PWM per ogni canale LED
in base all'ora del giorno e alla ricetta.
"""
# Determina se siamo nel fotoperiodo attivo
# Fotoperiodo: ore 6:00 - (6 + photoperiod_hours)
start_hour = 6.0
end_hour = start_hour + self.recipe.photoperiod_hours
if not (start_hour <= current_hour < end_hour):
return {
'red': 0.0, 'blue': 0.0,
'white': 0.0, 'farred': 0.0, 'uv': 0.0
}
# Calcola intensità normalizzata (0-1) dal PPFD target
# Assumendo che 100% PWM = 600 µmol/m²/s
max_ppfd = 600.0
intensity = min(self.recipe.ppfd_target / max_ppfd, 1.0)
return {
'red': round(intensity * self.recipe.spectrum_red_pct / 100, 4),
'blue': round(intensity * self.recipe.spectrum_blue_pct / 100, 4),
'white': round(intensity * self.recipe.spectrum_white_pct / 100, 4),
'farred': round(intensity * self.recipe.spectrum_farred_pct / 100, 4),
'uv': round(intensity * self.recipe.spectrum_uv_pct / 100, 4),
}
async def control_loop(self, sensor_reader, actuator_writer, interval: float = 30.0):
"""
Loop di controllo asincrono: legge sensori, calcola PID, scrive attuatori.
Frequenza default: ogni 30 secondi.
"""
logger.info(f"Avvio loop controllo zona {self.zone_id}")
while True:
try:
# Leggi sensori
sensors = await sensor_reader.read_zone(self.zone_id)
# Calcola output PID
temp_output = self.temp_pid.compute(sensors['temperature'])
co2_output = self.co2_pid.compute(sensors['co2_ppm'])
humidity_output = self.humidity_pid.compute(sensors['humidity_rh'])
# Calcola PWM LED
from datetime import datetime
current_hour = datetime.now().hour + datetime.now().minute / 60.0
led_pwm = self.compute_led_pwm(current_hour)
# Scrivi attuatori
await actuator_writer.set_hvac(self.zone_id, temp_output, humidity_output)
await actuator_writer.set_co2_valve(self.zone_id, co2_output)
await actuator_writer.set_led_channels(self.zone_id, led_pwm)
logger.debug(
f"Zona {self.zone_id} | "
f"T={sensors['temperature']:.1f}°C (PID:{temp_output:.1f}%) | "
f"CO2={sensors['co2_ppm']:.0f}ppm (valve:{co2_output:.1f}%) | "
f"RH={sensors['humidity_rh']:.1f}% | "
f"LED R:{led_pwm['red']:.2f} B:{led_pwm['blue']:.2f}"
)
except Exception as e:
logger.error(f"Errore loop controllo zona {self.zone_id}: {e}")
await asyncio.sleep(interval)
# --- Ricette standard per colture comuni ---
LETTUCE_VEGETATIVE = LightRecipe(
crop_name="Lattuga Lollo",
stage=CropStage.VEGETATIVE,
ppfd_target=200.0,
dli_target=17.0,
photoperiod_hours=16.0,
spectrum_red_pct=65.0,
spectrum_blue_pct=20.0,
spectrum_white_pct=10.0,
spectrum_farred_pct=5.0,
spectrum_uv_pct=0.0,
)
BASIL_VEGETATIVE = LightRecipe(
crop_name="Basilico Genovese",
stage=CropStage.VEGETATIVE,
ppfd_target=250.0,
dli_target=15.0,
photoperiod_hours=16.0,
spectrum_red_pct=60.0,
spectrum_blue_pct=25.0, # blue elevato per aromi
spectrum_white_pct=10.0,
spectrum_farred_pct=5.0,
spectrum_uv_pct=0.0,
)
STRAWBERRY_FLOWERING = LightRecipe(
crop_name="Fragola Elsanta",
stage=CropStage.MATURATION,
ppfd_target=350.0,
dli_target=22.0,
photoperiod_hours=12.0, # fotoperiodo corto per fioritura
spectrum_red_pct=55.0,
spectrum_blue_pct=20.0,
spectrum_white_pct=20.0,
spectrum_farred_pct=5.0,
spectrum_uv_pct=0.0,
)
Systemy nawadniające: NFT, DWC i aeroponika
Trzy dominujące technologie hydroponiczne w gospodarstwach pionowych mają architekturę sterowania różne, każdy z własnymi parametrami krytycznymi do monitorowania i regulacji.
Porównanie systemów hydroponicznych
| System | Idealne uprawy | Parametry krytyczne | Kontrola złożoności | Zużycie wody |
|---|---|---|---|---|
| NFT (Technika filmu odżywczego) | Sałaty, zioła | Natężenie przepływu, nachylenie kanału, EC, pH | Przeciętny | Minimalna (recyrkulacja) |
| DWC (Kultura głębokiej wody) | Sałaty, szpinak | Natlenienie (DO), EC, pH, temperatura | Niski | Bas |
| Aeroponika | Truskawki, korzenie, zioła | Cykl nebulizacji, ciśnienie, EC, pH | Wysoki | Minimum (90% w stosunku do gleby) |
| Podłoża (kokos/wełna mineralna) | Pomidory, papryka | Nawadnianie cykliczne, EC, pH, drenaż | Przeciętny | Umiarkowany |
"""
Irrigation Controller per sistema NFT
Gestione pompe, monitoraggio EC/pH, dosaggio nutrienti
"""
import asyncio
from dataclasses import dataclass
from typing import Optional
import logging
logger = logging.getLogger(__name__)
NUTRIENT_TARGETS = {
"lettuce": {"ec_ms_cm": 1.6, "ph": 6.0, "temp_c": 20.0},
"basil": {"ec_ms_cm": 1.8, "ph": 6.0, "temp_c": 21.0},
"spinach": {"ec_ms_cm": 2.0, "ph": 6.2, "temp_c": 20.0},
"strawberry": {"ec_ms_cm": 2.2, "ph": 5.8, "temp_c": 18.0},
"herbs": {"ec_ms_cm": 1.4, "ph": 6.0, "temp_c": 21.0},
}
@dataclass
class NFTController:
zone_id: str
crop_type: str
flow_rate_lpm: float = 1.5 # litri/minuto per canale
channel_slope_pct: float = 2.0 # pendenza canale in %
def get_targets(self) -> dict:
return NUTRIENT_TARGETS.get(self.crop_type, NUTRIENT_TARGETS["lettuce"])
async def check_and_adjust(self, ec_sensor: float, ph_sensor: float,
ec_doser, ph_doser) -> dict:
targets = self.get_targets()
actions = {}
# Controllo EC
ec_delta = targets["ec_ms_cm"] - ec_sensor
if abs(ec_delta) > 0.2:
if ec_delta > 0:
# EC troppo bassa: aggiungi concentrato nutrienti
dose_ml = ec_delta * 50 # ml di concentrato A+B
await ec_doser.dose(zone=self.zone_id, ml=dose_ml, solution="AB")
actions["ec_dosing"] = f"+{dose_ml:.1f}ml AB"
else:
# EC troppo alta: diluisci con acqua RO
await ec_doser.dose_water(zone=self.zone_id, ml=abs(ec_delta) * 100)
actions["ec_dilution"] = f"+{abs(ec_delta)*100:.0f}ml H2O"
# Controllo pH
ph_delta = targets["ph"] - ph_sensor
if abs(ph_delta) > 0.3:
if ph_delta > 0:
# pH troppo basso: aggiungi pH-up (KOH)
dose_ml = abs(ph_delta) * 10
await ph_doser.dose(zone=self.zone_id, ml=dose_ml, solution="ph_up")
actions["ph_adjust"] = f"pH-up +{dose_ml:.1f}ml"
else:
# pH troppo alto: aggiungi pH-down (H3PO4)
dose_ml = abs(ph_delta) * 10
await ph_doser.dose(zone=self.zone_id, ml=dose_ml, solution="ph_down")
actions["ph_adjust"] = f"pH-down +{dose_ml:.1f}ml"
logger.info(
f"NFT zona {self.zone_id} | "
f"EC: {ec_sensor:.2f} (target {targets['ec_ms_cm']:.2f}) | "
f"pH: {ph_sensor:.2f} (target {targets['ph']:.2f}) | "
f"Azioni: {actions}"
)
return actions
Robotyka i automatyka: ROS2 w gospodarstwie pionowym
Robotyka będzie najbardziej rewolucyjnym czynnikiem w rolnictwie pionowym w 2025 r. Operacje ręczne bardziej intensywne są wysiew (siew w tacach), przesadzanie (przesadzanie sadzonek do regały końcowe), zbiór i pakowanie. Jeden pracownik może przesadzić około 500-700 roślin na razie; robot do przesadzania działa z prędkością 2000–3000 sadzonek na godzinę przy niższym poziomie błędów na poziomie 1%. Z 30 000 tac sałaty dziennie (jak w przypadku Agricola Moderna w Agnadello), robotyka to nie wybór, to konieczność.
ROS2 (Robot Operating System 2) stał się de facto standardem w programowaniu robotów w środowisku zamkniętym. W porównaniu do ROS1 oferuje natywną obsługę w czasie rzeczywistym (oprogramowanie pośrednie DDS), Ulepszona architektura zabezpieczeń, natywna obsługa wielu robotów i zarządzane węzły cyklu życia. Struktura węzłów i tematów pozwala na wyraźne oddzielenie logiki planowania ruch, sterowanie motoryczne, sztuczne widzenie i interfejs z systemem zarządzania gospodarstwem.
"""
ROS2 Node per Harvesting Robot in Vertical Farm
Gestisce pianificazione percorso, prelievo e deposito vassoi
"""
import rclpy
from rclpy.node import Node
from rclpy.action import ActionServer
from geometry_msgs.msg import Pose, PoseStamped
from std_msgs.msg import String, Bool
from sensor_msgs.msg import Image
import json
import asyncio
# Messaggi custom per la farm (definiti in farm_interfaces package)
# from farm_interfaces.msg import TrayInfo, HarvestStatus
# from farm_interfaces.action import HarvestTray
# from farm_interfaces.srv import GetZoneLayout
class HarvestingRobotNode(Node):
"""
Nodo ROS2 per robot di raccolta in vertical farm.
Si interfaccia con:
- Sistema SCADA per ricevere job di raccolta
- Controller braccio robotico (MoveIt2)
- Sistema conveyor per deposito vassoi
- Computer vision per verifica maturita
"""
def __init__(self):
super().__init__('harvesting_robot_node')
# Publisher stato robot
self.status_pub = self.create_publisher(
String, '/farm/robot/harvest/status', 10
)
# Subscriber per job di raccolta da SCADA
self.job_sub = self.create_subscription(
String, '/farm/scada/harvest_jobs',
self.on_harvest_job, 10
)
# Subscriber per immagine camera end-effector
self.camera_sub = self.create_subscription(
Image, '/robot/camera/raw',
self.on_camera_frame, 10
)
# Client per servizio layout zona
# self.layout_client = self.create_client(GetZoneLayout, '/farm/zone/layout')
self.current_job: dict = {}
self.is_busy = False
self.get_logger().info('HarvestingRobotNode avviato')
def on_harvest_job(self, msg: String):
"""Riceve job di raccolta dallo SCADA"""
if self.is_busy:
self.get_logger().warn('Robot occupato, job ignorato')
return
try:
job = json.loads(msg.data)
self.get_logger().info(
f"Job ricevuto: zona={job['zone_id']}, "
f"tray={job['tray_id']}, "
f"crop={job['crop_type']}"
)
self.current_job = job
self.is_busy = True
# Avvia sequenza raccolta in thread separato
self.executor.create_task(self.execute_harvest(job))
except (json.JSONDecodeError, KeyError) as e:
self.get_logger().error(f"Job malformato: {e}")
async def execute_harvest(self, job: dict) -> bool:
"""
Sequenza completa raccolta:
1. Naviga verso zona target
2. Verifica maturita con visione artificiale
3. Preleva vassoio con braccio robotico
4. Trasporta a conveyor di uscita
5. Aggiorna SCADA
"""
try:
# Step 1: Navigazione
self.publish_status("NAVIGATING", job)
success = await self.navigate_to_zone(job['zone_id'], job['shelf_row'])
if not success:
self.publish_status("NAV_FAILED", job)
return False
# Step 2: Verifica maturita (computer vision)
maturity_score = await self.check_crop_maturity(job['tray_id'])
if maturity_score < 0.85:
self.get_logger().warn(
f"Vassoio {job['tray_id']}: maturita {maturity_score:.2f} "
f"sotto soglia 0.85, raccolta posticipata"
)
self.publish_status("MATURITY_INSUFFICIENT", job)
self.is_busy = False
return False
# Step 3: Raccolta
self.publish_status("HARVESTING", job)
await self.pick_tray(job['tray_id'], job['shelf_position'])
# Step 4: Deposito su conveyor
self.publish_status("DELIVERING", job)
await self.deliver_to_conveyor(job['destination_line'])
# Step 5: Completamento
self.publish_status("COMPLETED", job)
self.is_busy = False
return True
except Exception as e:
self.get_logger().error(f"Errore harvest job {job.get('tray_id')}: {e}")
self.publish_status("ERROR", job)
self.is_busy = False
return False
def publish_status(self, status: str, job: dict):
msg = String()
msg.data = json.dumps({
"robot_id": self.get_name(),
"status": status,
"tray_id": job.get("tray_id"),
"zone_id": job.get("zone_id"),
"timestamp": self.get_clock().now().to_msg().sec
})
self.status_pub.publish(msg)
async def navigate_to_zone(self, zone_id: str, shelf_row: int) -> bool:
"""Naviga AGV verso la zona target (stub - usa Nav2 in produzione)"""
self.get_logger().info(f"Navigazione verso zona {zone_id} fila {shelf_row}")
await asyncio.sleep(2.0) # simulazione movimento
return True
async def check_crop_maturity(self, tray_id: str) -> float:
"""Analisi visione artificiale per valutazione maturita (stub)"""
# In produzione: inferenza YOLO/custom model su immagine camera
await asyncio.sleep(0.5)
return 0.92 # score maturita 0-1
async def pick_tray(self, tray_id: str, position: dict) -> bool:
"""Controllo braccio robotico per prelievo vassoio via MoveIt2 (stub)"""
await asyncio.sleep(1.5)
return True
async def deliver_to_conveyor(self, destination_line: str) -> bool:
"""Deposita vassoio su conveyor di uscita (stub)"""
await asyncio.sleep(1.0)
return True
def on_camera_frame(self, msg: Image):
"""Callback per frame camera (elaborato async su richiesta)"""
pass
def main(args=None):
rclpy.init(args=args)
node = HarvestingRobotNode()
try:
rclpy.spin(node)
except KeyboardInterrupt:
pass
finally:
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
Projekt API dla farmy pionowej: Backend FastAPI REST
Warstwa API jest punktem integracji pomiędzy fizycznym systemem kontroli gospodarstwa a systemem świat zewnętrzny: korporacyjny ERP, portal klienta, aplikacja mobilna operatora, system WMS magazyn dystrybucyjny. Źle zaprojektowany interfejs API w tym kontekście prowadzi do niespójności w recepturach upraw, błędy w harmonogramie i potencjalne straty w plonach. Dobry Natomiast API to układ nerwowy koordynujący wszystkie podsystemy.
"""
FastAPI Backend per Gestione Vertical Farm
Endpoints: ricette colturali, zone, batch produzione, attuatori, sensori
"""
from fastapi import FastAPI, HTTPException, Depends, BackgroundTasks, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from pydantic import BaseModel, Field, validator
from typing import Optional, List
from datetime import datetime, date
from enum import Enum
import uuid
app = FastAPI(
title="Vertical Farm Control API",
description="API per gestione vertical farm: ricette, zone, robot, sensori",
version="2.1.0"
)
security = HTTPBearer()
# ============================================================
# MODELLI PYDANTIC
# ============================================================
class CropTypeEnum(str, Enum):
LETTUCE = "lettuce"
BASIL = "basil"
SPINACH = "spinach"
STRAWBERRY = "strawberry"
MICROGREENS = "microgreens"
HERBS = "herbs"
class GrowingSystemEnum(str, Enum):
NFT = "nft"
DWC = "dwc"
AEROPONICS = "aeroponics"
SUBSTRATE = "substrate"
class LightRecipeCreate(BaseModel):
name: str = Field(..., min_length=3, max_length=100)
crop_type: CropTypeEnum
growth_stage: str
ppfd_target: float = Field(..., ge=50, le=800)
dli_target: float = Field(..., ge=5, le=40)
photoperiod_hours: float = Field(..., ge=8, le=24)
spectrum_red_pct: float = Field(..., ge=0, le=100)
spectrum_blue_pct: float = Field(..., ge=0, le=100)
spectrum_white_pct: float = Field(..., ge=0, le=100)
spectrum_farred_pct: float = Field(default=0.0, ge=0, le=20)
spectrum_uv_pct: float = Field(default=0.0, ge=0, le=10)
notes: Optional[str] = None
@validator('spectrum_blue_pct')
def validate_spectrum_sum(cls, v, values):
total = (values.get('spectrum_red_pct', 0) + v +
values.get('spectrum_white_pct', 0))
# Tolleranza +/- 5% per arrotondamenti
if total > 105:
raise ValueError(f"Somma canali spettro {total}% supera 100%")
return v
class BatchCreate(BaseModel):
zone_id: str
recipe_id: str
crop_type: CropTypeEnum
growing_system: GrowingSystemEnum
seeding_date: date
expected_harvest_date: date
tray_count: int = Field(..., ge=1, le=10000)
seeds_per_tray: int = Field(default=50, ge=1, le=500)
client_order_id: Optional[str] = None
@validator('expected_harvest_date')
def harvest_after_seeding(cls, v, values):
seeding = values.get('seeding_date')
if seeding and v <= seeding:
raise ValueError("La data di raccolta deve essere successiva alla semina")
return v
class SensorReading(BaseModel):
zone_id: str
timestamp: datetime
temperature_c: float
humidity_rh: float
co2_ppm: float
ppfd_umol: Optional[float] = None
ec_ms_cm: Optional[float] = None
ph: Optional[float] = None
water_temp_c: Optional[float] = None
class ActuatorCommand(BaseModel):
zone_id: str
command_type: str # "led_update", "co2_valve", "pump_speed", "hvac"
parameters: dict
priority: int = Field(default=5, ge=1, le=10) # 10 = emergenza
# ============================================================
# ROUTES: RICETTE COLTURALI
# ============================================================
@app.post("/api/v1/recipes", status_code=status.HTTP_201_CREATED)
async def create_recipe(
recipe: LightRecipeCreate,
credentials: HTTPAuthorizationCredentials = Depends(security)
):
"""
Crea una nuova ricetta colturale nel sistema.
Le ricette definiscono parametri luminosi, ambientali e irrigazione.
"""
recipe_id = str(uuid.uuid4())
# In produzione: salvataggio su PostgreSQL
return {
"recipe_id": recipe_id,
"name": recipe.name,
"crop_type": recipe.crop_type,
"ppfd_target": recipe.ppfd_target,
"dli_target": recipe.dli_target,
"created_at": datetime.utcnow().isoformat(),
"status": "active"
}
@app.get("/api/v1/recipes/{recipe_id}")
async def get_recipe(recipe_id: str):
"""Recupera ricetta colturale per ID"""
# Stub - in produzione: query PostgreSQL
return {
"recipe_id": recipe_id,
"name": "Lattuga Lollo Rossa - Vegetativo",
"crop_type": "lettuce",
"ppfd_target": 200.0,
"dli_target": 17.0,
"photoperiod_hours": 16.0,
"spectrum": {"red": 65, "blue": 20, "white": 10, "farred": 5},
"env_targets": {"temp_c": 21.0, "humidity_rh": 70.0, "co2_ppm": 1200},
"nutrient_targets": {"ec_ms_cm": 1.6, "ph": 6.0}
}
@app.get("/api/v1/recipes")
async def list_recipes(
crop_type: Optional[CropTypeEnum] = None,
active_only: bool = True,
limit: int = Field(default=50, le=200)
):
"""Lista ricette con filtro per tipo coltura"""
return {
"recipes": [],
"total": 0,
"filters": {"crop_type": crop_type, "active_only": active_only}
}
# ============================================================
# ROUTES: BATCH PRODUZIONE
# ============================================================
@app.post("/api/v1/batches", status_code=status.HTTP_201_CREATED)
async def create_batch(
batch: BatchCreate,
background_tasks: BackgroundTasks,
credentials: HTTPAuthorizationCredentials = Depends(security)
):
"""
Avvia un nuovo batch di produzione.
Associa zona, ricetta, dati di seeding e target harvest.
Background: programma scheduling LED e irrigazione su SCADA.
"""
batch_id = str(uuid.uuid4())
background_tasks.add_task(schedule_batch_on_scada, batch_id, batch)
return {
"batch_id": batch_id,
"zone_id": batch.zone_id,
"crop_type": batch.crop_type,
"seeding_date": batch.seeding_date.isoformat(),
"expected_harvest_date": batch.expected_harvest_date.isoformat(),
"tray_count": batch.tray_count,
"status": "scheduled"
}
async def schedule_batch_on_scada(batch_id: str, batch: BatchCreate):
"""Task background: invia configurazione a SCADA per scheduling"""
# In produzione: chiamata API verso SCADA (Ignition, custom Python SCADA)
pass
# ============================================================
# ROUTES: SENSORI E TELEMETRIA
# ============================================================
@app.post("/api/v1/telemetry")
async def ingest_sensor_data(reading: SensorReading):
"""
Endpoint per ingestion dati sensori da edge controller.
Validazione, allarmi e storicizzazione su InfluxDB/TimescaleDB.
"""
alerts = []
# Allarmi temperatura
if reading.temperature_c > 28.0:
alerts.append({"type": "HIGH_TEMP", "value": reading.temperature_c, "threshold": 28.0})
elif reading.temperature_c < 16.0:
alerts.append({"type": "LOW_TEMP", "value": reading.temperature_c, "threshold": 16.0})
# Allarmi CO2
if reading.co2_ppm > 2000:
alerts.append({"type": "HIGH_CO2", "value": reading.co2_ppm, "threshold": 2000})
# Allarmi pH
if reading.ph is not None and (reading.ph < 5.0 or reading.ph > 7.5):
alerts.append({"type": "PH_OUT_OF_RANGE", "value": reading.ph})
# In produzione: write batch su InfluxDB e publish alert su Kafka/MQTT
return {
"status": "accepted",
"zone_id": reading.zone_id,
"timestamp": reading.timestamp.isoformat(),
"alerts": alerts,
"alert_count": len(alerts)
}
@app.get("/api/v1/zones/{zone_id}/current")
async def get_zone_current_state(zone_id: str):
"""Stato ambientale corrente di una zona (last value da InfluxDB)"""
# Stub
return {
"zone_id": zone_id,
"timestamp": datetime.utcnow().isoformat(),
"sensors": {
"temperature_c": 21.3,
"humidity_rh": 69.8,
"co2_ppm": 1185,
"ppfd_umol": 198.5,
"ec_ms_cm": 1.62,
"ph": 6.05
},
"actuators": {
"led_pwm": {"red": 0.617, "blue": 0.192, "white": 0.098, "farred": 0.049},
"co2_valve_pct": 12.5,
"hvac_cooling_pct": 35.0,
"pump_active": True
},
"active_batch_id": "b-2025-001-lollo",
"days_since_seeding": 18
}
# ============================================================
# ROUTES: CONTROLLO ATTUATORI
# ============================================================
@app.post("/api/v1/actuators/command")
async def send_actuator_command(
command: ActuatorCommand,
credentials: HTTPAuthorizationCredentials = Depends(security)
):
"""
Invia comando manuale a un attuatore di zona.
Usato per override manuali, manutenzione e test.
Richiede autenticazione e viene loggato per audit.
"""
allowed_commands = {"led_update", "co2_valve", "pump_speed", "hvac", "emergency_stop"}
if command.command_type not in allowed_commands:
raise HTTPException(
status_code=400,
detail=f"Tipo comando non valido: {command.command_type}"
)
command_id = str(uuid.uuid4())
# In produzione: publish su MQTT/OPC-UA verso edge controller
return {
"command_id": command_id,
"zone_id": command.zone_id,
"command_type": command.command_type,
"parameters": command.parameters,
"status": "sent",
"sent_at": datetime.utcnow().isoformat()
}
Cyfrowy bliźniak farmy: symulacja i optymalizacja
Cyfrowy bliźniak farmy pionowej i model obliczeniowy replikujący zachowanie fizyki gospodarstw rolnych na tyle dokładnie, aby umożliwić symulacje predykcyjne. Nie tak To nie jest wizualna replika 3D (to „wizualizacja”), ale model matematyczny który przy obecnym stanie parametrów środowiskowych przewiduje wzrost roślin i czas żniw.
Na tym podejściu opierają się najczęściej stosowane modele wzrostu roślin w kontrolowanych środowiskach efektywność wykorzystania promieniowania (RUE): zgromadzona biomasa jest proporcjonalna do światło przechwytywane (PAR) i wydajność konwersji, która zależy od temperatury, CO2, dostępność wody i składników odżywczych. Modele te, pierwotnie opracowane dla systemów prognoz plonów na polach otwartych (np. DSSAT, APSIM), zostały dostosowane do środowiska w pomieszczeniu z eksperymentalnie skalibrowanymi parametrami.
"""
Digital Twin - Modello di Crescita Vegetale per Vertical Farm
Basato su Radiation Use Efficiency (RUE) + effetti temperatura/CO2
"""
import numpy as np
from dataclasses import dataclass, field
from typing import List, Optional
from datetime import datetime, timedelta
@dataclass
class PlantGrowthModel:
"""
Modello semplificato di crescita per lattuga in sistema idroponico.
Parametri calibrati su dati sperimentali per Lactuca sativa.
"""
# Parametri biologici della coltura
rue_base: float = 1.8 # g biomassa / MJ PAR intercettato
temp_base: float = 5.0 # temperatura base (°C) - sotto non cresce
temp_opt: float = 22.0 # temperatura ottimale
temp_max: float = 32.0 # temperatura max sopravvivenza
co2_base_ppm: float = 400.0 # CO2 ambient reference
co2_enhancement: float = 0.002 # incremento RUE per ppm CO2 extra
# Stato corrente della pianta
fresh_weight_g: float = 0.5 # peso fresco iniziale (semenzale 5g DW)
dry_weight_g: float = 0.05 # peso secco iniziale
leaf_area_cm2: float = 5.0 # area fogliare iniziale
days_since_seeding: int = 0
# Target harvest
target_fresh_weight_g: float = 150.0 # lattuga da 150g
water_content: float = 0.95 # % acqua rispetto al peso fresco
def temperature_factor(self, temp: float) -> float:
"""
Fattore temperatura (0-1) usando funzione beta.
temp_opt da il massimo rendimento (1.0).
"""
if temp <= self.temp_base or temp >= self.temp_max:
return 0.0
if temp <= self.temp_opt:
return (temp - self.temp_base) / (self.temp_opt - self.temp_base)
else:
return (self.temp_max - temp) / (self.temp_max - self.temp_opt)
def co2_factor(self, co2_ppm: float) -> float:
"""Fattore arricchimento CO2 (1.0 ad ambient, >1 con arricchimento)"""
extra_co2 = max(0, co2_ppm - self.co2_base_ppm)
return 1.0 + (self.co2_enhancement * extra_co2)
def par_intercepted_mj(self, ppfd: float, leaf_area_cm2: float,
photoperiod_h: float) -> float:
"""
Calcola PAR intercettata dalla pianta in MJ/giorno.
ppfd: µmol/m²/s -> conversione a W/m² (1 W/m² ≈ 4.6 µmol/m²/s per LED)
"""
ppfd_wm2 = ppfd / 4.6
par_w = ppfd_wm2 * (leaf_area_cm2 / 10000) # in m²
par_mj_day = par_w * photoperiod_h * 3600 / 1e6
return par_mj_day
def simulate_day(self, temp: float, co2_ppm: float,
ppfd: float, photoperiod_h: float) -> dict:
"""
Simula un giorno di crescita e aggiorna lo stato della pianta.
Restituisce delta giornaliero e stato aggiornato.
"""
# Fattori ambientali
tf = self.temperature_factor(temp)
cf = self.co2_factor(co2_ppm)
par_intercepted = self.par_intercepted_mj(ppfd, self.leaf_area_cm2, photoperiod_h)
# Crescita biomassa secca (RUE model)
delta_dw = self.rue_base * par_intercepted * tf * cf
delta_fw = delta_dw / (1 - self.water_content)
self.dry_weight_g += delta_dw
self.fresh_weight_g += delta_fw
# Aggiornamento area fogliare (SLA - specific leaf area)
sla_cm2_per_g = 350 # cm²/g DW per lattuga
self.leaf_area_cm2 = self.dry_weight_g * sla_cm2_per_g
self.days_since_seeding += 1
# Check harvest readiness
harvest_ready = self.fresh_weight_g >= self.target_fresh_weight_g
return {
"day": self.days_since_seeding,
"fresh_weight_g": round(self.fresh_weight_g, 2),
"dry_weight_g": round(self.dry_weight_g, 3),
"leaf_area_cm2": round(self.leaf_area_cm2, 1),
"delta_fw_g": round(delta_fw, 3),
"temp_factor": round(tf, 3),
"co2_factor": round(cf, 3),
"par_intercepted_mj": round(par_intercepted, 6),
"harvest_ready": harvest_ready,
}
def simulate_full_cycle(self, daily_conditions: List[dict]) -> dict:
"""
Simula l'intero ciclo colturale con condizioni giornaliere variabili.
Restituisce proiezione completa e giorno stimato di raccolta.
"""
days_log = []
harvest_day = None
for day_idx, cond in enumerate(daily_conditions):
day_state = self.simulate_day(
temp=cond.get('temp', 21.0),
co2_ppm=cond.get('co2_ppm', 1200),
ppfd=cond.get('ppfd', 200),
photoperiod_h=cond.get('photoperiod_h', 16)
)
days_log.append(day_state)
if day_state['harvest_ready'] and harvest_day is None:
harvest_day = day_idx + 1
return {
"days_simulated": len(days_log),
"final_fresh_weight_g": self.fresh_weight_g,
"estimated_harvest_day": harvest_day,
"daily_log": days_log,
"achieved_target": self.fresh_weight_g >= self.target_fresh_weight_g
}
# Esempio utilizzo digital twin
def predict_harvest_date(recipe: dict, seeding_date: datetime) -> datetime:
"""
Usa il digital twin per predire la data di raccolta
dato una ricetta ambientale costante.
"""
model = PlantGrowthModel()
# Condizioni giornaliere dalla ricetta (costanti per semplicità)
daily_conditions = [{
'temp': recipe.get('temp_c', 21.0),
'co2_ppm': recipe.get('co2_ppm', 1200),
'ppfd': recipe.get('ppfd_target', 200),
'photoperiod_h': recipe.get('photoperiod_hours', 16)
}] * 40 # massimo 40 giorni di simulazione
result = model.simulate_full_cycle(daily_conditions)
harvest_day = result.get('estimated_harvest_day', 35)
return seeding_date + timedelta(days=harvest_day)
Sztuczna inteligencja do optymalizacji: uczenie się przez wzmacnianie dla jasnych przepisów
Cyfrowy bliźniak umożliwia coś potężniejszego niż proste przewidywanie: optymalizacja receptur poprzez uczenie się przez wzmacnianie (RL). Agent R.L wchodzi w interakcję z cyfrowym bliźniakiem (nie z prawdziwą farmą), bada tysiące kombinacji parametrów świetlnych i znaleźć konfiguracje, które maksymalizują wydajność przy jednoczesnej minimalizacji zużycia energiczny. Gdy w symulacji zostanie znaleziona optymalna receptura, jest ona poddawana walidacji obszar pilotażowy prawdziwego gospodarstwa przed wdrożeniem skali.
Takie podejście – stwierdził transfer z sim na reali granice badań w rolnictwie pionowym AI. Luka pomiędzy symulacją a rzeczywistością (luka pomiędzy symulacją a rzeczywistością) wymaga: ciągła kalibracja modelu wzrostu na rzeczywistych danych zebranych z gospodarstwa.
"""
Reinforcement Learning per Ottimizzazione Ricette Luminose
Usa Gymnasium + custom environment basato sul PlantGrowthModel
"""
import gymnasium as gym
import numpy as np
from gymnasium import spaces
from typing import Tuple, Optional
class VerticalFarmEnv(gym.Env):
"""
Ambiente Gymnasium per ottimizzazione ricette luminose.
Observation space: stato ambientale corrente + stato pianta
Action space: aggiustamenti parametri LED (continuo)
Reward: crescita giornaliera / consumo energetico
"""
metadata = {'render_modes': ['human']}
def __init__(self, crop_type: str = "lettuce", episode_days: int = 30):
super().__init__()
self.crop_type = crop_type
self.episode_days = episode_days
self.current_day = 0
# Action space: [delta_ppfd, delta_red_ratio, delta_blue_ratio, delta_photoperiod]
# Valori normalizzati in [-1, 1], scalati internamente
self.action_space = spaces.Box(
low=np.array([-1.0, -1.0, -1.0, -1.0], dtype=np.float32),
high=np.array([1.0, 1.0, 1.0, 1.0], dtype=np.float32)
)
# Observation space: [ppfd, red_ratio, blue_ratio, photoperiod,
# fresh_weight, leaf_area, days, temp, co2]
self.observation_space = spaces.Box(
low=np.array([50, 0, 0, 8, 0, 0, 0, 15, 400], dtype=np.float32),
high=np.array([800, 1, 1, 24, 500, 5000, 45, 30, 2000], dtype=np.float32)
)
# Stato corrente
self.ppfd = 200.0
self.red_ratio = 0.65
self.blue_ratio = 0.20
self.photoperiod = 16.0
self.plant_model = None
def reset(self, seed: Optional[int] = None, **kwargs) -> Tuple[np.ndarray, dict]:
super().reset(seed=seed)
self.current_day = 0
self.ppfd = 200.0
self.red_ratio = 0.65
self.blue_ratio = 0.20
self.photoperiod = 16.0
from digital_twin import PlantGrowthModel # import locale
self.plant_model = PlantGrowthModel()
return self._get_obs(), {}
def step(self, action: np.ndarray) -> Tuple[np.ndarray, float, bool, bool, dict]:
# Applica azione con scaling
self.ppfd = np.clip(self.ppfd + action[0] * 50, 50, 800)
self.red_ratio = np.clip(self.red_ratio + action[1] * 0.1, 0.3, 0.8)
self.blue_ratio = np.clip(self.blue_ratio + action[2] * 0.05, 0.1, 0.35)
self.photoperiod = np.clip(self.photoperiod + action[3] * 1.0, 10, 22)
# Simula giorno con nuove condizioni
day_result = self.plant_model.simulate_day(
temp=21.0, co2_ppm=1200,
ppfd=self.ppfd, photoperiod_h=self.photoperiod
)
# Calcola consumo energetico (kWh/giorno per m² crescita)
energy_kwh = (self.ppfd / 4.6) * (self.photoperiod / 1000) # semplificato
# Reward: crescita / energia (massimizza efficienza)
growth = day_result['delta_fw_g']
reward = growth / max(energy_kwh, 0.001) * 0.01
# Penalita per harvest_ready raggiunto troppo tardi
if self.current_day > 35 and not day_result['harvest_ready']:
reward -= 5.0
# Bonus per harvest_ready raggiunto nei tempi
if day_result['harvest_ready'] and self.current_day <= 28:
reward += 20.0
self.current_day += 1
done = day_result['harvest_ready'] or self.current_day >= self.episode_days
return self._get_obs(), reward, done, False, day_result
def _get_obs(self) -> np.ndarray:
pm = self.plant_model
return np.array([
self.ppfd, self.red_ratio, self.blue_ratio, self.photoperiod,
pm.fresh_weight_g if pm else 0.5,
pm.leaf_area_cm2 if pm else 5.0,
self.current_day, 21.0, 1200.0
], dtype=np.float32)
# Training con Stable-Baselines3
# from stable_baselines3 import PPO
# env = VerticalFarmEnv(crop_type="lettuce")
# model = PPO("MlpPolicy", env, verbose=1, learning_rate=3e-4)
# model.learn(total_timesteps=500_000)
# model.save("optimized_lettuce_recipe_v1")
Przemysłowa infrastruktura IoT: Modbus, MQTT, OPC-UA
Farmy pionowe wykorzystują nakładkę protokołów przemysłowych: Modbus RTU/TCP dla komunikacja ze starszymi czujnikami i elementami wykonawczymi (termohigrometry, mierniki CO2, sterowniki LED), OPC-UA do komunikacji ze sterownikami PLC Siemens/Beckhoff i systemami SCADA, MQTT dla wysyłanie danych do chmury. Wybór zależy od sprzętu dostawcy i opóźnienia wymagane i poziom bezpieczeństwa.
Protokoły IoT w rolnictwie pionowym: porównanie
| Protokół | Warstwy | Utajenie | Bezpieczeństwo | Typowy przypadek użycia |
|---|---|---|---|---|
| Modbus RTU | Pole-PLC | 10-100ms | Nieobecny (starość) | Czujniki EC/pH, sterowniki LED |
| Modbus TCP | PLC-SCADA | 5-50 ms | Opcjonalny TLS | Akwizycja danych PLC |
| OPC-UA | PLC-SCADA-Cloud | 1-50ms | X.509, podpis, szyfrowanie | Standardy Przemysłu 4.0 |
| MQTT | Chmura brzegowa | 10-500 ms | TLS + uwierzytelnianie | Telemetria do chmury |
| ODPOCZYNEK/HTTP | Chmura-chmura | 50-500ms | HTTPS, OAuth2 | API integracji ERP |
"""
Bridge Modbus -> MQTT per vertical farm
Legge sensori via Modbus RTU e pubblica su MQTT broker
"""
import asyncio
import json
import time
import logging
from pymodbus.client import AsyncModbusSerialClient
import paho.mqtt.client as mqtt
logger = logging.getLogger(__name__)
# Mappa registri Modbus per sensore combo Temp/RH/CO2 (esempio Vaisala HMP60)
MODBUS_REGISTER_MAP = {
"temperature": {"address": 0x0000, "count": 1, "scale": 0.1, "unit": "°C"},
"humidity": {"address": 0x0001, "count": 1, "scale": 0.1, "unit": "%RH"},
"co2_ppm": {"address": 0x0002, "count": 1, "scale": 1.0, "unit": "ppm"},
"ec_ms_cm": {"address": 0x0010, "count": 1, "scale": 0.01, "unit": "mS/cm"},
"ph": {"address": 0x0011, "count": 1, "scale": 0.01, "unit": "pH"},
}
class ModbusMQTTBridge:
def __init__(self, zone_id: str, modbus_port: str,
modbus_address: int, mqtt_broker: str, mqtt_port: int = 1883):
self.zone_id = zone_id
self.modbus_client = AsyncModbusSerialClient(
port=modbus_port, baudrate=9600, timeout=3
)
self.mqtt_client = mqtt.Client(client_id=f"bridge-{zone_id}")
self.mqtt_broker = mqtt_broker
self.mqtt_port = mqtt_port
self.mqtt_topic = f"farm/zones/{zone_id}/telemetry"
async def connect(self):
await self.modbus_client.connect()
self.mqtt_client.connect(self.mqtt_broker, self.mqtt_port, keepalive=60)
self.mqtt_client.loop_start()
logger.info(f"Bridge avviato per zona {self.zone_id}")
async def read_all_sensors(self, device_id: int = 1) -> dict:
readings = {"zone_id": self.zone_id, "timestamp": time.time()}
for sensor_name, reg in MODBUS_REGISTER_MAP.items():
try:
result = await self.modbus_client.read_holding_registers(
address=reg["address"],
count=reg["count"],
slave=device_id
)
if not result.isError():
raw_value = result.registers[0]
readings[sensor_name] = round(raw_value * reg["scale"], 3)
else:
logger.warning(f"Errore lettura {sensor_name} zona {self.zone_id}")
readings[sensor_name] = None
except Exception as e:
logger.error(f"Eccezione Modbus {sensor_name}: {e}")
readings[sensor_name] = None
return readings
async def publish_loop(self, interval_sec: float = 30.0):
while True:
readings = await self.read_all_sensors()
payload = json.dumps(readings)
result = self.mqtt_client.publish(
topic=self.mqtt_topic,
payload=payload,
qos=1
)
if result.rc == mqtt.MQTT_ERR_SUCCESS:
logger.debug(f"Pubblicato su {self.mqtt_topic}: {payload[:80]}...")
else:
logger.error(f"Errore publish MQTT: rc={result.rc}")
await asyncio.sleep(interval_sec)
Ekonomika rolnictwa pionowego: CAPEX, OPEX i próg rentowności
Czy rolnictwo pionowe jest zrównoważone ekonomicznie? Odpowiedź w 2025 roku brzmi: Zależy. Zależy to od uprawy (zioła i mikroliście są znacznie bardziej opłacalne niż sałata), od skala (ekonomia skali pojawia się powyżej 5000 m²), lokalizacja (koszt istotne są lokalne koszty energii i pracy) oraz według kanału sprzedaży (sprzedaż bezpośrednia B2C po cenach premium vs towary GDO).
Analiza ekonomiczna: Gospodarstwo o powierzchni 1000 m² uprawy netto
| Głos | Wartość | Notatki |
|---|---|---|
| CAPEX (inwestycja początkowa) | ||
| Struktura i system | 800 000 euro | Remont magazynu |
| Półki i kanały NFT | 300 000 euro | Systemy hydroponiczne |
| Oświetlenie LED | 600 000 euro | Wydajność 600 W/m² 2,8 µmol/J |
| HVAC i klimat | 250 000 euro | Chłodzenie + osuszanie |
| Układ CO2 | 50 000 euro | Magazynowanie + dystrybucja |
| Automatyka i robotyka | 400 000 euro | Siewnik, sadzarka, kombajn |
| Oprogramowanie i integracja | 150 000 euro | SCADA, API, cyfrowy bliźniak |
| Całkowite nakłady inwestycyjne | 2 550 000 euro | ~2550€/m² |
| ROCZNY OPEX | ||
| Elektryczność | 420 000 euro | 35% OPEX – główna energia krytyczna |
| Praca (10 operatorów) | 350 000 euro | 27% kosztów operacyjnych |
| Nasiona i podłoże | 80 000 euro | 6% OPEX |
| Składniki odżywcze i CO2 | 60 000 euro | 5% OPEX |
| Konserwacja | 90 000 euro | 7% OPEX |
| Pakowanie i logistyka | 120 000 euro | 9% OPEX |
| Inne (ubezpieczenie itp.) | 80 000 euro | 6% OPEX |
| Całkowity OPEX | 1 200 000 euro | 1200 €/m²/rok |
| PRZYCHODY | ||
| Produkcja sałaty (18 cykli x 25kg/m²) | 450 kg/m²/rok | Łącznie 450 000 kg |
| Cena sprzedaży premium GDO | 3,5 €/kg | w porównaniu do 0,8–1,2 euro na zewnątrz |
| Przychody brutto | 1 575 000 euro | |
| EBITDA | 375 000 euro | Marża 23,8%. |
| Amortyzacja CAPEX (10 lat) | 255 000 euro | |
| Zysk netto przed opodatkowaniem | 120 000 euro | Marża 7,6%. |
| Próg rentowności (lata) | ~8-10 lat | Z ziołami: 4-5 lat |
Problem energii w rolnictwie wertykalnym
Energetyczne i egzystencjalne wyzwanie rolnictwa wertykalnego. Najbardziej wydajne diody LED dostępne w 2025 roku osiągają ok 3,0-3,5 µmol/J wydajności fotonicznej. Aby wyprodukować A DLI wynoszący 17 mol/m²/dzień przy 16 godzinach fotoperiodu wymaga około 280 Wh/m²/dzień, tj. 102 kWh/m²/rok tylko do oświetlenia. O powierzchni 1000 m² i cenie energii na poziomie 0,15 euro/kWh (taryfa przemysłowa Włochy 2025), rachunek za diody LED wynosi już 153 000 euro rocznie. Dodaj HVAC (zwykle 60-70% energii LED), CO2, pompy, automatykę – masz to łatwo za 420 000 euro rocznie. Ci, którzy mają dostęp do taniej energii odnawialnej (np. rolnej Nowoczesne, w 100% odnawialne) lub posiadające zainstalowaną znaczną część osłon fotowoltaicznych potrzeb, ale nie wszystkich.
Rolnictwo pionowe nie jest to zrównoważone przy wszystkich cenach energii. Z energią powyżej 0,25 €/kWh (możliwy scenariusz w okresach kryzysowych, takich jak lata 2022-2023), wiele modeli ekonomicznych upadają. Zakład jest taki, że wydajność LED będzie stale się poprawiać, a energia odnawialna będzie nadal rosła kosztuje coraz mniej.
Studium przypadku: Farmy planetarne i nowoczesne rolnictwo – model włoski
We Włoszech realizowane są dwa z najbardziej zaawansowanych projektów rolnictwa pionowego w Europie z siedzibą w głębi Mediolanu. Ich ścieżki są różne, ale uzupełniają się i reprezentują dwa podejścia do problemu skalowalności i rentowności w europejskim rolnictwie wertykalnym.
Planet Farms – Cavenago w Brianza
Założona w 2018 roku przez Lucę Travagliniego i Massimiliano Loschiego firma Planet Farms zbudowała pierwsza fabryka w dawnym obszarze przemysłowym Cavenago (MB). Oryginalna roślina z m² i w 2024 roku stał się jednym z największych w Europie 20 000 m² powierzchnia wzrostu. W listopadzie 2023 r. firma Planet Farms zebrała rundę o wartości 40 milionów dolarów przy wycenie 500 milionów dolarów i jest jednym z największych w branży w Europie. Partnerstwo ze Swiss Life Asset Managers w 2025 roku stworzyło spółkę JV z 200 € miliony rozwój gospodarstw wertykalnych na dużą skalę w całej Europie.
Partnerstwo technologiczne z firmą Siemens leży u podstaw automatyzacji: systemy sterowania przemysłowy Siemens S7-1500 zarządza pętlami środowiskowymi, natomiast platforma Mindsphere (obecnie Siemens Industrial Copilot) zbiera i analizuje dane. Produkt sztandarowym produktem są „Żywe Zioła” – aromatyczne zioła sprzedawane na tackach jeszcze z dodatkiem korzeń, segment premium z wyższymi marżami niż sałata.
Nowoczesne rolnictwo - Agnadello (CR)
Agricola Moderna, założona w Mediolanie w 2018 roku przez Pierluigiego Giulianiego i Benjamina Franchettiego, nowa fabryka została otwarta we wrześniu 2024 roku 11 000 m² reklama Agnadello (Cremona), finansowane dzięki pożyczce w wysokości 10 milionów euro od Intesa Sanpaolo. Roślina produkuje 30 000 worków sałatki dziennie, zasilany W 100% ze źródeł odnawialnych, z wewnętrzną sztuczną inteligencją opracowaną przez zespół badawczo-rozwojowy optymalizować przepisy.
Zróżnicowanie technologiczne Agricola Moderna i system obrazowania hiperspektralnego (współpraca z firmą Specim) pozwalający na ocenę stanu odżywienia roślin zanim objawy staną się widoczne dla ludzkiego oka. Kamery RGB-D do rekonstrukcji 3D czaszy, czujniki środowiskowe z tysiącami punktów pomiarowych i algorytmy wewnętrznie opracowane komputerowe systemy wizyjne tworzą zastrzeżony stos technologii.
Porównanie farm planetarnych i nowoczesnej Agricoli
| Parametr | Farmy Planet | Nowoczesne rolnictwo |
|---|---|---|
| Powierzchnia | 20 000 m² | 11 000 m² |
| Produkcja/dzień | N.D. (koncentracja na ziołach) | 30 000 torebek sałatkowych |
| Finansowanie | Runda 40 milionów dolarów + JV o wartości 200 milionów euro | Intesa Sanpaolo za 10 mln euro |
| PLC/automatyka | Siemensa S7-1500 | Beckhoff + niestandardowe |
| Platforma AI | Drugi pilot przemysłowy Siemens | Rozwój wewnętrzny |
| Wizja komputerowa | Standardy RGB | Hiperspektralny (Specim) |
| Energia | Częściowo odnawialne | 100% odnawialne |
| Rynek docelowy | Detal premium GDO + ekspansja w Wielkiej Brytanii | Włoski handel detaliczny na dużą skalę (zakres IV) |
Wyzwania, ograniczenia i realia sektora
Uczciwy artykuł o rolnictwie wertykalnym musi przedstawiać prawdziwe wyzwania, a nie tylko korzyści. W latach 2022–2024 sektor przeżył poważną fazę rozczarowania, związaną z bankructwami sensacyjne, które spaliły miliardowe inwestycje. Zrozumienie dlaczego jest sprawą fundamentalną do budowania zrównoważonych systemów.
Prawdziwe wyzwania rolnictwa pionowego
- Koszt energii: I zabójca numer jeden. 60% operatorów farm pionowych Nie jest to jeszcze opłacalne, głównie ze względu na koszty energii elektrycznej. Bez energii niskokosztowe odnawialne źródła energii, model ekonomiczny dotyczy wyłącznie upraw premium.
- Ograniczone odmiany upraw: Rolnictwo pionowe doskonale nadaje się do warzywa liściaste (sałata, szpinak), zioła aromatyczne i mikroliście. Do pomidorów, papryki, ogórków wydajność energetyczna nie jest jeszcze konkurencyjna. Są to zboża, rośliny strączkowe i korzenie poza zasięgiem technicznym i ekonomicznym.
- Wysokie nakłady inwestycyjne: Profesjonalny montaż kosztuje 2000-4000 euro na m² uprawy. Próg rentowności dla sałaty wynosi 8–10 lat; zbyt wiele dla wielu inwestorów. Tylko zioła i mikroliście osiągają próg rentowności w ciągu 4-5 lat.
- Zależność od łańcucha dostaw LED: Wysokiej jakości chipy LED są produkowane przez kilku dostawców (Epistar, Lumileds, Osram). Zakłócenie w łańcuchu dostaw wpływa zarówno na początkowe nakłady inwestycyjne, jak i na utrzymanie.
- Rzeczywisty a postrzegany zrównoważony rozwój: Oszczędność wody (95%) jest realna. Jednak ślad węglowy w dużym stopniu zależy od koszyka energetycznego. Farma pionowa z napędem węgiel ma gorszy ślad węglowy niż rolnictwo na wolnym powietrzu. Tylko z OZE bilans staje się wyraźnie dodatni.
- Skalowalność techniczna: Przejście z farmy pilotażowej o powierzchni 500 m² do farmy pilotażowej powierzchnia handlowa 20 000 m² nie jest prostym mnożeniem. Systemy sterowania, wymagają zarządzania uprawami, logistyki wewnętrznej i systemów robotycznych głębokie przeprojektowanie przy każdym skoku skali.
Wdrożenie w produkcji: konteneryzacja i monitorowanie
Elementy oprogramowania farmy pionowej obejmują kontroler PID czasu rzeczywistego (który działa bezpośrednio na sterowniku PLC lub na Raspberry Pi, bez kontenera) do backendu FastAPI i Potoki AI (wygodne życie w kontenerach Docker). Typowa infrastruktura chmurowa łączy lokalną warstwę brzegową z płaszczyzną kontroli w chmurze.
# docker-compose.yml per stack vertical farm (dev/staging)
version: '3.9'
services:
# API principale
farm-api:
build: ./farm-api
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql://farm:farm@postgres:5432/farmdb
- MQTT_BROKER=emqx
- INFLUXDB_URL=http://influxdb:8086
- REDIS_URL=redis://redis:6379
depends_on:
- postgres
- influxdb
- emqx
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3
# MQTT Broker
emqx:
image: emqx/emqx:5.8
ports:
- "1883:1883" # MQTT
- "8883:8883" # MQTT TLS
- "18083:18083" # Dashboard
environment:
- EMQX_NODE__COOKIE=farm-secret-cookie
volumes:
- emqx_data:/opt/emqx/data
# Time-series database per dati sensori
influxdb:
image: influxdb:2.7
ports:
- "8086:8086"
environment:
- DOCKER_INFLUXDB_INIT_MODE=setup
- DOCKER_INFLUXDB_INIT_USERNAME=admin
- DOCKER_INFLUXDB_INIT_PASSWORD=farmpass123
- DOCKER_INFLUXDB_INIT_ORG=verticalfarm
- DOCKER_INFLUXDB_INIT_BUCKET=sensors
volumes:
- influxdb_data:/var/lib/influxdb2
# PostgreSQL per dati applicativi (ricette, batch, inventory)
postgres:
image: postgres:16-alpine
environment:
- POSTGRES_DB=farmdb
- POSTGRES_USER=farm
- POSTGRES_PASSWORD=farm
volumes:
- postgres_data:/var/lib/postgresql/data
# Redis per cache e job queue
redis:
image: redis:7-alpine
command: redis-server --appendonly yes
# Grafana per dashboard real-time
grafana:
image: grafana/grafana:11.0.0
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
volumes:
- grafana_data:/var/lib/grafana
depends_on:
- influxdb
# Worker ML per digital twin e ottimizzazione
ml-worker:
build: ./ml-worker
environment:
- INFLUXDB_URL=http://influxdb:8086
- REDIS_URL=redis://redis:6379
- MODEL_PATH=/models
volumes:
- ml_models:/models
volumes:
emqx_data:
influxdb_data:
postgres_data:
grafana_data:
ml_models:
Trendy i innowacje w rolnictwie wertykalnym 2025-2026
Sektor rozwija się szybko na trzech frontach: efektywności energetycznej, ekspansji uprawy i coraz głębsza integracja sztucznej inteligencji. Oto najważniejsze trendy.
Kluczowe innowacje w rolnictwie pionowym 2025
| Innowacja | Uderzenie | Status handlowy |
|---|---|---|
| Wydajność LED 4,0+ µmol/J | -20-25% kosztów energii oświetleniowej | Dostępne (LG, Signify) |
| Obrazowanie hiperspektralne | Wczesna diagnostyka stresu roślin, optymalizacja | Wczesne przyjęcie (Specim + Agricola M.) |
| RL dla receptur dynamicznych | +15-25% wydajności przy tej samej energii | Wyszukiwanie zaawansowane / wczesny prod |
| Robot wysiewający o dużej prędkości | Ponad 3000 siewów na godzinę w porównaniu do 700 siewów ręcznych | Komercyjne (dużo, 80 akrów) |
| Uprawa grzybów/grzybowców | Dywersyfikacja składu upraw, wysoka rentowność | Rozwój komercyjny |
| Farma pionowa jako ciepło odpadowe w centrum danych | Odzysk ciepła w celu redukcji HVAC | Pilot (Finlandia, Niemcy) |
| Integracja fotowoltaiki z BESS | -40-60% kosztów energii w optymalnych scenariuszach | Rozwój |
Szczególnie interesującym pojawiającym się trendem jest integracja rolnictwa wertykalnego z Centra danych: Serwery generują ciepło odpadowe, które można odzyskać i zmniejszyć koszt ogrzewania szklarni w zimnych miesiącach. W Finlandii, gdzie jest energia kosztowne, w kilku projektach pilotażowych testuje się tę symbiozę. Ciepło odpadowe w temperaturze 40-60°C i idealnie nadaje się do utrzymywania temperatury uprawy w szklarniach zimowych bez dodatkowego zużycia energii.
Wnioski: Rolnictwo pionowe jako projekt inżynierii oprogramowania
Nowoczesna farma pionowa i przede wszystkim system oprogramowania. Sprzęt (diody LED, czujniki, robot, regały) jest konieczne, ale niewystarczające: zmienia się poziom oprogramowania oświetlony magazyn w zoptymalizowanej fabryce żywności. Kontrolery PID zarządzają środowisko według naukowo sprawdzonych receptur; Interfejsy API REST łączą farmę do ekosystemu komercyjnego; ROS2 koordynuje roboty; cyfrowy bliźniak umożliwia symulacje przewidywalny; uczenie się przez wzmacnianie znajduje recepty, które są lepsze od tych zaprojektowanych ręcznie przez agronomów.
Rynek rolnictwa pionowego z 9,6 miliarda w 2025 r. do 39 miliardów w 2033 r. nie jest gwarantowane automatycznie: zależy od zdolności branży do rozwiązania problemu energii i poszerzyć zakres upraw, które można uprawiać ekonomicznie zrównoważony. Włochy, z Planet Farms i Agricola Moderna, są we właściwym miejscu być głównym bohaterem tego przejścia, zwłaszcza po wprowadzeniu PNRR i zachętach przejścia 5.0, które redukują koszty CAPEX technologiczny.
Dla programisty chcącego wejść do tego sektora najbardziej pożądanymi umiejętnościami są: Python dla automatyzacji i ML, FastAPI dla backendu przemysłowego, ROS2 dla robotyki, MQTT/Modbus/OPC-UA dla protokołów przemysłowych, InfluxDB dla szeregów czasowych i podstawy fizjologii roślin, aby zrozumieć parametry, które należy zoptymalizować. Nie musisz wiedzieć jak ogrodnictwo, ale zrozumienie, dlaczego rośliny rosną lepiej przy określonych długościach fal stanowi różnicę pomiędzy dobrym systemem kontroli a doskonałym systemem kontroli.
Narzędzia i zasoby, aby dowiedzieć się więcej
- Pymodbus - Biblioteka Pythona dla Modbus RTU/TCP, idealna do komunikacji ze sterownikami PLC i czujnikami
- Paho-MQTT - Klient MQTT Python do integracji z brokerami (EMQX, HiveMQ, Mosquitto)
- ROS2 Pokorny / Jazzowy - Framework robotyki, oficjalna dokumentacja na docs.ros.org
- FastAPI - Backend asynchroniczny w języku Python, idealny do przemysłowych interfejsów API kontroli
- InfluxDB 2.x - Baza danych szeregów czasowych z językiem zapytań Flux do analizy czujników
- Stabilne linie bazowe 3 - Wdrożenia RL (PPO, SAC, TD3) w celu optymalizacji receptur
- Sala gimnastyczna - Standard dla środowisk RL, używany w środowiskach cyfrowych bliźniaków
- Zapłon SCADA - Niedroga platforma przemysłowa SCADA z licencją OEM
- Pionowa farma codziennie - Główne źródło wiadomości na temat sektora pionowego
- Wytyczne USDA CEA - Parametry referencyjne dla rolnictwa w kontrolowanym środowisku
Następny z serii: Prognozowanie popytu w handlu detalicznym żywnością
W artykule 8 poruszamy odwrotny problem: jak przewidzieć popyt na produkty żywność w celu optymalizacji zleceń produkcyjnych (również z gospodarstw wertykalnych) i redukcji odpady. Będziemy używać Prophet by Meta do sezonowości i trendów, LightGBM do wzorców zaawansowane tabele, a my zbudujemy kompletny potok prognozowania z funkcjami specyficzna inżynieria dla włoskiego handlu detalicznego żywnością (efekty pogodowe, święta, promocje).
Kontynuuj za pomocą: Prognozowanie popytu na sprzedaż detaliczną żywności za pomocą Prophet i LightGBM
Powiązane artykuły z innych serii
- Seria rurociągów IoT: Rurociąg IoT dla rolnictwa precyzyjnego - MQTT, InfluxDB, Grafana do monitorowania czujników zewnętrznych
- Seria MLOps: MLOps dla modeli AI w środowisku produkcyjnym — jak wdrożyć model optymalizacji receptury RL jest w fazie produkcji
- Seria widzenia komputerowego: CV dla Kontroli Jakości - Techniki sztuczne widzenie w celu oceny dojrzałości i jakości plonów
- Seria biznesowa dotycząca danych i sztucznej inteligencji: Sztuczna inteligencja w produkcji – predykcyjna Konserwacja i Digital Twin, bliźniacze architektury mające zastosowanie w rolnictwie pionowym







