大家好 我是南木
自动驾驶感知的核心是“看懂路、认对物”,车道线检测决定车辆是否“不跑偏”,障碍物识别决定车辆是否“不撞车”。YOLOv8凭借其实时性(嵌入式端可达30+FPS)、高精度(障碍物AP>90%)、易部署(支持TensorRT/OpenVINO加速) 的优势,成为中小团队落地自动驾驶感知的首选框架。

本文将从“任务拆解→数据处理→模型适配→实车调试→避坑指南”五个维度,手把手教你用YOLOv8实现自动驾驶感知的两大核心模块,尤其聚焦“实车场景”的特殊性,整理出10个高频坑点及解决方案,附带代码片段、数据标注规范、车载部署步骤,让你少走90%的弯路。

同时需要学习规划、就业指导、技术答疑和系统课程学习的同学 欢迎扫码交流
点此展开:人工智能系统课程大纲
在这里插入图片描述

一、自动驾驶感知核心需求:车道线与障碍物识别的“实战标准”

在动手前,必须先明确自动驾驶场景对感知的“刚性要求”——这决定了后续模型设计和调试的方向,避免陷入“实验室精度高,实车不能用”的误区。

1.1 车道线检测:不止“看到”,更要“准确跟踪”

车道线是自动驾驶的“行驶轨道”,检测需求需满足以下标准:

评价维度 实战要求 失效后果
检测范围 至少覆盖前方30-100米(单目相机) 远距离车道线丢失,导致提前变道决策失误
鲁棒性 抗光照(逆光/隧道出入口)、抗遮挡(积水/落叶)、抗弯曲(急弯) 车道线误判,车辆偏离车道
输出精度 车道线中心点定位误差<5cm(相对车身) 车道保持时车辆左右偏移,存在安全风险
实时性 推理延迟<30ms(100km/h车速下,30ms移动0.83米) 检测滞后,导致紧急避让不及时

1.2 障碍物识别:不止“分类”,更要“精准测距”

障碍物是自动驾驶的“安全边界”,识别需求需满足以下标准:

评价维度 实战要求 失效后果
类别覆盖 至少包含:小汽车、货车、行人、骑行者、锥桶、护栏 漏检特殊障碍物(如锥桶),导致撞障
小目标检测 能识别50米外的行人(像素尺寸<30×30) 远距离障碍物漏检,紧急制动距离不足
测距精度 车辆类障碍物距离误差<5%,行人<10% 距离误判,导致跟车过近或紧急制动过早
动态跟踪 障碍物ID跟踪稳定性>90%(帧率30FPS) 同一障碍物频繁切换ID,决策模块误判为多目标

1.3 YOLOv8的适配优势:为什么选它做自动驾驶感知?

对比传统方法(如车道线用霍夫变换、障碍物用Faster R-CNN),YOLOv8的优势直接命中自动驾驶需求:

  1. 多任务支持:YOLOv8-seg可同时做“障碍物检测+车道线分割”,避免多模型部署的延迟叠加;
  2. 小目标优化:C2f模块增强低层特征提取,50米外小行人AP比YOLOv5提升8%;
  3. 嵌入式友好:YOLOv8-nano版本参数量仅1.2M,Jetson Orin上推理速度达45FPS;
  4. 动态适应:Anchor-Free设计无需针对不同障碍物尺寸调锚点,泛化性更强。

二、YOLOv8车道线检测:从数据到部署的“定制化方案”

车道线属于“细长、连续、结构化”目标,与YOLOv8默认检测的“块状目标”(如车、人)差异大,需针对性优化数据标注、模型结构、训练策略。

2.1 第一步:实车车道线数据处理(避坑关键!)

实车数据是车道线检测的“基石”,但90%的团队第一步就会踩“数据坑”——比如标注不规范、场景覆盖不全。

2.1.1 数据采集规范(车载设备选型+采集场景)
  • 车载相机选型:优先选1/1.7英寸以上CMOS、焦距8mm(单目)的相机,如Basler daA3840-45uc(分辨率3840×2160,帧率45FPS),确保远距离车道线清晰度;
  • 安装位置:相机中心与车身中轴线对齐,高度1.5-2米(SUV可略高),俯仰角15-20°(确保视野覆盖30-100米);
  • 采集场景清单(至少覆盖以下场景,避免泛化差):
    场景类型 具体场景 采集时长 目的
    光照场景 正午逆光、傍晚黄昏、隧道出入口、夜间路灯 各2小时 提升光照鲁棒性
    道路类型 城市快速路(多车道)、乡村公路(窄车道)、小区道路(无标线) 各3小时 适配不同车道宽度/类型
    干扰场景 雨天积水(反光)、落叶覆盖、施工区域(临时标线) 各1小时 抗干扰能力
    弯道场景 缓弯(曲率<5°)、急弯(曲率10-15°)、连续弯道 各2小时 解决弯道车道线检测偏移
2.1.2 数据标注:从“框标注”到“线标注”的适配

YOLOv8默认是“目标框标注”,但车道线是细长目标,用框标注会丢失连续特征——推荐两种标注方案:

方案1:基于YOLOv8-detect的“分段框标注”(适合快速落地)

  • 标注工具:LabelImg(免费,支持YOLO格式);
  • 标注规则:将连续车道线按10-15米分段,每段标注一个“细长框”(宽高比≈10:1),类别设为“lane_left”“lane_right”“lane_middle”(区分左/右/中间车道);
  • 避坑点:避免分段过密(<5米)导致模型学习到“碎片化特征”,或过疏(>20米)导致连续跟踪丢失。

方案2:基于YOLOv8-seg的“像素级标注”(适合高精度需求)

  • 标注工具:Labelme(免费,支持多边形标注);
  • 标注规则:用多边形勾勒车道线像素区域,导出为JSON格式后转YOLO-seg的掩码格式(每个车道线类别对应一个掩码文件);
  • 优势:能精准识别车道线边缘,即使有部分遮挡(如落叶)也能完整检测。
2.1.3 数据增强:针对车道线的“定向增强”

普通数据增强(如Mosaic)可能导致车道线断裂,需定制增强策略:

# YOLOv8车道线检测专用数据增强配置(修改ultralytics/cfg/datasets/coco.yaml)
train: ./lane_train  # 训练集路径
val: ./lane_val      # 验证集路径
nc: 3                # 类别数:左/右/中间车道
names: ['lane_left', 'lane_right', 'lane_middle']

# 增强参数调整(关键!)
augment:
  mosaic: 0.5        # 降低Mosaic概率(避免车道线断裂),默认1.0
  mixup: 0.2         # 适度MixUp,增强抗遮挡能力
  degrees: 1.0       # 旋转角度≤1°(避免车道线角度失真),默认10°
  perspective: 0.001 # 透视变换强度降低(避免车道线形状畸变),默认0.0015
  brightness: 0.4    # 亮度调整范围±40%(应对光照变化)
  contrast: 0.3      # 对比度调整±30%
  saturation: 0.3    # 饱和度调整±30%
  hue: 0.1           # 色调调整±10%(应对不同天气色温)

2.2 第二步:YOLOv8模型适配(增强细粒度特征)

车道线的像素占比低(通常<5%),YOLOv8默认模型对细粒度特征提取不足,需修改两处核心结构:

2.2.1 Backbone优化:增强低层特征传递

修改C2f模块的“分支数”,保留更多细粒度特征(适合车道线这类低像素目标):

# 修改ultralytics/nn/modules.py中的C2f类(增加并行分支)
class C2f(nn.Module):
    def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):
        super().__init__()
        self.c = int(c2 * e)  # 通道数缩放
        self.cv1 = Conv(c1, 2 * self.c, 1, 1)
        self.cv2 = Conv(2 * self.c, c2, 1)
        # 增加并行分支数(从n→n+2),增强细粒度特征提取
        self.m = nn.ModuleList(Bottleneck(self.c, self.c, shortcut, g, e=1.0) for _ in range(n+2))
    
    def forward(self, x):
        y = list(self.cv1(x).chunk(2, 1))
        y.extend(m(y[-1]) for m in self.m)  # 多分支特征融合
        return self.cv2(torch.cat(y, 1))
2.2.2 Head优化:针对车道线调整输出分辨率

YOLOv8默认输出3个尺度(80×80、40×40、20×20),车道线需要更高分辨率的输出,增加1个160×160的输出层:

# 修改ultralytics/cfg/models/v8/yolov8.yaml的Head部分
head:
  - [-1, 1, Conv, [256, 1, 1]]
  - [-1, 1, nn.Upsample, [None, 2, 'nearest']]
  - [[-1, 6], 1, Concat, [1]]  # 与Backbone第6层(高分辨率特征)融合
  - [-1, 3, C2f, [256, False]]  # 新增C2f模块
  - [-1, 1, Conv, [256, 1, 1]]
  - [-1, 1, nn.Upsample, [None, 2, 'nearest']]
  - [[-1, 4], 1, Concat, [1]]
  - [-1, 3, C2f, [256, False]]  # 原80×80输出层
  # 新增160×160输出层(针对车道线)
  - [-1, 1, Conv, [256, 1, 1]]
  - [-1, 1, nn.Upsample, [None, 2, 'nearest']]
  - [[-1, 2], 1, Concat, [1]]
  - [-1, 3, C2f, [256, False]]
  - [-1, 1, Detect, [nc]]  # 检测头,包含160×80×40×20四个尺度

2.3 第三步:训练与验证(重点看“跟踪连续性”)

2.3.1 训练参数配置(兼顾精度与实时性)
# 车道线检测训练命令(YOLOv8-seg,高精度场景)
yolo train model=yolov8s-seg.yaml data=lane_data.yaml epochs=100 batch=16 imgsz=640 \
  optimizer=AdamW lr0=0.001 lrf=0.01  # 用AdamW优化器,避免SGD震荡
  save=True cache=True  # 保存最佳模型,缓存数据加速训练
  project=lane_detect name=v8_seg_run  # 训练结果保存路径
2.3.2 验证指标:不止看AP,更看“车道线跟踪IOU”

传统AP指标无法反映车道线的“连续跟踪能力”,需自定义验证指标:

  • 车道线连续性得分:计算相邻帧车道线检测结果的IOU,平均值>0.8为合格;
  • 定位误差:用实车GPS+IMU数据标注车道线真实位置,模型检测结果与真实位置的距离误差<5cm为合格。

三、YOLOv8障碍物识别:适配自动驾驶“多类别+小目标”需求

自动驾驶中的障碍物识别需解决“类别多、小目标多、动态性强”三大问题,YOLOv8需在数据构建、模型优化、后处理三个层面定制。

3.1 第一步:障碍物数据集构建(避坑:类别定义与标注)

3.1.1 类别定义:避免“类别混淆”

自动驾驶障碍物类别易混淆(如“货车”与“SUV”、“骑行者”与“行人”),需明确类别划分:

类别名称 定义标准 示例
car 乘用车辆(含轿车、SUV、MPV),车长<6米 特斯拉Model 3、丰田汉兰达
truck 商用货车(含皮卡、轻卡、重卡),车长≥6米 福特F-150、东风重卡
pedestrian 行人(直立行走,无交通工具) 路边行人、过马路行人
cyclist 骑行者(含自行车、电动车、摩托车) 共享单车骑行者、电动摩托车
cone 交通锥桶(用于临时道路管控) 红色/橙色锥桶
barrier 道路护栏(固定或临时) 高速公路护栏、施工临时护栏
3.1.2 实车数据采集:重点覆盖“小目标+动态场景”
  • 小目标数据:专门采集50-100米外的行人、骑行者(像素尺寸<30×30),可通过相机变焦或后期裁剪补充;
  • 动态场景数据:采集障碍物变道、横穿马路、突然出现(如从绿化带冲出)的场景,各场景至少1小时;
  • 数据集扩充:融合公开数据集(KITTI、Waymo)与实车数据,公开数据集占比30%,实车数据占比70%(确保贴合实际路况)。
3.1.3 标注规范:避免“框不准”导致测距误差

障碍物标注的“框精度”直接影响后续测距(如单目相机用框高估算距离),标注规则:

  1. 框位置:障碍物外接矩形,紧贴目标边缘,不包含多余背景(如行人框不包含地面);
  2. 小目标标注:即使像素<20×20,也需标注(可用“放大标注”功能,避免漏标);
  3. 遮挡标注:若障碍物被遮挡>50%,标注“occluded=1”(训练时可针对性增强)。

3.2 第二步:YOLOv8模型优化(小目标+实时性)

3.2.1 小目标优化:增强低层特征权重

修改Neck部分的特征融合逻辑,增加低层特征(高分辨率)的权重:

# 修改ultralytics/nn/modules.py中的Concat类(增加低层特征权重)
class Concat(nn.Module):
    def __init__(self, dimension=1):
        super().__init__()
        self.d = dimension

    def forward(self, x):
        # 假设x[0]是高层特征,x[1]是低层特征(小目标关键),增加x[1]的权重
        if len(x) == 2:
            x[1] = x[1] * 1.5  # 低层特征权重×1.5,增强小目标特征
        return torch.cat(x, self.d)
3.2.2 实时性优化:适配车载嵌入式平台

自动驾驶感知需在车载GPU(如NVIDIA Orin)或FPGA上实时运行,需做模型轻量化:

  1. 模型选型:优先用YOLOv8-s(参数量6.9M)或YOLOv8-n(1.2M),避免用YOLOv8-x(84.3M);
  2. TensorRT量化:将模型导出为INT8量化的TensorRT引擎,推理速度提升2-3倍:
# YOLOv8模型导出为TensorRT INT8量化格式
yolo export model=best.pt format=engine device=0 \
  dynamic=False batch=1 imgsz=640  # 固定batch和尺寸,加速推理
  half=True int8=True  # 启用FP16和INT8量化
  data=obstacle_data.yaml  # 用数据集校准INT8量化精度

3.3 第三步:后处理优化(障碍物跟踪+测距)

3.3.1 障碍物ID跟踪:用ByteTrack结合YOLOv8

YOLOv8仅输出单帧检测结果,需用跟踪算法(如ByteTrack)实现跨帧ID关联:

# YOLOv8+ByteTrack障碍物跟踪(核心代码)
from ultralytics import YOLO
from bytetrack.byte_tracker import BYTETracker

# 加载YOLOv8模型
model = YOLO('obstacle_best.pt')
# 初始化ByteTrack跟踪器
tracker = BYTETracker(track_kwargs={'track_thresh': 0.5, 'match_thresh': 0.8})

# 实车视频流推理(或ROS图像话题)
cap = cv2.VideoCapture(0)  # 0为车载相机设备号
while cap.isOpened():
    ret, frame = cap.read()
    if not ret:
        break
    
    # 1. YOLOv8检测
    results = model(frame, conf=0.3, iou=0.5)
    dets = results[0].boxes.data.cpu().numpy()  # 检测结果:[x1,y1,x2,y2,conf,cls]
    
    # 2. ByteTrack跟踪(关联ID)
    tracks = tracker.update(dets, [frame.shape[0], frame.shape[1]])
    
    # 3. 绘制跟踪结果(含ID和类别)
    for track in tracks:
        x1, y1, x2, y2, track_id, cls = track
        cls_name = model.names[int(cls)]
        cv2.rectangle(frame, (int(x1), int(y1)), (int(x2), int(y2)), (0,255,0), 2)
        cv2.putText(frame, f'{cls_name}_ID:{int(track_id)}', 
                    (int(x1), int(y1)-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,255,0), 2)
    
    cv2.imshow('Obstacle Tracking', frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()
3.3.2 单目相机测距:基于“目标尺寸先验”

自动驾驶中常用单目相机测距(成本低),基于YOLOv8检测框的高度估算距离:

  • 原理:目标实际高度H已知(如行人H=1.7米,小汽车H=1.5米),检测框高度h(像素),相机焦距f已知,距离D= (H×f)/h;
  • 避坑点:需用实车标定相机焦距f(张正友标定法),避免焦距误差导致测距不准。

四、实车数据调试:10个高频坑点+解决方案(核心干货!)

这部分是本文的“精华”——基于我3次实车调试失败、最终落地的经验,整理出10个最易踩的坑,每个坑都附“场景+问题表现+原因+解决方案”。

坑1:实车场景泛化差(实验室AP95%,实车AP60%)

  • 场景:模型在实验室测试集(公开数据集)表现好,但装到实车上,面对真实逆光、积水场景,检测精度骤降;
  • 问题表现:车道线消失、障碍物误检(如把路牌误判为车辆);
  • 原因:训练数据“实验室化”,未覆盖实车特殊场景(如本地道路的路牌样式、积水反光);
  • 解决方案
    1. 补充“实车场景闭环”:用实车采集10小时本地道路数据,标注后加入训练集(占比提升至50%);
    2. 做“域适应训练”:用Domain-Adversarial Neural Network(DANN),将公开数据集(源域)与实车数据(目标域)对齐;
    3. 实时数据增强:在推理时加入轻度增强(如随机调整亮度±10%),应对突发光照变化。

坑2:车道线急弯检测偏移(弯道处车辆跑偏)

  • 场景:直道上车道线检测准确,但进入急弯(曲率>10°),模型检测的车道线向弯道外侧偏移;
  • 问题表现:车辆按检测结果行驶,实际偏离车道内侧;
  • 原因:训练数据中急弯样本少,YOLOv8默认的矩形框无法拟合弯道车道线的曲线特征;
  • 解决方案
    1. 补充急弯数据:采集1000+张急弯图像,标注时用“多段框”(每5米一段);
    2. 模型输出曲线参数:将YOLOv8的检测输出从“矩形框”改为“二次多项式参数(a,b,c,车道线方程y=ax²+bx+c)”;
    3. 后处理拟合:用RANSAC算法对检测到的车道线段做曲线拟合,平滑弯道轨迹。

坑3:小目标障碍物漏检(50米外行人看不见)

  • 场景:近距离(<30米)障碍物检测准确,但远距离(>50米)行人、骑行者漏检;
  • 问题表现:实车高速行驶时,远距离行人未被检测到,导致紧急制动不及时;
  • 原因:小目标像素占比低(<30×30),YOLOv8低层特征提取不足,且训练数据中小目标样本少;
  • 解决方案
    1. 小目标数据扩充:用“超分辨率技术”(如ESRGAN)将小目标图像放大2倍,再标注;
    2. 模型修改:在YOLOv8的Backbone中加入“小目标注意力模块”(如CBAM),增强小目标特征权重;
    3. 检测阈值调整:远距离小目标置信度低,将置信度阈值从0.3降至0.2,同时用DIoU-NMS减少误检。

坑4:实车推理延迟超标(检测延迟>50ms)

  • 场景:模型在PC端推理延迟20ms,但部署到车载嵌入式平台(如Jetson Xavier),延迟飙升至60ms;
  • 问题表现:检测结果滞后,车辆行驶100km/h时,60ms内移动1.67米,导致决策失误;
  • 原因:车载平台算力有限(Xavier算力30TOPS),未做模型优化;
  • 解决方案
    1. 模型轻量化:从YOLOv8-s换成YOLOv8-n,参数量减少83%,延迟降至30ms;
    2. TensorRT加速:导出为TensorRT FP16格式,推理速度提升2倍(需安装TensorRT 8.5+);
    3. 推理流水线:将“图像采集→预处理→推理→后处理”做成流水线(如用ROS节点并行处理),隐藏延迟。

坑5:传感器标定不准(检测结果与真实位置偏差>10cm)

  • 场景:模型检测的车道线位置与实车GPS标注的真实位置偏差大,障碍物测距误差>20%;
  • 问题表现:车辆按检测结果车道保持时,实际左右偏移>10cm;
  • 原因:车载相机内参(焦距、畸变系数)或外参(与车身相对位置)标定错误;
  • 解决方案
    1. 重新标定相机内参:用张正友标定法,打印棋盘格,采集20+张不同角度的标定图像,用OpenCV计算内参;
    2. 标定相机外参:用实车在已知尺寸的场地(如停车场,画1m×1m方格),测量相机与方格的相对位置,计算外参;
    3. 实时畸变矫正:在推理前,用标定好的畸变系数对图像做矫正,避免畸变导致检测偏移。

坑6:雨天积水反光导致车道线误检

  • 场景:雨天行驶时,路面积水反光,模型将反光区域误判为车道线;
  • 问题表现:模型检测到“虚假车道线”,车辆试图按虚假车道线行驶;
  • 原因:训练数据中雨天积水样本少,模型未学习到“反光”与“车道线”的差异特征;
  • 解决方案
    1. 补充雨天数据:采集雨天积水场景的图像,标注时明确区分“车道线”与“反光区域”;
    2. 图像预处理:推理时用“自适应阈值分割”(如Otsu算法)去除反光区域(反光区域亮度高,可阈值过滤);
    3. 多模态融合:若有激光雷达,用激光雷达的车道线点云验证视觉检测结果,剔除虚假车道线。

坑7:障碍物ID频繁切换(同一车辆ID每秒变3次)

  • 场景:跟踪障碍物时,同一辆前车的ID频繁变化(如从ID=1变成ID=5,再变回ID=1);
  • 问题表现:决策模块误判为多辆不同车辆,导致跟车距离不稳定;
  • 原因:ByteTrack跟踪器的“匹配阈值”设置过低,遮挡或快速移动时匹配失败;
  • 解决方案
    1. 调整跟踪参数:将ByteTrack的match_thresh从0.5提升至0.8,track_thresh从0.3提升至0.4;
    2. 加入运动预测:用卡尔曼滤波预测障碍物下一帧位置,辅助ID匹配;
    3. 遮挡处理:当障碍物被遮挡<50%时,保留ID,不触发新ID分配。

坑8:夜间路灯下障碍物误检(路灯光斑误判为行人)

  • 场景:夜间行驶,路灯光斑投射到地面,模型将光斑误判为行人或骑行者;
  • 问题表现:频繁触发虚假紧急制动,影响驾驶体验;
  • 原因:夜间数据标注时未标注“光斑”,模型将光斑的亮度特征误判为障碍物;
  • 解决方案
    1. 补充夜间光斑数据:采集夜间路灯场景图像,标注“光斑”类别(设为负样本),训练时用负样本抑制误检;
    2. 特征工程:在模型中加入“纹理特征”判断(光斑无纹理,行人有纹理),如用LBP(局部二值模式)提取纹理;
    3. 置信度阈值动态调整:夜间场景将障碍物置信度阈值从0.3提升至0.5,减少误检。

坑9:车道线施工区域检测失效(临时标线不识别)

  • 场景:道路施工区域,原有车道线被覆盖,设置临时标线(如黄色临时车道线),模型不识别;
  • 问题表现:车辆无法识别临时车道线,偏离施工区域通道;
  • 原因:训练数据中无临时标线样本,模型仅学习了标准车道线(白色/黄色实线);
  • 解决方案
    1. 补充临时标线数据:采集施工区域临时标线图像,标注“lane_temp”类别,加入训练集;
    2. 类别泛化:将“lane_left”“lane_right”合并为“lane_standard”,新增“lane_temp”类别,训练时增强类别鲁棒性;
    3. 后处理逻辑:若检测不到标准车道线,自动启用临时标线检测结果。

坑10:模型部署后内存泄漏(实车运行2小时后崩溃)

  • 场景:模型在车载平台上运行初期正常,但连续运行2小时后,内存占用从2GB飙升至8GB,最终崩溃;
  • 问题表现:实车感知模块无响应,自动驾驶系统切换为手动模式;
  • 原因:推理代码中存在内存未释放问题(如OpenCV图像未释放、PyTorch张量未回收);
  • 解决方案
    1. 代码排查:用memory_profiler工具检测内存泄漏点,重点检查循环中的图像变量、张量;
    2. 资源释放:在每次推理后,用del删除无用变量,调用torch.cuda.empty_cache()释放GPU内存;
    3. 定时重启:在车载系统中设置“每1小时重启感知模块”(毫秒级重启,不影响驾驶)。

五、实战案例:基于ROS的YOLOv8自动驾驶感知系统

最后,用一个完整的“ROS+YOLOv8”实战案例,展示如何将车道线检测和障碍物识别集成到自动驾驶系统中(基于Ubuntu 20.04+ROS Noetic)。

5.1 系统架构(ROS节点设计)

[车载相机节点] → /camera/image_raw(图像话题)
        ↓
[YOLOv8感知节点] → 订阅/image_raw,发布两个话题:
        ├─ /lane_detect/result(车道线检测结果:包含左/右车道线坐标)
        └─ /obstacle_detect/result(障碍物检测结果:包含ID、类别、距离)
        ↓
[决策规划节点] → 订阅两个检测话题,生成行驶轨迹
        ↓
[控制节点] → 执行轨迹,控制车辆转向、加速、制动

5.2 YOLOv8感知节点核心代码(Python)

#!/usr/bin/env python3
import rospy
import cv2
import torch
import numpy as np
from sensor_msgs.msg import Image
from cv_bridge import CvBridge
from ultralytics import YOLO
from bytetrack.byte_tracker import BYTETracker

# 自定义消息类型(需提前定义)
from autodrive_msgs.msg import LaneResult, ObstacleResult, ObstacleObject

class YOLOv8Perception:
    def __init__(self):
        rospy.init_node('yolov8_perception_node', anonymous=True)
        
        # 1. 初始化参数
        self.bridge = CvBridge()
        self.device = 'cuda' if torch.cuda.is_available() else 'cpu'
        rospy.loginfo(f"Using device: {self.device}")
        
        # 2. 加载模型(车道线分割+障碍物检测)
        self.lane_model = YOLO('lane_seg_best.pt').to(self.device)
        self.obstacle_model = YOLO('obstacle_det_best.pt').to(self.device)
        
        # 3. 初始化障碍物跟踪器
        self.obstacle_tracker = BYTETracker(track_kwargs={'track_thresh': 0.5, 'match_thresh': 0.8})
        
        # 4. ROS订阅与发布
        self.image_sub = rospy.Subscriber('/camera/image_raw', Image, self.image_callback)
        self.lane_pub = rospy.Publisher('/lane_detect/result', LaneResult, queue_size=10)
        self.obstacle_pub = rospy.Publisher('/obstacle_detect/result', ObstacleResult, queue_size=10)
        
        # 5. 相机参数(已标定)
        self.camera_fx = 1200.0  # 焦距
        self.obstacle_height = {'car': 1.5, 'truck': 3.0, 'pedestrian': 1.7, 'cyclist': 1.6}  # 目标实际高度
        
    def calculate_distance(self, cls_name, bbox_height):
        """基于检测框高度计算距离(单目测距)"""
        if cls_name not in self.obstacle_height:
            return -1.0
        H = self.obstacle_height[cls_name]
        f = self.camera_fx
        if bbox_height < 1e-3:
            return -1.0
        return (H * f) / bbox_height
    
    def image_callback(self, msg):
        # 1. 图像转换(ROS Image→OpenCV)
        try:
            cv_image = self.bridge.imgmsg_to_cv2(msg, "bgr8")
        except CvBridgeError as e:
            rospy.logerr(e)
            return
        
        # 2. 车道线检测(YOLOv8-seg)
        lane_results = self.lane_model(cv_image, conf=0.4, iou=0.5)
        lane_msg = LaneResult()
        # 提取车道线坐标(简化:取分割掩码的中心点)
        for result in lane_results:
            masks = result.masks
            if masks is None:
                continue
            for mask, cls in zip(masks.data.cpu().numpy(), result.boxes.cls.cpu().numpy()):
                mask = (mask > 0.5).astype(np.uint8)
                # 计算掩码中心点
                y, x = np.where(mask == 1)
                if len(x) == 0 or len(y) == 0:
                    continue
                cx, cy = np.mean(x), np.mean(y)
                if cls == 0:  # lane_left
                    lane_msg.left_lane.append((cx, cy))
                elif cls == 1:  # lane_right
                    lane_msg.right_lane.append((cx, cy))
                elif cls == 2:  # lane_middle
                    lane_msg.middle_lane.append((cx, cy))
        
        # 3. 障碍物检测与跟踪(YOLOv8-detect + ByteTrack)
        obstacle_results = self.obstacle_model(cv_image, conf=0.3, iou=0.5)
        dets = obstacle_results[0].boxes.data.cpu().numpy()  # [x1,y1,x2,y2,conf,cls]
        # 跟踪障碍物
        tracks = self.obstacle_tracker.update(dets, [cv_image.shape[0], cv_image.shape[1]])
        # 构造障碍物消息
        obstacle_msg = ObstacleResult()
        for track in tracks:
            x1, y1, x2, y2, track_id, cls = track
            cls_name = self.obstacle_model.names[int(cls)]
            bbox_height = y2 - y1
            distance = self.calculate_distance(cls_name, bbox_height)
            # 构造单个障碍物消息
            obj = ObstacleObject()
            obj.id = int(track_id)
            obj.cls = cls_name
            obj.x1, obj.y1, obj.x2, obj.y2 = int(x1), int(y1), int(x2), int(y2)
            obj.confidence = float(track[4])
            obj.distance = float(distance)
            obstacle_msg.obstacles.append(obj)
        
        # 4. 发布消息
        self.lane_pub.publish(lane_msg)
        self.obstacle_pub.publish(obstacle_msg)
        
        # 5. 可视化(可选,实车调试时启用)
        cv2.imshow('YOLOv8 Perception', cv_image)
        if cv2.waitKey(1) & 0xFF == ord('q'):
            rospy.signal_shutdown("User quit")
    
if __name__ == '__main__':
    try:
        YOLOv8Perception()
        rospy.spin()
    except rospy.ROSInterruptException:
        cv2.destroyAllWindows()

5.3 实车调试步骤

  1. 硬件连接:将车载相机(如Basler)通过USB3.0连接到车载电脑(如Jetson Orin),确保相机正常输出图像;
  2. ROS环境配置:设置ROS_Master为车载电脑IP,启动相机节点(如roslaunch basler_camera basler.launch);
  3. 模型部署:将训练好的YOLOv8模型(.pt或.trt格式)放到车载电脑,修改代码中的模型路径;
  4. 启动感知节点rosrun autodrive yolov8_perception.py,查看话题是否正常发布(rostopic echo /lane_detect/result);
  5. 闭环测试:在安全场地(如封闭停车场)启动自动驾驶系统,观察车辆是否按车道线行驶,是否能避让障碍物;
  6. 迭代优化:记录实车运行中的问题(如某场景漏检),补充数据后重新训练模型,迭代调试。

六、总结与工程化建议

用YOLOv8落地自动驾驶感知,核心不是“调参提升实验室精度”,而是“解决实车场景的痛点”——车道线的连续性、障碍物的小目标检测、实时性与稳定性,这些才是决定项目成败的关键。

最后给3条工程化建议:

  1. 数据闭环:建立“实车采集→标注→训练→部署→反馈”的闭环,每月补充10小时实车数据,持续优化模型泛化性;
  2. 冗余设计:关键模块(如车道线检测)采用“视觉+激光雷达”双重验证,避免单一传感器失效导致事故;
  3. 监控告警:在车载系统中加入“感知健康度监控”(如检测精度低于80%时告警),及时切换手动模式,确保安全。

如果你在实车调试中遇到其他问题,欢迎在评论区留言——我会定期分享自动驾驶感知的实战技巧,帮助更多团队少踩坑、快速落地。

在这里插入图片描述

Logo

立足具身智能前沿赛道,致力于搭建全球化、开源化、全栈式技术交流与实践共创平台。

更多推荐