AI Automatic Road Pavement Inspection System Development

We design and deploy artificial intelligence systems: from prototype to production-ready solutions. Our team combines expertise in machine learning, data engineering and MLOps to make AI work not in the lab, but in real business.
Showing 1 of 1 servicesAll 1566 services
AI Automatic Road Pavement Inspection System Development
Medium
~1-2 weeks
FAQ
AI Development Areas
AI Solution Development Stages
Latest works
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_logo-advance_0.png
    B2B Advance company logo design
    561
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    823
  • image_logo-aider_0.jpg
    AIDER company logo development
    762
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    848

AI road surface inspection system

Potholes, cracks, and ruts—the annual cost of fixing them runs into the billions. The problem is that by the time road workers notice them, the defect has already reached a critical stage. AI inspections using vehicle cameras or specialized machines can detect early signs of pavement degradation and prioritize repairs.

Classification of road surface defects

ASTM D6433 identifies 20 types of distress. In practice, we work with 7–8 key ones:

Type of defect Detection method Complexity
Potholes Object detection (bbox) Average
Longitudinal cracks Segmentation High
Transverse cracks Segmentation Average
Alligator cracks Texture classification High
Rutting 3D profile / stereo Very high
Potholes (raveling) Texture + anomalies Average
Drawdown (depression) 3D profile High

Detection and segmentation model

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'

Mobile Inspection: Vehicle-Mounted Camera

For public roads, a camera under the front bumper or in the radiator grille records at 25 fps and is linked to GPS. Additionally, an accelerometer automatically detects potholes based on vibration.

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

Case: Inspection of 120 km of city roads

Task: Prioritizing road repairs. Equipment: Ford Transit with 4 cameras (front + 2 sides + rear), RTK GPS. 120 km covered over 3 days of filming.

  • Processed: 1.2 million frames
  • Found: 3400 pits (P> 0.5), 47 km of cracks (segmentation)
  • Of these, critical (PCI < 25): 8.2 km - priority repair
  • Savings compared to manual inspection: 12 working days → 6 hours of processing + 3 hours of verification
Project type Term
Pit detector (basic) 3-5 weeks
Full inspection system (+ cracks, PCI) 7–12 weeks
Mobile system with GIS integration 10–16 weeks