我第一次把无人机在街道上空拍回来的大图拉进打标工具时,盯着屏幕上那些巴掌大的摊位愣了好一会儿:这套无人机街道巡检占道经营识别项目,最难的其实不是模型,而是怎么把“占道”这件事让机器看懂。核心任务很直接——用YOLO做目标检测,从无人机俯拍图里把违规摆摊、占道堆物、私搭棚伞这些目标自动找出来。我手头这批数据就是项目对应的第10437期占道经营数据集,覆盖了早市、商圈、学校周边、背街小巷等典型智慧城市治理场景。它解决的问题很现实:城市管理人员不可能一天到晚盯着每条街,无人机巡检一圈,模型跑一遍,几分钟就能把疑似点位带坐标列出来,外勤再上门核实。这篇分享适合准备做智慧城市、无人机行业应用,或者正在为小目标检测数据集发愁的开发者参考。
1. 项目整体设计与思路拆解
1.1 先想清楚:识别什么、什么叫“占道”
我不建议一上来就跑模型。先把任务定义清楚,否则标注标准一乱,后面全都白费。占道经营从城市管理的角度看,指的是商贩占用公共道路、人行道或者其他公共空间进行经营活动,常见形态是:三轮车和手推车流动摆摊、固定雨棚/遮阳伞摊位、直接把货物堆到路边的人货混合堆占,还有桌椅板凳外摆的餐饮占道。这里有个关键点:不是所有沿街摊位都该判占道,要看它是否侵占了通行空间。比如市场内部摊位,或者合规外摆区里按规定摆放的摊位,模型不应该报。这个判定逻辑要提前定死,写进标注规范,模型学的才会稳定。
我在这批第10437期数据里把目标定了五个类别:流动摊位(stall)、固定棚伞(canopy)、堆物占道(stacking)、占道车辆(cart)、行人聚集(crowd)。最后一个不是独立违规项,而是辅助模型区分“摊位边上有人”和“单纯一堆货物”的特征。多一个辅助类别,特征共享之后,主类别准确率反而会好一点,这是我试过几次后留下的做法。
1.2 为什么用YOLO而不是其它检测方案
无人机航拍巡检对检测速度的要求,很多人一开始会忽略。一次飞行二十分钟,拍出来上千张大图,如果每张图跑一次慢速检测器,处理时间比飞行时间还长,那所谓的“智慧巡检”就名存实亡了。YOLO这类单阶段检测器在精度和速度之间的平衡,在这个场景里非常合适。近几代的YOLO系列模型在小目标、密集场景上专门做过优化,在无人机视角这种目标普遍只有几十乘几十像素的场景里,硬指标比早期版本强了不少。而且它的数据格式、训练流程、部署工具很统一,训练完导出的模型可以直接转成边缘设备能跑的格式,不用折腾太多中间步骤。
有人会问,两阶段检测器精度是不是更高?在公开数据集上可能确实有优势,但要考虑一个实际问题:占道经营的外形千奇百怪,雨棚颜色各异,手推车形态各异,两阶段检测器在这类泛化场景里的优势并没有想象中那么明显。我自己的对比测试里,同条件下两阶段模型在mAP50-95上大概高一个多点,但推理耗时高了几倍,对于街道巡检这种批量图处理场景,性价比不划算。这个权衡不一定适合所有任务,但无人机巡检这种场景下,速度和吞吐量就是核心指标。
1.3 整体链路怎么搭
整个系统不是“训练个模型就完事”,而是一条完整链路:无人机按规划航线采集街道俯拍图、原始大图预处理和裁剪、送入检测模型推理、输出带坐标的目标框和置信度、结合地理坐标换算成具体路段点位、生成疑似占道清单推送给管理人员终端。第10437期这个编号看起来像个序号,实际上是数据版本管理的习惯:每一期飞行采集都会整理一批新图和标注,统一编号归档。这样模型迭代时,我们可以清楚知道新增了哪些场景、哪些badcase被回灌了,训练集不会越弄越乱。
这套链路的难点不在单点算法,而在于每一环的误差会往下游叠加。飞行高度不一样,目标尺度就不一样;裁剪尺寸不一样,模型看到的上下文就不一样;坐标换算如果没做对齐,即使检测准了,上报的点位也会偏。后面我会逐个环节拆开讲。
2. 核心细节解析与实操要点
2.1 占道目标在航拍图里长什么样
要让模型认得准,先得知道无人机视角下这些目标到底长什么样。航拍高度一般60米到120米,图上摊位目标往往只有30到150像素大小,相当于你屏幕上的一小片区域。流动摊位最明显的特征是车体结构——三轮车、平板车、改装电动车,有轮子和车厢,颜色上常和道路灰黑色背景形成反差,尤其是盖着那种红蓝条纹篷布的时候。固定棚伞一般是一个四边形色块,边界就是棚顶或伞面边缘,颜色很跳。堆物占道更麻烦,它没有固定形态,往往是水果筐、纸箱、衣服架混在一起,经常堆到人行道边缘。
容易出现的一个误解是:目标小就好检测,其实不是。小目标在卷积网络里经过几轮下采样后,特征会变得很弱,几十像素的目标占的feature map区域非常小,漏检率天然就高。这也是为什么后面的输入分辨率和切图策略会那么关键。
2.2 标注规范:训练集质量是模型上限
说实话,我认为任何检测项目里,标注规范的重要性都不低于模型结构。占道经营标注有几个特别容易扯皮的地方。第一是框的边界:一个流动摊位包含三轮车和地上的货物,框是只包车体,还是把摊开的货物也算进去?我的规范是“摊位主体的最小外接矩形”,包含车身和主营货物,但周围散落的纸片、顾客的水瓶不包。第二是棚伞和摊位的关系:同一个点位上棚子下面有摊位,两个框是分开标还是合并?我的做法是分开标,棚伞单独一个类别,因为它的特征和地面摊位差异很大,合并会导致类别内差异过大。第三是遮挡:棚子互相挨着、树荫挡住半个摊位,一律按可见部分标注,不要脑补被遮挡的部分,不然模型学到的框会乱跳。
多人标注时还要做“标框惯例”校准。我遇到过两个人标同一个摊位,一个人标了60×80的紧凑框,另一个人标了80×100的宽松框,最终模型训练出来预测框时宽时窄。解决办法是小组内先标200张图,互相检查,统一尺度后放大规模。每次新人对齐规范,至少安排一次双向核查。数据标注这种事,前期的慢是为了后期的快。
2.3 数据增强不能只靠自动那套
YOLO自带的马赛克增强能解决一部分泛化问题,但对无人机巡检数据来说还不够。因为航拍图的视角、光照、尺度分布相对固定,不是一句“增强一下”就能覆盖所有情况的。我在这批数据上做了三类补充:一是离线同尺度的仿射增强,比如轻微旋转(±10度)、水平翻转、亮度/饱和度扰动,模拟不同时间飞行的光照;二是切瓦片增强,把大图切成640到1280的瓦片后,随机对瓦片做翻转组合,相当于变相增加训练样本;三是复制粘贴增强,把标注好的摊位实例复制到没有摊位的背景图上,人为增加目标密度和位置多样性。第三种操作要小心,粘贴时别把框留空、别把摊位放到完全不可能的马路中央,否则会引入假样本。
另外一个经验:类别不平衡在我这个数据集里很明显。棚伞目标多,堆物占道目标少,如果直接训,堆物占道的召回率会很差。我先统计了各类别的实例数量,对少数类做重复采样,并在损失权重上适当提高,简单来说就是让模型对冷门类别更敏感一些。这个操作在mAP上的变化可能不大,但实际场景里,把平时最容易被漏掉的堆物找出来的价值,比整体mAP高一点点重要得多。
3. 实操过程与核心环节实现
3.1 数据采集与图像预处理
无人机采集这一步,很多团队会直接用正射影像底图来识别。我的经验是:底图适合做城市管理档案背景,不适合做巡检识别主图。因为底图更新慢,摊位是流动的,你今天识别时摊主可能刚挪走。最好是每次巡检飞行后,用当次拍摄的原始图做识别,保证数据时效。飞行规划上,建议航线重叠率30%以上,高度保持在80到120米,镜头垂直朝下。垂直视角的好处是目标透视变形小,标注和识别都稳定;斜射视角虽然有立体感,但同一个摊位在不同角度下框的形状差异太大,会影响模型一致性。
原始图一般很大,以常见的行业级无人机相机为例,单张可能到6000×4000像素。这种尺寸直接喂给YOLO不现实,显存撑不住,推理也慢。我的做法是先把大图按1280×1280的窗口滑动裁剪,相邻窗口之间保留200像素重叠,防止目标正好被切在缝上。裁剪完的瓦片作为训练和推理的基本单位。这个切瓦片过程对最终效果的影响非常大,我专门做过对比:按1280切图训练,小目标AP比直接用下采样小图训练高出好几个点,因为经过网络下采样后1280图上目标还剩足够的特征。
分块裁剪之后再筛选,不是所有瓦片都值得留。全黑的楼顶、纯色的空广场、大面积河道,这些图没有目标,留着只会拉低训练效率,直接删掉。我一般是先跑一个简单的目标分布统计,保证训练集里包含目标的瓦片占大多数,背景干扰图单独抽一小部分作为负样本。
3.2 标注与数据集结构
标注我用的是开源打标工具,导出格式统一为YOLO的txt格式。每一行是:类别id、中心点x、中心点y、框宽w、框高h,坐标全部归一化到0到1之间。对第10437期这批数据,我最后留下大约4200张有效图像,总共约1.8万个目标实例。按8:2划分训练集和验证集。划分的时候有一个坑:同一架次飞行拍出来的相邻图像内容高度重叠,如果随便随机划分,训练集和验证集会互相泄漏,评估指标会虚高。正确做法是按飞行架次或者按街道分组划分,保证同一个场景只出现在一个集合里。
目录结构参考:
dataset_10437/ ├── images/ │ ├── train/ │ │ ├── 3712_01.jpg │ │ └── ... │ └── val/ │ ├── 3715_03.jpg │ └── ... ├── labels/ │ ├── train/ │ │ ├── 3712_01.txt │ │ └── ... │ └── val/ │ └── ... └── data.yamldata.yaml里写好类别列表和路径就行。这个结构可以直接喂给现代YOLO训练工具链,不用额外转换。要注意的一点是,图片和标签的文件名要保持一致,我见过有团队在整理数据时改了图片名忘了改标签名,结果训练时报错半天找不到原因。
3.3 训练配置与参数选择
训练这块,我试过不同模型尺寸。第10437期数据量不算大,直接上最大号的模型很容易过拟合,所以我用中等体量模型做主力,并在更深模型上做了个对比。训练参数:
| 参数 | 数值 | 说明 |
|---|---|---|
| 输入分辨率 | 1280×1280 | 大图瓦片尺寸,保小目标特征 |
| batch size | 8 | 16G显存能稳定跑住 |
| epochs | 150 | 配合早停,防止过拟合 |
| 优化器 | SGD + momentum | 检测任务上收敛稳 |
| 初始学习率 | 0.01 | 配合余弦衰减 |
| 预训练权重 | 通用数据集权重 | 迁移学习基础 |
这里有一个非常值得说的点:预训练权重的使用策略。占道经营和通用目标相比,既有相似又很特殊。棚伞、摊位这些目标,不会出现在通用数据集里。我从预训练权重开始,先冻结骨干层训练20轮,让新加的检测头和类别分支先稳定下来,之后解冻全部层,用较小学习率再训完剩余轮次。这个流程比直接全量训练收敛更稳,小目标AP也更好。
训练结束后看指标,我用的是每个类别的AP和整体mAP。具体到这批数据,mAP50到了0.81左右,mAP50-95在0.55上下。这个数字单看不惊艳,但对航拍小目标场景来说算够用。更关键的是看每个类别的单独AP,尤其是堆物占道和流动摊位这两个,差的类别才是优化目标。
3.4 Badcase分析与迭代
评估完不要急着高兴,把验证集里预测错的结果全部导出来看。我每次迭代都会把badcase分成三类:漏检(有标注没预测出来)、误检(没标注却预测出来)、定位偏差(框和真值重叠度不够)。统计下来,漏检主要集中在小目标和遮挡场景,误检最典型的是把地面印的摊位格线、彩色地砖当成了棚伞,定位偏差集中在密集摊位互相挤在一起的时候。
针对这些badcase,我做了三轮迭代。第一轮加了一些白天不同时段的光照图片,解决黄昏时棚伞颜色偏淡的问题;第二轮专门标了一批只有堆物没有商贩的样本,缓解误检;第三轮对密集场景下调了NMS阈值,让相邻目标框不容易被吞掉。三轮之后,mAP50提升了大约3个点,单看小目标类提升更明显。这里我想强调的是,提升来源主要不是模型结构,而是数据层面的定向补充和推理参数微调。算法工程师容易忽略badcase的归类,但实际上针对问题去补数据,才是检测项目迭代里最划算的路。
4. 常见问题与排查技巧实录
4.1 小目标漏检怎么治
漏检是占道场景里最头疼的问题,毕竟高空视角下摊位本来就小。处理思路有三个,按优先级排:第一,提高输入分辨率,从640提到1280甚至1536,对漏检的改善立竿见影,代价是显存和推理耗时上升,需要自己权衡;第二,切瓦片识别,推理时也保持和大图训练一致的切片方式,保证目标尺度分布稳定;第三,多尺度训练,打开训练时的多尺度采样,让模型见过不同尺寸的同一目标。实测下来,三项一起上,漏检率能减少一半以上。
另外还有一个细节:航拍图里很多摊位被树木、电线杆、路灯阴影挡着,遇到这类遮挡严重的图,不要指望模型能有透视眼。我的处理办法是,把遮挡样本单独收集,在标注时把“可见部分”标清楚,然后单独测这一类数据的召回情况。如果遮挡太严重导致AP过低,就在巡检逻辑上增加“同一点位多次飞行确认”的规则,避免单次误判。
4.2 误检的重灾区:疑似目标太多
误检往往不是模型笨,而是训练数据里缺了反例。彩条地砖、红色顶棚、太阳伞,这些在模型眼里都可能和棚伞长得很像。解决思路不是单纯调阈值,而是把误检图片收集起来,人工确认后批量标注一个“非目标干扰项”或者直接归入背景,重新加进训练集。我管这个叫“负样本回灌”,对降低误检效果很好。另外,可以让模型输出时对置信度低于0.5的结果直接过滤,减少低质量候选框,让巡查清单干净一点。
还有一个容易踩的坑:如果数据里只有大晴天正午的图,模型对下午三四点那种长阴影环境会非常不适应,阴影的边缘经常被误检成堆物。所以采集时要刻意覆盖不同的日照角度和时间段,哪怕同一个地方多飞几次都行。数据多样性这种东西,买不到,只能靠采集规划里主动安排。
4.3 标注框抖动和尺寸波动
训练过程中我遇到一个奇怪现象:同一批验证图上,固定棚伞的框一会大一会小。排查后发现问题出在标注源头上。早期标注棚伞时,有的标注员框到伞的外沿,有的框到伞的支撑柱范围,框的尺度没有统一。后来我把标注规范明确成“伞面可见边缘外接矩形”,并把之前有争议的几百张图重新标了一遍,框的波动立刻就降下去了。如果你的训练数据是多人协作完成的,先做个框大小的数据分布统计,出现双峰分布的话,基本都是标注习惯不一致,要回炉。
这个分布统计很简单,把训练集所有标注框的宽和高画成散点图,按类别上色。正常情况应该是单峰,近似集中在某个尺度区间。如果看到明显的两个集中区,说明标框尺度不统一,不建议继续训练,先修数据。我身边有不少项目吃过这个亏,最后只能从头刷数据,反而更浪费时间。
4.4 边缘设备上的部署调整
巡检识别最终要跑在无人机地面站或边缘设备上,不可能每张图都传回云端的机房慢慢推理。边缘设备的算力远低于训练服务器,所以部署时要压缩模型。我常用的路径是:训练好的模型先做通道剪枝再蒸馏回原来的精度,然后做INT8量化,最后实测帧率比原模型快2到3倍,精度损失控制在可接受范围内。还要考虑控制器的推理频率,不需要每帧都跑,可以采取“每5秒抽检一张”的轮询策略,减少算力压力。
部署时还有一个非常容易翻车的地方:训练时的预处理和部署端的预处理不一致。训练时如果做过归一化、图像缩放,部署端也要保持一模一样的流程,否则精度会下降得莫名其妙。我见过有人模型在验证集上mAP有0.8,部署到设备直接掉到0.5,最后发现是部署端图像格式从RGB变成了BGR,颜色通道反了。这种问题不用慌,先把两端的预处理对齐,大多数时候都能恢复。
下面是一个常见问题的速查表,我习惯贴在项目文档首页,方便排查时快速翻:
| 现象 | 常见原因 | 排查方向 |
|---|---|---|
| 小目标漏检多 | 输入分辨率低/目标下采样丢失 | 提高imgsz、切瓦片、多尺度训练 |
| 棚伞和地砖误检 | 负样本不足/类别混淆 | 收集误检做负样本回灌 |
| 框忽大忽小 | 标注习惯不统一 | 统计框宽高分布,统一规范 |
| 同一个摊位被切成两块 | 瓦片重叠率不够 | 推理时增加窗口重叠率 |
| 部署后精度骤降 | 量化损失/预处理不一致 | 校准集覆盖各类别,对比输入尺寸 |
| 新街道泛化差 | 训练场景单一 | 按街道/架次划分验证集,补充场景 |
如果让我重新做一遍这个项目,我会把至少一半的时间花在数据源头的规范上,而不是一开始就去调模型结构。模型结构可以换,数据不规范,换谁都白搭。第10437期这批数据给我最大的启发是:智慧城市的识别类项目,成败往往不在“智能”,而在流程。无人机飞完、图像碎好、标注一致、迭代闭环,每一步都不炫酷,但每一步都决定了最终模型能不能落地。最后分享一个实用小习惯:每次训练完,把当前模型跑一遍全量历史数据,把新增的badcase按街道和类别做分布统计,然后用这些图去补训练集。这个看似重复的循环,是成本最低、收益最稳的优化方式。街道巡检占道经营识别这种场景,场景变化快、目标形态杂,靠的从来不是一次性的聪明,而是持续迭代的耐心。