ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

YOLOv7水位标尺识别与边缘端部署:从数据采集到预警闭环的完整实战

YOLOv7水位标尺识别与边缘端部署:从数据采集到预警闭环的完整实战 每年汛期水位观测都是防汛工作的第一道防线。传统做法是安排人工盯守或者定时去河边看水尺白天还好到了夜间暴雨、能见度差的时段读数不准、时效性滞后的问题会被成倍放大。拿YOLOv7去做河道水位标尺识别用目标检测自动读出水尺读数并触发分级预警这事儿听起来不复杂但真正落地的坑相当多。我从2023年开始陆续做了两套河道水位识别预警系统踩过数据采集、标注规范、模型训练、边缘端部署的不少坑这篇就完整复盘一下整个方案是怎么从零搭起来的。先说结论水位标尺识别跟常规的目标检测任务有一个很大的区别——它不追求识别出标尺就完事而是要从标尺画面里精确推算水位数据这决定了整个技术方案不能照搬通用目标检测的思路。模型只是整个链路里的一环前后还涉及到数据标定、透视校正、时序平滑、告警工程化缺一环都会在真实场景里出问题。1. 水位标尺识别的核心难点为什么通用目标检测方案直接搬会翻车1.1 标尺目标在画面中的真实尺寸占比很多人第一次做水位标尺识别习惯性把网上开源的YOLOv7工程下载下来标几百张图丢进去训练然后发现模型对水尺这个目标本身检测得还行但刻度、数字这些关键信息全糊了。原因很简单在真实河道监控画面里水尺是典型的小目标而且是小到刻度级别的小目标。拿我实际部署的站点举例一个标准的水尺宽约0.3米高1米安装距离摄像头大约12到15米。1920×1080的原始画面里水尺整体只占大约90×320像素而刻度线上的数字比如12、13这种单个字符可能只有10×16像素。经过YOLOv7的640×640输入缩放以及5次下采样之后骨干网络最深层的特征图是20×20一个10像素的字符在特征图上只剩不到1个像素。这意味着刻度数字的信息在深层特征图里几乎完全丢失。这个问题不是加训练轮数能解决的属于感知层面的硬约束。我在第一版方案里就是直接套默认配置训练结果数字目标的AP值只有45%左右根本没法用。1.2 环境干扰水面反光、雨滴附着与标尺污损河道场景的目标检测还有一个通用的痛点干扰远比工业质检场景复杂。我把实际采集数据里遇到的环境干扰分成了几类水面反光与波纹水面波动的倒影会干扰水面线定位太阳斜射时水面的强烈反光会把标尺下半部分吞掉。雨滴与雾气雨天标尺表面会附着水珠轻则局部模糊重则把数字区域完全遮住清晨河面起雾时整根标尺对比度极低。污损与老化河道标尺常年泡水表面会附着青苔、泥浆数字漆面脱落是常态。遮挡偶尔有漂浮物、树枝漂过挡住标尺虽然时间不长但如果在预警临界点出现就比较危险。夜间低照度很多河道站点没有路灯月光下可见光相机基本拍不到标尺细节。这些干扰在通用目标检测数据增强里覆盖不足。普通项目做一点随机亮度抖动、高斯噪声就应付过去但水位标尺识别不行——它是毫米级的读数任务雨滴遮住一个E字形刻度读数就可能偏差好几厘米。2. 数据采集与标注决定识别上限的源头工作2.1 多场景采集白天、黑夜、雨天、背光一个都不能少水位标尺识别的数据采集是典型的三分模型七分数据。我第一版训练集只有白天不同水位的照片大概600来张模型在白天测试还不错一到傍晚和雨天准确率就断崖式下跌。后来第二版数据做了系统性重采覆盖条件如下表场景维度采集要求说明水位范围覆盖设防水位以下到保证水位以上汛期提前架设相机连续录制尽量覆盖历史高水位时段白天、黄昏、夜间含月光、无月光夜间建议使用带红外补光的摄像头天气晴天、阴天、小雨、大雨、雾天雨天数据务必单独抽帧不能混在正常数据里光照方向顺光、侧光、逆光、水面强反光逆光场景单独采集这类数据增强很难模拟有个我踩过的坑用固定摄像头连续录制视频再抽帧比用手机在不同角度拍摄的效果好得多。原因是固定机位的视角、畸变、距离都和实际部署一致模型学到的特征更贴近部署环境。手机拍摄的图虽然角度多样但透视畸变太复杂反而拖低模型在固定机位上的精度。采集量方面单站点建议不少于1500张有效帧我最终用了2100张左右。如果涉及多个站点优先保证同一型号标尺的图像覆盖不同标尺样式E字型、数字型建议分开训练模型或至少独立标注。2.2 标注规范标尺主体、数字区域与刻度线的层级关系标注这件事我犯过一个印象深刻的错误第一版把标尺整体作为一个目标框检测模型确实能稳定找到标尺位置但后续要做读数换算时发现标尺框内没有数字的精确位置信息没法做水位读数。后来改成三个类别并行标注shuichi标尺主体外框用于后续裁剪和透视校正waterline水面线在标尺附近的水平线段用于定位水面位置scale_num标尺上的数字从最低到最高全部标注用于校验读数标注工具我用的是LabelImg和X-AnyLabeling前者轻量后者支持半自动预标注。对于标尺这种特征明显的场景先用第一个版本的模型做预标注再人工修正能把标注效率提升三倍以上。标注规范里有一个不能妥协的点数字目标必须标注精确宁可框小一点不要框大。框大了会把相邻刻度的纹理带进来模型学到错误特征。数字被遮挡超过30%的直接跳过不标标了会引入噪声。另外水面线这个目标需要专门说明标注的是水与标尺的交界位置不要包含水面的倒影区域否则会把换算值抬高。2.3 数据增强策略模拟雨线、反光与视角倾斜对标注好的数据我做了比默认配置更强的增强策略。YOLOv7自带的Mosaic、MixUp、HSV扰动肯定要开但针对水尺场景我额外加了三种增强随机雨线叠加用程序生成随机长度、角度、透明度的雨线叠加在训练图上。这一步对雨天实测AP提升非常明显我的对比实验中加了雨线增强后雨天场景的scale_numAP从62%提升到81%。亮度与对比度随机偏移模拟夜间和背光条件。我会把亮度偏移的下限调到-40%上限调到20%同时对比度做随机调整让模型在低照度下不至于完全失明。透视扰动对标尺区域做轻微的水平/垂直方向仿射变换模拟摄像头角度微小漂移。这一步能让模型对安装角度变化更鲁棒但要注意扰动幅度不能太大角度偏移控制在±10度以内太大会让数字形变得没法认。训练时我把Mosaic的启用概率从默认的1.0降到0.8这个细节是很多人忽略的。水位标尺目标在画面里的位置相对固定标尺总是竖立在河道边缘Mosaic把四张图拼一起会让目标位置和尺度分布变得跟实际场景差异很大。调低一点让模型多学点真实布局对小目标精度有帮助。3. YOLOv7选型与训练调参精度和部署成本的平衡点在哪3.1 为什么选YOLOv7而不是YOLOv5或YOLOv8很多朋友问我现在做项目为什么不直接用YOLOv8。我选YOLOv7有很实际的原因不是因为它最新最强而是它在精度、速度、部署成熟度这个三角形里平衡得最好。YOLOv7是2022年7月提出的核心亮点是E-ELAN结构通过扩展、打乱、合并基数来提升特征提取效率。对比同一时期的YOLOv5v7在同等输入尺寸下能拿到更高的mAP对比YOLOv8v7在推理速度上反而有优势——在单张GPU上未见明显差异但上了TensorRT之后重参数化卷积带来的加速收益非常明显。对河道监控这种边缘端部署的场景单帧推理时延每降低10毫秒都很值。另外一点是生态成熟度。YOLOv7训练权重可以直接转ONNX再转TensorRT engine流程已经非常顺社区踩坑记录也多遇到问题搜一下基本有解决方案。反观更新版本的模型往往需要等一段时间社区适配才跟上。对工程项目来说稳定可复现比好一点点重要得多。3.2 输入分辨率、Anchor聚类与核心超参数配置水位标尺识别这个任务我把输入分辨率从默认的640提到了960。这是小目标场景下最直接有效的操作。分辨率提高后10×16像素的数字在特征图上能多保留一到两行像素检测头的感受野能更好地覆盖目标。代价是训练显存和推理速度。训练阶段我用的是一张RTX 4090 24GBbatch size设16显存占用约20GB完全扛得住。推理阶段我在Jetson Orin Nano上跑960输入大约12FPS完全够用因为水位预警根本不需要每秒30帧实时检测每2到3秒抽一帧就够了。如果你推理设备性能更弱建议先保精度把检测间隔拉长而不是降低分辨率。超参数方面我最终用的配置如下参数数值说明输入尺寸960×960小目标精度优先optimizerSGDAdam收敛快但泛化略差SGD更稳初始学习率0.01配合cosine衰减batch size16根据显存调整epochs300数据量小时多训练几个周期mosaic概率0.8防止真实布局被过度破坏类别数3shuichi / waterline / scale_num还有一个关键步骤用k-means重新聚类Anchor。COCO预训练模型的Anchor是针对通用目标分布的里面的目标尺度范围很广但我们的目标高度集中——标尺主体是竖长的宽高比约1:3到1:4数字是极小的正方形水面线是极扁的长方形。如果不重新聚类模型在训练初期要多花很多轮次去调整Anchor而且可能陷入局部最优。我重新聚类后scale_num的召回率提升了4.5个百分点。训练结果记录640输入下mAP50约87.2%960输入下约93.6%scale_num单类AP50从640的74.8%提升到960的88.3%。这个精度已经足够支撑水位读数换算了。3.3 水位读数换算不是检测出标尺就完事了模型输出的三个类别怎么变成最终的水位值这是整个系统里最容易被人忽略的一环。做法是先用shuichi区域定位标尺位置裁剪出标尺矩形再用waterline定位水面线的像素行坐标最后从scale_num里找到水面线上方最近的数字根据数字的物理高度推算精确水位。关键细节在于透视校正。摄像头不可能完全正对标尺安装俯拍或侧拍会导致标尺在画面里是梯形的。直接用像素比例去算水位误差会非常大。我实测过一只30度俯拍角的摄像头不校正直接换算水位误差最高能到15厘米——这在防汛预警里是完全不能接受的。解决方案是透视校正利用shuichi四个角的坐标做一次四点透视变换把标尺区域拉正成标准矩形然后在校正后的图里算水位线相对底部刻度的比例。做了校正之后误差能控制在±2厘米以内。还有个细节如果场景里标尺不是垂直安装而是有一定倾斜需要在透视校正后再补一次旋转变换把标尺旋转到严格竖直方向否则比例换算依然有偏差。4. 预警闭环从模型输出到水位超限告警4.1 读数稳定性时序平滑与低置信度保护模型单帧识别会有抖动这是目标检测的天然属性。我在实际测试中发现即使画面完全静止相邻两帧识别出的水位读数也可能差3到5厘米原因是水面线的像素定位受波纹影响数字检测在不同帧也有微小的框位置波动。我最终实现了一套三层过滤逻辑第一层置信度过滤。waterline置信度低于0.5的帧直接丢弃不参与计算shuichi置信度低于0.7的帧直接判为不可信保留上一帧读数。第二层滑动窗口中值滤波。取最近15帧的读数排序取中值作为当前水位。中值比均值更抗离群点单帧误识别的数字不会把结果带偏。第三层变化幅度限幅。如果当前读数与前一帧读数差超过20厘米判定为异常跳变不更新水位同时记录一条疑似干扰日志。这三层下来实际运行中读数的稳定性能做到24小时最大波动不超过±2厘米前提是画面没有严重遮挡。还要处理的一个情况是暴雨天水面涨速快连续多帧读数大幅度上升是正常的。限幅逻辑不能误杀真实快速涨水。我的方案是如果连续3帧都朝同一方向变化且变化幅度都超过5厘米就解除限幅直接跟进因为这时候大概率是真实的快速涨水过程预警系统要的就是这种灵敏度。4.2 预警分级与推送通道设计预警分级按水利行业习惯来设防水位、警戒水位、保证水位三级。预警等级触发条件推送方式关注级水位超过设防水位公众号模板消息 平台记录预警级水位超过警戒水位公众号 短信报警级水位超过保证水位公众号 短信 电话语音可配置推送通道我建议至少做两条链路。公众号适合日常值班提醒便利且免费短信是防汛值班的刚需——夜间值班人员不一定盯着手机看公众号但短信的到达率和感知强度明显更高。更高一级可以接电话语音告警但要注意防止误报造成狼来了效应所以我给语音告警加了二次确认逻辑连续3次检测超过保证水位才触发中间任意一次低于阈值就取消。所有告警推送必须附带现场截图。截图是值班人员判断是否误报的最快手段比任何表格数据都直观。我实现的方案是触发告警时同步保存当时的原始帧通过webhook附带图片URL推送到公众号值班人员点开就能看到现场情况。4.3 低照度场景的补充方案夜间是水位标尺识别的重灾区。完全依赖可见光的方案在夜间基本不可用。我用的方案是红外补光——使用带红外灯的网络摄像头标尺在红外光下反射特征明显通常比可见光图像更容易识别字符轮廓。但红外方案有个副作用标尺上的红蓝漆面在红外下对比度下降部分数字可能变淡。我实测发现用红外灰度图重新标注一部分夜间数据训练一个独立的夜间模型比把所有数据混合训练效果更好。夜间模型把输入图转成灰度再进网络白天模型用RGB。两个模型按时间段切换解决了昼夜精度差异大的问题。如果现场条件不允许加红外灯还有个折中方案用可见光相机长曝光。水位标尺场景基本是静态的水面会有轻微波动长曝光会让水面线变模糊但对于标尺数字的影响不大。不过这招对相机防抖要求高而且夜晚光线不足时提升有限只能说作为备选。5. 边缘端部署与实测效果Jetson上的YOLOv7部署记录5.1 TensorRT导出与版本匹配的坑模型训练完是PyTorch权重但直接跑PyTorch推理在边缘设备上性能太差。我在Jetson Orin Nano上的优化路径是PyTorch → ONNX → TensorRT engine。这条链路里的坑主要集中在ONNX导出和TensorRT版本匹配上。YOLOv7官方仓库的export脚本支持直接导出ONNX但有几个参数要注意--dynamic要开因为实际推理的batch size和输入尺寸可能动态变化opset版本建议用16或17太低的opset会导致某些算子导出报错。TensorRT版本匹配是最大的坑。TensorRT 8.5和8.6之间对某些op的支持不同同一个ONNX文件在不同版本下生成的engine不能直接混用。我遇到过在8.5上正常生成的engine拷到8.6环境里加载直接报invalid engine错误。解决办法只有一个保证训练、转换、推理三端环境版本一致用同一套Docker镜像或者同一块SDK烧录的Jetson设备。另外建议ONNX里的NMS后处理放模型外。即导出时只保留检测头的原始输出NMS在TensorRT推理之后用Python或C实现。这么做的好处有两点一是避免ONNX解析NMS算子带来的兼容性风险二是方便在NMS前后插入自定义逻辑比如按类别设置不同的置信度阈值和IOU阈值。5.2 Jetson Orin Nano实测精度、速度与功耗数据我最终的部署环境是Jetson Orin Nano 8GB版本TensorRT FP16精度部署了960输入尺寸和640输入尺寸两个版本。项目960输入640输入单帧推理耗时约82ms约40ms折算FPS约12FPS约25FPSmAP5093.6%87.2%实际运行功耗7~10W7~9W水位读数误差±2cm±4cm因为预警场景不需要每帧都做推理我的程序设置是每3秒抽一帧做检测所以960输入版本在Orin Nano上完全够用还能留出算力给视频流解码和告警推送线程。功耗方面Orin Nano整机功耗在7到10瓦之间用一块太阳能板加蓄电池就能供起来很适合野外河道站点。相比在服务器上跑推理边缘端的优势是断网也能本地判断是否超警戒水位至少能本地存储告警日志网络恢复后再补传。5.3 高频踩坑清单给正在做同类项目的人做了两套系统、运行了大半年之后我整理了这份高频踩坑清单希望你能避开标尺起雾和水珠雨停之后标尺表面附着的水珠会造成数字误识别这时候水位读数标称值与真实值可能偏差5厘米以上。我用了一个简单办法结合前几帧的湿度判断如果检测置信度整体下降且读数异常跳变系统自动发一条标尺疑似遮挡请人工核查的提醒而不是直接上报水位。摄像头角度漂移长期风吹日晒摄像头支架会慢慢松动导致画面角度略微变化标尺的透视校正参数就失效了。我在系统里加了定期自检每周日凌晨自动分析一帧标尺区域跟初始标定时的标尺区域IoU比对偏差超过阈值就发维护通知。数字漆面脱落标尺用久了数字会掉漆模型识别10和16这类字形相近的数字时容易出错。我建议标注阶段对字体样式单独做无监督聚类不同样式各自增强这样即使某个数字漆面残缺模型还能靠周围刻度的相对位置做修正。模型漂移河道场景的环境变化是渐进的比如旁边长了棵树、装了块广告牌旧模型往往会误检。解决办法是定期增量训练每个月把新采集的、置信度低的帧抽出来人工复核补进训练集重新训练。我把它做成一个半自动流程一天就能跑完一次增量训练。这套系统运行到现在经历过两个完整汛期最大的一次实际效用是在台风暴雨期间连续监测到水位从设防水位突破到警戒水位提前40分钟发出告警给了下游值守人员足够的转移准备时间。做这类项目给我最大的感受是模型精度重要但更关键的是把整个预警闭环做扎实——从数据标定到读数换算再到告警推送的可靠性每一环都不能掉链子。如果你也在做水位识别或者类似的边缘端视觉项目希望这篇复盘能帮你少踩几个坑。
返回列表