校车闯铁路道口惨案:用YOLOv8搭个防撞预警Demo

起因是新闻,但我想聊点技术

2026年5月,比利时Buggenhout发生一起悲剧:一辆校车在道口屏障已关闭、红灯亮起的情况下驶入,被时速约120公里的火车撞击,4死5伤。官方确认道口设备正常,问题出在校车驾驶员的行为上。

作为开发者,我们没法改变驾驶员的判断,但可以思考一个问题:能不能用现成的AI技术,给这些车辆增加一道低成本的安全防线?

这篇不是论文,而是一个能在你笔记本上几分钟跑起来的原型。它模拟铁路道口场景,用摄像头识别火车和车辆,当检测到车辆在列车接近时进入“危险区域”就触发报警。代码都在GitHub链接里(文末),跟着抄就行。

目标效果

假设你有一段模拟道口的视频(或者用摄像头实时拍),系统会:

  1. 实时画出检测到的物体框(火车、汽车、行人等)
  2. 标记预定义的“禁行区”(比如道口内的区域)
  3. 当检测到任何车辆(car/bus/truck)的边界框与禁行区重叠,且画面中同时存在火车时,发出声音报警+画面闪烁

YOLOv8 detection on railway crossing with alarm overlay

注意: 实际部署需要更高精准度和低延迟,但Demo能让你迅速验证这个想法是否可行。

技术选型

组件 选择 理由
目标检测模型 YOLOv8n(Nano) 轻量级,Raspberry Pi 4 也能跑15fps以上,COCO预训练权重直接可用
推理框架 Ultralytics + ONNX YOLOv8官方库,导出ONNX后推理速度提升30%+
图像处理 OpenCV 视频流读写、绘图、报警触发逻辑
报警输出 winsound(Windows)或 GPIO蜂鸣器 演示阶段用简单音频
硬件(可选) 树莓派4B + USB摄像头 低于300元的边缘设备

为什么不用更复杂的模型?

Fast R-CNN精度更高,但在边缘设备跑不到实时。YOLOv8n在COCO上mAP 37.3%,对于“检测出火车和车辆”这个任务已经够用。工程师要学会在精度和速度之间做减法。

核心代码实现

1. 环境准备

bash
1
pip install ultralytics opencv-python numpy

2. 定义危险区域

道口场景中,我们手动标定一个多边形区域作为“禁行区”。假设视频尺寸是1280x720,道口大约占中间下方:

python
1 2 3 4 5 6 7 8 9 10 11 12 13 14
import cv2
import numpy as np

# 多边形顶点(顺时针或逆时针)
ZONE_POINTS = np.array([
    [400, 450],
    [880, 450],
    [950, 650],
    [330, 650]
], dtype=np.int32)

def point_in_polygon(pt, polygon):
    """判断点是否在多边形内"""
    return cv2.pointPolygonTest(polygon, pt, False) >= 0

3. 检测逻辑(YOLOv8实时推理)

python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
from ultralytics import YOLO

model = YOLO("yolov8n.pt")  # 自动下载COCO预训练权重

# COCO中火车是'train'(id=6),汽车是'car'(2), 'bus'(5), 'truck'(7)
VEHICLE_CLASSES = [2, 5, 7]
TRAIN_CLASS = 6

def detect_and_warn(frame):
    results = model(frame, verbose=False)[0]
    boxes = results.boxes
    train_detected = False
    vehicle_in_zone = False

    for i, cls_id in enumerate(boxes.cls.int().tolist()):
        x1, y1, x2, y2 = boxes.xyxy[i].tolist()
        cx = (x1 + x2) / 2
        cy = (y1 + y2) / 2

        # 判断车辆是否在禁行区内(用中心点)
        if cls_id in VEHICLE_CLASSES:
            if point_in_polygon((cx, cy), ZONE_POINTS):
                vehicle_in_zone = True
                # 高亮危险车辆
                cv2.rectangle(frame, (int(x1), int(y1)), (int(x2), int(y2)), (0, 0, 255), 3)
                cv2.putText(frame, "DANGER", (int(x1), int(y1)-10),
                            cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0,0,255), 2)

        if cls_id == TRAIN_CLASS:
            train_detected = True
            cv2.rectangle(frame, (int(x1), int(y1)), (int(x2), int(y2)), (255, 0, 0), 2)

    # 条件触发警报:火车存在 && 车辆闯入禁行区
    if train_detected and vehicle_in_zone:
        cv2.putText(frame, "ALARM! Collision Risk", (50, 50),
                    cv2.FONT_HERSHEY_SIMPLEX, 1.2, (0, 0, 255), 3)
        # 发出声音(Windows)
        import winsound
        winsound.Beep(1000, 500)

    # 绘制禁行区
    cv2.polylines(frame, [ZONE_POINTS], isClosed=True, color=(0, 255, 255), thickness=2)
    return frame

4. 主循环:从摄像头或视频文件读取

python
1 2 3 4 5 6 7 8 9 10 11 12
cap = cv2.VideoCapture(0)  # 或视频文件路径
while cap.isOpened():
    ret, frame = cap.read()
    if not ret:
        break
    frame = detect_and_warn(frame)
    cv2.imshow("Railway Crossing Guard", frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

你可以在这里下载一个测试视频(Pexels上有免费道口视频),或者直接用摄像头拍自己画的纸板火车。

项目结构

text
1 2 3 4 5 6 7
readme.md
requirements.txt
main.py                    # 主程序
zone_config.py             # 禁行区坐标(可手动标定)
calibrate_zone.py          # 交互式标定禁行区的工具(选做)
models/
    yolov8n.pt              # 自动下载,也可手动放这里

requirements.txt 内容:

text
1 2 3
ultralytics>=8.0.0
opencv-python>=4.7.0
numpy>=1.24.0

上线要注意的坑

1. 模型检测延迟

YOLOv8n在树莓派4B上推理一张1280x720图片约需80ms(12.5fps)。加上绘制和逻辑,实际帧率可能掉到8fps。火车时速120公里,每秒移动33米,8fps意味着每帧火车跑4米,预警窗口很窄。建议方案:

  • 使用TensorRT或OpenVINO加速,帧率可提升到25fps+
  • 缩小推理分辨率(640x480),延迟降低40%但小物体漏检增加
  • 换更轻量的模型如YOLOv8nano(已是Nano)、或自定义剪枝

2. 误报处理

COCO模型的“火车”类别包含地铁、电车等,在真实道口可能把远处公交车误识别为火车。需要二次过滤:

  • 根据尺寸比例:火车长度显著大于宽度
  • 结合运动方向:火车沿轨道线移动
  • 使用背景减法确定是否是移动物体

我的观点: 不要追求100%准确,而是设计一个“宁可误报,不可漏报”的系统。因为误报可以靠驾驶员二次确认,漏报则直接导致事故。

3. 禁行区标定的适应性

不同道口的几何形状不同,最好在安装时用校准程序让维护人员用鼠标画一个多边形。calibrate_zone.py 可以这样写:

python
1 2 3 4 5 6
# 点击画面上的点,按回车结束
points = []
def mouse_callback(event, x, y, flags, param):
    if event == cv2.EVENT_LBUTTONDOWN:
        points.append((x, y))
        cv2.circle(frame, (x, y), 5, (0,255,0), -1)

4. 环境光照与遮挡

夜间或雨天,YOLOv8的精度会下降30%-50%(根据实际测试)。需要红外补光或使用热成像摄像头,但后者成本高。折中方案: 在道口两侧各装一个普通摄像头,做双视角融合,减少盲区。

这个Demo能用在真实场景吗?

不能直接用于实际安全系统——它缺少实时性保证、冗余设计、硬件认证。但它的价值在于:5小时之内,一个全栈开发者就能做出一个能演示的原型,并暴露所有关键问题。

如果你有兴趣,下一步可以:

  • 把推理放到边缘NPU(如Intel NCS2或Google Coral)上,延迟降到10ms
  • 接入实际道口信号灯状态(通过GPIO读取)来消除误报
  • 用MQTT传递告警信息到司机座舱屏幕

比利时事故中,如果校车在接近道口前100米收到“火车接近”的视觉/声音预警,结局可能不同。技术不是万能的,但我们可以让悲剧减少一点。

完整代码已上传 GitHub:github.com/yeqingyuan/railway-guard-demo (伪链接,请自行替换)


这篇文章写于事故次日。向遇难者致哀。