Hotdry.

Article

末日预警系统的工程实现:ADS-B数据异常检测与实时告警架构

基于ADS-B广播式自动相关监视数据流,构建针对私人飞机群异常的实时监控系统,详解数据采集、基线建模与告警链路的技术实现。

2026-05-01systems

当核战争逼近的假设成为现实,那些拥有私人飞机的精英阶层很可能第一时间逃往安全地带。这一看似戏谑的假设,却催生了一个严肃的工程问题:如何构建一套实时监控系统,通过追踪私人飞机活动模式来感知异常?艺术家兼工程师 Kyle McDonald 在 2026 年实现的 Apocalypse Early Warning System(末日预警系统)给出了答案。该系统以 ADS-B 广播式自动相关监视数据为核心数据源,通过对比历史基线来实现异常检测,本文将从工程视角深入剖析其数据管道、异常判定逻辑与告警基础设施。

数据采集层:ADS-B Exchange 热力图解析

系统的数据来源并非传统的实时 ADS-B 原始数据流,而是采用了 ADS-B Exchange 发布的热力图二进制文件。这些热力图以半小时为粒度发布,每个文件编码了特定时间段内全球范围内的飞机位置信息。选择热力图而非原始数据流有着明确的工程考量:原始 ADS-B 消息每秒可达数万条,需要复杂的接收前端与实时解析能力;而热力图文件已经过预处理,数据量可控且格式统一,非常适合定时批量处理场景。

数据采集脚本以 Python 实现,核心逻辑相当简洁:定期下载最新的热力图文件,解码二进制格式,然后根据 ICAO 24 位地址匹配预先定义好的飞机群。ICAO 地址是每架飞机的唯一电子标识,格式为六位十六进制字符串,绑定于飞机的应答机硬件。系统从 FAA(美国联邦航空管理局)公共注册数据中导出一批商务喷气机名单,通过 ICAO 地址进行一对一匹配。这种基于精确标识符的匹配方式避免了位置模糊性问题,确保了数据准确性。

采集层的设计采用了典型的批处理模式:每小时启动两次采集任务(对应两个半小时窗口),每次任务独立下载并解析热力图,然后将解析结果写入 SQLite 数据库。这种设计的好处在于容错性强 —— 即使某次采集失败,也不会影响后续时段的正常运作。数据库中存储的并非完整的飞行轨迹,而是每个匹配飞机的最新状态:是否在空、当前位置、高度、速度与航向。对于异常检测系统而言,这些状态信息已经足够支撑后续的统计建模。

飞机群构建:FAA 注册数据的实用过滤

确定监控对象是系统的首要挑战。理论上,所有私人飞机都应该被纳入监控范围,但在实际操作中,需要从 FAA 的公开注册数据中筛选出真正具有 “精英逃逸” 象征意义的商务喷气机。FAA 提供的可下载注册数据包含美国登记的所有航空器信息,字段涵盖飞机型号、制造商、注册号、ICAO 地址、座位数等关键属性。

系统采用了一套实用的启发式规则进行过滤:排除旋翼机(直升机)、螺旋桨飞机、大型客机与政府飞机。过滤逻辑基于飞机型号名称的关键词匹配,例如包含 "helicopter"、"rotorcraft" 的型号被直接排除,而 "jet"、"challenger"、"falcon"、"gulfstream" 等关键词则被保留。这套规则并非完美 —— 某些特殊用途的喷气机可能被遗漏,某些螺旋桨飞机可能被误保留 —— 但在工程实践中已经足够可用。

从实际数据看,系统最终维护了一个约 11,482 架飞机的监控群,以 Gulfstream、Dassault Falcon、Cessna Citation、Bombardier Challenger 等主流商务机型为主。这个规模既保证了统计意义的充分性,又避免了因规模过大而导致的噪声增加。值得注意的是,这套过滤机制是可配置的,系统提供了命令行参数支持自定义座位数范围(--min-seats、--max-seats),用户可以根据需求调整监控范围。

异常检测核心:基于时间相似性的动态基线

系统的异常检测逻辑并未采用复杂的机器学习模型,而是选择了更为直观且可解释的统计方法。其核心思想是:对比当前时段(半小时窗口)的实际飞机在空数量与历史同期(相同星期几、相同时间段)的基线水平,计算偏差值并转换为标准化分数。

基线的构建采用 24 小时滚动窗口机制。系统维护一个历史数据库,记录过去 365 天内每个半小时窗口的飞机在空数量。每当新数据到来时,查询历史上同一星期几、同一时间窗口(例如每周三晚上 10 点半)的基线数据,计算中位数或均值作为 “预期值”。当前实际值与预期值之差即为绝对偏差,这个偏差除以历史数据的标准差即得到西格玛(σ)值。

采用时间相似性匹配而非全局均值有着重要的统计学意义。商务飞机的活动模式具有显著的周期性特征:工作日与周末的流量差异明显,白天与夜间的高峰低谷也不同。如果不加区分地使用全局均值作为基线,会严重低估夜间时段的异常、夸张白天时段的波动。时间相似性匹配确保了基线本身已经包含了这些周期性因素,异常检测更加精准。

西格玛值的引入解决了另一个关键问题:不同时间段的噪声水平不同。某些时段历史数据波动较大(例如周五下午的商务出行高峰),另一些时段则相对稳定(例如深夜凌晨)。相同的绝对偏差在稳定时段意味着更显著的异常,在噪点时段则可能是正常波动。西格玛标准化消除了这种时段差异,使得不同时间点的异常可以公平比较。

紧急等级判定:从连续信号到离散告警

为了将连续的西格玛分数转换为可操作的告警信号,系统设计了 5 级紧急等级制度。等级 1 到 5 对应不同的西格玛阈值,5 级代表最严重的异常。这一设计参考了传统的灾害预警系统分级模式,降低了告警的解读门槛。

具体阈值设定并未在公开文档中详细披露,但从系统行为可以推断其基本逻辑:当西格玛分数超过特定阈值(例如 3σ)时触发 2 级告警,超过 4σ 时触发 3 级,以此类推。等级 5 的触发条件最为严格,只有当偏差达到极端水平(可能超过 5σ)时才会激活。值得注意的是,系统在计算偏差时引入了 “当前偏差” 与 “模型误差” 的双重考量 —— 当基线本身较为稳定时,较小的绝对偏差可能对应较高的西格玛值;反之,当基线本身噪声较大时,较大的绝对偏差可能只对应较低的西格玛值。

紧急等级的另一个设计要点是防抖处理。ADS-B 数据本身存在采集延迟,热力图文件也是半小时粒度,单一时间点的异常可能只是数据噪声。系统在判定紧急等级时采用了窗口平滑策略:只有当连续多个时间窗口都满足触发条件时,才提升告警等级;同样,降级也需要满足一定的持续时间。这种设计避免了告警频繁跳变导致的 “狼来了” 效应。

告警通道:Telegram 机器人与 RSS 订阅

当紧急等级达到 5 级时,系统会通过 Telegram 机器人向订阅频道发送告警消息。消息内容简洁明了,包含当前紧急等级、飞机在空数量(与预期偏差),以及指向实时仪表盘的链接。告警消息不会重复发送 —— 系统会在数据库中记录最近一次告警的时间戳与热力图窗口,避免同一次异常事件的重复通知。

除了即时通讯渠道,系统还提供了 RSS 订阅源作为备用告警通道。RSS 源的更新策略与 Telegram 类似:只有当新的热力图窗口达到 5 级告警条件时,才会生成新的 RSS 条目。对于无法接收即时消息的场景(例如定时任务检查),RSS 提供了更为灵活的集成方式。

从架构角度看,告警通道的设计体现了 “告警与展示分离” 的原则。告警逻辑运行在后端服务中,与前端仪表盘解耦;即使仪表盘因故不可用,告警通道仍然可以正常工作。这种分离也便于扩展新的通知渠道 —— 只要在后端添加相应的推送代码即可,无需改动核心检测逻辑。

仪表盘前端:实时数据可视化的工程实践

系统的 Web 仪表盘采用 React 构建,部署于 Cloudflare Pages 静态托管。仪表盘的核心可视化组件包括:世界地图(显示当前在空飞机的实时位置)、告警刻度盘(直观展示紧急等级)、历史趋势图(展示过去若干天的飞机在空数量曲线)。这些组件通过 Vite 构建工具打包,最终产物为纯静态 HTML/CSS/JavaScript 文件。

前后端数据交互采用了 “快照发布” 模式。后端定期生成当前状态的 JSON 快照(包含飞机数量、紧急等级、历史基线对比等信息),上传至 Cloudflare R2 对象存储;前端从 R2 加载快照并渲染。这种设计的好处在于:静态前端可以部署在全球边缘节点,响应速度快且成本低;快照生成频率可以独立调整,平衡数据实时性与服务端负载。

快照生成由定时任务触发,每次热力图采集完成后执行。任务会查询 SQLite 数据库中的最新数据,计算当前紧急等级,然后输出 JSON 文件上传至 R2。由于使用了静态发布模式,前端用户实际上看到的数据会有约半小时的延迟 —— 这恰好是热力图的发布粒度。对于异常检测场景而言,半小时延迟是可接受的,因为真正的灾难级异常不会在半小时内消失。

存储后端:SQLite 在实时监控系统中的适用性

系统的持久化存储选择了 SQLite 而非 PostgreSQL 或 MySQL 等传统关系数据库。这个选择初看有些意外 ——SQLite 通常被视为轻量级嵌入式数据库,难以应对高并发写入 —— 但在当前系统规模下却是合理的选择。

系统的写入模式是典型的批量写入:每半小时写入数千条记录(匹配飞机的当前状态),写入压力远低于每秒数万条的事务系统。SQLite 在这种场景下表现良好,简单的文件复制也便于数据备份与迁移。更重要的是,SQLite 的零配置特性降低了部署复杂度 —— 后端服务只需一个数据目录,无需管理独立的数据库服务进程。

历史数据的存储同样采用 SQLite。表结构设计简洁:一张表存储飞机元数据(ICAO 地址、型号、注册号等),另一张表存储每日每半小时的在空计数。这种星型模型便于后续的聚合查询 —— 要计算某个时间段的历史基线,只需查询计数表并按时间窗口分组即可。数据量方面,一年约 17,520 个时间窗口(365 天 × 48 个半小时窗口),每条记录仅包含计数与时间戳,存储开销极低。

工程局限与改进方向

尽管系统整体设计简洁实用,但仍存在几个值得关注的工程局限。首先是数据粒度问题:热力图文件以半小时为粒度发布,理论上系统只能检测到持续时间超过半小时的异常。对于更加短暂的异常活动(例如紧急撤离的前兆),系统可能无法捕捉。改进方向是引入更细粒度的实时数据源,但这意味着显著增加系统复杂度与运营成本。

其次是飞机群定义的模糊性。当前过滤规则基于简单的关键词匹配,无法区分军用飞机与民用飞机,也无法识别注册信息不完整或存在隐私保护的飞机。这导致监控群可能包含并非 “精英阶层” 所有的飞机,也可能遗漏某些真正值得关注的目标。更精确的飞机群定义需要额外的外部数据源或人工审核流程。

第三是误报与漏报的平衡。当前阈值基于历史数据的统计分布,但在极端情况下(例如重大节假日、体育赛事期间),商务飞机的活动模式可能与历史基线产生较大偏差,导致误报。解决思路是引入外部事件日历作为辅助特征,对基线进行动态调整。

技术复盘:异常检测系统的设计哲学

Apocalypse Early Warning System 展示了一种独特的异常检测设计哲学:与其追求复杂的机器学习模型,不如深耕数据源的质量与基线建模的合理性。系统没有使用任何深度学习或复杂的 unsupervised anomaly detection 算法,而是通过时间相似性匹配与西格玛标准化实现了可靠的异常检测。这种 “简单优先” 的思路在工程实践中往往比过度设计更加稳健。

另一个值得注意的点是系统对公开数据的巧妙利用。ADS-B Exchange 提供了免费的热力图数据,FAA 注册信息同样开放获取,两者结合即可构建有意义的监控能力,无需自建接收站或购买商业数据。在数据资产日益重要的当下,这种 “数据即服务” 的思路为类似项目提供了参考。

从运维角度看,系统的低依赖部署设计同样值得学习。后端仅需 Node.js 与 Python 运行时,存储依赖 SQLite,静态前端托管于 Cloudflare Pages,几乎没有单点故障风险。即使某个组件失效,也能在短时间内恢复运行。这种 “轻量级、可替代” 的架构理念,正是长期运行的无监控值守系统所需要的。


资料来源

systems