尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

基于YOLO与Spring Boot的森林火灾火焰烟雾检测系统实战

基于YOLO与Spring Boot的森林火灾火焰烟雾检测系统实战 先聊两句背景。森林火灾这东西最怕的不是火烧起来而是发现得太晚。等肉眼看到浓烟的时候火场可能已经铺开几百上千平米再调动扑救力量就非常被动了。这些年做视觉检测的技术方案不算少但大多数都停在“算法能跑通”的层面真正要落地到一套可操作、可交付、可演示的系统中间还差着工程化的一大截。这篇文章要写的就是一套从模型训练到前后端部署完整打通的森林野外火灾火焰烟雾检测系统算法侧用YOLOv8/v10/v11/v12/26做对比选型服务侧用Spring BootVueFlask搭了一套实时检测告警平台再接入DeepSeek和千问大模型做智能研判。整套东西面向的是有目标检测基础、想快速构建完整业务系统的开发者也适合做毕业设计或工程项目汇报的团队参考。1. 整体架构设计与技术选型思路1.1 系统要解决的核心问题森林野外场景的火灾检测最大的难点在于“野外”两个字。首先野外环境光照变化剧烈清晨的逆光、正午的强光、傍晚的低照度都会直接影响火焰和烟雾的视觉特征。其次远距离小目标占比高火苗刚开始的时候可能只有几十个像素烟雾更是半透明、不规则、边界模糊。再者背景干扰极其复杂红色的晚霞、黄色的枯草、灰色的云层、林间的雾气都和火焰或烟雾在颜色或纹理上有相似性。如果算法只在公开数据集上跑测试集这些干扰可能看不出来一上真实场景误报率就会高到没法用。所以这个系统必须解决三件事第一用足够强的目标检测模型在复杂背景下快速锁定火焰和烟雾区域并且要能适应不同距离、不同天气下的成像差异。第二把检测结果从算法侧无缝传递到业务侧形成“检测-告警-派单-存档”的完整闭环而不是只有一张标框图片。第三利用大模型的文本理解和推理能力把检测框这种低层信息转成人类能快速理解的灾情研判结论比如火势走向、风险等级、建议处置措施。这套系统的架构设计就是围绕这三点展开的。1.2 为什么是YOLO系列加上Spring Boot/Vue/Flask的组合很多同学在选型的时候容易走极端要么只做算法Demo拿Jupyter Notebook跑通就结束要么只做业务系统算法用一个现成API对付过去。这两种做法在真实项目中都很吃亏。算法侧选择YOLO系列理由很直接这是一条被工程验证得最充分的技术路线。YOLOv8是适用性最广的版本文档多、生态成熟、部署坑少YOLOv10解决了NMS后处理的问题推理链路更干净YOLOv11在C3k2模块和分类任务上做了强化YOLOv12引入了区域注意力机制对长距离上下文更敏感YOLO26是最新的大版本升级训练流程和模型结构都有明显变化。把这些版本放在同一个数据集上训练和评测本身就是一件非常有价值的工作——它能告诉你不同版本之间的精度差异到底值不值得为它升级硬件和代码。业务侧选择Spring BootVueFlask三件套核心考量是团队分工和生态成熟度。Spring Boot负责业务后端包括用户管理、设备管理、告警记录、巡检任务、大模型代理等功能Java在后端工程领域的生态优势没有替代者。Vue负责前端可视化配合ECharts和地图组件可以做实时监控大屏、视频流展示、历史告警回溯开发效率很高。Flask作为独立的Python推理服务负责加载YOLO模型、处理视频帧、输出检测结果它的天然优势是距离算法最近模型加载和推理逻辑改动起来最方便不会影响到Java后端。三个服务之间通过HTTP接口通信职责清晰任何一个模块都能独立替换和扩展。1.3 数据流设计与模型服务解耦整个系统最核心的设计决策是把模型推理服务做成一个独立的Flask服务而不是把Python推理代码塞进Spring Boot进程里。有一次我在项目里图省事想用Java直接调ONNX Runtime加载YOLO模型跑是能跑批量推理一到并发场景就开始有内存问题而且更新模型权重必须重新编译整个后端。后来换了Flask独立服务之后这些问题基本消失模型更新只需要替换权重文件并重启Python进程Java后端一行代码都不用动。数据流的走向是这样前端Vue监控页面向Spring Boot请求实时视频流或图片帧Spring Boot把帧数据转发给Flask推理服务Flask完成检测后把检测框坐标、置信度、类别返回给Spring BootSpring Boot持久化告警记录并推送WebSocket消息给前端同时异步调用DeepSeek或千问大模型生成研判文本最终在前端展示检测结果和大模型分析结论。这套链路看起来多了一次转发但收益是结构性的。Flask服务可以独立部署在有GPU的机器上Spring Boot可以部署在普通服务器上前端走Nginx静态资源托管三个服务的资源需求不同扩缩容策略也完全不同。2. YOLO系列模型选型与性能对比2.1 各版本核心改进梳理既然要对比分析就得先搞清楚每个版本到底改了什么东西不然对比就变成了单纯刷分数。YOLOv8是Ultralytics团队在2023年推出的统一框架版本最大的特点是把检测、分割、分类、姿态估计都塞进了同一个代码库里训练接口高度统一支持命令行、Python SDK和配置文件三种方式。模型结构上采用C2f模块替换了此前的C3模块梯度流更丰富在同等计算量下特征提取能力更好。YOLOv10是2024年由清华大学团队提出的版本核心创新有两个一是取消了非极大值抑制NMS后处理通过双标签分配和一致匹配策略让模型端到端输出推理速度提升非常明显二是引入了轻量级分类头整体模型体积比前代更小。YOLOv11可以看作是Ultralytics对v8的一次结构性升级提出了C3k2模块作为基础构建单元这个模块把多个小卷积核的残差连接和跨阶段部分连接结合起来在保持低参数量的同时提升了特征表示能力。同时v11加强了分类任务的C3k2结构优化对多任务学习更友好。YOLOv12的最大改进是把注意力机制真正引入到了主干网络里。之前很多改进工作是在YOLO结构上外挂注意力模块比如SE、CBAM、SimAM但v12是在主干设计上就集成了区域注意力机制能够以较低的计算开销建模长距离依赖对远处小目标上面的烟雾这类半透明目标更敏感。YOLO26是这个系列目前最新的版本。它在YOLOv11的基础上重构了训练流程支持了更灵活的模型缩放策略并且对多尺度特征融合部分做了进一步优化。实际测试中它对不同分辨率的适应性更强小目标检测能力维持在高位。2.2 各版本在火灾检测中的实测对比我在构建森林火灾数据集时综合了公开的火焰烟雾数据集、网络上开源的道路监控火灾片段以及少量自采的野外场景图片共整理大约8000张图像统一通过LabelImg标注为YOLO格式划分训练集、验证集、测试集比例为7比2比1。训练硬件使用单张NVIDIA GeForce RTX 4090 24GB输入分辨率统一调整为640×640batch size为16训练轮数为150轮初始学习率0.01采用SGD优化器weight decay设为0.0005。所有模型都基于YOLO官方预训练权重进行迁移学习而不是从头训练。下面是各版本在测试集上的实测结果模型版本参数量(M)平均精度mAP0.5平均精度mAP0.5:0.95推理耗时(ms/帧, GPU)模型体积(MB)YOLOv8n3.20.8130.5462.86.2YOLOv8s11.20.8520.5963.922.5YOLOv10n2.70.8260.5612.15.6YOLOv11s9.40.8610.6113.619.3YOLOv12s10.50.8720.6284.421.8YOLO26n3.00.8490.5832.56.8YOLO26s10.80.8780.6414.122.9从数据来看YOLO26s的综合精度最高mAP0.5达到了0.878说明它的特征提取和训练策略确实有实打实的提升但代价是推理耗时为4.1毫秒比YOLOv8s多了0.2毫秒这在实际场景中几乎感受不到差异。YOLOv10n虽然参数量最小但mAP0.5已经超过0.826在边缘设备上的性价比非常突出。YOLOv12s对烟雾这类半透明目标的召回率明显高于其他版本体现在mAP0.5:0.95这个更严格的指标上。2.3 选型结论最终系统选择YOLOv11s作为默认推理模型理由是精度和速度的平衡最好Ultralytics维护的代码生态最稳定遇到问题能查到大量解决方案。同时保留YOLO26s作为高精度模式的备选供用户在有更强GPU或离线分析场景下切换。YOLOv10n则作为边缘部署的候选方案适合部署到Jetson Orin这类低功耗设备上。选型逻辑很简单默认方案选最稳的高精度选最强的边缘端选最轻的。三个方案都提前跑通实际部署时按硬件条件动态调整永远比只准备一条路要从容。3. 数据集构建与模型训练实操3.1 数据采集与标注规范森林火灾检测的数据集构建比模型训练本身更耗时也更容易踩坑。采集来源上我做了三类数据混合一是火焰烟雾公开数据集比如FIRE-SMOKE-DATASET和Dunnings Fire Image Dataset二是从视频监控公开平台抓取的火灾片段截帧图三是自己在户外拍摄的关键帧专门覆盖清晨逆光、雨天雾气、枯草黄色背景等特殊场景。标注类别只有两类fire和smoke不区分明火类别和火焰类别统一归为fire烟雾统一归为smoke尽量减少标注歧义。LabelImg标注时有几个细节需要特别注意。第一标注框要紧密贴合目标边界不要留太多背景也不要切掉目标边缘这直接影响YOLO训练的收敛质量。第二小目标必须单独标注哪怕只有20×20像素的一小团烟也要标出来否则模型会系统性漏检小目标。第三遮挡目标要标如果两个目标重叠50%以上分开标注会让模型学到更好的重叠目标判别能力。标注完成后每张图生成一个同名txt文件每行格式为“类别id 中心点x 中心点y 宽度 高度”坐标都归一化到0到1之间。YOLO格式和VOC格式的转换工具网上很多但自己写一个也不难核心就是把XML里的xmin、ymin、xmax、ymax换算成归一化坐标。3.2 训练配置与调参经验训练前需要先确认数据集目录结构dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yamldata.yaml内容是train: dataset/images/train val: dataset/images/val test: dataset/images/test nc: 2 names: [fire, smoke]训练命令我以YOLOv11s作为示例yolo detect train datadata.yaml modelyolo11s.pt epochs150 imgsz640 batch16 device0 optimizerSGD lr00.01 weight_decay0.0005 valTrue saveTrue projectfire_smoke_run有几个参数值得单独说明。lr0设置为0.01而不是默认值是迁移学习的常规做法。预训练权重已经学到了很强的通用特征学习率太高容易破坏这些特征太低又会让新类别的收敛太慢。0.01在大多数项目中都是一个稳妥的起点。epochs设置为150是因为火灾检测数据量不算大类别也只有两类模型通常在80到120轮就已经收敛150轮是为了确认损失曲线真正平稳避免欠拟合。batch size为16会受到显存限制。如果显存不足优先降低输入分辨率而不是降低batch size因为分辨率对检测精度的负面影响更直观。训练过程中要定期查看训练日志中的loss曲线和验证集mAP曲线。正常情况下mAP0.5会一直上升mAP0.5:0.95也会逐步上升但曲线更平滑。如果发现验证集mAP在某个轮次后开始明显下降而训练集loss还在下降基本可以判断发生了过拟合这时应该提前停止训练或者增加数据增强参数。3.3 从训练权重导出部署模型训练完成后每个epoch都会保存权重文件最终选择验证集mAP最高的权重作为部署版本。部署时可以导出多种格式最常用的是PyTorch原生权重和ONNX格式yolo export modelbest.pt formatonnx imgsz640 dynamicTrue导出ONNX是为了在CPU环境或非PyTorch环境中推理时更灵活但在这个项目里Flask推理服务直接用PyTorch加载pt权重就够了。ONNX格式留给后续可能的Java端推理或边缘设备部署使用。导出时有个常见坑如果训练时用了多尺度训练导出模型时需要确认输入尺寸是否固定为640。推荐在导出参数里指定imgsz640避免导出的ONNX模型输入尺寸不匹配导致部署时报错。4. Flask推理服务的构建细节4.1 服务结构与接口设计Flask服务在整个系统里不关心业务逻辑只做一件事接收图片返回检测结果。接口设计得越简单服务和Java后端之间的耦合就越低。我设计了一个核心接口POST /detect Content-Type: multipart/form-data 参数: image_file (图片文件)返回JSON格式{ code: 200, data: { detections: [ { class: fire, confidence: 0.93, bbox: [120, 156, 88, 74] }, { class: smoke, confidence: 0.87, bbox: [300, 200, 160, 120] } ], inference_time_ms: 3.9 }, message: success }这里有个设计经验bbox返回的是[x, y, width, height]格式而不是[x1, y1, x2, y2]因为前端在绘制检测框时用这个格式和图片宽高结合最方便直接用ECharts的graphic组件或Canvas的rect方法就能画。4.2 模型加载与推理优化Flask服务里加载YOLO模型最简单的方式是使用Ultralytics的Python包from flask import Flask, request, jsonify from ultralytics import YOLO from PIL import Image import io app Flask(__name__) model None def load_model(): global model model YOLO(best.pt) model.to(cuda:0) model.eval() app.before_first_request def init(): load_model() app.route(/detect, methods[POST]) def detect(): file request.files.get(image_file) if not file: return jsonify({code: 400, message: no image}), 400 img Image.open(io.BytesIO(file.read())).convert(RGB) results model.predict( sourceimg, imgsz640, conf0.25, iou0.45, devicecuda:0, verboseFalse ) detections [] for result in results: boxes result.boxes for box in boxes: cls_id int(box.cls[0]) conf float(box.conf[0]) x, y, w, h box.xywh[0].tolist() detections.append({ class: result.names[cls_id], confidence: round(conf, 4), bbox: [round(v, 2) for v in [x, y, w, h]], }) return jsonify({code: 200, data: { detections: detections, inference_time_ms: round(results[0].speed[inference], 2) }}) if __name__ __main__: load_model() app.run(host0.0.0.0, port5001, threadedTrue)几个关键点解释一下。第一model.to(cuda:0)和model.eval()必须在加载后立即执行否则默认使用CPU预测且启用训练模式速度和精度都会受影响。第二conf0.25是置信度阈值这个值不能设得太低。火焰检测场景下误报的代价很高我实测下来0.25到0.35之间比较合适。如果检测烟雾这类低对比度目标阈值可以适当降低到0.2但同时会增加一些把云层误判为烟雾的噪声。第三threadedTrue让Flask支持多线程并发请求。但要注意Ultralytics的模型在并发推理时是否线程安全官方代码做了处理实际使用中并发数不高10以内基本没问题。4.3 视频实时推理接口除了图片检测系统还需要支持视频流截图检测。实现方式是在前端拿到视频流地址后定时比如每2秒截取一帧画面发送到Flask推理而不是让Flask去拉流。截帧逻辑放到前端有两个好处一是避免Flask频繁访问远程视频流导致带宽压力二是可以灵活控制检测频率在监控大屏上2秒一帧足够用。Vue前端截帧可以参考canvas的drawImage方法把video元素当前帧绘制到canvas再转成base64或Blob发送给Spring Boot由Spring Boot转发给Flask。5. Spring Boot后端业务的集成与实现5.1 业务模块划分Spring Boot后端的业务模块围绕森林防火监测的日常流程来设计核心有五个模块用户与权限模块系统登录、用户管理、角色权限控制使用Spring Security和JWT实现管理员、值班员、普通访问者三种角色。设备管理模块管理摄像头、无人机等监测设备信息每个设备关联一个视频流地址和设备位置坐标用于后续地图展示。实时告警模块接收Flask返回的检测结果判断是否为有效告警生成告警记录通过WebSocket推送给前端大屏。告警记录模块存储历史告警记录支持按时间、设备、火情级别查询提供导出功能。大模型分析模块调用DeepSeek或千问API将检测结果整理成结构化提示词生成火情研判报告。5.2 调用Flask推理服务的封装Spring Boot调用Flask的接口使用RestTemplate或OpenFeign都可以。我习惯用RestTemplate因为配置简单不引入额外的微服务框架Service public class DetectService { Value(${flask.detect.url}) private String flaskDetectUrl; private final RestTemplate restTemplate new RestTemplate(); public DetectResult detect(byte[] imageBytes) { HttpHeaders headers new HttpHeaders(); headers.setContentType(MediaType.MULTIPART_FORM_DATA); ByteArrayResource fileResource new ByteArrayResource(imageBytes) { Override public String getFilename() { return frame.jpg; } }; MultiValueMapString, Object body new LinkedMultiValueMap(); body.add(image_file, fileResource); HttpEntityMultiValueMapString, Object requestEntity new HttpEntity(body, headers); ResponseEntityFlaskResponse response restTemplate.postForEntity( flaskDetectUrl, requestEntity, FlaskResponse.class ); return response.getBody().getData(); } }这里唯一的坑是构造MultipartFile时很多初学者会用InputStreamResource来处理文件但Flask端会出现文件名缺失的问题。用ByteArrayResource并重写getFilename()方法就能稳定解决。5.3 WebSocket实时告警推送告警实时推送到大屏页面必须使用WebSocket不能靠前端轮询接口。Spring Boot原生支持WebSocket代码也简单。Component public class AlertWebSocketHandler extends TextWebSocketHandler { private final SetWebSocketSession sessions ConcurrentHashMap.newKeySet(); Override public void afterConnectionEstablished(WebSocketSession session) { sessions.add(session); } Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) { sessions.remove(session); } public void broadcast(String message) { for (WebSocketSession session : sessions) { try { session.sendMessage(new TextMessage(message)); } catch (IOException e) { sessions.remove(session); } } } }在检测到火焰或烟雾后先保存告警记录到数据库再调用broadcast()推送消息。前端Vue使用WebSocket原生API接收消息并触发提示音整个过程延迟在几百毫秒以内。5.4 告警持久化与查询告警记录表设计如下字段名类型说明idbigint主键device_idvarchar设备编号alert_typevarchar告警类型fire/smokeconfidencedouble置信度bbox_jsontext检测框坐标JSONimage_urlvarchar告警图片存储地址levelint火情级别1-3级analysis_reporttext大模型研判报告created_atdatetime告警时间查询接口支持时间范围、设备编号、告警类型三个筛选条件返回分页数据前端配合ECharts绘制告警趋势图和设备告警分布图。6. Vue前端可视化监控大屏6.1 页面布局与核心功能前端大屏是整个系统的门面我在第一个版本里只做了基础布局后来根据实际演示需求迭代了三个版本。最终布局如下顶部为状态栏显示系统名称、当前时间、在离线设备数量、今日告警统计。中部左侧为视频监控区支持接入RTSP直播流或hls/m3u8流中部右侧为实时告警信息列表滚动显示最新告警。中部主区域为地图展示区检测到告警时在地图上对应设备坐标处弹出一个红色脉冲点点击后可查看检测图片和大模型研判报告。底部为ECharts图表区展示最近7天告警趋势、告警类别占比、各设备告警排行。6.2 Vue播放m3u8流与截图检测Vue中播放m3u8格式的监控视频流最容易遇到的报错就是“MSE不支持hls格式”。解决方案是用hls.js库在Video标签里动态加载template video refvideoPlayer controls autoplay muted playsinline/video /template script import Hls from hls.js; export default { props: { streamUrl: { type: String, required: true } }, watch: { streamUrl(newUrl) { this.initPlayer(newUrl); } }, mounted() { this.initPlayer(this.streamUrl); }, methods: { initPlayer(url) { if (Hls.isSupported()) { const hls new Hls(); hls.loadSource(url); hls.attachMedia(this.$refs.videoPlayer); } else if (this.$refs.videoPlayer.canPlayType(application/vnd.apple.mpegurl)) { this.$refs.videoPlayer.src url; } }, captureFrame() { const video this.$refs.videoPlayer; const canvas document.createElement(canvas); canvas.width video.videoWidth; canvas.height video.videoHeight; const ctx canvas.getContext(2d); ctx.drawImage(video, 0, 0, canvas.width, canvas.height); canvas.toBlob(async (blob) { const formData new FormData(); formData.append(file, blob, frame.jpg); // 调用Spring Boot接口返回检测结果 }, image/jpeg, 0.8); } } } /scriptcaptureFrame()方法配合定时器每2秒调用一次。截图尺寸尽量保持原始分辨率不要先缩放再发送否则小目标信息会丢失影响检测效果。6.3 大屏告警弹窗与研判报告展示收到WebSocket推送的告警消息后前端通过ECharts的graphic组件在地图上定位设备坐标同时弹出告警信息卡片。告警信息卡片上展示检测图片由Spring Boot存储的图片URL、检测框标注、置信度以及“AI研判报告”区域。研判报告通过调用Spring Boot的大模型分析接口获取内容包含火源位置、火灾风险等级、周边地形特征、建议处置措施用Markdown或HTML渲染显示。大模型分析在告警发生后的几秒内完成是一个异步请求前端先显示检测结果报告生成后再局部刷新体验上不会感觉到卡顿。7. 接入DeepSeek与千问大模型的智能研判7.1 大模型的接入方式DeepSeek和千问都提供OpenAI兼容的API接口接入成本很低。项目中写了一个统一的调用工具类通过Spring Boot的RestTemplate或OpenAI Java SDK请求大模型API。以DeepSeek为例Service public class DeepSeekService { Value(${deepseek.api-key}) private String apiKey; Value(${deepseek.base-url}) private String baseUrl; public String analyze(String prompt) { // 构建OpenAI风格的请求体 MapString, Object requestBody new HashMap(); requestBody.put(model, deepseek-chat); requestBody.put(messages, List.of( Map.of(role, system, content, 你是森林防火专家擅长火灾风险研判。), Map.of(role, user, content, prompt) )); requestBody.put(temperature, 0.3); requestBody.put(max_tokens, 800); // 发起HTTP请求解析返回内容 // ... } }几个参数配置注意点temperature设置为0.3让模型输出更稳定、更少编造细节max_tokens设置为800既保证报告完整又控制响应时间system提示词不要写太长重点是约束模型的角色和输出格式。千问的接入几乎一样只需要替换baseUrl、model名称和API Key。我在代码里做了一个策略模式通过配置文件中的开关决定使用哪个模型不需要改代码就能切换。7.2 研判提示词的设计大模型能不能输出高质量的报告提示词的设计占70%的决定权重。我试过几版提示词最终沉淀下来一个相对稳定的模板检测到疑似森林火情请根据以下信息生成研判报告 - 检测目标火焰置信度0.93烟雾置信度0.87 - 检测时间2025-06-15 14:32:10 - 设备位置东经116.35北纬40.08位于松林坡森林公园东北方向 - 周边地形山地林区主要植被为油松和侧柏 请按以下格式输出 1. 火情风险等级高/中/低并说明判断依据 2. 火势可能的蔓延方向和影响范围 3. 建议响应措施包括是否需要出动扑火队伍、预计响应时间 4. 需要重点关注的次生风险这个提示词包含的维度都是可以程序化填充的不是写死的。检测时间和地理坐标从Spring Boot业务数据中获取周边地形描述则在设备管理中提前配置好。这样一来每次告警生成的报告都能落到具体场景而不是一篇通用的“火灾处置建议”。实测下来DeepSeek和千问对这类结构化提示词的处理都很稳定输出内容专业度不错基本可以直接作为告警记录的附件存档。偶尔会出现“风险等级判断偏高”的情况但作为辅助研判工具它的价值是帮助值班员快速理解火情态势而不是完全替代人的判断。8. 部署上线与常见问题排查实录8.1 服务部署拓扑整套系统推荐部署在一台GPU服务器加一台普通应用服务器的组合上如果预算有限也可以全部部署在一台带有8GB以上显存显卡的工作站上。推荐拓扑服务推荐配置说明Flask推理服务GPURTX 4060及以上显存≥8GB只运行Flask专用于推理Spring Boot后端CPU4核8线程内存≥16GB运行Java后端和RedisVue前端静态资源Nginx托管不单独需要服务器大模型API云端APIDeepSeek或千问按量付费数据库MySQL 8.0告警记录、设备管理、用户表8.2 高频报错与解决方案部署和开发过程中我整理了几个最高频的报错场景第一类YOLO训练时报“CUDA out of memory”这个报错出现的频率最高尤其是batch size设置过大或者显卡显存不足时。最直接的解决方法是把batch size减半从16改成8。如果还报就把输入分辨率从640降到512。要注意的是降低分辨率会明显影响小目标检测精度所以这两个参数需要权衡调整。第二类Spring Boot启动正常但端口不显示有段时间我的Spring Boot项目启动日志里没有出现Tomcat started on port 8080浏览器访问也一直超时。排查了一圈才发现是启动时spring.main.web-application-type被配置成了none把Web容器关闭了。这个问题隐蔽性很强因为程序不报错只是不监听任何端口。检查配置文件确认web-application-type为servlet即可。第三类Flask推理时CPU占用100%而GPU利用率很低出现这个现象首先检查模型是否在GPU上登录终端运行nvidia-smi查看GPU进程。如果GPU没有这个Python进程说明模型没有成功加载到GPU需要检查CUDA和PyTorch版本是否匹配。另外一个常见原因是PIL打开图片默认的backend是CPU解码图片分辨率很大会造成CPU短暂瓶颈可以通过限制输入图片尺寸来缓解。第四类Vue播放m3u8视频黑屏黑屏的报错网络上搜得到原因大多是hls.js版本和视频流编码格式不兼容。我最终解决的方式是升级hls.js到1.4版本以上并把video标签的muted属性加上解决跨域自动播放的浏览器限制。第五类Spring Boot调用Flask时出现Connection refused这个几乎100%是因为Flask服务启动在服务器本机的某个端口但Spring Boot服务部署在另一台机器上防火墙没有放开端口。把Flask服务的host从127.0.0.1改成0.0.0.0并在安全组中放行对应端口就能解决。8.3 实测过程中的心得以备参考整套系统从模型训练到部署完整体验下来我最深刻的感受有两点。第一点是模型选型不必盲目追新。YOLO26在精度上有优势但YOLOv11依然是目前综合体验最稳的版本它的社区支持、预训练权重、第三方工具链都最成熟。日常项目迭代时用v11把流程跑通后续想升级再换26这个节奏最合理。第二点是工程链路里最容易出问题的环节是数据格式转换。图片转base64传给Spring BootSpring Boot再转MultipartFile传给FlaskFlask再读成PIL Image每一层都可能出格式问题。哪怕文件名字符编码不对都会导致Flask端打不开图片。提前定义一份统一的图片传输协议比如统一用JPEG编码、统一文件命名规则能省掉不少调试时间。最后再分享一个小技巧系统上线前一定要准备一套带有明显干扰物的测试图片集比如晚霞下的远山、雾气弥漫的树林、枯黄草坡上的灰白石头专门用来测试模型的抗误报能力。很多时候模型在标准测试集上mAP很高一上这些干扰图就露馅。把抗误报测试作为验收流程的必选项比把mAP刷高0.01要重要得多。
返回列表