ADAS自动驾驶-前车碰撞预警(追尾预警、碰撞检测)系统

目录

【计算机毕业设计】前车碰撞预警(追尾预警)系统 + yolov11/yolov10/yolov9/yolov8/yolov7/yolov5 +(源码+论文+数据库)

前言

项目展示视频如下:

一.视频链接

二.动图如下

可提供整套代码(含详细注释)、训练好的权重、测试视频和详细说明文档,可自行修改源码部署到树莓派、香橙派、Jetson Nano、瑞芯微RK3588等开发板上,也可调用摄像头视频流进行实时推理。

本项目基于yolov5/yolov8/yolov11/yolov12全系列、Ultra-Fast-Lane-Detection-v2车道线检测、标定测距算法实现了一个交通追尾相关的碰撞预警系统,可为一些同学的课程设计、大作业、毕业设计等提供参考。

项目实现了优美的QT界面,可自主调节界面主题,实现了卡车、小轿车、自行车、摩托车、公交车、火车的实时检测、检测目标特效流的开关、实时车距测量、当前行车车道的实时检测、基于人脑反应时间和刹车时间范围的碰撞预警功能。效果如下图所示:

1.绿色框代表符合安全的低风险目标,黄色框代表中风险目标,红色框代表易发生追尾的高风险目标。地面虚影为当前车道,每辆车都会显示检测置信度、类别、安全状态、到行车摄像头的直线距离

初始界面

可自定义界面主题

2.可训练自己的数据集,可置换成yolov5到yolov11/yolov12各版本的权重

一.项目环境配置

1.cuda安装

最好使用gpu训练模型,速度会快很多,用NVIDIA显卡可以安装cuda,大家可以先看这篇博客:最详细!Windows下的CUDA与cuDNN详细安装教程

2.pytorch安装

不熟悉pycharm的anaconda的同学请先看这篇博客,了解pycharm和anaconda的基本操作:如何在pycharm中配置anaconda的虚拟环境

anaconda安装完成之后请切换到国内的源来提高下载速度 ,命令如下:

bash 复制代码
  conda config --remove-key channels
  conda config --add channels https://mirrors.ustc.edu.cn/anaconda/pkgs/main/
  conda config --add channels https://mirrors.ustc.edu.cn/anaconda/pkgs/free/
  conda config --add channels https://mirrors.bfsu.edu.cn/anaconda/cloud/pytorch/
  conda config --set show_channel_urls yes
  pip config set global.index-url https://mirrors.ustc.edu.cn/pypi/web/simple

3.pip依赖安装

bash 复制代码
   1.进入文件
   
   cd forward-collision-warning-system
bash 复制代码
   2.创建虚拟环境
   
   conda create -n fcw python=3.10
bash 复制代码
   3.激活环境
   
   conda activate fcw
bash 复制代码
   4.安装依赖
   
   安装pytorch依赖
   # 根据自己电脑的cuda和cpu情况选择相应版本,或者自行pytorch官网下载
   # CUDA 11.8
   pip install torch==2.5.0 torchvision==0.20.0 torchaudio==2.5.0 --index-url https://download.pytorch.org/whl/cu118
   # CUDA 12.1
   pip install torch==2.5.0 torchvision==0.20.0 torchaudio==2.5.0 --index-url https://download.pytorch.org/whl/cu121
   # CUDA 12.4
   pip install torch==2.5.0 torchvision==0.20.0 torchaudio==2.5.0 --index-url https://download.pytorch.org/whl/cu124
   # CPU only
   pip install torch==2.5.0 torchvision==0.20.0 torchaudio==2.5.0 --index-url https://download.pytorch.org/whl/cpu
   
   安装其他相应依赖
   pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
bash 复制代码
   运行程序
   python main.py

二.车辆检测、标定测距算法及代码解读

1.主函数讲解

main.py

python 复制代码
    # 2.车辆检测
    model_path = "models/engine/car_detector.engine" # TensorRT推理
    # model_path = "models/yolov5/car_detector.pt" # pytorch推理(没有TensorRT框架的时候,默认推理模式)
    # model_path = "models/yolo11-seg/yolo11s-seg.pt" # 分割模型推理
    self.vehicle_detector = VehicleDetector(model_path)

如上代码所示,可以根据自己的需求更改,使用yolov5到yolov11/yolov12全系列预训练权重均可,可以使用自己训练的权重,也可以使用TensorRT推理(注意:需要安装环境和Cuda对应版本的TensorRT),本人用的是yolov5自己训练的car_detector.pt

vehicle_detection.py

python 复制代码
    def detect_vehicles_yolo(self, image: np.ndarray):
    """
    使用YOLO模型检测车辆
    
    Args:
        image (np.ndarray): 输入图像
    
    Returns:
        List[Tuple[int, int, int, int, float, int]]: 检测结果列表,每个元素为(x1, y1, x2, y2, confidence, class_id)
    """
    vehicles = []
    contours = []
    
    if self.net is None:
        return vehicles
    
    height, width = image.shape[:2]
    # torch推理
    if self.model_type == 'pt' or self.model_type == 'pth':
        detections = v5_inference.run_inference(image, self.net)
    # TensorRT推理
    elif self.model_type == 'engine' or self.model_type == 'trt':
        detections  = self.net.infer(image)
    else:
        detections = np.array([])
    
    for detection in detections:
        confidence = round(detection[4], 2) # 置信度
        class_id = round(detection[5]) # 车辆类别
        # 如果不属于车辆类别或者低于阈值,则跳过
        if class_id not in self.vehicle_classes or  confidence < self.confidence_threshold:
            continue
        # 只保留车辆类别且置信度高于阈值的检测
        x1 = round(detection[0])
        y1 = round(detection[1])
        x2 = round(detection[2])
        y2 = round(detection[3])
        vehicles.append([x1, y1, x2, y2, confidence, class_id])
    
    return vehicles, contours

如上代码所示,通过yolo检测模型推理得到目标,拿到需要的目标信息,保存在数组里面,返回到主函数里面,进行后续加工处理。

2.测距算法实现

使用yolo实现目标检测,拿到当前车辆位于地面的坐标,然后就可以扔到后面的测距算法进行距离计算。由于网上各种开源的测距算法,要么准确率不是很好,要么就是条件限制太苛刻了。最后经过不懈努力,最终敲定使用一篇专利文章叫车载摄像头动态标定与视觉测距算法说明书来实现, 因为这个算法需要进行提前的参数标定,只要标定是准确的,测到的距离就会非常准确,而且还能实现理论上的动态标定,然后我就尝试去复现了一下,结果发现效果还是很好的。

关于这个算法的实现,我们都知道,汽车上装的摄像头能拍到前方的画面,但摄像头只看到二维的图像, 如果要靠它去判断"前方那辆车有多远""地上的线离我多远",就必须先搞清楚摄像头和真实世界之间的几何关系,也就是摄像头的"姿态"和"参数"。这一步就叫"标定"。 传统的标定要用专门的标定板,在实验室里调很久,还要保证摄像头位置固定,非常麻烦。而这项算法的聪明之处在于: 它不需要任何标定板,也不用人工操作,直接利用道路上的虚线当"天然标定板",就能自动完成标定。第一种情况:道路尺寸已知。 比如我们知道城市道路上的车道宽3.25米,每段虚线长2米,中间间隔4米。这时摄像头拍下的图像里,只要找到这几段虚线的顶点,就相当于知道了这些点在现实世界中的实际距离。 算法会用这四个顶点(A、B、C、D)来算出摄像头相对于地面的角度、位置和高度,这就叫"单消失点标定法"。 简单说就是:"我知道地上格子的大小,也知道照片里格子变成了什么样,从变形程度就能推回摄像头的角度。" 第二种情况:道路尺寸不知道,但是这种情况我复现起来很复杂,然后就放弃了。 比如有些路,我们不知道虚线间距有多大。这时算法会利用车辆自身的运动来"创造已知距离"。 摄像头先在画面里找出两条平行的虚线顶点(A、C),然后跟踪它们在视频的后几帧中的位置变化(A′、C′)。 同时,车上可以知道自己的行驶速度(比如每秒7.5米),那就能推算出车辆在这几帧之间移动了多远。 这四个点(A、C、A′、C′)也就成了一个新的"标定模板",同样可以反推出摄像头的角度和位置。 这就像是"我不知道地面格子多大,但我自己往前走了几步,用自己的运动当尺子,就能量出比例关系"。

当摄像头的参数算出来之后,接下来就能进行视觉测距。 摄像头通过目标检测算法(YOLO)找到前方车辆或行人,取它在图像中的底部中心点。然后根据刚才标定出来的摄像头角度、高度、焦距等参数,把这个点的图像坐标转换成地面坐标,最后算出它与摄像头在地面投影点之间的真实距离。 如果目标不是在地面上(比如车顶或人头),算法还会根据高度差进行修正,得到实际的三维距离。同时我还巧妙的设计了一个算法,基于车的宽度去矫正车距,让距离更加准确。由于我们是知道车辆的宽度范围的,国标车宽大概为1.8到2.1米之间,当我们首次标定成功后,我们不仅可以算法摄像头到车辆的距离, 还可以基于车辆左右轮胎的地面坐标算出真实车宽,由于算法的算术逻辑,车宽跟标定的参数中的s和t是成单调相关的,所以我设计了一个二分查找算法,去调整摄像头角度t,可以改变三角函数s的值,进而去调整标定参数,让算出来的车宽接近1.8到2.1米这个区间,当满足国标车宽后,则停止标定,相当于每一帧 都会进行车距的矫正。基于我后续的实验,确实会降低距离测量不准的情况,让测出来的各个距离处于一个合理的范围之内,实现了测距精度的提升。

算法原理图解:

1.上图为摄像机成像模型

2.上图为单消失点标定法的标定模板

3.上图为专利算法视觉距离测定方法的一个具体实施例

4.上图为本项目视觉距离测定方法的一个具体实施例

3.核心代码

本项目,我将测速的代码封装在core下面的distance_measure目录下,里面有代码的详细注释,还有测距算法的使用方法。

python 复制代码
    def calibration(self, calibP):
    # Center normalized coordinates
    xaf = calibP.xa - calibP.imageWidth / 2
    xbf = calibP.xb - calibP.imageWidth / 2
    xcf = calibP.xc - calibP.imageWidth / 2
    xdf = calibP.xd - calibP.imageWidth / 2
    yaf = calibP.imageHeight / 2 - calibP.ya
    ybf = calibP.imageHeight / 2 - calibP.yb
    ycf = calibP.imageHeight / 2 - calibP.yc
    ydf = calibP.imageHeight / 2 - calibP.yd

    calib_succeed = self.calibTwoVanish(xbf, ybf, xdf, ydf, xaf, yaf, xcf, ycf, calibP.L)
    self.p += 90

    if not calib_succeed:  # if p is not a number, must use one vanishing point method
        calib_succeed = self.calibOneVanish(xaf, yaf, xbf, ybf, xcf, ycf, xdf, ydf, calibP.W, calibP.L)
        if not calib_succeed:
            calib_succeed = self.calibOneVanish(xbf, ybf, xdf, ydf, xaf, yaf, xcf, ycf, calibP.L, calibP.W)
            self.p += 90
    else:
        if -360 < self.p < 360 and (int(self.p + 0.5) + 360 - 30) % 90 >= 30:  # (90 > p >= 60) or 180 > p >= 150)
            if (int(self.p + 0.5) + 360 + 45) % 180 >= 90:  # 135 > p > 45
                self.calibOneVanish(xaf, yaf, xbf, ybf, xcf, ycf, xdf, ydf, calibP.W, calibP.L)
            else:
                if self.calibOneVanish(xbf, ybf, xdf, ydf, xaf, yaf, xcf, ycf, calibP.L, calibP.W):
                    self.p += 90

    self.p = (int(self.p) + 179) % 360 - 179 + self.p - int(self.p)
    self.p = self.p * math.pi / 180
    self.calibrated = calib_succeed

以上代码是标定的算术实现代码。

python 复制代码
    def cal_distance_a2b(self, point_a, point_b):
        # 1.标定得到标定参数
        self.speed_measure.calibration(self.calibP)

        # 2.坐标预处理(相较于中心点的偏移)
        pointA_x, pointA_y = point_a[0], point_a[1]
        pointA_x = pointA_x - self.calibP.imageWidth / 2
        pointA_y = self.calibP.imageHeight / 2 - pointA_y
        A_xReal, A_yReal = self.speed_measure.image2realXY(pointA_x, pointA_y)

        pointB_x, pointB_y = point_b[0], point_b[1]
        pointB_x = pointB_x - self.calibP.imageWidth / 2
        pointB_y = self.calibP.imageHeight / 2 - pointB_y
        B_xReal, B_yReal = self.speed_measure.image2realXY(pointB_x, pointB_y)

        # 计算出x方向和y方向的距离
        x_distance = abs(A_xReal - B_xReal) # 6.292835816507722,单位m
        y_distance = abs(A_yReal - B_yReal) # 0.13401646258543254 单位m,y的距离有小范围的偏差是正常现象
    
        return x_distance, y_distance

以上代码是标定完成之后,计算图像上的地面坐标a到地面坐标b的真实距离。

python 复制代码
    def cal_distance_a2cam(self, point_a):
        # 1.标定得到标定参数
        self.speed_measure.calibration(self.calibP)

        # 2.坐标预处理(相较于中心点的偏移)
        pointA_x, pointA_y = point_a[0], point_a[1]
        pointA_x = pointA_x - self.calibP.imageWidth / 2
        pointA_y = self.calibP.imageHeight / 2 - pointA_y
        A_xReal, A_yReal = self.speed_measure.image2realXY(pointA_x, pointA_y)
        cam_xReal, cam_yReal = self.speed_measure.get_cam_xy()

        # 计算出x方向和y方向的距离
        x_distance = abs(A_xReal - cam_xReal) # 6.292835816507722,单位m
        y_distance = abs(A_yReal - cam_yReal) # 0.13401646258543254 单位m,y的距离有小范围的偏差是正常现象

        return x_distance, y_distance

以上代码是标定完成之后,计算摄像头到图像上的地面坐标a的真实距离。

python 复制代码
    def calib_correction(self, rect_width, point_a, point_b):

        lower_car_width = 1.6
        upper_car_width = 1.85
        # 正常车辆的宽度范围:1.6 米到 1.85 米 之间
        if lower_car_width < rect_width < upper_car_width:
            return rect_width

        # 二分查找实现
        max_iter = 60 # 设置迭代上限,防止死循环
        left_deg = 0  # 调整角度
        right_deg = 45
        eps = 0.01 # 精度控制

        for i in range(max_iter):
            gap_deg = right_deg - left_deg
            if gap_deg <= eps:
                break

            mid_deg = (left_deg + right_deg) / 2.0
            t_rad = math.radians(mid_deg)  # 统一使用弧度
            s = math.sin(t_rad)

            # 更新系数(使用弧度)
            self.speed_measure.t = t_rad
            self.speed_measure.l = - self.speed_measure.h / s

            # 开始重新计算
            _, y_distance_ab = self.cal_distance_a2b_no_calib(point_a, point_b)
            rect_width = y_distance_ab  # 赋值给车宽

            # 正常车辆的宽度范围:1.6 米到 1.85 米 之间
            if lower_car_width < y_distance_ab < upper_car_width:
                return rect_width

            # 假设 y_distance_ab和mid_deg 是单调关系
            if y_distance_ab < 1.85:
                left_deg = mid_deg  # 太小,需要更大的角度
            else:
                right_deg = mid_deg  # 太大,需要更小的角度

        return rect_width

以上代码是车距矫正的实现,基于二分查找算法,依据摄像头参数跟距离的三角函数单调相关的特性实现。

4.效果展示

距离测量的很精准,只要标定按照规范来,目测和实测都是误差比较小。

三.车道线检测算法及代码解读

1.主要函数讲解

python 复制代码
    def _init_lane_detector(self):
        """
        初始化车道线检测器
        """
        if self.lane_detection_enabled and self.lane_detector is None:
            try:
                engine_path = "core/lane_detection/weights/culane_res34.engine"
                config_path = "core/lane_detection/configs/culane_res34.py"
                ori_size = (1600, 320)  # 固定好size
                self.lane_detector = LaneDetector(engine_path, config_path, ori_size)
                self.lane_detector_cache = self.lane_detector # 增加缓存
            except Exception as e:
                print(f"车道线检测器初始化失败,错误为: {e}")
                print(f"需要进行车道线检测器的环境配置和模型下载,可以选择torch推理,当运行的时候,需要关闭车道线检测,即可正常运行!\n")
                self.lane_detector = None

    def set_lane_detection_enabled(self, enabled):
        """
        设置车道线检测开关状态
        """
        self.lane_detection_enabled = enabled
        if enabled:
            if self.lane_detector_init <= 1: # 初始化的次数
                self._init_lane_detector()
                self.lane_detector_init += 1
            else:
                self.lane_detector = self.lane_detector_cache
        else:
            self.lane_detector = None

由于车道线检测是基于开源项目Ultra-Fast-Lane-Detection-v2实现的,算力要求比较高,需要用到gpu,同时是需要使用TensorRT推理才能实时推理,需要配置TensorRT环境,可能有些同学硬件不支持,但是本系统支持开关车道线检测,所以不用担心运行报错的问题。

车道线检测同理需要加载预训练好的engine模型,推理得到数据后,需要进行复杂的后处理环节,还要还原到原图坐标上,需要去裁剪图像的下部分区域进行推理,得到的点再拼接回原图,同时使用opencv的contour函数绘制轮廓,显示出来。

python 复制代码
    # ✅1.车道线检测
    a = time.time()
    if gap_detect and self.lane_detection_enabled and self.lane_detector is not None:
        self.is_exist_lane = False # 更新车道线检测状况,每次设定刚进来都没检测到车道线
        coords = self.lane_detector.get_lane_coordinates(result_frame)
        # 绘制车道线
        result_frame, left_lane, right_lane = postprocess_coords_with_draw(result_frame, coords)
        self.last_detect_lane_frame = result_frame

        # 构造封闭区域多边形(左车道 + 右车道反转)
        if left_lane and right_lane:
            polygon_points = left_lane + right_lane[::-1]
            # lane_polygon = Polygon(polygon_points)
            # 如果首尾不同,补上闭合点
            if polygon_points[0] != polygon_points[-1]:
                polygon_points.append(polygon_points[0])

            # 只有数量足够才创建 Polygon
            if len(polygon_points) >= 4:
                lane_polygon = Polygon(polygon_points)
            else:
                lane_polygon = None  # 或者跳过

            self.last_lane_polygon = lane_polygon
        else:
            lane_polygon = None
    else:
        # 使用缓存的检测结果或原始帧(当车道线检测关闭时)
        if self.lane_detection_enabled and hasattr(self, 'last_detect_lane_frame') and self.last_detect_lane_frame is not None:
            result_frame = self.last_detect_lane_frame
            lane_polygon = self.last_lane_polygon
        else:
            result_frame = frame.copy()
            lane_polygon = None
    
    # e.判断预警等级
    # 增加判定条件,只针对于在当前车道线的车辆才开启碰撞预警
    # 也就是判定(x_center, y_center)是否在 left_lane 和 right_lane 相交的区域里面
    if x_distance < self.config.SAFE_DISTANCE:
        danger_distances.append(x_distance)
        if lane_polygon: # 存在车道区域
            self.is_exist_lane = True
            # 判断是否在车道区域内
            distances_is_included_lane[x_distance] = lane_polygon.contains(Point(point_center))

    elif self.config.SAFE_DISTANCE < x_distance < self.config.SAFE_DISTANCE * 1.5:
        warning_distances.append(x_distance)
        if lane_polygon: # 存在车道区域
            self.is_exist_lane = True
            # 判断是否在车道区域内
            distances_is_included_lane[x_distance] = lane_polygon.contains(Point(point_center))

以上代码是针对当前车道行车时碰撞预警的业务逻辑实现,当检测到车道线的时候,如果有目标车辆位于车道线区域,则进行距离判定,如果为低于安全距离阈值,则会提示预警,反之亦然;

如果目标车辆不位于车道线区域,则会同时判定前方所有的车辆,根据真实距离去进行预警判断。

2.算法实现

车道线检测实现的网络结构如下:

这套算法的基本流程是这样的:首先,模型输入一帧道路图像,经过一个卷积网络提取特征,再基于这些特征按照预定义的"锚(anchors)"来表示可能的车道线路位置,最终输出每条车道在这些锚上的离散位置分类,再通过数学计算转成连续的车道线坐标。 我将根据我了解到的算法解读,下面逐步拆开来说明。

1.特征提取 图像通过一个典型的 CNN(如 ResNet18/34)作为 backbone 提取出深度特征。这样做是为了让网络既能"看到"局部车道线信息(如边缘、颜色、纹理)也能获得"全局上下文"信息(如整条车道线的走向、被遮挡或断裂的部分)------项目作者指出,在极端遮挡或弱光情况下,人类识别车道更多依赖全局语境而不是单个像素。

2.锚的定义与表示 与传统将车道线视为像素级分割任务不同,这个算法预设了 "锚" 用于表示车道。具体来说,它定义了一系列 行锚(row anchors) 或 列锚(column anchors),即沿图像高度(行)或者宽度(列)固定几条采样线。对于每一条车道,在每个锚上模型只需判断该车道是否与该锚交汇,如果交汇, 则预测该交点的坐标(在该锚线上是一个位置),如果不交汇,则标记空。这样,一个车道就被稀疏地表示为 "在每条锚线上一个 x(或 y)位置" 的序列。

3.混合锚策略(Hybrid anchors) 项目作者进一步发现,不同方向或不同位置的车道,用单一类型锚可能带来误差增大的问题。例如,如果车道线较为水平,用行锚测 x 值可能产生放大误差(因为锚与线方向近乎平行时,小角度偏差导致大的坐标偏差)。为此,算法采用 "混合锚" ------ 对于靠近自己车道(ego-lane)使用行锚, 对侧边或走向不同的车道使用列锚。这样能更好匹配车道走向,减少定位误差。

4.从坐标回归到"序数分类" 接下来,在每个锚线上,模型并不是直接回归一个实数坐标,而是先将该锚线上可能的位置离散为 K 个格(cells)或类别,然后进行分类,预测每个类别的概率。因为这些格是有顺序的(左到右或者上到下),项目作者称之为 "序数分类(ordinal classification)"。 然后,用预测出的类别概率分布的"数学期望"来计算一个连续坐标值。这样做有几个好处:分类任务通常比回归更稳定,输出概率可以表征不确定性,而期望运算可以恢复连续值。

5.存在性判断与车道输出 在锚线上,还会判断 "该车道在此锚上是否存在交点" 的二分类(existence)任务。也就是说,如果车道在某锚位置根本没能与该锚线交汇,模型会输出 "不存在"。这样可以避免在图像下方、远处、被遮挡或者车道线消失处,错误地输出虚假的位置。 然后模型把所有锚上的坐标(那些存在的)串联起来,插值构成完整的车道线。

6.训练损失设计 模型训练期间包括三大类损失: a.分类损失(交叉熵)用于训练每个锚线上类别预测的准确性。 a.期望损失(如平滑 L1)用于期望输出坐标与真实坐标之间的差距,确保"概率分布 → 坐标"的输出合理。 b.存在性分类损失用于判断交点是否存在。 综合这些损失,巧妙的集合,网络既学会在哪些锚上该有交点、该落在锚线上哪一格,又学会输出合理的连续坐标。

7.速度与鲁棒性优势 速度方面:由于只在锚线上做稀疏分类,而不是对全图每个像素做分割,所以计算量大幅降低。项目作者报告轻量版可达 300+ FPS,但是我在本地的4060测量,只能跑到60左右的fps。所以这一块有待考证。 鲁棒性方面:采用全图特征 + large-receptive-field(感受野大)结构,使其在遮挡、光照差、车道断裂等困难场景中表现更稳,因为模型可以"看全局"而不仅局部。 总结来说,把车道线从"像素掩码问题"简化成"在若干预定义锚线上选点的问题",然后使用"分类+期望"的方式来得到连续坐标。这样既把复杂度压下来、又提升了对困难场景的适应能力。 熟悉图像分类/物体检测领域的同学,这种思路其实类似"先定义anchors+分类/regression"的方法,但这里把"车道线"这种结构化几何对象做了特别设计。
下图是我绘制的一个简单的思路说明:

3.效果展示

下图的虚影区域为车道线检测区域,基于深度学习的车道线检测,相较于传统算法,泛化性和鲁棒性大大增强,准确率也很稳定。

四.追尾预警算法及代码解读

1.算法实现

由于要实现碰撞预警的功能,首先需要处理视频帧,最好是每帧处理,考虑到设备性能,也可以隔帧处理,本系统设置了隔帧处理的滑动条,可以自行调节。

python 复制代码
    # 隔帧检测车辆、检测车道线,减少计算负担
    gap_detect = (self.frame_count % self.detection_interval == 0)

main.py里面的process_frame是实现碰撞预警功能的主要函数,首先我们从函数入口传入视频帧,然后再判断车道线检测开关是否存在,绘制车道检测的前景;第二步我们需要把原生的视频帧,传入目标检测函数返回结果,拿到目标车辆的信息,存入字典或者列表里面; 第三步我们准备好ai科技感可视化数据,由于这里是我想实现好看的可视化数据,所以不关键这一步;第四步我们需要提前标定好需要处理的视频参数,然后在这里,就会通过前三步拿到的数据进行计算,得到结果后,再去判断安全预警的等级,需要结合人脸反应的时间、 刹车的时间,需要通过距离计算出来刹车时间,也可以预设一个值,我这里目前还没实现上去,所以暂时预设值为3s,最后我们将得到一个完整数据的detection_data字典,包含box、label、score、distance、class_id;第五步可以根据不同渲染方式的开关, 进行结果渲染,渲染的函数入口参数就是第四步得到的最终结果,然后我们发送预警信号(可选降低频率)到QT前端去,这样子就能完美的显示碰撞预警的实时状态了。关于里面很多方法的详细实现,大家可以参考main.py,都是一些通俗易懂的代码,以下代码是最主要的部分, 我已经给大家讲解了。

python 复制代码
    def process_frame(self, frame):
        """
        a.处理单帧图像
        b.集成科技感可视化
        c.线程主要耗时部分

        Args:
            frame (np.ndarray): 输入帧

        Returns:
            np.ndarray: 处理后的帧
        """
        # 复制原始帧
        result_frame = frame.copy()
        # 性能优化:
        # 隔帧检测车辆、检测车道线,减少计算负担
        gap_detect = (self.frame_count % self.detection_interval == 0)

        # ✅1.车道线检测
        a = time.time()
        if gap_detect and self.lane_detection_enabled and self.lane_detector is not None:
            self.is_exist_lane = False # 更新车道线检测状况,每次设定刚进来都没检测到车道线
            coords = self.lane_detector.get_lane_coordinates(result_frame)
            # 绘制车道线
            result_frame, left_lane, right_lane = postprocess_coords_with_draw(result_frame, coords)
            self.last_detect_lane_frame = result_frame

            # 构造封闭区域多边形(左车道 + 右车道反转)
            if left_lane and right_lane:
                polygon_points = left_lane + right_lane[::-1]
                # lane_polygon = Polygon(polygon_points)
                # 如果首尾不同,补上闭合点
                if polygon_points[0] != polygon_points[-1]:
                    polygon_points.append(polygon_points[0])

                # 只有数量足够才创建 Polygon
                if len(polygon_points) >= 4:
                    lane_polygon = Polygon(polygon_points)
                else:
                    lane_polygon = None  # 或者跳过

                self.last_lane_polygon = lane_polygon
            else:
                lane_polygon = None
        else:
            # 使用缓存的检测结果或原始帧(当车道线检测关闭时)
            if self.lane_detection_enabled and hasattr(self, 'last_detect_lane_frame') and self.last_detect_lane_frame is not None:
                result_frame = self.last_detect_lane_frame
                lane_polygon = self.last_lane_polygon
            else:
                result_frame = frame.copy()
                lane_polygon = None

        b = time.time()
        # print(f'检测车道耗时:{(b - a):.2f}s')

        # ✅2.目标检测
        c = time.time()
        if gap_detect:
            # 进行车辆检测
            vehicles, contours = self.vehicle_detector.detect_vehicles(frame)
            # 缓存检测结果
            self.last_vehicles = vehicles
            # 缓存掩码结果
            self.last_contours = contours
        else:
            # 使用缓存的检测结果
            vehicles = self.last_vehicles
            contours = self.last_contours
        # 更新车辆数量
        self.vehicle_count = len(vehicles)

        d = time.time()
        # print(f'检测车辆耗时:{(d - c):.2f}s')

        # ✅3.准备科技感可视化数据
        e = time.time()
        detections = []  # 科技感需要的检测数据列表
        warning_level = "safe"  # 预警等级:safe, warning, danger
        warning_message = "" # 提示信息

        all_distances = []  # 所有车辆距离
        danger_distances = []  # 危险距离列表
        warning_distances = []  # 预警距离列表
        distances_is_included_lane = {} # 车辆距离对应是否位于检测车道线上

        # ✅3.自动标定摄像头参数--暂未开放
        # self.distance_calculator.detect_lane_line(frame)
        # ✅3. 准备需要的数据参数
        # 判断检测结果是否为缓存的检测结果
        # if vehicles == self.last_vehicles:
        #     pass
        if self.frame_count % self.detection_interval == 0:
            for vehicle in vehicles:
                x1, y1, x2, y2, confidence, class_id = vehicle
                # 计算检测目标底部中心点
                x_center, y_bottom = self.vehicle_detector.get_vehicle_bottom_center(vehicle)
                point_center = (x_center, y_bottom)

                # a.先计算两点之间的距离
                _, y_distance_ab = self.distance_calculator.cal_distance_a2b((x1, y2), (x2, y2))

                # 一般来说,车宽大约在 1.6 米到 1.85 米 之间
                # 1.直接通过y_distance_ab,来排除误差较大的数据
                # if not 1.2 - 0.2 < y_distance_ab < 2 + 0.2:
                #     continue

                # 2.通过矫正标定参数,精准测量距离
                car_width = self.distance_calculator.calib_correction(y_distance_ab, (x1, y2), (x2, y2))
                if 1.6 <= car_width <= 1.85:
                    # print(f"矫正成功,车宽为{car_width:.2f}米")
                    pass
                else:
                    # print(f"矫正失败,车宽为{car_width:.2f}米")
                    pass
                # print(f"车宽为:{round(car_width, 2)}米") # 车宽为 car_width

                # b.再计算车辆到相机的距离
                x_distance, _ = self.distance_calculator.cal_distance_a2cam(point_center)
                # print(f"车辆到相机点的距离为:{round(x_distance, 2)}米") # 距离为x_distance

                # c.记录所有距离
                all_distances.append(x_distance)

                # d.获取车辆类别名称
                vehicle_class_name = self.vehicle_detector.vehicle_classes.get(class_id, "vehicle")

                # e.判断预警等级
                # 增加判定条件,只针对于在当前车道线的车辆才开启碰撞预警
                # 也就是判定(x_center, y_center)是否在 left_lane 和 right_lane 相交的区域里面

                if x_distance < self.config.SAFE_DISTANCE:
                    danger_distances.append(x_distance)
                    if lane_polygon: # 存在车道区域
                        self.is_exist_lane = True
                        # 判断是否在车道区域内
                        distances_is_included_lane[x_distance] = lane_polygon.contains(Point(point_center))

                elif self.config.SAFE_DISTANCE < x_distance < self.config.SAFE_DISTANCE * 1.5:
                    warning_distances.append(x_distance)
                    if lane_polygon: # 存在车道区域
                        self.is_exist_lane = True
                        # 判断是否在车道区域内
                        distances_is_included_lane[x_distance] = lane_polygon.contains(Point(point_center))

                # 构建检测数据用于科技感可视化
                detection_data = {
                    "box": (x1, y1, x2, y2),
                    "label": vehicle_class_name,
                    "score": confidence,
                    "distance": x_distance,
                    "class_id": class_id
                }
                detections.append(detection_data)

            # 确定整体预警等级和消息
            if danger_distances and distances_is_included_lane.get(min(danger_distances), True):
                warning_level = "danger"
                warning_message = f"危险!前方车辆距离:{min(danger_distances):.1f}米"

            elif warning_distances and distances_is_included_lane.get(min(warning_distances), True):
                warning_level = "warning"
                warning_message = f"注意!前方车辆距离:{min(warning_distances):.1f}米"

            else:
                warning_level = "safe"
                warning_message = "系统正常运行"

            self.last_visual_datas = [detections, warning_level, warning_message, distances_is_included_lane] # 缓存可视化数据结果

        else:

            detections, warning_level, warning_message, distances_is_included_lane = self.last_visual_datas

        f = time.time()
        # print(f'数据准备耗时:{(f - e):.2f}s')


        # 准备好的数据,以下内容需要:
        # 1.detections
        # 2.warning_level
        # 3.warning_message

        # ✅4. 根据开关选择渲染方式
        g = time.time()
        if not self.tech_visual_enabled:
            result_frame = self.opencv_visual(img=result_frame,
                                              detections=detections,
                                              distances_in_included_lane=distances_is_included_lane,
                                              contours=contours
                                              )
            h = time.time()
            # print(f"传统opencv绘制耗时:{(h - g):.2f}s")

        else:
            # 使用科技感可视化器渲染结果
            i = time.time()
            result_frame = self.tech_visualizer.visualize_detections(
                img=result_frame,
                detections=detections,
                frame_id=self.frame_count,
                safe_distance=self.config.SAFE_DISTANCE
            )
            j = time.time()
            # print(f"使用科技感可视化渲染耗时:{(j - i):.2f}s")

        # 发送预警信号(可选:降低频率)
        frequency_warning = 1
        if self.frame_count % frequency_warning == 0:  # 每2帧发送一次预警信号
            self.warning_signal.emit(warning_level, warning_message)

        return result_frame

2.效果展示

整个界面实现了文件、摄像头、主题设定、车道线检测开关等按钮;还实现了参数调节、性能优化的滑动块调节;预警状态、行车状态、系统日志功能都实现了实时的刷新显示。如图所示,前方车辆距离4.4米,同时位于同一行车车道, 所以触发了追尾危险预警,其余车辆由于处于不同车道,就算距离过近,也不会触发追尾预警,所以都是安全的绿色框,只有正前方车辆为危险的红色框。

五.项目总结及源码获取

1.项目总结

本项目基于深度学习的目标检测、车道线检测和复杂的三角函数计算技术,结合了图像处理的前后处理算法,实现了车辆检测、车道线检测、实时测距、实时测速(可实现)和追尾预警等功能,识别稳、准确率高、实时性好, 在大作业、毕业设计、自动驾驶、环境感知等领域,里面很多技术实现具有一定的参考意义和商业实用价值

2.源码获取

yolov5、yolov7、yolov8、yolov9、yolov10、yolov11、yolov12版本均可使用,可以免费提供整套代码+训练好的模型权重,还有测试数据、详细说明文档和论文,代码全都有注释,如果有技术疑问,可以进群交流。

资源源码:

☆☆☆☆☆

为了更好的跟大家沟通,互帮互助,我建了一个讨论群。如您有兴趣,请搜V号 Springli404ve ,共同交流。谢谢!

相关推荐
老蒋新思维1 小时前
创客匠人峰会复盘:AI 赋能 IP 创新增长,知识变现的 4 大实战路径与跨行业案例
大数据·网络·人工智能·tcp/ip·创始人ip·创客匠人·知识变现
人工小情绪1 小时前
PyTorch 转 ONNX 实用教程
人工智能·pytorch·python
ManageEngineITSM1 小时前
IT 资产扫描工具与企业服务台的数字化底层价值
大数据·运维·人工智能·itsm·工单系统
andeyeluguo1 小时前
BM25的简单计算实例
深度学习
skywalk81631 小时前
智能营养食谱平台 - 项目创意策划书
人工智能
酷柚易汛智推官1 小时前
从“废歌”到热单:Mureka新模型改写AIGC音乐的产业规则
人工智能·aigc·酷柚易汛
海底的星星fly1 小时前
【Prompt学习技能树地图】LangChain原理及应用操作指南
人工智能·语言模型·langchain·prompt
free-elcmacom1 小时前
机器学习入门<2>决策树算法
人工智能·python·机器学习
lxmyzzs1 小时前
vLLM、SGLang 与 TensorRT-LLM 综合对比分析报告
人工智能·自然语言处理