11 KiB
11 KiB
实时船只监控系统 — 实施计划
状态:规划中 创建日期:2026-04-27 优先数据源:BarentsWatch AIS(免费但需要 OAuth client credentials)→ AISHub / MarineTraffic(TODO,付费)
已确认决策
| 项目 | 决策 |
|---|---|
| 数据源 | BarentsWatch 先行;AISHub / MarineTraffic TODO |
| 船只规模 | BarentsWatch 阶段全部显示;全球数据接入后按需加船型过滤(默认 Cargo + Tanker + Passenger) |
| 更新频率 | 准实时:前端 5 分钟轮询,后端 Collector 每分钟拉取写库 |
| 历史轨迹 | 保留(vessel_position 表保留 24h,后期按需扩展) |
| 推送方式 | 前端展示仍可先用 HTTP 拉取聚合结果;AISStream 等实时源应单独实现 WebSocket 采集器 |
一、技术背景
船只通过 AIS(自动识别系统)每 2–10 秒广播位置、航速、航向、目的地等信息。全球约 50 万艘持证船只在线,实时数据通过以下方式获取:
| 来源类型 | 典型服务 | 覆盖范围 | 成本 | 状态 |
|---|---|---|---|---|
| BarentsWatch AIS API | live.ais.barentswatch.no | 挪威海域实时 | 免费,需要 AIS API client credentials | 当前使用 |
| AISHub | aishub.net | 全球实时 | 免费/小额 | TODO:付费接入 |
| MarineTraffic API | marinetraffic.com | 全球实时 | $50–$500/月 | TODO:评估 tier |
| VesselFinder API | vesselfinder.com | 全球实时 | $50–$300/月 | TODO:备选 |
| 自建 SDR 接收 | RTL-SDR + AIS-catcher | 仅本地 30–50km | 硬件 $30 | 不考虑 |
| NOAA 历史数据 | Marine Cadastre | 美国近海历史 | 免费 | 可用于冷启动 |
BarentsWatch AIS API
- 端点:
https://live.ais.barentswatch.no/v1/latest/combined - 需要在 BarentsWatch developer portal 创建
AIS - APIclient,通过 client credentials 获取scope=ais的 access token 后请求 AIS endpoint - 字段:mmsi, lat, lon, sog, cog, heading, nav_status, name, vessel_type, flag
- 刷新频率:数据约 30–60s 更新一次,可随意轮询
TODO:多源 AIS 与实时流接入
- 接入 AISStream WebSocket 采集器,作为 BarentsWatch 覆盖不足的实时补充
- 将 BarentsWatch、AISStream、自定义
vessel_ais映射源统一写入原始观测层 - 通过聚合接口做去重、字段合并、冲突记录和默认来源选择
- 开放字段级聚合策略配置,让用户决定不同字段优先信任哪个来源
- 评估 AISHub 订阅(全球覆盖,约 $30/月),接入全球实时流
- 评估 MarineTraffic API tier,对比 AISHub 数据质量与成本
- 实现多数据源适配器,通过
datasource_config切换 - 真实高频 AIS 稳定接入后,评估将
vessel_position迁移为 TimescaleDB hypertable(保留 Postgres 原生分区作为备选)
多源 AIS 的详细设计见 AIS 多源采集、冲突记录与聚合接口计划。
二、实施计划
Phase 0 — 数据源验证与链路打通(1–2 天)
- 接入 BarentsWatch AIS API,验证 OAuth token、数据格式与字段
- 构建全球 mock 数据生成器(用于前端渲染压测,补充 BarentsWatch 的地域限制)
- 确认前端可渲染船只点,整条链路走通
Phase 1 — 后端基础设施(3–4 天)
1.1 数据库 Schema
-- 船只静态信息(每 6h 刷新一次)
CREATE TABLE vessel_static (
mmsi BIGINT PRIMARY KEY,
name VARCHAR(128),
callsign VARCHAR(16),
vessel_type SMALLINT,
vessel_type_name VARCHAR(64),
flag VARCHAR(4), -- ISO 国家码
length FLOAT,
width FLOAT,
draught FLOAT,
imo BIGINT,
updated_at TIMESTAMPTZ
);
-- 船只实时位置(高频写入,保留 24h 轨迹)
CREATE TABLE vessel_position (
id BIGSERIAL PRIMARY KEY,
mmsi BIGINT NOT NULL,
lat FLOAT NOT NULL,
lon FLOAT NOT NULL,
sog FLOAT, -- Speed over ground(节)
cog FLOAT, -- Course over ground(度)
heading SMALLINT, -- 真北航向
nav_status SMALLINT, -- 0=航行 1=锚泊 5=停靠 ...
received_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_vessel_pos_mmsi_time ON vessel_position(mmsi, received_at DESC);
CREATE INDEX idx_vessel_pos_time ON vessel_position(received_at DESC);
-- 最新位置物化视图(地图渲染主数据源,避免全表扫描)
CREATE MATERIALIZED VIEW vessel_latest AS
SELECT DISTINCT ON (mmsi)
vp.*, vs.name, vs.vessel_type_name, vs.flag, vs.length
FROM vessel_position vp
LEFT JOIN vessel_static vs USING (mmsi)
ORDER BY mmsi, received_at DESC;
CREATE UNIQUE INDEX ON vessel_latest(mmsi);
后期如需完整历史轨迹查询,迁移
vessel_position到 TimescaleDB 或按天分区。
1.2 Collector:VesselAISCollector
文件:backend/app/services/collectors/vessel_ais.py
- 继承
BaseCollector,注册到collector_registry - 轮询间隔:30–60s(由数据源限速决定)
- 支持多数据源切换,通过
datasource_config配置 URL + API Key - 写入逻辑:upsert
vessel_latest,appendvessel_position - 接入现有调度系统(
scheduler.py)
1.3 API 端点
GET /api/v1/visualization/geo/vessels
?bbox=lon_min,lat_min,lon_max,lat_max # 视口裁剪
?type=cargo,tanker,passenger # 船型过滤
?limit=0 # 可选;不传或 0 表示不裁剪数量
→ GeoJSON FeatureCollection(Point)
GET /api/v1/visualization/vessels/{mmsi} # 单船详情
GET /api/v1/visualization/vessels/{mmsi}/track # 历史轨迹(默认 6h)
?hours=6
→ GeoJSON LineString
GeoJSON Feature 格式:
{
"type": "Feature",
"geometry": { "type": "Point", "coordinates": [lon, lat] },
"properties": {
"mmsi": 123456789,
"name": "EVER GIVEN",
"vessel_type": 70,
"vessel_type_name": "Cargo",
"flag": "PA",
"sog": 12.4,
"cog": 247.0,
"heading": 245,
"nav_status": 0,
"length": 400,
"received_at": "2026-04-27T10:00:00Z"
}
}
1.4 更新机制
前端聚合结果拉取 + 后端实时采集:
- 前端
setInterval(fetchVessels, 5 * 60 * 1000)定期拉取最新快照 - 后端 BarentsWatch collector 继续以 HTTP polling 方式采集
- AISStream 等实时源以独立 WebSocket collector 写入原始观测层
- 展示接口从聚合服务读取当前船只视图,而不是由单个 collector 决定最终展示值
- 前端默认不再给
/geo/vessels传limit=5000,VESSEL_CONFIG.maxRenderedMarkers = 0表示不做前端数量裁剪;后续如性能不足再引入显式 LOD 上限 - marker 颜色、详情卡、hover 和搜索结果必须共享
vessel_type_display船型归一化结果,避免 AIS 数字类型码已驱动颜色但卡片仍显示Other - 前端是否升级为 WebSocket delta push 是独立优化,不影响后端采集器可以使用 WebSocket 接上游实时源
Phase 2 — 前端渲染(3–4 天)
文件:frontend/public/earth/js/vessels.js
2.1 渲染方案
参考现有卫星系统(satellites.js)的 InstancedMesh 模式:
THREE.InstancedMesh:每个实例 = 一艘船,矩阵包含位置 + 旋转(朝向 COG)- 行进船:三角箭头图标,朝向 COG 方向
- 静止/锚泊船:圆点图标
- SVG 图标输出到
frontend/public/earth/assets/icons/vessel-arrow.svg和vessel-dot.svg
2.2 船型颜色规范
| 船型 | 颜色 |
|---|---|
| 货轮 Cargo | #4A90D9 蓝 |
| 油轮 Tanker | #E85D04 橙红 |
| 客船 Passenger | #06D6A0 绿 |
| 渔船 Fishing | #FFD166 黄 |
| 军舰 Military | #73797E 灰 |
| 其他 | #9B9B9B 浅灰 |
| 锚泊/停靠 | 降低饱和度 0.4x |
2.3 LOD(相机距离细节层次)
| 相机距离 | 渲染策略 |
|---|---|
| > 400 | 默认渲染当前接口返回的全部船只;如性能不足,再引入可配置 LOD 上限 |
| 200–400 | 默认渲染当前接口返回的全部船只;如性能不足,再引入可配置 LOD 上限 |
| < 200 | 渲染当前视口 bbox 内全部船只 |
前端根据相机位置动态计算 bbox,附加到 API 请求中。
2.4 图层集成
接入现有图层系统,新增"船只"图层项,支持:
- 图层开/关,状态持久化
- 子过滤(按船型选择显示哪类,可在图例或设置面板中配置)
- 与海缆、BGP、卫星层级共存(renderOrder 待定,参考现有层级文档)
2.5 Info Card
复用 showInfoCard 机制,点击船只弹出:
EVER GIVEN 🚢
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
MMSI 123456789
IMO 9811000
旗帜 巴拿马 🇵🇦
船型 散货轮
当前航速 12.4 kn
航向 247°
状态 航行中
目的地 ROTTERDAM
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[ 查看轨迹 ] [ MarineTraffic ↗ ]
2.6 轨迹可视化
点击"查看轨迹" → 请求 /vessels/{mmsi}/track → 用 THREE.CatmullRomCurve3 渲染插值轨迹线,风格与海缆一致。
Phase 3 — 功能完善(2–3 天)
| 功能 | 说明 |
|---|---|
| 船只搜索 | 接入现有搜索面板,按名称 / MMSI 搜索 |
| 统计 HUD | 显示当前在线船只数、各类型分布 |
| 密度热图 | 超低 zoom 时切换为 hex-bin 热力图(避免点云爆炸) |
| 港口标注 | 加载 WorldPorts 数据集,显示主要港口标记 |
| 关键水道监控 | 马六甲、霍尔木兹、苏伊士等高亮 + 流量统计 |
Phase 4 — 性能与生产化(2–3 天)
vessel_position按天分区,7 天自动清理- TODO:真实数据量达到百万级/日后,将
vessel_position升级为 TimescaleDB hypertable,配置 retention policy 与压缩策略 - GeoJSON endpoint 用 Redis 缓存 15s
- 若需 bbox 精确查询,引入 PostGIS
geography+ST_DWithin - InstancedMesh + frustum culling,目标 5 万船只 60fps
三、工作量估算
| Phase | 内容 | 估计时间 |
|---|---|---|
| Phase 0 | 数据源验证、mock | 1–2 天 |
| Phase 1 | 后端 Schema + Collector + API | 3–4 天 |
| Phase 2 | 前端渲染(InstancedMesh + 图层 + Info Card) | 3–4 天 |
| Phase 3 | 搜索 + 统计 + 轨迹 | 2–3 天 |
| Phase 4 | 性能优化 + 生产数据源接入 | 2–3 天 |
| 合计 | 约 2–3 周 |
四、参考资料
- BarentsWatch AIS API 文档:https://www.barentswatch.no/en/developer/ais-api/
- MarineTraffic API:https://www.marinetraffic.com/en/ais-api-services
- AISHub:https://www.aishub.net/api
- AIS 导航状态码:ITU-R M.1371-5
- 船型编码(vessel_type):ITU/IMO AIS Message 5 Type and Cargo
- WorldPorts 数据集:https://msi.nga.mil/Publications/WPI