Sztuczna inteligencja w logistyce: optymalizacja tras, automatyzacja magazynów i inteligencja łańcucha dostaw
Logistyka i łańcuch dostaw zawsze były zdominowane przez złożoność: miliony zmienne współzależne, wąskie okna czasowe, koszty operacyjne obniżające marże, oraz nieprzewidywalność popytu, która zagraża ciągłości usług. Przez dziesięciolecia firmy próbowały ujarzmić tę złożoność za pomocą arkuszy Excela, praktycznych zasad i doświadczenia starszych planistów. Dziś sztuczna inteligencja na nowo projektuje zasady gry.
Globalny rynekSztuczna inteligencja zastosowana w łańcuchu dostaw osiągnął 9,8 miliarda dolarów w 2025 r., przy czym prognozy wskazują na wzrost do 32 miliardów do 2030 r (CAGR 26,4%). To nie jest żadna bzdura: firmy, które zaadoptowały sztuczną inteligencję w procesach logistycznych rekordowe obniżki kosztów transportu o 10-15%, przyspieszenie terminów dostaw o 15-20%. dostaw i około 30% mniej opóźnień w dostawach. Amazon obsługuje ponad 520 000 robotów W swoich magazynach wykorzystuje sztuczną inteligencję, redukując koszty realizacji o 20% i przetwarzając 40%. na razie więcej zamówień.
W tym artykule badamy technologie AI, które zmieniają logistykę: od Problem z wyznaczaniem trasy pojazdu (VRP) rozwiązane za pomocą OR-Tools i uczenia się przez wzmacnianie, prognozowanie zapotrzebowania za pomocą Temporal Fusion Transformer, aż po automatyzację magazynu, aż po optymalizację ostatniej mili i inteligentne zarządzanie zapasami. Zobaczymy konkretne implementacje z działającym kodem Pythona i rzeczywistymi przypadkami użycia z kontekstu włoskiego.
Czego dowiesz się w tym artykule
- Jak rozwiązać problem wyznaczania trasy pojazdu (VRP) za pomocą narzędzi Google OR w Pythonie
- Prognozowanie popytu za pomocą Prophet, LightGBM i Temporal Fusion Transformer
- Optymalizacja zapasów z uczeniem się przez wzmocnienie (PPO/DQN)
- Automatyzacja magazynu: robotyka, optymalizacja kompletacji i inteligentne systemy WMS
- Dostawa ostatniej mili: sztuczna inteligencja, drony i pojazdy autonomiczne w kontekście miejskim
- Widoczność w czasie rzeczywistym i cyfrowy bliźniak łańcucha dostaw
- Optymalizacja śladu węglowego i zrównoważona logistyka
- Włoskie przypadki użycia: Amazon IT, Poste Italiane, GLS
Stanowisko w serii hurtowni danych, sztucznej inteligencji i transformacji cyfrowej
| # | Przedmiot | Państwo |
|---|---|---|
| 1 | Ewolucja hurtowni danych | Opublikowany |
| 2 | Siatka danych i zdecentralizowana architektura | Opublikowany |
| 3 | ETL kontra nowoczesny ELT: dbt, Airbyte i Fivetran | Opublikowany |
| 4 | Orkiestracja rurociągu: przepływ powietrza, dagster i prefekt | Opublikowany |
| 5 | Sztuczna inteligencja w produkcji: konserwacja predykcyjna | Opublikowany |
| 6 | Sztuczna inteligencja w finansach: wykrywanie oszustw i punktacja kredytowa | Opublikowany |
| 7 | Sztuczna inteligencja w handlu detalicznym: prognozowanie popytu i rekomendacje | Opublikowany |
| 8 | Sztuczna inteligencja w opiece zdrowotnej: diagnostyka i odkrywanie leków | Opublikowany |
| 9 | AI w logistyce (tutaj jesteś) | Aktualny |
| 10 | LLM w biznesie: RAG Enterprise i poręcze | Następny |
Problem z wyznaczaniem trasy pojazdu: optymalizacja trasy za pomocą narzędzi OR
Il Problem z wyznaczaniem trasy pojazdu (VRP) i jeden z najczęściej badanych problemów badawczych operacyjne: biorąc pod uwagę grupę klientów z konkretnymi żądaniami dostawy i flotę pojazdów zaczynając od jednej lub kilku zajezdni, jak przypisywać klientów do pojazdów i planować trasy zminimalizować całkowity koszt (odległość, czas, paliwo)?
VRP jest NP-trudne: nie ma algorytmu, który rozwiązałby go dokładnie w czasie wielomianowym duże przypadki. Z tego powodu w praktycznych rozwiązaniach stosuje się kombinację z metaheurystyka (Symulowane wyżarzanie, wyszukiwanie Tabu, algorytmy genetyczne) e rozwiązania komercyjne i open source. Google OR-Tools, a dziś narzędzie najczęściej używany open source do tego typu problemów: obsługuje CVRP (pojemnościowe VRP), VRPTW (z oknami czasowymi), Multi-Depot VRP i wiele realistycznych wariantów.
System ORION firmy UPS, bazując na podobnych technikach, oblicza 30 000 optymalizacji tras na minutę i zaoszczędziło 38 milionów litrów paliwa rocznie, unikając 100 milionów mil niepotrzebnej jazdy. To nie jest marginalna przewaga: to przewaga konkurencyjna strukturalne, co przekłada się na dziesiątki milionów dolarów rocznych oszczędności.
Wdrożenie CVRP za pomocą narzędzi Google OR
Zobaczmy pełną implementację pojemnościowego VRP z oknami czasowymi (VRPTW), najpopularniejszy typ w rzeczywistych kontekstach logistycznych, w których każdy klient ma określone godziny otwarcia.
"""
VRPTW - Vehicle Routing Problem with Time Windows
Risolto con Google OR-Tools
Scenario: consegne B2B in area metropolitana italiana
"""
from ortools.constraint_solver import routing_enums_pb2
from ortools.constraint_solver import pywrapcp
import numpy as np
from typing import List, Dict, Tuple
import json
# ============================================================
# DEFINIZIONE DEI DATI DEL PROBLEMA
# ============================================================
def create_data_model() -> Dict:
"""
Crea il modello dati per il VRPTW.
In produzione questi dati vengono da:
- Database ordini (PostgreSQL/DWH)
- API di geocoding per le coordinate
- API Google Maps Distance Matrix per le distanze
"""
data = {}
# Matrice delle distanze in secondi (tempo di viaggio)
# Indice 0 = deposito, indici 1-N = clienti
data['time_matrix'] = [
[0, 548, 776, 696, 582, 274, 502, 194, 308, 194, 536, 502, 388, 354],
[548, 0, 684, 308, 194, 502, 730, 354, 696, 742, 1084, 594, 480, 514],
[776, 684, 0, 992, 878, 502, 274, 810, 468, 742, 400, 1278, 1164, 1130],
[696, 308, 992, 0, 114, 650, 878, 502, 844, 890, 1232, 514, 628, 822],
[582, 194, 878, 114, 0, 536, 764, 388, 730, 776, 1118, 400, 514, 708],
[274, 502, 502, 650, 536, 0, 228, 308, 194, 240, 582, 776, 662, 628],
[502, 730, 274, 878, 764, 228, 0, 536, 194, 468, 354, 1004, 890, 856],
[194, 354, 810, 502, 388, 308, 536, 0, 342, 388, 730, 468, 354, 320],
[308, 696, 468, 844, 730, 194, 194, 342, 0, 274, 388, 810, 696, 662],
[194, 742, 742, 890, 776, 240, 468, 388, 274, 0, 342, 536, 422, 388],
[536, 1084, 400, 1232, 1118, 582, 354, 730, 388, 342, 0, 878, 764, 730],
[502, 594, 1278, 514, 400, 776, 1004, 468, 810, 536, 878, 0, 114, 308],
[388, 480, 1164, 628, 514, 662, 890, 354, 696, 422, 764, 114, 0, 194],
[354, 514, 1130, 822, 708, 628, 856, 320, 662, 388, 730, 308, 194, 0],
]
# Finestre temporali [inizio, fine] in secondi dall'apertura deposito
# 0 = 08:00, 3600 = 09:00, 28800 = 16:00
data['time_windows'] = [
(0, 28800), # Deposito: aperto tutto il giorno
(7200, 14400), # Cliente 1: 10:00-12:00
(10800, 18000), # Cliente 2: 11:00-13:00
(3600, 14400), # Cliente 3: 09:00-12:00
(0, 10800), # Cliente 4: 08:00-11:00
(14400, 21600), # Cliente 5: 12:00-14:00
(0, 14400), # Cliente 6: 08:00-12:00
(7200, 18000), # Cliente 7: 10:00-13:00
(0, 21600), # Cliente 8: 08:00-14:00
(3600, 10800), # Cliente 9: 09:00-11:00
(18000, 25200), # Cliente 10: 13:00-15:00
(0, 14400), # Cliente 11: 08:00-12:00
(3600, 18000), # Cliente 12: 09:00-13:00
(7200, 21600), # Cliente 13: 10:00-14:00
]
# capacità di ciascun veicolo (in kg)
data['vehicle_capacities'] = [1000, 1000, 800, 800]
data['num_vehicles'] = 4
# Indice del deposito
data['depot'] = 0
# Domanda di ogni cliente (in kg)
data['demands'] = [0, 120, 80, 200, 150, 90, 110, 60, 180, 70, 200, 130, 95, 85]
return data
# ============================================================
# CALLBACK FUNCTIONS PER OR-TOOLS
# ============================================================
def create_time_callback(data: Dict, manager):
"""Ritorna una callback per il tempo di percorrenza."""
time_matrix = data['time_matrix']
def time_callback(from_index, to_index):
from_node = manager.IndexToNode(from_index)
to_node = manager.IndexToNode(to_index)
return time_matrix[from_node][to_node]
return time_callback
def create_demand_callback(data: Dict, manager):
"""Ritorna una callback per le domande dei clienti."""
demands = data['demands']
def demand_callback(from_index):
from_node = manager.IndexToNode(from_index)
return demands[from_node]
return demand_callback
# ============================================================
# RISOLUZIONE DEL PROBLEMA
# ============================================================
def solve_vrptw(data: Dict) -> Dict:
"""
Risolve il VRPTW con OR-Tools.
Returns:
Dict con i percorsi ottimizzati e le metriche
"""
# Crea il gestore dell'indice dei nodi
manager = pywrapcp.RoutingIndexManager(
len(data['time_matrix']),
data['num_vehicles'],
data['depot']
)
# Crea il modello di routing
routing = pywrapcp.RoutingModel(manager)
# Registra le callback
time_callback = create_time_callback(data, manager)
transit_callback_index = routing.RegisterTransitCallback(time_callback)
demand_callback = create_demand_callback(data, manager)
demand_callback_index = routing.RegisterUnaryTransitCallback(demand_callback)
# Imposta il costo dell'arco (tempo di percorrenza)
routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index)
# Aggiunge il vincolo di capacità
routing.AddDimensionWithVehicleCapacity(
demand_callback_index,
0, # Slack iniziale
data['vehicle_capacities'], # capacità massima per veicolo
True, # Start cumul to zero
'Capacity'
)
# Aggiunge la dimensione temporale con finestre
routing.AddDimension(
transit_callback_index,
30, # Slack max (attesa max in secondi)
28800, # Orizzonte temporale massimo (8 ore)
False, # Non forzare start a zero
'Time'
)
time_dimension = routing.GetDimensionOrDie('Time')
# Imposta le finestre temporali
for location_idx, time_window in enumerate(data['time_windows']):
if location_idx == data['depot']:
continue
index = manager.NodeToIndex(location_idx)
time_dimension.CumulVar(index).SetRange(time_window[0], time_window[1])
# Imposta le finestre temporali del deposito per ogni veicolo
depot_idx = data['depot']
for vehicle_id in range(data['num_vehicles']):
index = routing.Start(vehicle_id)
time_dimension.CumulVar(index).SetRange(
data['time_windows'][depot_idx][0],
data['time_windows'][depot_idx][1]
)
# Minimizza il tempo totale di percorrenza
for i in range(data['num_vehicles']):
routing.AddVariableMinimizedByFinalizer(
time_dimension.CumulVar(routing.Start(i))
)
routing.AddVariableMinimizedByFinalizer(
time_dimension.CumulVar(routing.End(i))
)
# Parametri di ricerca: PATH_CHEAPEST_ARC come prima soluzione,
# poi miglioramento con GUIDED_LOCAL_SEARCH
search_parameters = pywrapcp.DefaultRoutingSearchParameters()
search_parameters.first_solution_strategy = (
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC
)
search_parameters.local_search_metaheuristic = (
routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH
)
search_parameters.time_limit.FromSeconds(30) # 30 secondi di ottimizzazione
# Risoluzione
solution = routing.SolveWithParameters(search_parameters)
if not solution:
return {"status": "INFEASIBLE", "routes": []}
# Estrai i risultati
return extract_solution(data, manager, routing, solution)
def extract_solution(data, manager, routing, solution) -> Dict:
"""Estrae la soluzione in formato leggibile."""
time_dimension = routing.GetDimensionOrDie('Time')
results = {
"status": "OPTIMAL",
"total_time": 0,
"routes": []
}
for vehicle_id in range(data['num_vehicles']):
index = routing.Start(vehicle_id)
route = {
"vehicle_id": vehicle_id,
"stops": [],
"total_time": 0,
"total_load": 0
}
while not routing.IsEnd(index):
node_index = manager.IndexToNode(index)
time_var = time_dimension.CumulVar(index)
route["stops"].append({
"node": node_index,
"arrival": solution.Min(time_var),
"departure": solution.Max(time_var)
})
route["total_load"] += data['demands'][node_index]
index = solution.Value(routing.NextVar(index))
# Nodo finale (deposito)
time_var = time_dimension.CumulVar(index)
route["total_time"] = solution.Min(time_var)
results["routes"].append(route)
results["total_time"] += route["total_time"]
return results
# ============================================================
# MAIN
# ============================================================
if __name__ == "__main__":
data = create_data_model()
result = solve_vrptw(data)
print(f"Status: {result['status']}")
print(f"Tempo totale di percorrenza: {result['total_time']} secondi")
for route in result["routes"]:
print(f"\nVeicolo {route['vehicle_id']}:")
print(f" Carico totale: {route['total_load']} kg")
stops_str = " -> ".join(
[f"Cliente{s['node']}({s['arrival']//3600}:{(s['arrival']%3600)//60:02d})"
for s in route["stops"] if s["node"] != 0]
)
print(f" Percorso: Deposito -> {stops_str} -> Deposito")
W kontekście produkcyjnym macierz czasu podróży jest obliczana w czasie rzeczywistym za pośrednictwem Google Maps Distance Matrix lub Here Routing API, biorąc pod uwagę aktualny ruch. Dane klientów pochodzą z systemu ERP firmy i są aktualizowane co godzinę. Narzędzia OR zwraca rozwiązanie w ciągu kilku sekund dla instancji liczących do 200-300 klientów; dla przykładów stosowane są podejścia z większymi klastrami lub solwery akcelerowane przez GPU, takie jak NVIDIA cuOpt.
Prognozowanie popytu: Prognozowanie popytu za pomocą ML
Dokładne prognozowanie popytu jest podstawą całego łańcucha dostaw. Bez wiedzy ile produktów będzie zamawianych w ciągu najbliższych kilku tygodni, nie da się tego zoptymalizować zakupów, wielkości magazynu, planowania transportu i gwarantowania poziomów usługi. Przez dziesięciolecia firmy korzystały z klasycznych modeli statystycznych, takich jak ARIMA, SARIMA i wygładzanie wykładnicze. Obecnie modele uczenia maszynowego stale osiągają lepsze wyniki te wartości bazowe.
Najciekawsze porównanie przeprowadzone w 2025 r. dotyczy trzech różnych podejść:
Porównanie modeli prognozowania popytu
| Model | Typ | Mocne strony | Ograniczenia | MAPA Typowe |
|---|---|---|---|---|
| Prorok (Meta) | Dodatek Bayesa | Zarządza wieloma porami roku, świętami, trendami | Nie można go łatwo skalować do tysięcy jednostek SKU | 8-12% |
| LekkiGBM | Wzmocnienie gradientowe | Szybka i elastyczna inżynieria funkcji, produkcja | Wymaga ręcznej inżynierii funkcji | 5-9% |
| Transformator fuzyjny czasowy | Głębokie uczenie się | Zmienne wielohoryzontalne, interpretowalne, egzogeniczne | Wolniejsze trenowanie, wymagany procesor graficzny | 4-7% |
| SARIMA (wartość bazowa) | Statystyczny | Proste, zrozumiałe | Nie wychwytuje nieliniowości | 12-20% |
Prognozowanie popytu za pomocą LightGBM dla łańcucha dostaw
LightGBM jest często najlepszym wyborem do wdrożenia produkcyjnego: szybkie szkolenie, wnioskowanie milisekundowe, natywna obsługa brakujących wartości i świetna skalowalność tysiące SKU. Oto kompletna implementacja z inżynierią funkcji specyficzną dla logistyki.
"""
Demand Forecasting per Supply Chain con LightGBM
Feature engineering avanzato per serie temporali logistiche
"""
import pandas as pd
import numpy as np
import lightgbm as lgb
from sklearn.model_selection import TimeSeriesSplit
from sklearn.metrics import mean_absolute_percentage_error
from typing import List, Tuple
import warnings
warnings.filterwarnings('ignore')
# ============================================================
# FEATURE ENGINEERING PER SERIE TEMPORALI LOGISTICHE
# ============================================================
def create_lag_features(df: pd.DataFrame, target_col: str,
lags: List[int]) -> pd.DataFrame:
"""Crea feature di lag per catturare la dipendenza temporale."""
df = df.copy()
for lag in lags:
df[f'lag_{lag}'] = df.groupby('sku_id')[target_col].shift(lag)
return df
def create_rolling_features(df: pd.DataFrame, target_col: str,
windows: List[int]) -> pd.DataFrame:
"""Media e deviazione standard mobile per catturare trend e variabilità."""
df = df.copy()
for window in windows:
df[f'rolling_mean_{window}'] = (
df.groupby('sku_id')[target_col]
.transform(lambda x: x.shift(1).rolling(window).mean())
)
df[f'rolling_std_{window}'] = (
df.groupby('sku_id')[target_col]
.transform(lambda x: x.shift(1).rolling(window).std())
)
return df
def create_calendar_features(df: pd.DataFrame, date_col: str) -> pd.DataFrame:
"""Feature calendario: stagionalita, festivi italiani, weekend."""
df = df.copy()
df['date'] = pd.to_datetime(df[date_col])
# Feature temporali base
df['day_of_week'] = df['date'].dt.dayofweek
df['day_of_month'] = df['date'].dt.day
df['week_of_year'] = df['date'].dt.isocalendar().week.astype(int)
df['month'] = df['date'].dt.month
df['quarter'] = df['date'].dt.quarter
df['is_weekend'] = (df['day_of_week'] >= 5).astype(int)
# Festivi italiani (principali)
italian_holidays = [
'01-01', # Capodanno
'04-25', # Festa della Liberazione
'05-01', # Festa del Lavoro
'06-02', # Festa della Repubblica
'08-15', # Ferragosto
'11-01', # Ognissanti
'12-08', # Immacolata
'12-25', # Natale
'12-26', # Santo Stefano
]
df['is_holiday'] = df['date'].apply(
lambda d: 1 if f'{d.month:02d}-{d.day:02d}' in italian_holidays else 0
)
# Proximity ai festivi (effetti anticipazione/posticipazione)
df['days_to_holiday'] = df.apply(
lambda row: min(
abs((row['date'] - pd.Timestamp(f"{row['date'].year}-{h}")).days)
for h in italian_holidays
), axis=1
).clip(upper=7)
# Effetto Ferragosto (agosto): domanda compressa
df['is_august'] = (df['month'] == 8).astype(int)
# Peak season (Q4: ottobre-dicembre, Black Friday, Natale)
df['is_peak_season'] = df['month'].isin([10, 11, 12]).astype(int)
return df
def engineer_features(df: pd.DataFrame) -> pd.DataFrame:
"""
Pipeline completa di feature engineering.
Input DataFrame deve avere: sku_id, date, quantity, price,
promotions, stock_level, supplier_lead_time
"""
# Ordina per SKU e data
df = df.sort_values(['sku_id', 'date']).reset_index(drop=True)
# Lag features: 1, 7, 14, 28, 56 giorni
df = create_lag_features(df, 'quantity', lags=[1, 7, 14, 28, 56])
# Rolling features: 7, 14, 28 giorni
df = create_rolling_features(df, 'quantity', windows=[7, 14, 28])
# Feature calendario
df = create_calendar_features(df, 'date')
# Rapporto tra prezzo corrente e media storica (effetto promo)
df['price_ratio'] = df.groupby('sku_id')['price'].transform(
lambda x: x / x.expanding().mean()
)
# Indicatore di stockout recente (qualità del dato)
df['recent_stockout'] = (
df.groupby('sku_id')['stock_level']
.transform(lambda x: x.shift(1).rolling(7).min()) == 0
).astype(int)
return df
# ============================================================
# TRAINING E VALIDAZIONE
# ============================================================
def train_lgbm_forecaster(df: pd.DataFrame) -> Tuple[lgb.Booster, List[str]]:
"""
Addestra LightGBM con validazione time-series (walk-forward).
Returns:
Modello addestrato e lista delle feature usate
"""
FEATURE_COLS = [
# Lag features
'lag_1', 'lag_7', 'lag_14', 'lag_28', 'lag_56',
# Rolling features
'rolling_mean_7', 'rolling_mean_14', 'rolling_mean_28',
'rolling_std_7', 'rolling_std_14', 'rolling_std_28',
# Calendar
'day_of_week', 'day_of_month', 'week_of_year', 'month', 'quarter',
'is_weekend', 'is_holiday', 'days_to_holiday',
'is_august', 'is_peak_season',
# Business features
'price_ratio', 'promotions', 'supplier_lead_time', 'recent_stockout'
]
# Rimuovi righe con NaN dalle feature lag
df_train = df.dropna(subset=FEATURE_COLS).copy()
X = df_train[FEATURE_COLS]
y = df_train['quantity']
# Validazione time-series: non shuffle!
tscv = TimeSeriesSplit(n_splits=5)
lgb_params = {
'objective': 'regression_l1', # MAE loss, robusta agli outlier
'metric': 'mape',
'num_leaves': 127,
'learning_rate': 0.05,
'feature_fraction': 0.8,
'bagging_fraction': 0.8,
'bagging_freq': 5,
'min_data_in_leaf': 50,
'lambda_l1': 0.1,
'lambda_l2': 0.1,
'verbose': -1,
'n_jobs': -1
}
mape_scores = []
for fold, (train_idx, val_idx) in enumerate(tscv.split(X)):
X_train, X_val = X.iloc[train_idx], X.iloc[val_idx]
y_train, y_val = y.iloc[train_idx], y.iloc[val_idx]
dtrain = lgb.Dataset(X_train, label=y_train)
dval = lgb.Dataset(X_val, label=y_val, reference=dtrain)
model = lgb.train(
lgb_params,
dtrain,
num_boost_round=1000,
valid_sets=[dval],
callbacks=[lgb.early_stopping(50), lgb.log_evaluation(100)]
)
y_pred = model.predict(X_val)
mape = mean_absolute_percentage_error(y_val, np.maximum(y_pred, 0))
mape_scores.append(mape)
print(f"Fold {fold+1} MAPE: {mape:.2%}")
print(f"\nMAPE medio: {np.mean(mape_scores):.2%} (+/-{np.std(mape_scores):.2%})")
# Addestramento finale su tutti i dati
dtrain_full = lgb.Dataset(X, label=y)
final_model = lgb.train(lgb_params, dtrain_full, num_boost_round=model.best_iteration)
return final_model, FEATURE_COLS
# ============================================================
# PREVISIONE MULTI-STEP (28 GIORNI)
# ============================================================
def forecast_next_28_days(model: lgb.Booster, history: pd.DataFrame,
sku_id: str, feature_cols: List[str]) -> pd.DataFrame:
"""
Genera previsioni per i prossimi 28 giorni per uno SKU specifico.
Usa previsioni iterative (ogni giorno usa i valori previsti precedenti).
"""
sku_history = history[history['sku_id'] == sku_id].copy()
last_date = sku_history['date'].max()
forecasts = []
for day in range(1, 29):
next_date = last_date + pd.Timedelta(days=day)
# Costruisce il vettore di feature per questa data
row = pd.DataFrame([{
'sku_id': sku_id,
'date': next_date,
# Usa le ultime previsioni come lag (previsione iterativa)
'lag_1': sku_history['quantity'].iloc[-1],
'lag_7': sku_history['quantity'].iloc[-7] if len(sku_history) >= 7 else 0,
'lag_14': sku_history['quantity'].iloc[-14] if len(sku_history) >= 14 else 0,
'lag_28': sku_history['quantity'].iloc[-28] if len(sku_history) >= 28 else 0,
'lag_56': sku_history['quantity'].iloc[-56] if len(sku_history) >= 56 else 0,
'rolling_mean_7': sku_history['quantity'].iloc[-7:].mean(),
'rolling_mean_14': sku_history['quantity'].iloc[-14:].mean(),
'rolling_mean_28': sku_history['quantity'].iloc[-28:].mean(),
'rolling_std_7': sku_history['quantity'].iloc[-7:].std(),
'rolling_std_14': sku_history['quantity'].iloc[-14:].std(),
'rolling_std_28': sku_history['quantity'].iloc[-28:].std(),
# Valori business (assume stabili nel breve periodo)
'price_ratio': 1.0,
'promotions': 0,
'supplier_lead_time': sku_history['supplier_lead_time'].iloc[-1],
'recent_stockout': 0
}])
# Aggiunge feature calendario
row = create_calendar_features(row, 'date')
# Previsione
X_pred = row[feature_cols]
quantity_pred = max(0, model.predict(X_pred)[0])
forecasts.append({
'date': next_date,
'sku_id': sku_id,
'forecast': round(quantity_pred, 1),
'lower_bound': round(quantity_pred * 0.85, 1), # 15% di incertezza
'upper_bound': round(quantity_pred * 1.15, 1)
})
# Aggiunge la previsione alla history per il passo successivo
new_row = sku_history.iloc[-1:].copy()
new_row['date'] = next_date
new_row['quantity'] = quantity_pred
sku_history = pd.concat([sku_history, new_row], ignore_index=True)
return pd.DataFrame(forecasts)
Optymalizacja zapasów poprzez uczenie się przez wzmacnianie
Zarządzanie zapasami to problem podejmowania decyzji sekwencyjnych: każdego dnia musisz podjąć decyzję ile jednostek zamówić dla każdego SKU, równoważąc koszt utrzymania zapasów (kapitał trwały, przestrzeń fizyczna, ryzyko starzenia się) z kosztem wyczerpania zapasów (utracona sprzedaż, kary umowne, utrata reputacji). Klasyczne modele, np Model EOQ (ekonomiczna ilość zamówienia). e il stały punkt ponownego zamówienia nie uwzględniają odpowiednio popytu niestacjonarnego, zależności SKU i zakłóceń łańcucha dostaw.
Il Uczenie się przez wzmacnianie (RL) oferuje potężniejsze podejście: agenta naucz się optymalnej polityki zmiany kolejności, wchodząc w interakcję z symulacją środowiska. Z ostatnich badań (2025 r.) wynika, że podejście oparte na Polityka bliższa Optymalizacja (PPO) zmniejsza koszty ponownego zamawiania o 12,31% i zmniejsza braki w magazynie na poziomie 2,21%, znacznie przewyższając tradycyjne metody.
"""
Inventory Optimization con Reinforcement Learning (PPO)
Usando Gymnasium (ex OpenAI Gym) e Stable-Baselines3
"""
import gymnasium as gym
import numpy as np
from stable_baselines3 import PPO
from stable_baselines3.common.env_checker import check_env
from stable_baselines3.common.callbacks import EvalCallback
import pandas as pd
from typing import Optional
class InventoryEnv(gym.Env):
"""
Ambiente custom per ottimizzazione inventario.
Stato: [stock_corrente, domanda_media_7g, lead_time_atteso,
giorni_alla_scadenza (se deperibile), prezzo_corrente]
Azione: quantità da ordinare (discreta, 0-10 volte il MOQ)
Reward: -costo_holding - costo_stockout - costo_ordine
"""
metadata = {"render_modes": ["human"]}
def __init__(self, demand_data: np.ndarray, config: dict):
super().__init__()
self.demand_data = demand_data
self.n_steps = len(demand_data)
# Parametri del problema
self.holding_cost = config.get('holding_cost', 0.5) # Euro/unita/giorno
self.stockout_cost = config.get('stockout_cost', 5.0) # Euro/unita/mancante
self.order_cost = config.get('order_cost', 50.0) # Euro per ordine
self.lead_time = config.get('lead_time', 3) # Giorni di lead time
self.max_stock = config.get('max_stock', 1000) # capacità max
self.moq = config.get('moq', 10) # Minimum Order Quantity
# Spazio delle azioni: 0 (non ordinare) fino a 10 MOQ
self.action_space = gym.spaces.Discrete(11)
# Spazio degli stati: 5 variabili normalizzate
self.observation_space = gym.spaces.Box(
low=np.float32([0, 0, 0, 0, 0]),
high=np.float32([1, 1, 1, 1, 1]),
dtype=np.float32
)
self.reset()
def reset(self, seed: Optional[int] = None, options=None):
super().reset(seed=seed)
self.current_step = 0
self.stock = self.max_stock // 2 # Inizia a meta capacità
self.pending_orders = [] # (quantità, giorno_arrivo)
self.total_cost = 0.0
self.stockouts = 0
return self._get_observation(), {}
def _get_observation(self) -> np.ndarray:
"""Osservazione normalizzata dell'ambiente."""
demand_window = self.demand_data[
self.current_step:self.current_step + 7
]
avg_demand_7d = np.mean(demand_window) if len(demand_window) > 0 else 0
return np.float32([
self.stock / self.max_stock, # Stock attuale
avg_demand_7d / 100, # Domanda media 7 giorni
self.lead_time / 14, # Lead time normalizzato
len(self.pending_orders) / 5, # Ordini in transito
min(1.0, self.total_cost / 10000) # Costo accumulato (reward signal)
])
def step(self, action: int):
"""Esegue un passo: riceve ordini, soddisfa domanda, emette nuovi ordini."""
# 1. Ricevi ordini in arrivo
arrived = [qty for qty, arrive_day in self.pending_orders
if arrive_day <= self.current_step]
self.pending_orders = [(qty, day) for qty, day in self.pending_orders
if day > self.current_step]
for qty in arrived:
self.stock = min(self.max_stock, self.stock + qty)
# 2. Emetti nuovo ordine (azione)
order_qty = action * self.moq
order_cost = 0
if order_qty > 0:
order_cost = self.order_cost
arrive_day = self.current_step + self.lead_time
self.pending_orders.append((order_qty, arrive_day))
# 3. Soddisfa la domanda
demand = self.demand_data[min(self.current_step, self.n_steps - 1)]
if demand <= self.stock:
# Domanda soddisfatta
self.stock -= demand
stockout_cost = 0
else:
# Stockout parziale
unsatisfied = demand - self.stock
self.stock = 0
stockout_cost = unsatisfied * self.stockout_cost
self.stockouts += 1
# 4. Calcola costi
holding_cost = self.stock * self.holding_cost
step_cost = holding_cost + stockout_cost + order_cost
self.total_cost += step_cost
# Reward negativo (minimizziamo i costi)
reward = -step_cost / 100 # Scala il reward
# 5. Avanza
self.current_step += 1
terminated = self.current_step >= self.n_steps
return self._get_observation(), reward, terminated, False, {
"step_cost": step_cost,
"holding_cost": holding_cost,
"stockout_cost": stockout_cost,
"stock": self.stock,
"stockouts": self.stockouts
}
def train_inventory_agent(demand_data: np.ndarray, config: dict) -> PPO:
"""
Addestra un agente PPO per l'ottimizzazione dell'inventario.
PPO (Proximal Policy Optimization) e la scelta standard per:
- Ambienti con azioni discrete o continue
- Necessità di stabilità nell'addestramento
- Deployment in produzione
"""
# Crea l'ambiente
env = InventoryEnv(demand_data, config)
check_env(env, warn=True) # Valida la compatibilità Gymnasium
# Callback di valutazione: salva il modello migliore
eval_env = InventoryEnv(demand_data, config)
eval_callback = EvalCallback(
eval_env,
best_model_save_path="./inventory_agent/",
log_path="./inventory_logs/",
eval_freq=5000,
deterministic=True,
render=False
)
# Configura e addestra l'agente PPO
model = PPO(
"MlpPolicy",
env,
verbose=1,
learning_rate=3e-4,
n_steps=2048,
batch_size=64,
n_epochs=10,
gamma=0.99, # Fattore di sconto: 0.99 per problemi a lungo termine
gae_lambda=0.95,
clip_range=0.2, # Clip del ratio PPO
ent_coef=0.01, # Coefficiente di entropia per esplorazione
tensorboard_log="./inventory_tensorboard/"
)
model.learn(
total_timesteps=500_000,
callback=eval_callback,
progress_bar=True
)
return model
Automatyzacja magazynów: sztuczna inteligencja w nowoczesnych magazynach
Automatyzacja magazynu to nie tylko fizyczne roboty. Sztuczna inteligencja się zmienia każdy aspekt operacji magazynowych, od umieszczania towarów na półkach (optymalizacja slotów) po planowanie tras kompletacji, począwszy od kontroli jakości zautomatyzowane do dynamicznego zarządzania personelem.
Kluczowe Technologie Automatyki
Stos technologii inteligentnego magazynu (2025)
| Poziom | Technologia | Funkcjonować | Typowy zwrot z inwestycji |
|---|---|---|---|
| Fizyka | AMR (autonomiczne roboty mobilne) | Transport pojemników/półek do operatorów | Wydajność kompletacji 30-40%. |
| Fizyka | Ramiona robotyczne z wizją komputerową | Pick-and-place, depaletyzacja | Praca 24 godziny na dobę, 7 dni w tygodniu, -60% błędów |
| Oprogramowanie | WMS z AI (Manhattan, Blue Yonder) | Orkiestracja operacji, przeplatanie zadań | Przepustowość 15-25%. |
| Oprogramowanie | Optymalizacja szczelinowania ML | Umieść przedmioty o dużym rotacji w pobliżu wyjść | Zmniejszenie odległości kompletacji o 20%. |
| Oprogramowanie | Kontrola jakości widzenia komputerowego | Sprawdź wymiary, uszkodzenia, etykiety | Dokładność 99,5% w porównaniu do 96% działania człowieka |
| Dane | Magazyn Digital Twin | Symulacja i optymalizacja układu | Skraca czas ponownego rysowania o 70% |
Wybierz optymalizację ścieżki za pomocą TSP
Operator kompletacji, który musi zebrać w magazynie 20 artykułów, podróżuje średnio 1,5-2,5 km na misję, jeśli wykonujesz niezoptymalizowany rozkaz. Z Podróżujący sprzedawca Heurystyka problemu (TSP)., trasa jest skrócona o 20-30%, co przekłada się na znaczne oszczędności czasu i kosztów operacyjnych.
"""
Pick Path Optimization per magazzino con layout a corridoi.
Algoritmo: S-shape + nearest neighbor heuristic
"""
from dataclasses import dataclass
from typing import List, Tuple, Dict
import math
@dataclass
class Location:
"""Posizione di uno slot nel magazzino."""
aisle: int # Numero corridoio (1-N)
bay: int # Posizione nel corridoio (1-M)
level: int # Piano (0=pavimento, 1=primo ripiano, ecc.)
@dataclass
class PickItem:
"""Articolo da prelevare."""
sku_id: str
location: Location
quantity: int
def manhattan_distance(loc1: Location, loc2: Location,
aisle_width: float = 3.0,
bay_depth: float = 1.2) -> float:
"""
Distanza di Manhattan tra due posizioni nel magazzino.
Considera la necessità di uscire e rientrare nei corridoi.
"""
# Se stesso corridoio: percorso diretto
if loc1.aisle == loc2.aisle:
return abs(loc1.bay - loc2.bay) * bay_depth
# Corridoi diversi: esce dal corridoio 1, percorre il main aisle, entra nel 2
aisle_distance = abs(loc1.aisle - loc2.aisle) * aisle_width
# Scegli l'uscita più vicina (testa o coda del corridoio)
max_bay = max(loc1.bay, loc2.bay)
exit_distance = min(loc1.bay, max_bay - loc1.bay + 1) * bay_depth
entry_distance = min(loc2.bay, max_bay - loc2.bay + 1) * bay_depth
return aisle_distance + exit_distance + entry_distance
def s_shape_routing(items: List[PickItem]) -> List[PickItem]:
"""
S-Shape routing: percorre i corridoi in senso alternato
(avanti-indietro) - ottimale per missioni con molti articoli.
"""
# Raggruppa per corridoio
by_aisle: Dict[int, List[PickItem]] = {}
for item in items:
aisle = item.location.aisle
if aisle not in by_aisle:
by_aisle[aisle] = []
by_aisle[aisle].append(item)
sorted_aisles = sorted(by_aisle.keys())
route = []
for i, aisle in enumerate(sorted_aisles):
aisle_items = sorted(by_aisle[aisle], key=lambda x: x.location.bay)
# Corridoi pari: percorri in avanti; dispari: in senso inverso
if i % 2 == 0:
route.extend(aisle_items)
else:
route.extend(reversed(aisle_items))
return route
def nearest_neighbor_routing(items: List[PickItem],
start: Location = None) -> List[PickItem]:
"""
Nearest Neighbor heuristic: scegli sempre l'articolo più vicino.
Ottimale per missioni con pochi articoli dispersi.
"""
if not items:
return []
if start is None:
start = Location(aisle=1, bay=1, level=0)
remaining = list(items)
route = []
current = start
while remaining:
# Trova l'articolo più vicino dalla posizione corrente
nearest = min(remaining,
key=lambda x: manhattan_distance(current, x.location))
route.append(nearest)
current = nearest.location
remaining.remove(nearest)
return route
def optimize_pick_mission(items: List[PickItem]) -> Tuple[List[PickItem], float]:
"""
Sceglie la strategia di routing migliore in base alla missione.
- Pochi item (<= 10): Nearest Neighbor
- Molti item (> 10): S-Shape
Returns:
(route_ottimizzato, distanza_totale_metri)
"""
if len(items) <= 10:
route = nearest_neighbor_routing(items)
else:
route = s_shape_routing(items)
# Calcola distanza totale
total_distance = 0.0
start = Location(aisle=1, bay=1, level=0) # Punto di partenza (ingresso)
current = start
for item in route:
total_distance += manhattan_distance(current, item.location)
current = item.location
# Ritorno al punto di deposito
total_distance += manhattan_distance(current, start)
return route, total_distance
# Esempio d'uso
if __name__ == "__main__":
# Missione di picking con 15 articoli sparsi nel magazzino
mission_items = [
PickItem("SKU-001", Location(2, 5, 0), 3),
PickItem("SKU-002", Location(5, 12, 1), 1),
PickItem("SKU-003", Location(1, 3, 0), 2),
PickItem("SKU-004", Location(7, 8, 0), 5),
PickItem("SKU-005", Location(3, 15, 1), 1),
PickItem("SKU-006", Location(4, 2, 0), 2),
PickItem("SKU-007", Location(6, 10, 0), 3),
PickItem("SKU-008", Location(2, 18, 1), 1),
PickItem("SKU-009", Location(8, 4, 0), 4),
PickItem("SKU-010", Location(1, 20, 0), 2),
PickItem("SKU-011", Location(9, 7, 1), 1),
PickItem("SKU-012", Location(3, 11, 0), 3),
PickItem("SKU-013", Location(5, 16, 0), 2),
PickItem("SKU-014", Location(7, 3, 1), 1),
PickItem("SKU-015", Location(6, 14, 0), 2),
]
route, distance = optimize_pick_mission(mission_items)
print(f"Missione ottimizzata: {len(route)} articoli")
print(f"Distanza totale: {distance:.1f} metri")
print("\nSequenza di picking:")
for i, item in enumerate(route, 1):
print(f" {i:2d}. {item.sku_id} - "
f"Corridoio {item.location.aisle}, "
f"Posizione {item.location.bay}, "
f"Livello {item.location.level} "
f"(qty: {item.quantity})")
Dostawa ostatniej mili: optymalizacja ostatniej mili
Ostatnia mila to najdroższy i najbardziej złożony etap łańcucha dostaw: stanowi 28–40% całkowitego kosztu dostawy, a jednocześnie jest on najbardziej widoczny dla klienta końcowego. W kontekstach Włoskie obszary miejskie wyzwanie zwiększają ZTL, ruch uliczny, trudne parkowanie i fragmentacja miejsc zamieszkania.
Technologie sztucznej inteligencji umożliwiają tworzenie nowych modeli „ostatniej mili”:
Technologie AI na ostatniej mili w 2025 r
| Technologia | Państwo | Redukcja kosztów | Ograniczenia |
|---|---|---|---|
| Optymalizacja tras AI | Dojrzały, szeroko rozpowszechniony | 10-20% | To zależy od jakości danych |
| Dynamiczne przekierowanie | Dojrzały | 5-10% | Integracja ze sterownikami aplikacji |
| Drony (dostawy lotnicze) | Pilot, ograniczona | Potencjał 40% | Przepisy ENAC, ładowność, pogoda |
| Robot dostawczy | Eksperymentalne (IT) | Potencjał 60% | Infrastruktura, regulacja |
| Centrum mikrorealizacji | Rozwój | 15-30% | Koszty nieruchomości miejskich |
| Dostawa oparta na tłumie | Nisza | Zmienny | jakość usług |
Włoskie przypadki użycia: jak firmy IT wykorzystują sztuczną inteligencję w logistyce
Kontekst włoski przedstawia szczególne wyzwania, które powodują przyjęcie logistycznej sztucznej inteligencji im bardziej potrzebne, tym bardziej złożone: niejednorodna infrastruktura drogowa, silna obecność MŚP o rozdrobnionym wolumenie, wyraźną sezonowością (turystyka, rolnictwo, moda), oraz kulturę „zamawiania w ostatniej chwili”, która wywiera presję na systemy planowania.
Amazon Włochy: Najbardziej zaawansowany ekosystem automatyzacji
Amazon dokonał masowych inwestycji we Włoszech: centra dystrybucyjne Castel San Giovanni (PC), Vercelli, Passo Corese (RI), Castelguglielmo (RO) i węzły sortowania są laboratoria innowacji logistycznych. Główne cechy:
- Roboty Kiva/Sparrow: regały mobilne przesuwające się w stronę operatora, prawie całkowicie eliminujące chodzenie. Wydajność kompletacji wzrasta o 200-300%.
- Przewidująca wysyłka: Algorytmy ML wstępnie umieszczają produkty, które najprawdopodobniej zostaną zamówione w następnym tygodniu, w magazynie położonym geograficznie najbliżej docelowych klientów.
- Partnerzy Amazon Delivery Service (DSP): Algorytmy dynamicznego routingu, które dostosowują się w czasie rzeczywistym do ruchu, warunków pogodowych i nieudanych prób dostawy.
- Wizja komputerowa dla kontroli jakości: Kamery AI sprawdzają każdą wychodzącą paczkę, wykrywając uszkodzenia i niezgodności z zamówieniem w ciągu milisekund.
Poste Italiane: Cyfrowa transformacja operatora historycznego
Poste Italiane zarządza 60 milionami przesyłek rocznie, korzystając z sieci ponad 35 000 listonoszy i 13 000 urzędów pocztowych. Cyfrowa transformacja logistyki Poste ma trzy główne osie:
- Kurier ekspresowy SDA: System wyznaczania tras oparty na technologii ML do optymalizacji tras kurierskich, zintegrowany z rozwiązaniem TomTom WEBFLEET do śledzenia w czasie rzeczywistym.
- Zarządzanie szczytowym zapotrzebowaniem: algorytmy predykcyjne, które przewidują wielkość handlu elektronicznego w Czarny Piątek i okres świąteczny, umożliwiając proaktywną redukcję personelu i pojazdów.
- PostePay i logistyka o wartości dodanej: Zintegruj dane dotyczące płatności i wysyłek, aby uzyskać szczegółowe informacje o zagregowanym popycie.
- Inteligentne szafki: Sieć Punto Poste ze sztuczną inteligencją w celu optymalizacji dystrybucji geograficznej i przewidywania wskaźników wykorzystania.
GLS Italy: Analiza tras dla B2B
Grupa GLS (z silną obecnością we Włoszech) wdrożyła platformę wywiadowczą logistyka skupiona na segmencie B2B, gdzie punktualność ma kluczowe znaczenie i kontraktuje uwzględnić ALS z karami. Kluczowe innowacje:
- Dynamiczne codzienne wyznaczanie tras: trasy nie są stałe, ale są przeliczane każdej nocy na podstawie rzeczywistego wolumenu, z korektami w ciągu dnia, jeśli w punkcie odbioru występują nietypowe wolumeny.
- Prognoza wskaźnika powodzenia dostaw: Modele ML przewidują prawdopodobieństwo powodzenia dostawy dla każdego adresu/dzień, co pozwala efektywniej organizować próby.
- Integracja z systemem ERP klienta: Interfejsy API, które umożliwiają klientom B2B otrzymywanie dokładnych prognoz dostaw z 48-godzinnym wyprzedzeniem, co poprawia satysfakcję klienta końcowego.
Widoczność łańcucha dostaw w czasie rzeczywistym i cyfrowy bliźniak
Widoczność w czasie rzeczywistym jest warunkiem wstępnym każdej formy optymalizacji sztucznej inteligencji. Bez wiedzy gdzie znajduje się towar, jaki jest status zamówień dostawcy i jakie są dostępne moce produkcyjne w magazynach każdy model predykcyjny działa w ciemności.
La widoczność łańcucha dostaw nowoczesność opiera się na trzech filarach technologicznych:
Architektura łańcucha dostaw Widoczność w czasie rzeczywistym
| Poziom | Technologia | Zebrane dane | Utajenie |
|---|---|---|---|
| Kolekcja | IoT (GPS, RFID, czujniki temperatury/wilgotności) | Lokalizacja, warunki środowiskowe | 1-30 sekund |
| Transmisja strumieniowa | Apache Kafka + Flink | Strumień zdarzeń ze wszystkich punktów kontaktowych | < 1 sekunda |
| Przetwarzanie | Wykrywanie anomalii ML | Odchylenia od ETA, proaktywne alerty | 1-5 sekund |
| Wyobrażanie sobie | Wieża kontrolna (kostki danych/płatek śniegu) | Ujednolicony pulpit operacyjny | 5-30 sekund |
| Symulacja | Cyfrowy bliźniak | Wirtualna replikacja łańcucha dostaw | Partia (co noc) |
Optymalizacja śladu węglowego
W miarę zbliżania się terminów Dyrektywa CSRD (Zrównoważony rozwój przedsiębiorstwa Dyrektywa w sprawie sprawozdawczości), pomiaru i redukcji emisji logistycznych m.in stało się priorytetem biznesowym, a nie tylko etycznym. Spółki podlegające CSRD muszą raport Emisje z zakresu 3 (obejmującego logistykę) począwszy od 2025 roku.
Sztuczna inteligencja przyczynia się na trzy konkretne sposoby do zmniejszenia śladu węglowego logistyki:
- Konsolidacja obciążenia: Algorytmy ML maksymalizują współczynnik wypełnienia pojazdów, redukując liczbę pustych przejazdów (pustych mil), które stanowią średnio 20-25% ruchu towarowego we Włoszech.
- Tryb zmiany: optymalizacja multimodalna preferująca kabotaż kolejowy i morski, jeśli pozwalają na to czasy dostaw.
- Eko-trasowanie: obliczanie tras minimalizujących emisję CO2 zamiast samej odległości, biorąc pod uwagę profil wysokości i warunki ruchu.
Najlepsze praktyki i anty-wzorce w logistyce AI
Anty-wzorce, których należy unikać
- Optymalizacja w silosach: optymalizacja tras bez uwzględnienia dostępności magazynu lub odwrotnie, prowadzi do lokalnie optymalnych, ale globalnie nieoptymalnych rozwiązań.
- Ignoruj rzeczywiste ograniczenia operacyjne: okna czasowe, godziny otwarcia klientów, ograniczenia ZTL, maksymalny nacisk na oś pojazdów. Model, który ich nie zna, generuje rozwiązania nieużyteczne.
- Dane historyczne bez skorygowanej sezonowości: szkolenie modelu prognozowania popytu na danych obejmujących okresy anomalne (COVID, kryzys chipowy, 15 sierpnia) bez odpowiedniego przetwarzania wstępnego powoduje zniekształcenie prognoz.
- Brak monitorowania po wdrożeniu: Zmieniają się wzorce popytu, zmieniają się sieci drogowe, zmieniają się klienci. Niemonitorowany model po cichu ulega degradacji.
- Implementacja Wielkiego Wybuchu: Nie zastępuj od razu wszystkich procesów logistycznych sztuczną inteligencją. Zacznij od przypadku użycia o wysokim ROI, zademonstruj wartość, a następnie skaluj.
Najlepsze praktyki wdrażania AI w logistyce
- Jakość danych przede wszystkim: Przed szkoleniem dowolnego modelu upewnij się, że lokalizacja klienta, wielkość pojazdu, pojemność magazynu i dane dotyczące historii popytu są czyste i spójne.
- Podejście hybrydowe: łączy reguły biznesowe (doświadczenie planistów) ze sztuczną inteligencją. Modele oparte na czystym uczeniu maszynowym często naruszają ograniczenia, które ludzki planista instynktownie przestrzegałby.
- Wyjaśnialność dla decydentów: Menedżerowie logistyki muszą zrozumieć, dlaczego system sugeruje trasę lub zmianę kolejności. Używaj wartości SHAP i wyjaśnień w języku naturalnym.
- Powrót pełen wdzięku: gdy model jest niepewny (niska pewność), powraca do reguł heurystycznych, zamiast wydawać niewiarygodne przewidywania.
- Rygorystyczny pomiar ROI: Zdefiniuj podstawowe wskaźniki przed uruchomieniem (koszt na km, współczynnik wypełnienia, OTIF, wskaźnik wyczerpania zapasów) i mierz deltę co kwartał.
Plan działania dotyczący wdrożenia sztucznej inteligencji w logistyce dla MŚP
Dla włoskich MŚP, które chcą rozpocząć proces wdrażania logistycznej sztucznej inteligencji, proponujemy trójfazowy plan działania, ze skalowalnymi inwestycjami i mierzalnym zwrotem z inwestycji na każdym etapie:
Trzyletni plan działania AI w logistyce
| Faza | Oś czasu | Inicjatywy | Inwestycje (EUR) | Oczekiwany zwrot z inwestycji |
|---|---|---|---|---|
| Fundacja | Rok 1 | Jakość danych, nowoczesny WMS, podstawowa optymalizacja tras, statystyczne prognozowanie zapotrzebowania | 50 tys. - 200 tys | 15-25% |
| Inteligencja | Rok 2 | Prognozowanie popytu ML, zaawansowana VRPTW, optymalizacja zapasów, śledzenie w czasie rzeczywistym | 150 tys. - 500 tys | 25-40% |
| Automatyzacja | Rok 3 | Magazyn AMR, planowanie autonomiczne, cyfrowy bliźniak, AI raportująca emisję dwutlenku węgla | 300 tys. - 2 mln | 40-60% |
Połączenia z innymi artykułami z serii
- MLOps dla biznesu: Jak wprowadzić modele prognozowania popytu i routingu do środowiska produkcyjnego za pomocą MLflow i potoku CI/CD.
- LLM w biznesie: Jak używać modeli wielkojęzycznych do tworzenia konwersacyjnych wież kontroli i zautomatyzowanego raportowania łańcucha dostaw.
- Przedsiębiorstwo baz danych wektorowych: jak używać pgvector i Pinecone do wyszukiwania semantycznego w dokumentacji dostawcy i ścieżce audytu logistycznego.
- Zarządzanie danymi: Zgodność CSRD w zakresie raportowania emisji z zakresu 3 w logistyce.
Wnioski
Sztuczna inteligencja w logistyce nie jest już eksperymentem laboratoryjnym: jest rzeczywistością operacyjną które najbardziej konkurencyjne przedsiębiorstwa już wykorzystują do uzyskania przewagi strukturalnej. Problem trasowania pojazdów rozwiązany za pomocą OR-Tools, prognozowanie popytu za pomocą LightGBM i TFT, optymalizacja zapasów za pomocą Reinforcement Learning, fizyczna automatyzacja magazyny z systemem AMR i wizją komputerową: każdy element tej układanki przyczynia się do zaopatrzenia bardziej wydajny, bardziej zrównoważony i odporny łańcuch.
Dla włoskich MŚP dobra wiadomość jest taka, że nie trzeba zajmować się wszystkim wspólnie. Trójfazowy plan działania przedstawiony w tym artykule pozwala rozpocząć od inwestycji treści (50-200 tys. EUR w pierwszym roku) i wykazać konkretny zwrot z inwestycji przed skalowaniem. The PNRR Transition 5.0, na który przydzielono 12,7 miliarda euro (z czego tylko 1,7 miliarda zastosowany na początku 2026 r.), oferuje istotne zachęty podatkowe dla inwestycji w cyfryzacja i automatyzacja: szansa, której nie mają włoskie firmy logistyczne mogą sobie pozwolić na ignorowanie.
W kolejnym artykule z tej serii przyjrzymy się bliżej LLM w biznesie: jak budować Systemy korporacyjne RAG do dokumentacji wewnętrznej, dostrajanie na zastrzeżonych danych i poręcze, aby zapewnić bezpieczne i zgodne z przepisami reakcje w krytycznych kontekstach biznesowych.







