先说一个反直觉的结论:安全锥检测这个任务,在YOLO官方预训练模型里连一个类别都不占,但真正把它做成一套能落地的系统时,牵扯到的工程量往往比“人脸检测”还要多。原因很简单——这是一个典型的复合型工程:前面是YOLOv8/YOLOv10/YOLOv11/YOLOv12四代模型的选型、训练与推理部署,中间是SpringBoot承担业务后端,后面还要接上千问和DeepSeek两个大模型做智能研判,最终用前后端分离的Web界面把整条链路串起来给用户用。
这篇文章就把整个系统的设计思路、训练细节、代码结构和避坑过程一次性讲清楚,适合正在做目标检测项目往工程化方向扩展的同学,也适合想了解“YOLO怎么和大模型结合”的人参考。我不会只贴一堆概念,而是把实际项目中能直接抄走的配置、脚本和踩坑点都拿出来。
1. 项目背景与痛点:安全锥检测为什么比想象中复杂
1.1 安全锥场景的特殊性:看起来简单,实际上很容易翻车
安全锥(交通锥)在道路上是很常见的东西——施工路段、事故现场、收费广场、学校门口都能看到。需求方最初提的需求也很简单:“你用YOLO识别一下锥桶,把数量统计出来就行。”一开始我也觉得这不就是一个单类别检测任务嘛,随便找个模型练一下应该不难。
但真正开始做数据清洗和实测之后,才发现这个场景远没有想象的那么轻松。安全锥本身的视觉特征相对简单(橙色/红色锥体加反光条),但它的“环境”极其复杂:锥桶往往很小,在1080P画面里可能只占几十个像素;它经常被车辆、行人、施工机械遮挡;夜间全靠反光条,而反光条在车灯照射下会有明显的过曝和光晕;雨天锥桶表面有水膜,颜色偏移很严重;夏天路面阴影多,橙色锥桶在夕阳下的色值和地面几乎融合。
这些问题的核心在于:安全锥检测任务不是一个“在标准数据集上刷点”的任务,而是一个需要在真实部署场景里反复打磨的视觉系统。YOLO各版本虽然越来越强,但如果数据侧没把这些情况覆盖到,无论模型多新都白搭。
1.2 需求拆解:检测、分析、展示三层能力缺一不可
和需求方反复对齐后,系统的能力边界最终收敛为三层。
第一层是实时检测。通过摄像头或上传图片,用YOLO系列模型识别图像中的安全锥,输出类别、坐标框、置信度,这是整个系统最底层的视觉能力。
第二层是智能分析。检测结果本身只是一堆矩形框和数字——比如“12个0.85置信度”、“坐标点一堆”——业务人员根本不想看这些。这一层要利用千问和DeepSeek大模型,把检测结果转成可读的安全研判结论,比如“车道右侧连续摆放12个安全锥,疑似施工封闭区,建议提前减速并发布预警”,同时支持用户用自然语言做历史数据问答。
第三层是Web交互。包括实时画面展示、检测结果叠加、历史记录查询、分析报告查看、模型版本切换等。前端采用前后端分离架构,Vue负责展示,SpringBoot提供接口,Python推理服务作为后端的能力引擎。
这三层在架构上是完全解耦的,但在业务逻辑上又层层依赖。这也是我最终选择SpringBoot做中台的原因——它可以把视觉模型的输出、大模型的输出统一封装成业务接口,而不需要让前端直接面对一堆Python进程。
2. 技术选型复盘:为什么是YOLO多版本+SpringBoot+双大模型
2.1 YOLOv8/v10/v11/v12四版同台:不是卷版本,是各有胜负
很多同学看到项目标题里写了四个YOLO版本,第一反应是“这不就是在蹭热度吗”。实际上这里有一个很现实的工程考量:不同部署环境、不同硬件条件、不同业务精度要求下,四个版本的适用性差异非常大。
我在项目里整理过一个简单的对比表,供选型时参考:
| 版本 | 核心特点 | 适合场景 | 我们的实践结论 |
|---|---|---|---|
| YOLOv8 | 生态最成熟,Ultralytics官方支持,文档和社区资源最多 | 需要稳定迭代、团队对API最熟悉的场景 | 作为默认基线模型,绝大多数接口兼容它 |
| YOLOv10 | 端到端检测,推理阶段去掉NMS,由清华大学团队提出 | 部署帧率要求高、对推理延迟敏感的边缘设备 | 在Jetson系列上省掉了NMS的耗时,帧率明显提升 |
| YOLOv11 | 2024年Ultralytics推出的新架构,C3k2模块替换了部分C2f结构 | 想在v8基础上升级又不想改变太多工程代码的场景 | mAP比v8普遍高1-2个点,速度和v8持平 |
| YOLOv12 | 引入注意力机制优化,对长距离依赖建模更好 | 大分辨率图像、小目标密集场景 | 在1080P以上的图像里对远处小锥桶召回更好,但显存占用也更高 |
所以“四版同台”的本质是:我把四个模型都训练出来,线上通过配置中心动态切换。业务方要求低延迟时切v10,要求精度时切v12,需要稳定生态时留在v8/v11。这比“只挑一个最新版”稳妥得多,因为AI模型这东西,实测效果大于纸面参数。
2.2 SpringBoot在后端扮演的角色:把模型输出变成业务能力
检测服务本身用Python实现是最顺手的,因为YOLO生态基本都在Python侧。但企业级项目不会让所有模块都用Python去写,尤其是涉及用户权限、数据管理、报表生成、第三方系统对接的部分,Java后端依然是主流。
SpringBoot在这套系统里承担的核心职责有三块:
- 统一接收Web前端的HTTP请求,做参数校验、JWT鉴权、权限控制。
- 封装对Python推理服务和两个大模型API的调用,把检测、分析能力变成REST接口。
- 负责数据持久化,包括用户信息、检测记录、图片路径、分析报告、模型配置项等。
简单说,SpringBoot是整个系统的“门面”和“大脑协调者”,Python推理服务是“眼睛”,大模型是“分析员”。这个分层我认为是这类项目里最不容易翻车的架构——它让视觉模型的迭代和大模型提示词的调整,都不需要动业务前端。
2.3 千问与DeepSeek的分工:双大模型不是摆造型
为什么要同时接入千问和DeepSeek?很多类似项目只接一个大模型就当卖点,实际用起来会发现,单一模型很难同时满足“复杂推理”和“稳定结构化输出”两个要求。
我在这套系统里的分工策略是:
- DeepSeek负责复杂推理和研判。它的推理链路较长、逻辑性强,适合做“按检测数据推断安全风险等级”这类任务。比如给它一帧图像里所有安全锥的坐标分布、数量、置信度,它能给出“左侧车道出现连续安全锥,疑似车道封闭,且锥桶间距偏大,存在车辆闯入风险”这类有决策价值的判断。
- 千问负责面向业务用户的对话查询和统计问答。比如用户问“今天上午G2路段检测到多少次安全锥数量异常”,千问在中文口语理解这块表现稳定,配合后端统计算法,能给出比较自然的回答。
另外还有一个重要的工程原因:双模型可以做故障切换和成本路由。DeepSeek高峰期可能有响应延迟,那么可以切到千问;千问在阿里云上集成方便,适合调用通义系列配套能力。我在配置中心里维护了一份llm-router配置,每次请求会根据任务类型和当前两个API的健康状态决定走哪条路。
3. 数据与训练:从YOLO数据准备到多版本模型落地
3.1 KITTI标注转YOLO格式:字段映射与脚本实现
提起数据准备,绝大多数YOLO项目都会面临一个现实问题:公开数据集里的标注格式五花八门,KITTI是最常见的来源之一。KITTI的目标检测标注是每行一个物体,字段为:class truncated occluded alpha bbox_left bbox_top bbox_right bbox_bottom dimensions_x_y_z location_x_y_z rotation_y。
而YOLO格式要求每行是:class_id x_center y_center width height,其中中心坐标和宽高都是归一化到0-1的浮点数(相对于图片宽高)。
转换脚本的核心逻辑其实不复杂,但有几个值得注意的细节。KITTI里bbox_left等四个值代表2D框的左上角和右下角像素坐标;需要先算出框的宽高和中心点,再除以图片尺寸做归一化。同时要过滤掉那些truncated(截断)和occluded(遮挡)程度过高的样本,否则会把一堆残缺框喂给模型。
我写过一个精简版的转换脚本,核心部分如下:
import os from PIL import Image def kitti_to_yolo(kitti_label_path, img_width, img_height): yolo_lines = [] with open(kitti_label_path, 'r', encoding='utf-8') as f: for line in f: parts = line.strip().split() if len(parts) < 9: continue cls_name = parts[0] if cls_name not in class_map: continue # KITTI的2D框是第5到第8个字段:left, top, right, bottom left, top, right, bottom = map(float, parts[4:8]) if right <= left or bottom <= top: continue box_w = right - left box_h = bottom - top x_center = left + box_w / 2.0 y_center = top + box_h / 2.0 # 归一化 x_center /= img_width y_center /= img_height box_w /= img_width box_h /= img_height # 坐标越界裁剪,防止训练时报错 x_center = min(max(x_center, 0.0), 1.0) y_center = min(max(y_center, 0.0), 1.0) box_w = min(box_w, 1.0) box_h = min(box_h, 1.0) yolo_lines.append(f"{class_map[cls_name]} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}") return "\n".join(yolo_lines)转换完成之后,还需要用可视化脚本把YOLO标注框画回到原图上抽查一遍。这一步非常重要——我曾经因为KITTI某个子数据集里坐标从1开始计数,导致所有框整体偏移了几个像素,不仔细看发现不了,结果在验证集上mAP一直差0.5个点,排查了整整一天。
除了KITTI,实际项目中还会补充自采数据:找几个晴天、阴天、夜间的道路视频,抽帧后人工标注。标注工具我推荐用labelImg或X-AnyLabeling,前者轻量,后者支持半自动辅助,能省不少时间。
3.2 数据增强与标注细节:安全锥场景容易踩的暗坑
安全锥数据的标注规范如果一开始不统一,后面训练出来的模型很容易出现“验证集mAP很高,真实场景一测就废”的尴尬局面。我在项目里定了几条死规矩:
- 无论锥桶是否被遮挡,只要人眼能判断出“这是一个锥桶”,就必须画框;遮挡超过70%的才允许丢弃。
- 框必须贴着锥桶外轮廓,包含反光条和底座,但不能把地面影子包含进去。
- 重叠的锥桶必须分开标注,不能因为密集就合并成一个框。
数据增强方面,我们除了YOLO自带的mosaic(马赛克增强)、HSV色域扰动、随机翻转之外,还针对安全锥场景做了两个定制增强:一个是模拟雨天效果的模糊和噪点叠加,另一个是模拟夜间反光条的亮度增强和光晕模糊。
这里重点说下mosaic。Ultralytics的YOLO训练里mosaic是默认开启且效果很猛的数据增强方式,它会把4张图片拼接在一起,强制模型学习在更复杂的背景下识别目标。但mosaic也有副作用——它生成的大量拼接框会导致小目标边界信息丢失。我在实验中发现,训练后期把mosaic关闭(Ultralytics里是close_mosaic=10,意思是最后10个epoch关闭mosaic)能让最终mAP提升0.8个点左右。
3.3 各版本训练配置对比与损失函数理解
四份模型我用的训练参数不完全一样,但保持了大框架一致:输入尺寸640x640,优化器SGD(momentum=0.937,weight_decay=0.0005),初始学习率0.01,训练300个epoch,数据集的train/val/test按8:1:1划分。
| 配置项 | YOLOv8 | YOLOv10 | YOLOv11 | YOLOv12 |
|---|---|---|---|---|
| 输入尺寸 | 640 | 640 | 640 | 640/1280 |
| batch_size | 32 | 24 | 32 | 16 |
| epochs | 300 | 300 | 300 | 300 |
| 初始学习率 | 0.01 | 0.01 | 0.01 | 0.005 |
| close_mosaic | 10 | 10 | 10 | 15 |
| 权重初始化 | pretrained | pretrained | pretrained | pretrained |
YOLO的损失函数一路走过来变化挺明显的,理解它对调参很有帮助。YOLOv5时代的损失还包含GIOU作为回归损失,到了YOLOv8就变成了CIOU配合DFL(Distribution Focal Loss),分类用BCE;DFL的引入让框回归不再直接预测一个绝对坐标,而是预测坐标分布,极大提升了边框定位的精度。
YOLOv10最大的变化是把One-to-Many和One-to-One双分支引入训练,推理时不需要再做NMS,所以它的推理pipeline里少了一步后处理,在边缘设备上帧率提升明显。但这也意味着训练时它自己内部有一套标签分配逻辑,不能完全照搬v8的anchors经验去推断。
YOLOv11和v12的损失主框架和v8类似,但在特征提取模块上做了改造,尤其是v12引入了注意力机制,让模型在小目标定位上有一定收益。
训练命令直接用Ultralytics的CLI就能跑:
yolo train data=security_cone.yaml model=yolov8s.pt epochs=300 imgsz=640 batch=32 yolo train data=security_cone.yaml model=yolov10s.pt epochs=300 imgsz=640 batch=24 yolo train data=security_cone.yaml model=yolo11s.pt epochs=300 imgsz=640 batch=32 yolo train data=security_cone.yaml model=yolov12s.pt epochs=300 imgsz=640 batch=16security_cone.yaml里的核心就是指定训练集和验证集路径,以及类别名称:
path: /data/cone_dataset train: images/train val: images/val test: images/test names: 0: traffic_cone最终四版模型的mAP@0.5:0.95大约在0.92-0.95之间,v12最高但显存占用最狠,v10推理最快但略掉点。
4. 后端工程化:SpringBoot如何把模型服务变成可用产品
4.1 Python推理服务的封装:进程管理、接口设计与并发控制
模型训练好之后,不能直接把.pt文件丢给SpringBoot去调,Java侧加载PyTorch模型太绕了。最稳妥的做法是用Python写一个独立的推理服务,对外提供HTTP接口,SpringBoot只负责HTTP调用。
推理服务我选的是FastAPI,加载YOLO模型,提供两个接口:一个用于单张图片检测,一个用于批量检测或视频帧检测。代码结构大致如下:
from fastapi import FastAPI, UploadFile, File from ultralytics import YOLO import numpy as np app = FastAPI() model = YOLO("/models/best.pt") @app.on_event("startup") def warmup(): # 预热模型,避免第一次请求特别慢 model.predict(np.zeros((640, 640, 3), dtype=np.uint8)) @app.post("/detect") async def detect(file: UploadFile = File(...)): image_bytes = await file.read() results = model.predict(source=image_bytes, conf=0.4, iou=0.45) dets = [] for r in results: for box in r.boxes: dets.append({ "class_id": int(box.cls[0]), "confidence": float(box.conf[0]), "bbox": [float(x) for x in box.xyxy[0]] }) return {"success": True, "detections": dets}这个服务看起来很简洁,但线上运行要处理三个问题:模型预热、并发控制和显存保护。
模型加载到显存后,并发请求不能无限制往里打,否则显存溢出直接崩服务。我用了一个简单的信号量控制并发数,SEMAPHORE = asyncio.Semaphore(4),超出并发数的请求排队等待。同时推理服务要设置一个健康检查接口,SpringBoot定时轮询,发现服务不健康就切换备用GPU节点。
4.2 数据库设计与自动建表:MyBatis的实用技巧
SpringBoot侧的数据库设计,我保留了四张核心表:用户表、检测记录表、检测明细表、分析报告表。其中检测明细表专门存YOLO输出的每一个目标框,这和大模型的研判结论是分开的——视觉结果和分析结论要能对应上,但业务上解耦。
表结构设计时一张典型的检测记录表大概是这样的:
CREATE TABLE IF NOT EXISTS detection_record ( id BIGINT AUTO_INCREMENT PRIMARY KEY, image_url VARCHAR(512) NOT NULL, source_type TINYINT NOT NULL DEFAULT 0 COMMENT '0图片,1视频帧,2摄像头', detect_time DATETIME NOT NULL, total_count INT NOT NULL DEFAULT 0, model_version VARCHAR(32) NOT NULL, llm_analysis TEXT, risk_level TINYINT DEFAULT 0, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;很多Java项目用了MyBatis之后,最烦的就是开发阶段改表结构要手动执行SQL。我实际采用的方式是利用SpringBoot的SQL初始化机制,把建表语句放在schema.sql里,配置spring.sql.init.mode=always,再配合CREATE TABLE IF NOT EXISTS,这样每次发版时新环境启动就能自动建表,不需要人工去数据库敲一遍。
注意一个坑:SpringBoot 2.5以后,spring.sql.init和旧版的spring.datasource.initialization-mode行为有差异。SpringBoot 3.x下mode=always会每次启动都执行,CREATE TABLE IF NOT EXISTS没问题,但如果写的是INSERT语句就要小心重复插入。所以我只把DDL放schema.sql,数据初始化单独走迁移脚本,避免启动重复执行。
还有一处工程细节:application.yml里的数据库密码和第三方API密钥不能明文暴露。项目里引入了Jasypt对敏感配置做了加密,启动时通过-Djasypt.encryptor.password传入解密密钥,这样配置仓库即使泄露,数据库账号和大模型API key也不会直接暴露。
4.3 千问+DeepSeek智能分析模块的接入与提示词设计
大模型接入是整个系统里“看起来简单、做起来全是细节”的部分。DeepSeek的API走的是OpenAI兼容格式,所以用HTTP客户端调用特别顺手。核心就是构造Messages数组,里面包含system提示词和user请求。
我用一个简单的requests示例说明DeepSeek的调用方式:
import requests headers = {"Authorization": "Bearer YOUR_DEEPSEEK_API_KEY"} payload = { "model": "deepseek-chat", "messages": [ {"role": "system", "content": "你是交通安全分析助手,根据检测结果输出研判结论。"}, {"role": "user", "content": "检测数据:" + json.dumps(detection_summary, ensure_ascii=False)} ], "stream": False } resp = requests.post("https://api.deepseek.com/chat/completions", headers=headers, json=payload)千问的接入稍微特殊一点。我最初在Postman里调试阿里云DashScope兼容模式的接口,先确认鉴权和响应结构,再写SpringBoot代码。千问的OpenAI兼容接口端点是https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions,模型名填qwen-plus或qwen-max。Postman调试时记得在Header里加Authorization: Bearer <your-api-key>,请求体用JSON格式。
我建议所有做这类集成的人,先在Postman把两个大模型API完全调通,不要一上来就写代码。因为大模型API最容易出问题的不是代码,而是鉴权、模型名拼写、参数格式这些细节。Postman调试成功后再平移成Java代码,能省一半调试时间。
提示词设计是整个智能分析模块的灵魂。我总结了几个经验:
- 检测结果不能直接扔给大模型,要先在SpringBoot里做聚合。比如把YOLO返回的几十个框按照位置区域聚类,统计出“左侧车道12个、右侧车道3个”,再把汇总信息发给大模型。
- system提示词里必须明确输出格式。我要求大模型必须返回JSON结构:
{"risk_level": "high", "conclusion": "……", "suggestion": "……"},方便后端直接解析,也方便前端直接渲染。 - 要在提示词里加入“检测数据可能存在漏检误检”的前置说明,这样大模型不会对绝对数字进行过度解读,减少胡说八道的情况。
另外,双模型路由我在这套系统里做了一个很轻量的实现:在数据库里维护一张llm_config表,包含模型名称、API地址、API Key、权重、任务类型。SpringBoot启动时加载到内存Map,请求进来后按加权随机或健康检查结果选模型。这样线上想切换DeepSeek和千问,只需要改一行配置,不用改代码。
5. Web交互界面与前后端分离:从检测框到可视化大屏
5.1 前后端分离架构下的接口约定
前端这块我们用了Vue 3 + Element Plus,和后端完全分离部署。所谓前后端分离,不只是代码仓库分开,更重要的是接口约定要稳定,不然两边各改各的,联调阶段会非常痛苦。
我设计接口时定了几条约定:
- 所有接口统一返回结构:
{"code": 0, "message": "success", "data": {...}},code为0表示成功,非0表示业务异常。 - 鉴权统一走JWT。前端登录成功后把token存到localStorage,每次请求在Authorization头带上;后端用Spring Security拦截器统一校验。
- 跨域问题通过后端配置
CorsFilter解决,或者前端通过Nginx反向代理,让前后端同域,这也是生产环境更推荐的做法。
核心接口大概这么几个:
| 接口 | 方法 | 功能 |
|---|---|---|
/api/detect/image | POST | 上传图片检测 |
/api/detect/stream | GET | SSE实时检测结果推送 |
/api/records | GET | 分页查询检测历史 |
/api/records/{id} | GET | 查看单次检测详情 |
/api/llm/analyze | POST | 对检测结果触发大模型分析 |
/api/models | GET | 查询可用YOLO模型版本 |
5.2 实时展示方案:轮询、SSE与WebSocket选择
安全锥检测系统有一个很核心的交互场景:用户希望看到摄像头实时画面的检测效果,而不是等图片上传完再等结果。实时展示这块常见有三种方案:前端轮询、SSE(Server-Sent Events)和WebSocket。
我的选择是:单路画面检测用SSE,多路交互需要双向通信时用WebSocket。SSE实现简单,就是服务端往一个连接里持续推送消息,前端用EventSource接收。相比WebSocket,SSE天生支持自动重连,且只需要服务端单向推送,和“检测结果持续返回”这个场景完美匹配。
前端接收检测结果并画框的代码很直观:
const source = new EventSource('/api/detect/stream?cameraId=001'); source.onmessage = function (event) { const data = JSON.parse(event.data); drawBoxes(data.detections); };这里有一个细节:画框不要后端返回整张带框的图片,那样带宽消耗太大。我们的做法是后端只返回检测框坐标和置信度,前端拿到坐标之后在<canvas>或视频帧的绝对定位层上自己画。这样既流畅又能灵活控制样式,用户还可以选择只显示数量而不显示框。
6. 部署与避坑实录:从本地开发机到服务器
6.1 硬件与CUDA:AMD RX 580到底能不能跑YOLO
在技术社区里“AMD 580显卡能跑YOLO吗?需要安装CUDA吗?”是出现频率非常高的问题。结合我自己的实测结论:RX 580理论上可以跑YOLO,但过程远比N卡麻烦,而且收益很低。
RX 580是AMD的GCN架构显卡,PyTorch官方对GCN架构的ROCm支持并不完整,你需要装特定版本的ROCm,而且很多算子优化在ROCm下根本用不上,推理速度相比同价位N卡差很多。所以我给朋友的建议很直接:如果手上只有RX 580,首选方案是CPU推理 + ONNX Runtime,用小模型(YOLOv8s或YOLOv10s)反而能在CPU上跑到可用的帧率;如果需要GPU加速做训练,云GPU实例或者换一张NVIDIA显卡是更省心的路线。
这个问题背后有一个更值得讲的工程教训:模型选型不能只看精度,一定要先了解目标部署环境的硬件边界。我在项目里同时保留v8s和v12s两个模型,就是因为客户现场既有带N卡的工作站,也有纯CPU的老服务器。CPU上跑v12s帧率惨不忍睹,但跑v8s还能勉强维持在10帧左右做图片轮询检测。
6.2 服务器部署、进程守护与版本问题
部署层面,我们用的是阿里云ECS,系统架构是Nginx + SpringBoot + Python推理服务。Nginx负责两件事:一是托管前端静态资源,二是把/api开头的请求反代到SpringBoot的8080端口,把/detect请求反代到Python推理服务的8000端口。
Python推理服务和SpringBoot都需要进程守护。我在服务器上为推理服务写了一个systemd unit文件,保证崩溃后自动拉起:
[Unit] Description=YOLO Inference Service After=network.target [Service] User=deploy WorkingDirectory=/opt/cone-system/inference ExecStart=/opt/cone-system/venv/bin/uvicorn main:app --host 0.0.0.0 --port 8000 Restart=always RestartSec=5 [Install] WantedBy=multi-user.target这里必须提醒一个实际遇到的坑:SpringBoot版本不要盲目追新。有次我把项目从SpringBoot 2.7升到3.4,结果整整花了一下午处理Jakarta命名空间变更(javax改jakarta)、Spring Security 6配置项不兼容、以及MyBatis Starter版本对不上等一堆问题。生产项目讲究稳定优先,SpringBoot 2.7或3.2这种长时间维护的版本,比所谓的最新版靠谱得多。如果你是非Java背景接手这个项目,更不要在架构稳定后动大版本。
6.3 项目复盘与后续扩展方向
这套系统从零到上线,前后经历了约一个半月。回头看的几个关键决策,我认为都算正确:一是四个YOLO版本并行训练,使得客户更换部署硬件时不用重新训练;二是把大模型模块做成独立配置驱动,上线后调整研判逻辑完全不发版;三是前端实时展示用SSE而不是WebSocket,少了不少双向通信的复杂度。
如果后续继续迭代,我会优先做三件事:第一,把检测结果的错例自动收集起来,定期补充进训练集,形成数据闭环,让模型越用越准;第二,增加多路摄像头的流媒体接入,用RTSP拉流代替现在的图片上传模式;第三,把大模型从“反馈研判”升级为“主动预警”,比如结合锥桶间距和车道位置,自动判断安全锥摆放是否符合规范,异常时直接推送告警到钉钉或企业微信。
这也是我给同类项目的一个总结性建议:目标检测只是起点,真正的价值在于检测之后那一连串业务动作。谁先把检测、分析、交互、反馈串成闭环,谁的系统才真正具有生产意义。