在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

演示视频:

https://www.bilibili.com/video/BV1xZ8u6JEzk/

在这里插入图片描述

一、写在前面:为什么又做了一个车辆检测系统

老实说,车辆目标检测这个方向从来不缺轮子。从早期的 R-CNN 系列,到后来的 SSD、RetinaNet,再到如今几乎一统天下的 YOLO 家族,公开数据集和开源项目一抓一大把。那为什么斌擎科技还要专门花时间,把它重新做一遍,而且还要把五个版本的 YOLO 一起塞进同一个系统里跑横向对比?

原因其实很简单:市面上大多数车辆检测的 Demo,要么只跑一张图、要么只支持一个模型、要么干脆就是个 Jupyter Notebook,离"能交付、能用、能演示"还差得远。我们想要的是一套完整闭环的东西——前端有界面、后端有业务逻辑、AI 推理有独立服务、检测记录能落库、模型能在线训练、还能让用户在同一份数据上直观地看到"换个模型到底差在哪"。

更进一步,YOLO 这个系列更新太快了。从 v8 到现在的 YOLO26,几乎每隔几个月就冒出一个新版本,每个版本都号称"更快更准"。但口号归口号,落到具体业务场景(比如我们要做的七类车辆细分)里,到底哪个更合适?光看论文里的 COCO 指标是看不出来的,必须自己拿同一份数据、同一套训练流程、同一套评测口径去跑一遍,才靠谱。这也是这套系统存在的另一个核心价值:它本身就是一个 YOLO 选型的实验台。

这套系统最终覆盖了七种车辆类型的细粒度识别,集成了 YOLOv8、YOLOv10、YOLOv11、YOLOv12、YOLO26 五个版本的检测模型,支持图片、视频、摄像头三种检测入口,接入了 DeepSeek 和 Qwen 两个大模型做检测结果分析,并且把模型训练、检测记录、用户管理全部做成了可视化模块。下面我会从头到尾把它讲清楚,尽量少贴代码、多讲设计思路和实际效果。


二、这套系统到底能干什么(功能全景)

先用一段话概括:用户登录之后,可以在网页上选择任意一个 YOLO 模型,对上传的图片、视频或者本地摄像头画面进行车辆类型检测,系统会实时返回标注框、类别、置信度和耗时,并把每一次检测的原始素材、结果图片、AI 分析建议都存进数据库,形成可查询、可回溯的检测记录;同时管理员还能在系统内直接发起模型训练,实时观察 loss 和 mAP 曲线。

把功能拆开来看,一共是这么几大块:

  1. 首页与数据看板:系统总览、检测统计、五个模型的性能对比图表。
  2. 登录与注册:用户体系,区分管理员和普通用户权限。
  3. 图片检测:单张图片上传 → 选模型 → 设置信度阈值 → 出结果。
  4. 视频检测:上传或填写视频地址 → 实时流式检测(边下边播边检测)。
  5. 摄像头检测:直接调起本机摄像头,做实时画面检测,可录制可停止。
  6. 图片检测记录 / 视频检测记录 / 摄像头检测记录:三类记录分库存储,支持列表浏览和详情回看。
  7. 模型训练:在线选择预训练权重、配置超参,一键开训,实时回传训练进度。
  8. 用户管理:管理员视角,对注册用户进行增删改查。
  9. 个人中心:普通用户视角,管理自己的资料和密码。

可以看到,这不是一个"跑通一张图就完事"的玩具,而是一个把推理、存储、训练、管理串起来的完整产品形态。后面每一块我都会展开讲。


三、先认识一下我们的"主角":七种车辆类型

在很多公开数据集里,车辆往往被笼统地归为一个大类"car",顶多再分个"truck"“bus”。但真实业务场景里,这种粒度远远不够。比如一个智慧卡口系统,它关心的不是"这里有没有车",而是"这里过的是小型汽车还是重型货车,是不是油罐车这种需要重点监管的危险品运输车辆"。

所以这套系统把车辆切成了七类,每一类都有明确的语义和监管意义:

序号类别名称说明典型场景
1小型汽车乘用车、轿车、SUV 等城市道路主体车流
2中型车中巴、轻客等班车、接驳车
3大型车大型客车长途客运、公交
4轻型货车蓝牌小货车城配物流
5重型货车黄牌重卡干线物流、限行关注对象
6油罐车槽罐类危化品运输车危化品监管、禁区预警
7特种车辆工程、消防、清障、救护等特殊用途车辆应急通道、专项管理

这七类是模型 predictImg 推理时直接输出的标签集合:

self.labels = ['小型汽车', '中型车', '大型车', '轻型货车', '重型货车', '油罐车', '特种车辆']

之所以说这个分类有业务价值,是因为不同类别对应的监管策略完全不同。油罐车进入特定区域要报警,重型货车在部分路段分时限行,特种车辆则需要让行。如果检测粒度只到"车"这一层,这些上层逻辑根本无从谈起。所以七分类不是凑数,而是贴合实际管理需求的设计。

当然,细分类也带来了一个直接的技术挑战:类间相似度高。比如"中型车"和"大型车"在远景视角下外观差异很小,"轻型货车"和"重型货车"的区别很多时候只在牌照颜色和车厢尺寸上。这对模型的特征提取能力提出了更高要求,也是后面我们要重点看五个 YOLO 版本表现差异的地方。


四、技术栈与整体架构

这套系统不是单体的,而是三服务 + 一数据库的分布式结构。之所以这么拆,是因为 AI 推理(Python 生态)和业务后端(Java 生态)天然是两个世界,硬塞在一起只会互相拖累。我们让它们各司其职、用 HTTP 通信,反而清晰。

4.1 整体架构图(文字版)

┌──────────────┐     HTTP/REST      ┌──────────────────┐     HTTP/REST     ┌─────────────────┐
│   浏览器端    │  ───────────────▶  │  Spring Boot     │  ──────────────▶  │   Flask AI 服务  │
│  Vue3 + TS   │                    │  业务后端 (9999)  │                   │   推理服务 (5000) │
│  Element UI  │  ◀───────────────  │  MyBatis-Plus    │  ◀──────────────  │  Ultralytics YOLO│
└──────────────┘     JSON 结果      │  用户/记录/文件   │    推理结果/MJPEG  │  SocketIO/WebSocket│
                                     └────────┬─────────┘                   └─────────────────┘
                                              │ JDBC
                                              ▼
                                      ┌──────────────────┐
                                      │   MySQL 数据库    │
                                      │  用户表/记录表    │
                                      └──────────────────┘

三个服务的职责划分非常清楚:

  • Vue3 前端:只负责展示和交互,不直接碰 AI。
  • Spring Boot 后端(端口 9999):业务中枢,管用户、管记录、管文件上传,同时作为代理把推理请求转发给 Flask。
  • Flask AI 服务(端口 5000):纯粹的 AI 推理 + 训练服务,加载 YOLO 权重、跑图片/视频/摄像头推理、负责模型训练,并通过 SocketIO 把实时进度推给前端。

4.2 前端技术栈

前端我们用的是 Vue3 + TypeScript + Vite 的组合,配合 Pinia 做状态管理、Vue Router 做路由、i18n 做国际化预留。UI 组件方面走的是 Element Plus 那一套(项目里能看到完整的 components、layout、theme 目录结构)。选 Vue3 主要是看中它的 Composition API 写起来顺手,配合 TypeScript 在中型项目里可维护性好很多,不至于写到后面自己都看不懂。

前端目录里比较关键的几块:

  • views/ 下按业务模块分了 12 个子目录(home、login、imgPredict、videoPredict、cameraPredict、imgRecord、videoRecord、cameraRecord、trainModel、userManage、personal、error),每个模块自成一格,路由清晰。
  • api/ 封装了所有和后端通信的请求函数,统一管理接口。
  • stores/ 用 Pinia 管用户态、模型列表等全局状态。
  • layout/ 负责整体框架(侧边栏、顶栏、内容区)。

视频和摄像头检测这两个模块比较特殊,它们不是用普通的 axios 请求拿结果,而是直接把 Flask 返回的 MJPEG 流塞进 <img> 标签的 src 里,实现"边推理边显示"的实时效果。这个设计后面会专门讲。

4.3 业务后端:Spring Boot + MyBatis-Plus + MySQL

业务后端用的是 Spring Boot,ORM 选了 MyBatis-Plus(比原生 MyBatis 省很多模板代码),数据库是 MySQL。配置很简单,核心就几行:

spring.datasource.url=jdbc:mysql://localhost:3306/20?serverTimezone=Asia/Shanghai
server.port=9999
spring.servlet.multipart.max-file-size=500MB
spring.servlet.multipart.max-request-size=500MB

注意那个 500MB 的文件上传上限——这是为了支持大视频检测特意放开的,默认的 1MB/10MB 在视频场景下根本不够用。

后端的代码结构遵循标准的 controller / entity / mapper / common 分层:

  • controller:7 个控制器,分别对接前端的不同模块。其中 PredictionController 是关键,它负责把前端的检测请求转发给 Flask。
  • entity:5 个实体类,对应数据库里的表,比如 ImgRecords(图片检测记录)、VideoRecords(视频检测记录)、CameraRecords(摄像头检测记录)、User(用户)、Orchard。
  • mapper:MyBatis-Plus 的 Mapper 接口,提供基础 CRUD。
  • common:Result 统一返回封装、CorsConfig 跨域配置、MybatisPlusConfig 分页配置。

这里有个设计点想单独说一下:业务后端不直接做 AI 推理,而是用 RestTemplate 把请求转给 Flask。比如图片检测的流程是:前端 → Spring Boot 的 /flask/predict → Flask 的 /predictImg → 拿到结果 → Spring Boot 把结果写进 imgrecords 表 → 返回给前端。这样做的好处是 AI 推理和业务逻辑彻底解耦,以后想换推理框架(比如从 Ultralytics 换成别的)完全不用动 Spring Boot 这边。

4.4 AI 推理服务:Flask + Ultralytics + SocketIO

Flask 这边是整套系统技术含量最高的部分,它一口气承担了四个职责:

  1. 图片/视频/摄像头推理:加载 weights/ 下的五个 .pt 权重文件,按前端传来的模型名动态选择。
  2. MJPEG 实时流:视频和摄像头检测时,把每一帧的推理结果编码成 JPEG,通过 multipart/x-mixed-replace 流式推给前端。
  3. WebSocket 推送:用 Flask-SocketIO 在检测关键节点(下载完成、开始检测、AI 建议生成中)给前端推消息。
  4. 模型训练:开后台线程跑 Ultralytics 的训练 API,实时把 loss、mAP 回传。

Flask 启动在 5000 端口,路由设计得很 RESTful,比如:

  • POST /predictImg:图片检测
  • GET /predictVideo:视频检测(返回 MJPEG 流)
  • GET /predictCamera:摄像头检测
  • GET /model_comparison:模型对比数据
  • POST /start_training:启动训练
  • GET /file_names:列出可用模型

这套架构看似有点重(三个服务),但实际跑起来分工明确,调试和扩展都很方便。斌擎科技在做的时候,最初也考虑过把 AI 直接塞进 Spring Boot(用 DJL 之类的 Java 推理库),但试了一圈发现 Python 生态在 YOLO 这块的优势实在太大,Ultralytics 的 API 几行代码就能搞定的事,Java 那边要绕一大圈,最终还是回到了 Python 推理 + Java 业务的组合。事实证明这个选择是对的。

4.5 分层架构与请求流转

为了把架构讲透,我们换一个视角,从分层的角度再拆一遍。整套系统可以清晰地分成五层,每一层只对相邻层负责,依赖方向严格自上而下:

┌─────────────────────────────────────────────────────────────┐
│  表现层(Presentation Layer)  ——  Vue3 + Element Plus        │
│  路由 / 状态管理(Pinia) / 组件 / MJPEG流渲染 / SocketIO客户端 │
└───────────────────────────┬─────────────────────────────────┘
                            │  HTTP(REST) + WebSocket + MJPEG
┌───────────────────────────▼─────────────────────────────────┐
│  业务层(Business Layer)  ——  Spring Boot (端口 9999)        │
│  Controller / Service / 权限校验 / 记录管理 / 文件上传 / 代理  │
└───────────┬─────────────────────────────────┬───────────────┘
            │ JDBC                            │ HTTP(RestTemplate)
┌───────────▼───────────┐         ┌───────────▼───────────────┐
│  数据持久层(Data)     │         │  AI 推理层(Inference)    │
│  MySQL + MyBatis-Plus  │         │  Flask (端口 5000)         │
│  用户表 / 三类记录表    │         │  Ultralytics YOLO / OpenCV │
└────────────────────────┘         │  SocketIO / MJPEG / 训练    │
                                   └───────────┬───────────────┘
                                               │ 加载
                                   ┌───────────▼───────────────┐
                                   │  模型权重层(Weights)     │
                                   │  YOLOv8/v10/v11/v12/26.pt  │
                                   └───────────────────────────┘

这种分层带来的最大好处是单一职责:表现层绝不直连数据库,业务层绝不直接碰模型权重,AI 推理层也完全不知道有 MySQL 这回事。任何一层出问题,排查范围都能快速收敛。比如某次检测没出结果,我们先看 Flask 日志有没有报错(推理层)→ 再看 Spring Boot 有没有转发成功(业务层)→ 最后看前端有没有拿到响应(表现层),三层各查各的,互不干扰。

一个典型的图片检测请求,完整流转路径是这样的(共 9 步):

浏览器 ──①上传图片+选模型──▶ Spring Boot(9999)
                                   │
                                   │ ②文件存盘,拿到访问URL
                                   │ ③封装JSON(模型名/conf/图URL/用户名)
                                   ▼
                                Flask(5000) ──④下载图片到本地──▶ 临时文件
                                   │
                                   │ ⑤加载对应 .pt 权重,YOLO推理
                                   │ ⑥画框保存 runs/result.jpg
                                   │ ⑦(可选)调 DeepSeek/Qwen 生成建议
                                   │ ⑧上传结果图,组装返回JSON
                                   ▼
                                Spring Boot(9999)
                                   │
                                   │ ⑨把记录写入 imgrecords 表
                                   │   返回结果给浏览器
                                   ▼
                                浏览器渲染结果图 + 类别 + 置信度 + AI建议

视频和摄像头检测的流转稍有不同——它们不走 Spring Boot 的常规 JSON 往返,而是浏览器直接向 Flask 请求一条 MJPEG 长连接,Spring Boot 只负责"启动检测"和"落库记录"两件事。这样设计是为了避免视频流经过 Spring Boot 中转造成不必要的内存拷贝和延迟,让流数据走最短路径。

4.6 数据库设计

数据库是整套系统"可追溯"能力的根基。我们用了 5 张核心表,分别对应用户和三类检测记录,外加一张业务辅助表。下面是各表的职责和关键字段设计。

表清单一览:

表名中文名用途主要写入方
user用户表存账号、密码、角色、头像注册/用户管理模块
imgrecords图片检测记录每次图片检测的完整快照图片检测模块
videorecords视频检测记录每次视频检测的完整快照视频检测模块
camerarecords摄像头检测记录每次摄像头检测的完整快照摄像头检测模块
orchard业务辅助表预留的业务扩展表后台管理

以图片检测记录表 imgrecords 为例,它的字段设计紧扣"一次检测需要留存什么"这个核心问题。从对应的实体类能看出字段构成:

@TableName("imgrecords")
public class ImgRecords {
    private Integer id;          // 主键,自增
    private String weight;       // 使用的模型权重名(如 YOLOv8.pt)
    private String inputImg;     // 原图访问URL
    private String outImg;       // 结果图访问URL
    private String confidence;   // 各类别置信度JSON串
    private String allTime;      // 本次检测耗时
    private String conf;         // 置信度阈值
    private String label;        // 检测到的类别JSON串
    private String username;     // 检测者用户名(关联user表)
    private String startTime;    // 检测开始时间
    private String ai;           // 选择的AI(DeepSeek/Qwen/无)
    private String suggestion;   // AI生成的分析建议
}

对应的建表 SQL(以图片记录表为例,其余两张记录表结构类似):

CREATE TABLE `imgrecords` (
  `id`          INT          NOT NULL AUTO_INCREMENT COMMENT '主键',
  `weight`      VARCHAR(64)  DEFAULT NULL COMMENT '模型权重名',
  `inputImg`    VARCHAR(512) DEFAULT NULL COMMENT '原图URL',
  `outImg`      VARCHAR(512) DEFAULT NULL COMMENT '结果图URL',
  `confidence`  TEXT         DEFAULT NULL COMMENT '置信度JSON',
  `allTime`     VARCHAR(32)  DEFAULT NULL COMMENT '检测耗时',
  `conf`        VARCHAR(16)  DEFAULT NULL COMMENT '置信度阈值',
  `label`       TEXT         DEFAULT NULL COMMENT '检测类别JSON',
  `username`    VARCHAR(64)  DEFAULT NULL COMMENT '检测者',
  `startTime`   VARCHAR(32)  DEFAULT NULL COMMENT '检测开始时间',
  `ai`          VARCHAR(32)  DEFAULT NULL COMMENT 'AI类型',
  `suggestion`  TEXT         DEFAULT NULL COMMENT 'AI建议',
  PRIMARY KEY (`id`),
  KEY `idx_username` (`username`),
  KEY `idx_starttime` (`startTime`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='图片检测记录';

几个设计要点值得说一下:

  • username 而非 user_id 外键:记录表用用户名关联而不是外键,是为了简化跨服务查询(Flask 侧也能直接用用户名落库,不依赖 user 表的主键)。代价是改用户名时记录不会自动跟随,但本场景用户名不可改,所以没问题。
  • label 和 confidence 用 TEXT 存 JSON:因为一次检测可能检出多辆车、多类别,用 JSON 串比拆多行更简单,读取时反序列化即可。
  • 加 idx_username 和 idx_starttime 索引:列表页最常用的筛选条件就是"按用户查"和"按时间查",加索引能保证数据量大了之后列表不卡。
  • 统一 utf8mb4:因为 AI 建议和类别名都是中文,必须用 utf8mb4 才能完整支持(utf8 在某些 emoji 和生僻字上会出问题)。

视频记录表和摄像头记录表在图片记录表基础上,额外多了视频相关的字段(如视频时长、帧率、摄像头编号、起止时间等),结构思路一致,就不重复贴了。

4.7 接口设计

接口分两套:前端调 Spring Boot 的业务接口,Spring Boot 调 Flask 的推理接口。下表把核心接口梳理出来,方便对照。

前端 → Spring Boot(业务接口,端口 9999):

接口方法说明
/user/loginPOST用户登录
/user/registerPOST用户注册
/user/selectAllGET查询全部用户(管理员)
/flask/predictPOST图片检测(转发至Flask并落库)
/flask/predictCameraGET启动摄像头检测
/flask/stopCameraGET停止摄像头检测
/flask/getCameraListGET获取可用摄像头列表
/flask/file_namesGET获取可用模型列表
/imgRecords/selectByPageGET图片检测记录分页查询
/videoRecords/selectByPageGET视频检测记录分页查询
/cameraRecords/selectByPageGET摄像头检测记录分页查询
/file/uploadPOST文件上传(图片/视频)

Spring Boot → Flask(推理接口,端口 5000):

接口方法返回类型说明
/predictImgPOSTJSON图片检测,返回类别/置信度/结果图URL/AI建议
/predictVideoGETMJPEG流视频检测,流式返回每帧标注结果
/predictCameraGETMJPEG流摄像头检测,流式返回
/stopCameraGETJSON停止摄像头检测
/get_camera_listGETJSON探测可用摄像头
/model_comparisonGETJSON获取五模型对比数据
/model_summaryGETJSON获取模型摘要与最佳模型
/model_historyGETJSON获取指定模型训练历史
/start_trainingPOSTSocketIO流启动模型训练,实时推送日志
/stop_trainingPOSTJSON停止训练
/file_namesGETJSON列出 weights 目录下的模型
/result_videoGET视频流获取检测后的结果视频

接口设计上有几个统一约定:所有业务接口走统一的 Result 封装返回(code + msg + data),前端只用判 code 就知道成功与否;推理接口因为要返回流,不走 Result 封装,直接返回原始流或 JSON。这种"业务接口统一封装、流接口特殊处理"的约定,让前端调用层写起来很规整。

4.8 关键业务时序流程

光看接口列表还不够直观,下面用文字时序图把两个最复杂的流程画出来——视频检测和模型训练,它们最能体现多服务协作。

视频检测时序(异步下载 + 流式推理 + 落库):

浏览器            SpringBoot(9999)        Flask(5000)          MySQL
   │ ①请求视频检测     │                     │                   │
   │─────────────────▶│ ②转发启动指令        │                   │
   │                   │────────────────────▶│                   │
   │                   │  ③开线程下载视频     │                   │
   │ ④订阅MJPEG流(直连Flask)                │                   │
   │───────────────────────────────────────▶│                   │
   │ ⑤推占位帧"下载中" │                     │                   │
   │◀───────────────────────────────────────│                   │
   │                   │   下载完成,逐帧推理  │                   │
   │ ⑥推每帧标注画面   │                     │                   │
   │◀───────────────────────────────────────│                   │
   │  ⑦WebSocket推送"检测完成"               │                   │
   │◀───────────────────────────────────────│                   │
   │ ⑧请求落库(带结果视频URL/类别/耗时)      │                   │
   │─────────────────▶│────────────────────────────────────────▶│
   │ ⑨返回成功        │                     │                   │
   │◀─────────────────│                     │                   │

模型训练时序(后台线程 + SocketIO 实时回传):

浏览器            SpringBoot(9999)        Flask(5000)
   │ ①提交训练配置     │                     │
   │─────────────────▶│ ②转发               │
   │                   │────────────────────▶│
   │                   │  ③开训练线程         │
   │                   │  ④SocketIO连接建立   │
   │◀───────────────────────────────────────│
   │ ⑤每个epoch推送loss/mAP                 │
   │◀───────────────────────────────────────│
   │   ...训练中...                          │
   │ ⑥(可随时)请求中止训练                   │
   │─────────────────▶│────────────────────▶│ ⑦置中止标志位
   │ ⑧训练结束/中止,推送最终状态             │
   │◀───────────────────────────────────────│

这两个时序图揭示了一个共同模式:长耗时任务都用"后台线程 + 实时推送"来处理,绝不让 HTTP 请求一直挂着等。这是高交互性后台系统的通用套路,斌擎科技在别的项目里也反复验证过它的有效性。

4.9 技术选型对比与理由

最后用一张表把关键技术选型的"为什么"说清楚。技术选型从来不是拍脑袋,每个选择背后都有备选方案和取舍。

决策点最终选择备选方案选择理由
推理框架Ultralytics YOLOMMdet / PaddleDet / DJL(Java)YOLO 生态最全、API 最简、五版本统一接口
推理语言/服务Flask + PythonSpringBoot内嵌DJL / FastAPIPython AI 生态碾压,Flask 轻量够用
业务后端Spring BootNode.js/NestJS / Python(Django)Java 企业级成熟、MyBatis-Plus 省代码、团队熟
ORMMyBatis-PlusJPA/Hibernate / 原生MyBatisCRUD 零模板代码、复杂SQL仍可手写
数据库MySQLPostgreSQL / SQLite部署最普遍、性能够用、运维成本低
前端框架Vue3 + TSReact / AngularComposition API 顺、TS 可维护、生态全
前端构建ViteWebpack冷启动快、HMR 体验好、配置简单
实时通信SocketIO原生WebSocket / SSE自带重连和房间机制、Flask 有现成集成
视频流方案MJPEGWebRTC / HLS / WebSocket推帧实现最简单、兼容性最好、无需额外服务
AI 大模型DeepSeek + QwenOpenAI / 通义千问网页国产、中文好、有API、可双选对比

这张表其实也是斌擎科技踩过一些坑后的总结。比如视频流方案,最早试过 WebSocket 逐帧推 JSON 再前端 canvas 绘框,性能和开发量都不理想;后来换成 MJPEG,前端一个 <img> 标签搞定,后端 OpenCV 编码 JPEG 也高效,瞬间清爽。再比如 ORM,早期用纯 MyBatis 写 XML 写到手酸,换 MyBatis-Plus 后基础 CRUD 全自动,只在复杂查询时手写,效率提升明显。

4.10 项目目录结构

把目录结构摆出来,能帮助想复现的同学快速建立全局认知。整套项目分三个子工程,目录树(精简版)如下:

七种车辆类型检测系统/
├── VehicleType_flask/                 # AI 推理服务(Python)
│   ├── main.py                        # Flask 入口,路由与业务编排
│   ├── weights/                       # 五个模型权重
│   │   ├── YOLOv8.pt  YOLOv10.pt  YOLOv11.pt  YOLOv12.pt  YOLO26.pt
│   ├── utils/                         # 工具模块
│   │   ├── predictImg.py              # 图片推理封装
│   │   ├── model_comparison.py        # 模型对比数据读取
│   │   └── chatApi.py                 # DeepSeek/Qwen 大模型调用
│   ├── YOLOv8/ YOLOv10/ YOLOv11/ YOLOv12/ YOLO26/   # 各模型训练输出
│   │   └── results.csv  args.yaml  weights/best.pt  confusion_matrix.png ...
│   └── runs/                          # 推理结果输出目录
│
├── VehicleType_springboot/            # 业务后端(Java)
│   └── src/main/java/com/example/Kcsj/
│       ├── Kcsj.java                  # Spring Boot 启动类
│       ├── common/                    # Result/Cors/MybatisPlus 配置
│       ├── controller/                # 7 个控制器
│       ├── entity/                    # 5 个实体类
│       └── mapper/                    # 5 个 Mapper 接口
│
└── VehicleType_vue/                   # 前端(Vue3 + TS)
    └── src/
        ├── views/                     # 12 个业务页面
        │   ├── home/ login/ imgPredict/ videoPredict/ cameraPredict/
        │   ├── imgRecord/ videoRecord/ cameraRecord/
        │   └── trainModel/ userManage/ personal/ error/
        ├── api/ stores/ components/ layout/ router/ i18n/ theme/
        └── App.vue  main.ts

这个结构有几个特点:三个子工程物理隔离,各自独立构建部署,互不耦合代码;Flask 侧把每个模型的训练输出目录(YOLOv8/ 等)原样保留,里面的 results.csv 直接被首页看板读取,做到了"训练产物即数据源";前端按页面切分 views/,每个页面一个目录,符合 Vue 社区惯例,新人接手能快速定位。

4.11 依赖与环境清单

复现项目时,依赖版本对不上是常见坑。这里把三端的依赖清单列出来(仅列关键依赖,完整清单见各工程的 pom.xml / requirements / package.json)。

Python 推理服务关键依赖(requirements 节选):

ultralytics>=8.2.0          # YOLO 推理与训练核心库
flask>=2.3.0                # Web 框架
flask-cors>=4.0.0           # 跨域支持
flask-socketio>=5.3.0       # WebSocket 实时通信
opencv-python>=4.8.0        # 图像/视频处理
numpy>=1.24.0               # 数值计算
requests>=2.31.0            # HTTP 请求(调大模型API)
torch>=2.0.0                # PyTorch,建议配 CUDA 版本

Spring Boot 业务后端关键依赖(pom.xml 节选):

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.5</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.83</version>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>

Vue 前端关键依赖(package.json 节选):

{
  "dependencies": {
    "vue": "^3.4.0",
    "vue-router": "^4.2.0",
    "pinia": "^2.1.0",
    "element-plus": "^2.4.0",
    "axios": "^1.6.0",
    "echarts": "^5.4.0",
    "socket.io-client": "^4.7.0"
  },
  "devDependencies": {
    "vite": "^5.0.0",
    "typescript": "^5.3.0",
    "@vitejs/plugin-vue": "^5.0.0"
  }
}

几个依赖上的注意点:Python 侧的 torch 一定要装和本机 CUDA 匹配的版本,否则要么用不了 GPU,要么直接报错;前端用 echarts 画模型对比图表、用 socket.io-client 接收训练实时进度,这两个是首页看板和训练模块的关键依赖;Spring Boot 侧用 fastjson 做 Flask 返回 JSON 的解析,用 lombok 省掉实体类的 getter/setter 模板代码。

4.12 性能优化实践

一套检测系统光能跑通不算完,跑得快、跑得稳才是工程化的关键。斌擎科技在实际打磨过程中做了几处性能优化,效果立竿见影,这里分享一下。

优化一:半精度推理(half=True)。 在图片推理时开启了 FP16 半精度,模型权重和激活值都用 16 位浮点表示。在支持 Tensor Core 的 NVIDIA 显卡上,这能带来 30%~50% 的提速,而精度损失几乎可以忽略(mAP 通常只掉千分之几)。代价是部分老旧显卡不支持,需要做兼容判断,但对现代部署环境来说基本无忧。

优化二:模型懒加载与复用。 最早的实现是每次检测都重新 YOLO(weights_path) 加载一次模型,这在连续检测时非常浪费——加载一个 19MB 的 YOLO26 权重要几百毫秒。后来改成按模型名缓存已加载的模型实例,二次检测直接复用,省掉了重复加载开销。这对高频检测场景(比如摄像头连续推理)意义很大。

优化三:视频流异步下载。 前面讲过,视频检测把"下载"和"推理"用线程隔开,下载在后台跑、推理主流程不阻塞,配合占位帧让用户立刻有反馈。这把"用户感知等待时间"从"下载时长 + 推理时长"压缩到了"几乎即时响应"。

优化四:MJPEG 帧率自适应。 视频和摄像头检测时,如果推理速度跟不上视频原始帧率(比如视频 30fps 但 GPU 只能跑 15fps),就按推理能力尽可能推送,而不是死等原始帧率导致卡顿。体验上就是"画面稍微慢放但流畅",比"卡顿跳跃"好得多。

优化五:数据库索引。 三类记录表的 username 和 startTime 字段加了索引,列表分页查询从全表扫描变成索引查找。数据量小时看不出差别,但记录积累到几万条后,无索引的列表会卡到秒级,加索引后仍是毫秒级。

优化六:结果图按需生成。 推理结果图先存本地 runs/,只有当前端请求查看时才通过 URL 访问,而不是每次检测都把大图塞进 JSON 返回。这避免了 JSON 里塞 base64 大图导致的传输膨胀。

这六处优化里,前两项是"模型推理"层面的,中间两项是"实时流"层面的,后两项是"数据存取"层面的,恰好覆盖了系统的三个性能瓶颈点。优化后,单张图片检测端到端响应稳定在 1 秒以内(含网络),摄像头检测能跑到 15~30fps(取决于 GPU 和模型规格),记录列表万条数据毫秒级响应。


五、核心功能模块详解

接下来把每个功能模块掰开讲讲,重点说说设计上的一些取舍和实际效果。我会配上对应的水印截图,方便对照。

5.1 首页与数据看板

首页是用户登录后看到的第一屏,它不是单纯的"欢迎页",而是一个数据看板。除了展示系统名称、轮播图这些常规元素,最核心的是一块模型性能对比区,直接把五个 YOLO 模型的 mAP50、mAP50-95、精确率、召回率用图表画出来。
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

这块数据不是写死的,而是通过 Flask 的 /model_comparison 接口实时读取每个模型训练目录下的 results.csv 算出来的。也就是说,如果你重新训练了某个模型,首页的对比图表会自动更新。这个"活数据"的设计比写死一个表格要高级得多,也让系统看起来更像一个真正的工具而不是静态展示。

5.2 登录与注册

这块比较常规,用户名 + 密码登录,新用户走注册流程。用户信息存在 MySQL 的 user 表里,密码做了加密处理。系统区分了管理员和普通用户两种角色,普通用户只能看到自己的检测记录和个人中心,管理员额外能看到用户管理模块。
在这里插入图片描述
在这里插入图片描述

5.3 图片检测(重头戏之一)

图片检测是整套系统最基础也最常用的功能。整个交互流程是这样的:

在这里插入图片描述

在这里插入图片描述

  1. 用户在检测页上传一张图片(或者从历史记录里选)。
  2. 选择要用的 YOLO 模型(下拉框里有 YOLOv8、YOLOv10、YOLOv11、YOLOv12、YOLO26 五个选项)。
  3. 拖动滑块设置置信度阈值(conf),默认 0.5,越低越容易检出但误检也多。
  4. 可选:勾选是否调用 AI(DeepSeek 或 Qwen)生成分析建议。
  5. 点击开始检测。

后端的处理逻辑值得说一说。Flask 收到请求后,会按前端指定的模型名去 weights/ 目录加载对应的 .pt 文件,调用 Ultralytics 的推理 API。这里有个小细节——推理时开了 half=True(半精度推理),能在几乎不损失精度的情况下显著提速,尤其在支持 Tensor Core 的显卡上效果明显。

推理结束后,结果分两部分返回:一是结构化数据(每辆车是什么类型、置信度多少、总耗时多少),二是画好框的结果图(保存到 runs/ 再回传 URL)。如果用户勾选了 AI 分析,系统会把检测到的车辆类型拼成一段提示词,喂给 DeepSeek 或 Qwen,让大模型输出一段"人话"版本的解读,比如"画面中检测到 2 辆小型汽车和 1 辆油罐车,油罐车属于危化品运输……"。

这个 AI 建议功能是斌擎科技特意加的。因为我们发现,纯靠置信度数字和框图,非技术用户其实看不懂"这检测得到底准不准、意味着什么"。大模型把这层翻译做了,让检测结果的业务含义一目了然。当然,它也只是辅助,最终判断还是看模型本身的输出。

最后,整个检测过程的关键信息(用的哪个模型、原图、结果图、置信度阈值、各类别置信度、耗时、AI 建议)都会通过 Spring Boot 写进 imgrecords 表,形成一条可追溯的记录。

5.4 视频检测(技术含量最高的模块)

视频检测比图片检测复杂得多,因为视频是"流"的,不能等整段处理完再返回。我们的方案是 MJPEG 流式输出 + 后台异步下载。
在这里插入图片描述

具体来说,前端把视频地址传给 Flask,Flask 会做这几件事:

  1. 先开一个后台线程去下载视频(如果是网络地址),同时立即给前端推一帧"视频下载中,请稍候"的占位画面,避免用户干等。
  2. 下载完成后,主线程用 OpenCV 读视频,逐帧送进 YOLO 推理。
  3. 每一帧推理完,画框,编码成 JPEG,包成 MJPEG 的帧格式推给前端。
  4. 同时用 VideoWriter 把带框的画面写成一个结果视频文件,检测完供用户回看。

这套"边下边播边检"的流水线,关键技术点在于用线程做下载、用生成器做流式响应、用 SocketIO 做状态通知三者配合。如果让下载阻塞在主流程里,前端就会卡在白屏;如果不用生成器,就得等整段视频处理完才能返回,体验极差。这里我们踩过坑,最早版本就是同步下载 + 一次性返回,结果一个 50MB 的视频要让用户等一两分钟没任何反馈,后来改成现在这套异步流式方案,体验就好了。

视频检测的结果同样会落库,存进 videorecords 表。

5.5 摄像头检测(实时性要求最高)

摄像头检测和视频检测的流式逻辑类似,但数据源从"视频文件"换成了"本机摄像头"。Flask 启动时会探测 0~9 号摄像头哪些可用,列给前端选。

在这里插入图片描述

这块有个贴心的设计:如果探测不到任何摄像头(比如部署在服务器上没有摄像头),系统不会直接报错,而是降级返回几个默认的摄像头编号让用户试,保证界面不会崩。

摄像头检测支持随时停止(/stopCamera 接口),停止时会释放摄像头资源、关闭 VideoWriter,避免资源泄漏。这类实时模块最容易出的问题就是"关不掉",导致下次再开的时候摄像头被占用,我们在测试中专门处理了资源释放的逻辑。

摄像头检测记录存 camerarecords 表。

5.6 三类检测记录管理

检测不是一锤子买卖,记录的可查询、可回看同样是系统的核心价值。我们按检测入口分了三类记录:图片检测记录、视频检测记录、摄像头检测记录。每类都有列表页和详情页两个视图。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

列表页展示检测时间、使用的模型、检测到的类别、耗时等关键字段,支持分页和筛选。详情页则把当时检测的原图、结果图、各类别置信度、AI 建议全部调出来,相当于一次检测的"完整回放"。

为什么要把三类记录分开存表而不是合并?因为它们的字段差异其实不小:视频记录有帧率、时长,摄像头记录有摄像头编号、起止时间,图片记录最简单。硬合并成一张表会有大量空字段,反而乱。分开存,每张表都干净,查询也高效。这是 MyBatis-Plus 用起来很舒服的一点——多表多 Mapper,模板代码极少。

5.7 模型训练(在线训练,实时回传)

这是整套系统里"硬核"程度仅次于推理的模块。它允许用户直接在网页上发起一次 YOLO 训练,选预训练权重、设 epoch、设 batch、设 imgsz,点开始就跑。

在这里插入图片描述

技术上,Flask 用一个独立线程跑 Ultralytics 的 model.train(),避免阻塞主服务。训练过程中的日志和指标通过 SocketIO 实时推给前端,前端用一个终端样式的面板展示训练输出,能直观看到每个 epoch 的 loss 在往下掉、mAP 在往上升。同时支持随时中止训练(/stop_training,通过标志位让训练线程退出)。

这里要提醒一句:在线训练对硬件要求不低,尤其是显卡。斌擎科技在做的时候,实际训练是在带 GPU 的机器上跑的(从 args.yaml 里能看到部分模型是在 AutoDL 的云服务器上训的,路径是 /root/autodl-tmp/20/)。如果是纯 CPU 环境,训练会非常慢,这个模块更多是"能力展示"和"小数据集微调"用,大规模训练还是建议在 GPU 环境离线做。

5.8 用户管理与个人中心

在这里插入图片描述

在这里插入图片描述

这两块是配套的权限和资料管理。用户管理是管理员专属,能看所有注册用户、能改角色、能禁用。个人中心是所有用户都能进,改自己的昵称、头像、密码。属于后台系统的标配,不展开。


六、五个 YOLO 模型横向对比(本文重点)

前面铺垫了那么多,现在进入这套系统最值得讲的部分——五个 YOLO 版本在同一份七类车辆数据集上的横向对比。

先把背景说清楚:我们用的是同一份自建的七分类车辆数据集(小型汽车、中型车、大型车、轻型货车、重型货车、油罐车、特种车辆),同一套训练流程(Ultralytics 框架、imgsz=640、optimizer=auto),只是预训练权重和训练轮数根据各模型特点略有调整。所有指标都来自各模型训练目录下的 results.csv,是真实跑出来的,不是编的。

6.1 先认识这五个版本

YOLO 这个系列从 Ultralytics 接手后,命名和迭代节奏就变得很快。这里简单梳理一下我们集成的五个版本:

  • YOLOv8(2023 年初):Ultralytics 的标志性版本,anchor-free 设计,解耦头,成熟稳定,生态最完整。可以说后面几个版本都是在 v8 的骨架上演进的。
  • YOLOv10(2024 年):清华团队提出,主打"消除 NMS"。它用一致性双重分配策略,训练时用一对多分配保证精度,推理时用一对一分配去掉 NMS,从而降低端到端延迟。
  • YOLOv11(2024 年下半年):Ultralytics 官方迭代,引入了 C3k2 模块、空间金字塔池化改进等,强调特征提取效率。
  • YOLOv12(2025 年):一个比较有意思的版本,它把注意力机制(area-attention)引入了 YOLO 主干,试图在保持实时性的前提下用注意力换精度。
  • YOLO26(最新):Ultralytics 的新版本,命名直接跳到了数字 26(对应 2026)。架构上做了较大调整,目标是更高的精度-效率平衡。

这五个版本代表了 YOLO 系列近两三年的演进脉络:从 v8 的稳扎稳打,到 v10 的去 NMS,到 v11/v12 的结构微调和注意力引入,再到 YOLO26 的新架构。把它们放在同一个系统里对比,能很直观地看到"这几年的 YOLO 到底进步了多少、进步在哪"。

6.2 数据集与训练过程

在聊具体配置前,先说说数据集,因为数据是决定模型上限的根本因素。

这份七分类车辆数据集是斌擎科技针对实际道路场景整理的,覆盖了前面列出的七种车辆类型。数据来源包括公开道路采集、网络爬取补充、以及对部分公开车辆数据集的再标注。每张图都用 LabelImg 之类的标注工具画了矩形框、打了类别标签。为了提升模型的泛化能力,我们在数据准备阶段做了一些工作:

  • 类别均衡:尽量让七类样本数量接近,避免某类过多导致模型偏科。其中"小型汽车"作为最常见的类别样本最多,“特种车辆”"油罐车"这类稀有类别样本相对少,我们做了过采样和补充采集来缓解长尾问题。
  • 场景多样:包含白天/夜间、晴天/雨天、顺光/逆光、近景/远景、城市道路/高速/停车场等多种场景,保证模型不会只认一种环境。
  • 角度与遮挡:纳入了侧视、俯视、部分遮挡等真实卡口常见的视角,提升模型在复杂条件下的鲁棒性。
  • 数据划分:按训练集 / 验证集划分,验证集独立于训练集,确保指标反映的是真实泛化能力而非过拟合。

训练方面,所有模型都基于 Ultralytics 框架,imgsz 统一为 640,优化器用 auto(让框架自动选 SGD 或 AdamW),数据增强(mosaic、mixup、HSV 抖动、随机翻转等)沿用 Ultralytics 默认配置。这套配置是经过验证的"稳妥配方",能在大多数检测任务上拿到不错的基线。我们刻意没有过度调参,因为这次对比的重点是"在相同条件下看五个版本谁强",而不是"把每个模型都调到极致"——后者会让对比失去公平性。

训练硬件上,部分模型(YOLOv11、YOLOv12)是在 AutoDL 的云 GPU 服务器上跑的,YOLOv8、YOLOv10、YOLO26 在本地 GPU 工作站上完成。训练日志和指标都自动写进各自目录的 results.csv,这套系统首页的模型看板就是直接读这些文件来渲染图表的。

6.2.1 训练配置一览

先把各模型的训练配置摆出来,方便后面看指标时对照:

模型预训练权重训练轮数批次大小图像尺寸优化器权重文件大小
YOLOv8yolov8n.pt10016640auto约 6.0 MB
YOLOv10yolov10s.pt1008640auto约 15.8 MB
YOLOv11yolo11s.pt1178640auto约 18.3 MB
YOLOv12yolov12s.pt1178640auto约 17.8 MB
YOLO26yolo26s.pt2008640auto约 19.4 MB

有几点要说明:

  1. YOLOv8 用的是 n(nano)规格,其余都是 s(small)规格。这不是偏心,而是为了公平比较"小模型"档位的能力——v8n 已经是 v8 系列里最小的,参数量和后面的 s 档接近。不过严格说,跨规格对比会有偏差,后面解读指标时会考虑这一点。
  2. YOLO26 训了 200 轮,其余 100~117 轮。这是因为 YOLO26 收敛相对慢一些,多训了一些轮次让它充分收敛,避免"没训够"导致的低估。
  3. batch 大小有 8 和 16 的差异,主要是受显存限制,s 规格模型占用更大,batch 降到 8。
  4. 权重文件大小直接反映了模型复杂度:YOLOv8n 最小(6MB),YOLO26s 最大(约 19MB),体积差了三倍多。这对部署是有意义的——边缘设备上,YOLOv8n 的轻量优势很明显。

6.3 最终指标对比(核心数据)

下面这张表是各模型训练完成后的最终验证指标,数据全部来自 results.csv 的最后一行:

模型精确率 P召回率 RmAP50mAP50-95
YOLOv80.9790.9720.9870.870
YOLOv100.9770.9770.9900.860
YOLOv110.9770.9730.9890.873
YOLOv120.9590.9880.9890.874
YOLO260.9780.9810.9900.877

先看整体结论:五个模型在这份七分类车辆数据集上表现都很能打,mAP50 全部在 0.987 以上,mAP50-95 全部在 0.86 以上。这说明两件事——一是这份数据集质量不错、类别虽然细但可分性强;二是当下的 YOLO 系列,哪怕是几年前的 v8,在中等难度的细分类任务上都已经能达到相当高的水准。

但仔细看还是有差异的,我们逐项解读:

关于 mAP50(IoU=0.5 时的平均精度):这是最宽松的指标,五个模型都在 0.987~0.990 之间,差距非常小。YOLOv10 和 YOLO26 并列最高(0.990),YOLOv8 最低(0.987)。在 IoU=0.5 这种宽松门槛下,各模型基本都能把车找出来、分对类,差异被压缩了。

关于 mAP50-95(IoU 从 0.5 到 0.95 的平均,更严格):这个指标更能拉开差距,因为它要求定位非常准。这里排名就变了——YOLO26 最高(0.877),其次是 YOLOv12(0.874)、YOLOv11(0.873)、YOLOv8(0.870),YOLOv10 反而最低(0.860)。这说明在定位精度上,新版本(YOLO26、v12)确实比老版本有提升,YOLOv10 虽然去 NMS 带来了速度优势,但严格定位精度反而略逊一筹。

关于精确率和召回率的权衡:这是个有意思的观察。

  • YOLOv8:P=0.979,R=0.972,精确率略高于召回率,倾向于"宁缺毋滥"。
  • YOLOv10:P=R=0.977,两者均衡。
  • YOLOv11:P=0.977,R=0.973,和 v8 类似的倾向。
  • YOLOv12:P=0.959,R=0.988,召回率显著高于精确率,是五个模型里最"激进"的——它更倾向于把疑似目标都检出来,代价是误检稍多。这和它引入注意力机制后对全局上下文更敏感有关,能抓到更多目标,但也更容易把背景误判。
  • YOLO26:P=0.978,R=0.981,召回率略高于精确率,整体均衡且偏召回。

这个 P-R 权衡的差异,在实际业务里非常有指导意义。如果场景是"危化品油罐车监管,漏检后果严重",那就该选 YOLOv12 或 YOLO26 这种高召回的;如果场景是"卡口计费,误检会引发纠纷",那 YOLOv8 这种高精确率的更合适。没有绝对最好的模型,只有最合适的模型——这正是我们把五个模型都做进系统、让用户自选的初衷。

6.3.1 逐模型深度点评

光看表格里的数字还不够立体,斌擎科技结合实际训练过程和推理体验,逐个模型再聊聊感受。

YOLOv8:老牌劲旅,稳字当头。 作为 Ultralytics 时代的开山之作,v8 的成熟度是五个模型里最高的——文档全、踩坑少、社区案例多,几乎任何报错都能搜到解决方案。在我们这份七分类数据上,它用最小的 n 规格就拿下了 mAP50=0.987、mAP50-95=0.870 的成绩,考虑到权重只有 6MB,性价比堪称恐怖。它的精确率(0.979)是五个模型里最高的,意味着它输出的框"基本不会错",这点在计费、执法这类"错一个就出问题"的场景里非常吃香。缺点是 mAP50-95 略低,定位精度(框贴得紧不紧)比新版本稍逊。如果你要做的是边缘部署、或者追求稳妥不折腾,v8 仍然是首选。

YOLOv10:去 NMS 的激进派。 v10 最大的卖点是去掉 NMS。传统 YOLO 推理完会输出一堆重叠的框,需要非极大值抑制(NMS)去重,这一步虽然有优化但仍是端到端延迟里不可忽略的一环。v10 用一致性双重分配,让模型在推理阶段直接吐出一对一的结果,省掉 NMS。理论上这让它在高帧率、低延迟场景有天然优势。但代价是它的训练更复杂(有 om/oo 两个优化头),显存占用也更大。从我们的指标看,它的 mAP50 拿到了并列最高(0.990),但 mAP50-95 反而是最低的(0.860)。一种解释是:去掉 NMS 后,模型在重叠目标的精细定位上做了一些妥协。所以 v10 适合"要快、对框的贴合度要求没那么苛刻"的实时场景,比如车流统计这种只数数量的活儿。

YOLOv11:稳中求进的中间派。 v11 没有像 v10 那样做架构层面的激进改动,而是在 v8 的骨架上做了若干模块级优化(C3k2、改进的 SPPF 等),属于"小步快跑"的迭代。它的各项指标都很均衡:mAP50=0.989、mAP50-95=0.873,P 和 R 也接近。在斌擎科技看来,v11 是一个"不会出错的选择"——如果你正在用 v8 想升级,又不想承担 v10/v12 那种架构变化带来的不确定性,v11 是最平滑的过渡。它的训练稳定性和 v8 一样好,指标又有可见的提升(mAP50-95 从 0.870 涨到 0.873)。

YOLOv12:注意力加持的"贪心"模型。 v12 是五个模型里最特殊的。它把 area-attention(区域注意力)机制引入了 YOLO 主干,这是 YOLO 系列第一次正经地把注意力作为核心组件。注意力的好处是模型能更好地捕捉全局上下文,所以它能在画面里"看到"更多潜在目标——这直接反映在它高达 0.988 的召回率上,是五个模型里最高的。但注意力也有副作用:模型变得更"敏感",容易把一些边缘的、模糊的目标也当成正样本,导致精确率掉到 0.959(最低)。换句话说,v12 是个"宁可错杀不可放过"的模型。这种特性在危化品监管、安全预警这类漏检代价远大于误检的场景里非常合适。需要注意的是,注意力机制在 CPU 上有额外开销,v12 最好部署在 GPU 环境下。

YOLO26:新王加冕,全面领先。 YOLO26 是这次对比里综合表现最好的模型:mAP50 并列最高(0.990)、mAP50-95 最高(0.877)、P 和 R 都很高且均衡(0.978 / 0.981)。它代表了 YOLO 系列截至 2026 年的最强水准。从训练过程看,它收敛相对慢(我们给了 200 轮才充分收敛),权重也最大(19MB),说明它的容量和复杂度都是最高的。这种"大而强"的特性,让它在算力充足的中心化部署场景(云服务器、工作站)下能发挥最大价值。如果是卡口核心节点、对精度要求最高的位置,YOLO26 是当仁不让的选择。但在边缘设备上,它的体积和算力需求会是个负担。

把这五个模型的性格画个像:v8 是稳重的老兵,v10 是激进的革新者,v11 是稳健的中间派,v12 是贪心的侦察兵,YOLO26 是全面的新王者。了解它们的"性格",比记住几个数字更重要。

6.4 速度与部署成本

精度只是一方面,实际落地还得看速度和体积。这块我们没有在统一硬件上做严格 benchmark(因为各模型在不同硬件上表现差异大),但可以结合权重文件大小和架构特点给一些定性结论:

  • YOLOv8n:体积最小(6MB),推理最快,适合边缘设备、嵌入式、移动端。精度虽然 mAP50-95 最低,但和最高的 YOLO26 也只差 0.007,在资源受限场景下性价比极高。
  • YOLOv10:去 NMS 设计让端到端延迟更低(省掉了 NMS 这一步的后处理时间),在对延迟敏感的实时场景(比如高帧率摄像头)有优势。但要注意它有两个优化头(om/oo),训练时显存占用更大。
  • YOLOv11:架构优化带来较好的精度-速度平衡,是 v8 之后比较稳妥的升级选择,生态成熟。
  • YOLOv12:注意力机制带来精度提升(尤其 mAP50-95),但注意力本身有一定计算开销,纯 CPU 上会比 v8/v11 慢一些,GPU 上影响较小。召回率高,适合宁可误检不可漏检的场景。
  • YOLO26:综合最强,mAP50 和 mAP50-95 都是最高的,P-R 也均衡。代价是体积最大(19MB)、训练轮数需求最多(200 轮)。适合对精度要求最高、算力充足的部署环境。

6.5 选型建议(针对七类车辆检测场景)

综合上面的数据,斌擎科技给一些具体的选型建议:

场景特征推荐模型理由
边缘设备 / 移动端 / 低算力YOLOv8n体积小、速度快,精度损失可接受
高帧率实时检测 / 低延迟要求YOLOv10去 NMS,端到端延迟低
通用稳妥升级(从 v8 迁移)YOLOv11平衡性好,生态成熟
危化品监管 / 漏检零容忍YOLOv12召回率最高(0.988),抓得全
精度优先 / 算力充足 / 卡口核心YOLO26综合指标最高,定位最准

这套对比不是停留在论文层面,而是用户在系统里每次检测都能切换模型,用自己的实际图片去验证哪个模型在自己业务数据上表现最好。这种"所见即所得"的模型选型体验,是这套系统区别于一般 Demo 的关键。


七、AI 智能分析:让检测结果说"人话"

前面提到检测完会调 DeepSeek 或 Qwen 生成建议,这里展开讲讲这块的设计思路。

传统的检测结果就是一堆框和数字:['小型汽车', '油罐车']、['98.23%', '95.67%']。对算法工程师来说够用了,但对一线业务人员(卡口值班、车队调度、安全监管)来说,这串数据需要二次解读才能变成决策依据。

我们在 Flask 里接了两个国产大模型的 API(DeepSeek 和 Qwen),让用户在检测时可选地触发 AI 分析。流程是:把 YOLO 检测到的车辆类别拼成一段自然语言提示词,比如"我用 yolo 初步检测出小型汽车,油罐车,我用 YOLO 检测出来了车辆信息,帮我分析一下",发给大模型,拿回一段结构化的解读。

这段解读的价值在于:

  1. 翻译技术指标:把"置信度 95%“翻译成"模型比较确定这是一辆油罐车”。
  2. 补充业务背景:识别到油罐车时,主动提示这属于危化品运输,需要关注行驶路线合规性。
  3. 给出行动建议:比如检测到重型货车 + 特种车辆同时出现,提示可能存在特殊运输任务,建议人工复核。

为什么接两个模型而不是一个?因为不同大模型的风格和能力有差异——DeepSeek 的逻辑推理强一些,Qwen 的中文表达更自然一些。让用户自选,相当于在 AI 这层也做了一次"模型选型"。当然,这部分只是辅助增强,核心检测精度还是由 YOLO 决定,大模型不会改变检测结果本身。

需要提醒的是,大模型 API 需要配置自己的密钥才能用,系统里预留了接口,实际部署时替换成自己的 key 即可。


八、系统的几个设计亮点

写到这里,我想把这套系统里几个斌擎科技觉得比较用心的设计点单独拎出来讲讲,这些也是它区别于普通"YOLO Demo"的地方。

8.1 模型可热切换

绝大多数车辆检测项目都把模型写死成一个,换模型要改代码、重新部署。我们这套系统把五个权重文件统一放在 weights/ 目录,前端通过下拉框选择,Flask 按需加载。用户甚至可以对同一张图片分别用五个模型跑一遍,直观对比结果差异。这种"模型即配置"的设计,让系统天然成了一个模型评测工具。

8.2 推理与业务彻底解耦

AI 推理(Flask/Python)和业务逻辑(Spring Boot/Java)分离,通过 HTTP 通信。好处是双重的:Python 这边可以自由用 Ultralytics、OpenCV 等 AI 生态工具,不受 Java 限制;Java 这边专注于用户、权限、记录、文件这些业务能力,不受 AI 框架版本牵连。任何一边升级都不会影响另一边。

8.3 实时流式体验

视频和摄像头检测用 MJPEG 流 + WebSocket 双通道:MJPEG 负责画面实时刷新,WebSocket 负责状态通知(下载中、检测中、AI 生成中)。视频还做了异步下载 + 占位帧,用户点完按钮立刻有反馈,不会盯着白屏干等。这种"心理等待时间"的优化,对实际使用体验影响很大。

8.4 检测结果全链路可追溯

每一次检测,从输入素材、使用模型、置信度阈值、各类别置信度、耗时、结果图、AI 建议,全字段落库。三类记录(图片/视频/摄像头)分表存储,列表 + 详情两级浏览。这意味着系统不是"用完即走",而是形成了可复盘的检测档案,对监管类业务尤其重要。

8.5 在线训练 + 实时回传

把训练也搬进了网页。用户能选预训练权重、配超参、看实时训练日志、随时中止。虽然大规模训练还是建议离线 GPU 跑,但这个能力让系统成了一个完整的 YOLO 工作台——从训练到推理到对比,闭环了。

8.6 活数据驱动的模型看板

首页的模型对比图表不是写死的静态数据,而是实时读取各模型 results.csv 算出来的。重新训练后图表自动更新,这种"活数据"让看板始终反映模型的真实最新状态,而不是过时的快照。


九、部署与运行

简单说一下怎么把这套系统跑起来,方便想复现的同学。

9.1 环境准备

  • Python 环境:建议 Python 3.9+,安装 ultralytics、flask、flask-cors、flask-socketio、opencv-python、numpy、requests。有 NVIDIA 显卡的话装好 CUDA 和对应版本的 PyTorch,推理和训练都会快很多。
  • Java 环境:JDK 8 或更高,Maven 用来构建 Spring Boot 项目。
  • MySQL:5.7 或 8.0,建好数据库(配置里库名是 20),导入用户表和三类记录表的结构。
  • Node.js:16+,用来构建 Vue 前端。

9.2 启动顺序

  1. 先启动 MySQL,确保库表就绪。
  2. 启动 Flask AI 服务(python main.py),监听 5000 端口。它会加载 weights/ 下的五个模型权重。
  3. 启动 Spring Boot 业务后端,监听 9999 端口。它会连 MySQL 并代理推理请求到 Flask。
  4. 启动 Vue 前端开发服务器(npm run dev)或构建产物(npm run build 后部署)。
  5. 浏览器访问前端地址,注册/登录后即可使用全部功能。

9.3 权重文件

五个模型的权重文件已经放在 VehicleType_flask/weights/ 下,分别是 YOLOv8.pt、YOLOv10.pt、YOLOv11.pt、YOLOv12.pt、YOLO26.pt,开箱即用,不用自己重新训练就能直接推理。如果想用自己训练的权重,替换对应文件即可,或者在模型训练模块里跑出自己的 best.pt。

9.4 配置要点

  • Flask 里的 DeepSeek 和 Qwen 的 API key 需要替换成自己的。
  • Spring Boot 的 application.properties 里数据库账号密码要改成自己的(默认是 root/111111,仅作示例)。
  • 文件上传上限已经放宽到 500MB,满足大视频检测,但要注意服务器磁盘空间。

十、应用场景

这套系统虽然是一个通用车辆检测平台,但因为做了七分类细粒度识别 + 多模型可选 + 检测记录可追溯,它能落到不少实际场景里:

  1. 智慧卡口 / 交通监测:对路口过往车辆做类型统计,区分客运、货运、危化品,为限行管理、流量分析提供数据。
  2. 危化品运输监管:重点识别油罐车等危化品车辆,进入禁行区域自动预警,配合高召回的 YOLOv12/YOLO26 减少漏检。
  3. 物流园区车辆调度:区分轻型/重型货车,辅助调度和车位分配。
  4. 停车场管理:识别车型做差异化收费(小型汽车 vs 大型车)。
  5. 应急通道监控:识别特种车辆(消防、救护),联动道闸优先放行。
  6. 教育与科研:作为 YOLO 多版本对比的教学/实验平台,让学生直观理解不同版本 YOLO 的差异。
  7. 模型选型实验台:企业在新业务上线前,用它来评测哪个 YOLO 版本最适合自己的数据分布。

最后两个场景其实是这套系统一个意外的附加价值——它不只是一个检测工具,也是一个 YOLO 选型的工作台。


十一、踩过的坑与反思

做这个项目的过程中,斌擎科技也踩了不少坑,挑几个有代表性的分享出来,希望能帮到同样在做类似事情的同学。

坑一:视频检测的同步下载导致白屏。最早版本是前端传视频地址、Flask 同步下载完再开始检测,结果网络稍慢用户就盯着白屏几十秒,体验极差。后来改成后台线程下载 + 占位帧 + 流式输出,问题才解决。教训是:任何可能耗时的操作都不能阻塞在请求主流程里,要么异步、要么流式、要么先给反馈。

坑二:摄像头资源释放。摄像头检测的"停止"按钮一开始没处理好,停了之后摄像头没真正释放,再开就报"设备被占用"。后来在停止逻辑里显式 cap.release() 和关闭 VideoWriter 才稳定。实时设备的资源生命周期管理,比文件类资源要谨慎得多。

坑三:跨模型对比的公平性。一开始想偷懒,五个模型都用同一套轮数和 batch,结果发现 YOLO26 没收敛、指标偏低,差点得出"新模型不如老模型"的错误结论。后来根据各模型特点调整了轮数(YOLO26 给到 200 轮),才得到公平的对比。教训是:模型对比必须保证每个模型都充分收敛,否则对比没意义。

坑四:YOLOv8n 和 s 规格的混用。v8 用 n、其他用 s,严格说不是完全对等的对比。这是受当时权重可用性和显存限制的折中。更严谨的做法是五个模型都用同一规格(全 n 或全 s),这个我们后续会补上。

坑五:大模型 API 的稳定性。DeepSeek 和 Qwen 的 API 偶尔会超时或限流,导致 AI 建议生成失败。这块加了 try-except 兜底,失败时返回"AI 建议生成失败,请稍后重试",不影响主检测流程。第三方依赖一定要有降级方案,不能让它拖垮核心功能。


十二、后续可以继续做的事

这套系统目前功能已经比较完整,但斌擎科技觉得还有不少可以深化的方向:

  1. 部署端优化:把 PyTorch 推理换成 ONNX Runtime 或 TensorRT,进一步压榨推理速度,尤其让 YOLO26 这种大模型在普通显卡上也能流畅跑实时。
  2. 模型量化:对 YOLOv8n 做 INT8 量化,目标是在树莓派、Jetson 这类边缘设备上跑实时检测。
  3. 追踪集成:接入 ByteTrack/BoT-SORT 做多目标追踪,让摄像头检测不仅能识别车型,还能持续跟踪同一辆车,做车流计数、轨迹分析。
  4. 车牌识别联动:车辆类型 + 车牌识别组合,形成更完整的卡口能力。
  5. 更公平的对比:把五个模型统一到同一规格、同一轮数、同一硬件上重新跑一遍,并加上严格的推理速度 benchmark,形成更严谨的选型报告。
  6. 数据集扩充:加入夜间、雨雪、逆光等复杂光照条件的数据,测试模型在恶劣条件下的鲁棒性,这往往是真实场景里拉开差距的地方。
  7. 告警规则引擎:在检测基础上叠加业务规则(油罐车进禁区告警、重型货车违时段告警),让系统从"检测"升级到"预警"。

十三、写在最后

回到开头那个问题:为什么又做了一个车辆检测系统?

做下来之后,斌擎科技的体会是:市面上的车辆检测 Demo 多的是,但真正把"多模型对比 + 完整业务闭环 + 可追溯记录 + 在线训练"这几件事凑齐的,其实不多。这套系统的价值不在于"它能检测车辆"——这事儿随便一个 YOLO Demo 都能干——而在于它把检测做成了一个可运营、可治理、可演进的平台。

对使用者来说,它是一个能直接拿去演示和落地的车辆类型检测产品;对研究者来说,它是一个 YOLO 多版本横向对比的实验台;对我们自己来说,它也是这两年 YOLO 系列演进的一次完整复盘——从 v8 的稳,到 v10 的去 NMS,到 v12 的注意力,再到 YOLO26 的综合最强,这条线我们在一份真实数据上亲手摸了一遍。

五个模型,七种车型,三种检测入口,一套完整记录,两个大模型辅助——这就是斌擎科技这套"七种车辆类型检测系统"的全貌。希望这篇文章对正在做类似项目、或者在 YOLO 选型上犹豫的同学有点参考价值。

如果你对某个模块的实现细节、某个模型的训练配置、或者部署中的具体问题感兴趣,欢迎在评论区交流,斌擎科技会尽量回复。也欢迎指出文中指标解读或者技术描述上不准确的地方,我们一起把这套东西打磨得更好。

更多推荐