做这个项目之前,我一直觉得“不明飞行物”(НЛО)这个缩写只适合出现在凌晨三点的猎奇视频里。直到某实验室组织脑暴,提出要做一套“空中异常目标识别系统”——不是去证明飞碟存在,而是当普通市民拍下一段天空视频后,系统能在几分钟内给出结构化的观测结论:哪些是无人机、气球、飞鸟,哪些暂时无法分类。这个思路让我眼前一亮,因为视觉识别、多目标跟踪、雷达融合全都能在一套系统里串起来。这篇文章就是整个项目的复盘,从需求拆解到部署实测,覆盖目标检测、轨迹分析、多源数据融合和落地过程中踩过的坑。适合正在做视觉算法、想了解传感器融合,或者对“如何理性处理无法解释的空中现象”感兴趣的开发者。
1. 整体设计:先搞清楚这个项目到底要干什么
1.1 核心需求拆解:从“看到”到“可复核的报告”
项目启动前,我们花了两天时间做需求澄清,把“识别不明飞行物”这种模糊目标翻译成工程能落地的需求。真实任务场景是这样的:有人用手机或监控摄像头拍下了一段天空影像,画面里出现一个或多个疑似异常的移动目标;系统要自动把所有可疑目标检测出来,并输出目标类型、置信度、像素坐标、运动轨迹、速度估计和异常分数。最终交付物不是一张打了框的图,而是一份可追溯的观测报告,包含时间线、事件摘要、目标分类理由。如果后续有雷达或气象数据证明那是高空无人机,系统可以更新结论;如果特征确实新颖,就只在报告里标为“未知目标”,不强做解释。
这套需求里最重要的点是“可复核”。我们不能只输出一个“是UFO”的结论——那既没价值,也有误导风险。所以我在设计标签体系时没有采用单分类,而是用了一组互相独立的类别:无人机、飞机、直升机、飞鸟、气球、云层边缘、镜头炫光、未知目标。这样,系统给出的每个判断,下游复核人员都能顺着特征重新验证。未知目标也不是“外星人”,而是“当前特征不足以归类”的意思。写清楚这一点后,算法组和目标方达成一致,后面的指标设定才比较顺利。
另一个容易被忽视的需求是“单路视频连续运行下的误报次数”。城市天空里有很多干扰:探照灯、飞机航行灯、风筝、飞鸟、云层光影,甚至镜头上的一粒水渍都可能被当成目标。所以我把评估指标拆成了两部分:一是在正常天空视频上,每小时误触发事件的次数不超过一次;二是对真实目击片段,目标能被连续跟踪超过可见时长的80%。这两条业务指标,直接决定了系统最终能不能被人信任。
1.2 为什么只靠一路视频流远远不够
刚开始我们天真地想,用检测模型直接扫视频帧就行。后来发现民间目击视频的质量普遍很差:分辨率低、压缩严重、画面抖动、逆光、夜间噪点,目标在画面里往往只有几十个像素。如果直接送进模型,不仅小目标漏检率高,还容易把静止的灯光误判成移动物体。于是我们把“时空一致性”纳入设计:不仅要看每一帧图片里像什么,还要看目标在时间序列上是否具备连续运动特征。
但视频信息本身有一个硬伤:它很难精确测距。哪怕目标检测做得再好,单目摄像头也无法告诉用户这个目标距离观测点是多远、真实速度是每秒多少米。所以项目中期决定引入雷达作为第二信源。雷达的特点和视觉恰好互补:它在夜间和远距离上表现稳定,能提供方位角、俯仰角和距离,但分辨率低,没法判断目标是无人机还是鸟;视觉能分类,却缺乏精确距离。只有把两个信源做时间对齐和空间标定,才能输出“在哪个方位、多高、多大速度、看起来像什么”这样的完整描述。
这套多传感器融合思路在安防和低空管控领域已经很常见,但在“不明飞行物观测”这种偏民用的项目里,工程复杂度没有降低,反而因为目标类别不明确而更难。你需要容忍一部分无法判定的事件,也要防止系统在空天上频繁“见鬼”。这个平衡点贯穿了整个开发过程。
2. 视觉检测与跟踪:让算法先“看见”天空
2.1 目标检测模型选型:小目标到低空场景的取舍
视觉模块是整个系统的入口。我们实验了多个以YOLO系列为代表的单阶段检测模型,重点比较它们在“小目标”上的召回率。通用检测模型在自然场景数据集上表现很好,但天空场景目标少、背景干净,模型很容易把所有模糊变化都当成前景。我们第一个版本的误报就出在云层边缘:云在缓慢移动,边缘有光晕,模型不断给出高置信度检测框。这个问题不是换一个更大模型能解决的,而是需要在预处理和样本设计上做文章。
在模型选择上,我们最终没有采用最重的模型,而选了一个中等规模版本,同时把输入分辨率从640提升到960。提升分辨率后,小目标召回率改善明显,代价是显存占用上升。为了不把边缘设备拖垮,我加了动态分辨率策略:当画面整体亮度高、云少时用低分辨率做快速扫描;一旦检测到疑似目标,再对目标区域做局部放大,把原图裁剪出区域重新推理。这个策略在实测里很管用,既控制平均时延,又保留小目标识别能力。
还有一个细节是类别不均衡。天空中“飞鸟”出现频率高,“无人机”反而少。模型很容易把所有小目标都预测成飞鸟。我做了类别频率统计,对少样本类别加了更高的损失权重,同时从模拟数据和真实样本里专门挑出无人机在各种距离和姿态下的画面。这样即使目标很小,模型也愿意给出“无人机”这个更稀疏的类别,而不是永远偏向多数类别。
2.2 视频稳定与背景抑制:把云层和抖动的影响压住
做这个项目之前,我以为第一步是检测,实际第一步是视频稳定。手持手机拍摄天空时,镜头抖动、风吹云动都会让背景像素发生位移。如果不先把背景对齐,帧差得到的运动区域会非常杂,检测器分不清是目标在移动还是相机在晃动。我们的预处理流水线先对输入帧做特征点提取和仿射变换估计,把当前帧对齐到前几帧的背景坐标系,然后再做背景差分。
背景抑制阶段,我用了两层规则:第一层是光流一致性,计算全图光流后,把那些流向和全局背景运动方向高度一致的区域直接剔除;第二层是纹理置信度,云层和天空在局部空间上变化平缓,而无人机、飞鸟这类目标通常带有相对清晰的边缘轮廓。两层规则过滤后,误报数量下降了一个量级。
这个阶段容易踩坑的地方是“过度稳定”。对一张整体静止、只有云层缓慢漂移的天空画面,如果强行做仿射对齐,云层边缘会被拉出很多虚假纹理。后来我们给稳定模块加了一个“场景运动量估计”,只有当画面全局位移超过设定阈值时才启动对齐,否则直接跳到背景抑制。这样有效避免了极端条件下的画质损伤。
2.3 多目标跟踪与轨迹预测:别让目标“瞬移”
检测器只负责在每一帧给出检测框,它并不知道当前帧的框和上一帧的框是不是同一个物体。要让目标在时间线上保持身份,必须有跟踪模块。我用了一个经典组合:卡尔曼滤波加匈牙利匹配。每个跟踪目标维护位置、速度、加速度和上一帧的检测框,卡尔曼滤波负责预测目标在下一帧可能出现的位置,然后用匈牙利算法把检测框和预测位置做最优匹配。
这套组合对“天空目标”尤其合适。天空背景稀疏,目标之间不容易发生严重遮挡,轨迹连续性比拥挤场景好得多。但困难在于目标可能突然消失或突然出现。比如无人机快速爬升到远距离后,目标面积缩小到几个像素,检测器某几帧会漏检。卡尔曼滤波的好处是能维持一部分轨迹预测,只要目标在一段时间内重新出现,还能接回原ID。如果目标消失超过3秒,轨迹就被标记为“结束”,同时保留最后的位置和速度,供后续报告使用。
轨迹特征反过来也是“异常判断”的核心。系统会计算目标在时间窗口内的加速度变化和轨迹弯曲度。悬停、突然加速、瞬间消失,这些行为在轨迹上会呈现非常规形状。我们把这些轨迹特征送给一个浅层分类器,输出“轨迹正常”或“轨迹异常”。异常分数进入报告,也成为是否触发雷达复核的关键条件。
3. 多源数据融合:给视觉系统接上“电子眼”
3.1 雷达与视觉坐标对齐
加入雷达之后,首先要解决坐标系对齐问题。雷达输出的是极坐标:方位角、俯仰角、距离;摄像头输出的是图像像素坐标。要把两类数据放在同一个平面上比较,需要做一次“雷达-相机标定”。工程上常用做法是找几个角反射器作为合作目标,在场景里摆放不同距离和角度,记录雷达目标回波和图像中的像素位置,再通过最小二乘法估计外参矩阵。
这里最容易被忽略的是时间同步。雷达扫描频率和摄像头帧率往往不同,如果直接把最近一次雷达回波硬套到当前图像帧上,目标会有明显的错位,融合效果反而不如不融合。我采用的方案是以视觉帧的时间戳为基准,在雷达时间序列里找前后两个相邻回波,做线性插值,得到当前时刻的等效雷达位置。插值后的雷达点可以直接投影到图像上,再和视觉检测框做空间关联。
有一次现场标定后,投影点总是整体偏移图像中心大约15个像素。排查了半天,发现问题出在雷达安装角度上,它和相机光轴之间存在一个很细微的俯仰差,标定时候没完全消除。后来我在雷达输出端加了一个固定角偏置校正,并且把校正值写进配置文件,问题才解决。建议做类似项目时,标定完不仅要看误差数值,还要做一次“目检”,把雷达点叠加到实拍视频上看看是不是真的贴合。
3.2 决策逻辑:怎样才算“值得相信”
融合之后不能简单地把视觉框和雷达点直接叠加。我们设计了三级决策逻辑:
第一级,视觉检测置信度超过阈值,同时雷达轨迹点落在对应视觉框的范围内,并且目标在若干帧内持续有雷达回波,判定为“高置信目标”。这类事件会直接生成报告,触发录像留存。
第二级,只有视觉检测框,没有雷达回波,但目标在视觉上呈现稳定移动轨迹,判定为“候选目标”,在界面上显示,提醒复核人员注意。
第三级,只有雷达回波,没有视觉特征,通常发生在距离特别远或者逆光条件下,判定为“雷达异常点”,只记录不自动确认。
这个分层看起来简单,但非常有用。它避免了系统一有风吹草动就弹窗。实际上,雷达在某些天气条件下会被云雨干扰产生虚假回波,视觉在夜晚也会把路灯误判成目标。只有让两者互相交叉验证,才能把单源数据的信心压低,避免过度反应。
另外,置信度阈值不能是固定值。夜间和白天、晴天和阴天,视觉模型的表现差异很大。我们把光照和环境能见度作为条件变量,动态调整第一级触发阈值。夜间允许视觉置信度稍微低一点,但要求雷达回波连续性更强;白天则相反,视觉置信度要求高,可以允许没有雷达回波。这套策略极大降低了误报率。
4. 数据准备与评估:没有样本怎么训练
4.1 模拟数据生成策略:把“没见过”的变成“见过”的
这个项目最缺的不是算力,而是样本。真实目击视频少得可怜,而且大多没有标注。我们的解决办法分三步:先在若干开源航空目标数据集上做迁移预训练,再拿项目自己的背景素材做微调,最后用随机合成方式自动生成大量“天空光点+运动轨迹”的训练数据。
模拟数据生成绝不是把目标贴到天空图上那么简单。我们做了几个关键设计:
- 目标生成器按物理规律模拟运动:无人机直线和悬停、飞鸟的随机转向、气球的随风漂移、飞机的匀速直线。每种运动都带细微的噪声,让轨迹更真实。
- 背景库除了晴空,还有云层、晨昏、市区光污染、雾气等多种情况。
- 在目标周围随机叠加镜头炫光、噪点、压缩伪影,迫使模型学会区分目标本体和画质退化带来的假象。
为了让模型不记住背景纹理,我把背景图随机旋转、镜像、调色,目标大小覆盖从256像素到十几个像素的区间。训练完成后,模型在真实夜间视频上的表现比只拿真实样本微调的版本好很多。
4.2 评估指标:光看mAP远远不够
很多检测类项目把mAP当作核心指标,但在这个项目里,mAP会给你一个“乐观错觉”。因为天空背景很干净,真阳性框和假阳性框在空间上往往重叠,mAP对类别错的惩罚不够敏感。我们另外增加了两条业务指标:一是“每小时误触发次数”,统计系统在纯云层视频上连续运行产生的误报数;二是“事件完整性”,看目标能不能被连续跟踪超过可见时长的80%。
经过几轮迭代,视觉模型在测试集上的mAP稳定在0.72左右,单路视频推理在中端桌面显卡上约15帧,边缘盒子上掉到5-8帧。只看mAP似乎还可以,但真正让我决定改预处理版本的是“每小时误触发次数”从3.4降到了0.8。这说明业务指标比模型精度更能反映现场体验。
评估时还有个小坑:公开数据集的图里通常目标大、画质好,模型在那些图上表现好不代表在真实视频上表现好。后来我们把每个评估视频按时间切成30秒片段,每个片段单独统计检测、跟踪、误报这三维结果,这样能看出系统在哪段时间上稳定性差,而不是只看一个笼统的均值。这个做法建议其他做长视频检测的团队直接抄作业。
5. 部署与现场实测:从代码到能用的观测终端
5.1 边缘部署配置与性能取舍
部署端选择了带独立GPU的边缘计算盒子,配普通可见光枪机。系统启动后自动进入观测模式,视频流通过RTSP拉取,检测模型经过推理加速工具转换成专用格式。为了保持长时间稳定运行,我加入了一个看门狗进程:如果模型连续两帧卡死,或者内存占用异常,就自动重启推理进程,同时把关键帧和原始视频按时间分段保存。
边缘设备的散热和功耗也要注意。部署地点不是机房,夏天太阳直射设备外壳温度很容易超过50度,GPU会因过热降频,推理速度明显下降。后来加了小型散热风扇,并把功耗限制在合理范围,问题得到解决。还有一个看似无关但实际影响巨大的细节:摄像头的防尘罩。有一天下雨后罩子上沾了水渍,视觉模块开始频繁触发误报,水渍在画面里形成半透明形状,被模型当成了空中目标。加了一条“镜头洁净度检测”逻辑之后,才及时提醒维护人员擦镜头。
5.2 实测过程与结果
我在某片近郊场地连续测了三天半:白天测鸟群和无人机,傍晚测热气球,夜间用探照灯模拟“发光悬停物”。最典型的一幕是系统先提示一个低空慢速目标进入视场,检测类别为“无人机”,置信度0.81;随后视觉轨迹显示目标以约7米每秒的速度向南平移,雷达点连续3秒对齐,最后判定为高置信度“无人机”事件,整条链路从识别到生成报告不到5秒。
另一个案例则体现出“未知目标”标签的价值。远景云层边缘被算法标记为“未知目标”,因为没有雷达回波,也没有合理运动轨迹,系统把它降级为“待复核”,而不是硬说是“不明飞行物”。如果系统把每个模糊光点都当作异常,很快就会失去参考价值,所以学会说“不知道”,反而让结果更可信。
实测数据还暴露了一个性能瓶颈:当画面里同时出现飞鸟群和多架无人机时,跟踪模块的计算量会急剧增加。匈牙利匹配在目标数量超过20个之后耗时明显上升,单帧处理时间超过了限制。后来我把匹配算法改成级联策略:先用位置预测快速匹配高置信目标,再用外观特征匹配剩下的候选目标。这样在目标数量较小时几乎不增加耗时,目标数量大时也能稳定运行。
6. 常见问题与排查经验
6.1 误报率高得离谱,该怎么排查
误报排查要按链路分步走。第一步,把检测阈值和所有检测框输出到日志里,统计框集中在图像的哪些区域。如果分布区域固定,很可能是反光、水渍或固定晕影;如果随机分布且随时间变化,大概率是云层或杂波。第二步,看目标的光流方向,如果是大面积一致方向,多半是背景运动被误判为目标,要用全局运动补偿过滤。第三步,查看雷达数据,如果雷达没有对应回波,视觉目标又符合飘动特征,那基本都是背景或镜头伪影。
我在一个版本里被镜头水渍害得不浅,水渍清晰度不高,但边缘会产生类似轮廓的变化。经过分析后,我们在系统里增加了“近处遮挡检测”,利用对焦和色彩饱和度特征识别镜头表面的异常区域,发现可疑遮挡就提示清洁,而不是直接当成空中目标。
6.2 夜间和逆光场景怎么应对
夜间能见度低,目标在画面里就是一两个像素点。简单提高增益会把噪点一起放大,导致误报率骤升。我先把夜间图像做小波降噪,把高频噪声压下去,再做小幅增益,让暗淡目标稍微亮一点。飞机航行灯是最大的夜间干扰源,因为它亮、闪烁、移动且有固定轨迹。我们通过频率域分析提取闪烁周期,把高频规则闪烁的“点状目标”单独归类,不进入主报告。
逆光场景则更容易把飞鸟轮廓虚化,视觉模型几乎失效。这种时候不要硬靠视觉,直接把逆光方向区域的视觉置信度下降,更多依赖雷达回波。实测下来,夜间误判率从每小时四次左右降到了零点几次,逆光场景虽然识别率低,但已经不会频繁打扰复核人员。
6.3 目标太小导致漏检,有没有低成本的补救办法
当目标在画面里少于30像素时,任何模型都很难稳定识别。低成本补救办法有三种:第一种是图像金字塔,原图放大1.5倍后再做分块检测,能提升大约10%的召回率;第二种是历史帧缓存,把目标出现的前几帧叠加到当前帧上,相当于增强信噪比;第三种更彻底,用主相机发现目标大致方位后,通过云台控制高速长焦相机锁定目标再采集细节。
我最后把三种方法组合起来:主相机负责广域搜索,长焦相机负责细节确认。这个方案让我在最后一个迭代版本里稳定识别出了1公里外的一架微型无人机,放在项目初期想都不敢想。如果你的项目也遇到类似场景,建议优先做历史帧缓存,因为它成本最低,而且对静止背景下的弱目标效果非常明显。
我个人在实际操作中最大的体会是,这套系统最难的地方不是精度,而是如何处理“不确定”。把结果分成“已知目标”和“未知目标”,承认模型不知道,反而能获得更多合作方的信任。另外再分享一个小技巧:把所有被人工复核过的轨迹回填到训练集里,哪怕只是顺手点了“否”的误报,也要保留下来。三个月后,系统对“怀疑—确认—排除”整个流程会肉眼可见地变快。这个项目最终没有证明任何一起“不明飞行物”事件,但它让每段模糊视频都有了可以被检验、被推翻的工程化答案,我觉得这才是它真正的价值。