AI-система інспекції дорожнього покриття
Ями, тріщини, вибоїни - щорічні витрати на їхнє усунення обчислюються мільярдами. Проблема в тому, що на момент візуального виявлення дорожнім робітникам, дефект уже перейшов у критичну стадію. AI-інспекція з камер транспортних засобів або спеціальних машин дозволяє виявляти ранні ознаки деградації покриття та пріоритизувати ремонт.
Класифікація дефектів дорожнього покриття
Стандарт ASTM D6433 виділяє 20 типів дистресу. На практиці працюємо з 7-8 ключовими:
| Тип дефекту | Метод виявлення | Складність |
|---|---|---|
| Ями (potholes) | Object detection (bbox) | Середня |
| Поздовжні тріщини | Segmentation | Висока |
| Поперечні тріщини | Segmentation | Середня |
| Сітчасті тріщини (alligator) | Texture classification | Висока |
| Колійність (rutting) | 3D профіль / stereo | Дуже висока |
| Вибоїни (raveling) | Texture + anomaly | Середня |
| Просідання (depression) | 3D профіль | Висока |
Модель детекції та сегментації
import torch
import numpy as np
import segmentation_models_pytorch as smp
from ultralytics import YOLO
import cv2
class PavementInspector:
def __init__(self, seg_model_path: str, det_model_path: str):
# Сегментация трещин: UNet++ с ResNet50 энкодером
# Дообучен на RDD2022 (Road Damage Dataset, 47k изображений)
self.seg_model = smp.UnetPlusPlus(
encoder_name='resnet50',
encoder_weights=None,
in_channels=3,
classes=4, # background, longitudinal, transverse, alligator
)
seg_ckpt = torch.load(seg_model_path)
self.seg_model.load_state_dict(seg_ckpt)
self.seg_model.eval()
# YOLOv8m для ям и выбоин (bbox достаточно)
self.det_model = YOLO(det_model_path)
# Маппинг классов сегментации
self.seg_classes = {
0: 'background',
1: 'longitudinal_crack',
2: 'transverse_crack',
3: 'alligator_crack'
}
# Маппинг для оценки тяжести (PCI-based)
self.severity_thresholds = {
'pothole': {'low': 0.01, 'medium': 0.05}, # % площади кадра
'crack': {'low': 0.02, 'medium': 0.08}
}
@torch.no_grad()
def inspect(self, frame: np.ndarray) -> dict:
h, w = frame.shape[:2]
# 1. Сегментация трещин
input_tensor = self._preprocess(frame)
seg_output = self.seg_model(input_tensor)
seg_mask = seg_output.argmax(dim=1)[0].numpy()
crack_analysis = self._analyze_cracks(seg_mask, w * h)
# 2. Детекция ям
det_results = self.det_model(frame, conf=0.45)
potholes = self._analyze_potholes(det_results, w * h)
# 3. Индекс состояния покрытия (упрощённый PCI)
pci = self._compute_pci(crack_analysis, potholes)
return {
'crack_analysis': crack_analysis,
'potholes': potholes,
'pci_score': pci,
'condition': self._pci_to_condition(pci),
'seg_mask': seg_mask
}
def _analyze_cracks(self, mask: np.ndarray,
total_pixels: int) -> dict:
analysis = {}
for cls_id, cls_name in self.seg_classes.items():
if cls_id == 0:
continue
crack_pixels = int((mask == cls_id).sum())
ratio = crack_pixels / total_pixels
analysis[cls_name] = {
'pixel_count': crack_pixels,
'area_ratio': ratio,
'severity': 'high' if ratio > 0.08 else
'medium' if ratio > 0.02 else 'low'
}
return analysis
def _compute_pci(self, cracks: dict, potholes: list) -> float:
"""
PCI 0–100: 100 = идеальное покрытие, 0 = полная деградация.
Упрощённая формула на основе ASTM D6433.
"""
deduct = 0.0
for crack_type, data in cracks.items():
ratio = data['area_ratio']
if ratio > 0.08:
deduct += 25
elif ratio > 0.02:
deduct += 12
elif ratio > 0.005:
deduct += 5
for pothole in potholes:
area = pothole['area_ratio']
if area > 0.03:
deduct += 30
elif area > 0.01:
deduct += 15
return max(0, 100 - deduct)
def _pci_to_condition(self, pci: float) -> str:
if pci >= 85: return 'excellent'
elif pci >= 70: return 'good'
elif pci >= 55: return 'fair'
elif pci >= 40: return 'poor'
elif pci >= 25: return 'very_poor'
else: return 'failed'
Мобільна інспекція: камера на транспортному засобі
Для доріг загального користування камера під переднім бампером або в решітці радіатора, запис зі швидкістю 25fps, прив'язка до GPS. Додатково акселерометр для автоматичної фіксації ям по вібрації.
class MobileRoadSurvey:
def __init__(self, gps_logger, inspector: PavementInspector):
self.gps = gps_logger
self.inspector = inspector
self.survey_log = []
def process_frame_with_geotagging(self, frame: np.ndarray,
timestamp: float) -> dict:
gps_coords = self.gps.get_coords(timestamp)
results = self.inspector.inspect(frame)
record = {
'timestamp': timestamp,
'lat': gps_coords['lat'],
'lon': gps_coords['lon'],
'pci': results['pci_score'],
'condition': results['condition'],
'defects': results
}
self.survey_log.append(record)
return record
Кейс: інспекція 120 км доріг міста
Завдання: пріоритизація дорожнього ремонту. Інструмент: Ford Transit з 4 камерами (перед + 2 борти + зад), GPS RTK. За 3 дні зйомки вкрито 120 км.
- оброблено: 1.2 млн кадрів
- Виявлено: 3400 ям (P > 0.5), 47 км тріщин (сегментація)
- З них критичних (PCI < 25): 8.2 км - першочерговий ремонт
- Економія порівняно з ручним обстеженням: 12 робочих днів → 6 годин обробки + 3 години верифікації
| Тип проекту | Термін |
|---|---|
| Детектор ям (базовий) | 3-5 тижнів |
| Повна інспекційна система (+ тріщини, PCI) | 7–12 тижнів |
| Мобільна система з GIS-інтеграцією | 10-16 тижнів |







