0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

飛行機をリアルタイムで追跡する データパイプラインを作ってみた

0
Posted at

飛行機をリアルタイムで追跡するデータパイプラインを作ってみた — Confluent Platform × Apache Flink

スクリーンショット 2026-10-21 20.05.48.png


同様のブログを、IBM Community Blogにも投稿しています。
https://community.ibm.com/community/user/blogs/shumpei-kubo/2026/10/21/realtime-flight-tracker-with-confluent-platform

ADS-B データを Kafka に流し、Apache Flink で SQL 分析、WebSocket でブラウザにリアルタイム配信——
FlightRadar24 風のダッシュボードを OSS とデータ技術だけで構築しました。

はじめに — なぜ「飛行機追跡」なのか?

リアルタイムデータパイプラインのデモをするとき、「どんなデータを使うか」が非常に重要です。
株価や IoT センサーはよく使われますが、今回あえて 飛行機のリアルタイム位置情報 を選びました。

理由はシンプルで、誰でも直感的に「リアルタイム」を体験できる からです。
地図上を実際に動く飛行機アイコンを見ると、データが本当にリアルタイムに流れていることが一目でわかります。
IoT や金融でも「ああ、こういう感じか」と想像してもらいやすくなります。

使用データ: ADSB.lol という無料・無認証の ADS-B 受信 API。
日本周辺4エリアを10秒ごとにポーリングすると、500〜1,000 機体のリアルタイム位置情報が取得できます。


アーキテクチャ — 5 レイヤー構成

全体の構成はシンプルです。データは一方向に流れます。

ADSB.lol API  →  Apache Kafka  →  Apache Flink  →  Python/WS サーバー  →  ブラウザ
(ADS-B 受信)     (flight_positions)  (SQL 分析)     (WebSocket 配信)     (Leaflet.js 地図)
レイヤー コンポーネント 役割
データ収集 1_fetch_flights.py ADSB.lol API を 10 秒ごとにポーリング。各機体を個別メッセージ(key=ICAO24)に分解して Kafka へ produce
メッセージング Kafka: flight_positions 全機体の最新スナップショットを保持するソーストピック(秒速 1,500 件以上を処理)
ストリーム処理 Apache Flink SQL INSERT ジョブが 4 本稼働中。フィルタ・集計・ウィンドウ処理を担当し、派生トピックへ書き出す
バックエンド 3_dashboard_server.py Kafka Consumer + WebSocket サーバー。10 秒ごとに全クライアントへブロードキャスト
フロントエンド 4_dashboard_map.html Leaflet.js による地図 UI。WebSocket メッセージを受信するたびにアイコンをリアルタイム更新

Kafka の役割 — 秒速 1,500 件をさばくデータの通り道

Kafka はデータを 「一時的に溜めながら高速に流す」 パイプです。
Publisher(Producer)と Subscriber(Consumer)が完全に非同期で動き、どちらかが遅れても全体が止まりません。

このデモでは flight_positions という 1 つのトピックがソースになります。
Flink・ダッシュボードサーバーの両方がこのトピックを 独立して 消費しています。

指標 値
Kafka 受信速度 〜1,500 msgs/s
追跡機体数(日本周辺) 500〜1,000 機
データ更新サイクル 10 秒

全世界モードにすると?
ADSB.lol の /v2/all エンドポイントに切り替えると、5,000〜20,000 機体が対象になります。
Kafka の真価(高スループット・スケーラビリティ)を見せるときはこのモードが効果的です。


Apache Flink の役割 — SQL でストリームを分析する

このデモの 一番のポイント が Flink です。
通常のデータベースは「貯まったデータ」を後から分析しますが、Flink は 流れているデータをリアルタイムに分析 できます。

川(Kafka)を流れる水(データ)を、川の途中に仕掛けた網(Flink)でリアルタイムに選別・計測するイメージです。

このデモでは 4 本の Flink SQL INSERT ジョブ が常時稼働しています。

ジョブ名 出力トピック 処理内容
flink-insert-japan flink_flight_japan registration LIKE 'JA%' で日本登録機をフィルタ
flink-insert-airline flink_airline_stats 航空会社別集計(60 秒タンブリングウィンドウ)
flink-insert-altitude flink_altitude_stats 高度帯別フライト数(60 秒ウィンドウ)
flink-insert-phase flink_phase_stats フライトフェーズ別集計(60 秒ウィンドウ)

SQL で書いたフィルタの例

日本登録機フィルタ:

INSERT INTO flink_flight_japan
SELECT *
FROM   flight_positions_stream
WHERE  registration LIKE 'JA%'  -- JA で始まる = 日本登録機(例: JA986A)
  AND  on_ground = false;        -- 飛行中のみ

なぜ callsign ではなく registration で判定するのか?

callsign(コールサイン)は「ANA762」のように航空会社コード+便名であり、日本の航空会社でも「JA」で始まりません。
一方 registration(登録番号)は日本で登録された機体には必ず「JA」が付きます(例: JA986A)。

callsign フィルタを使うと日本の定期便の約 71% を取りこぼしてしまいます。これは実データ約 128 機体で検証済みです。

航空会社別 60 秒タンブリングウィンドウ集計:

INSERT INTO flink_airline_stats
SELECT
    SUBSTRING(callsign, 1, 3)  AS airline_code,
    COUNT(*)                   AS flight_count,
    AVG(baro_altitude)         AS avg_altitude_m,
    AVG(velocity)              AS avg_velocity_ms,
    MAX(baro_altitude)         AS max_altitude_m
FROM   flight_positions_stream
GROUP BY
    TUMBLE(TABLE flight_positions_stream, DESCRIPTOR(ts), INTERVAL '60' SECOND),
    SUBSTRING(callsign, 1, 3);

コールサインの先頭 3 文字(JAL、ANA、ADA など)を航空会社コードとして識別し、60 秒ごとにフライト数・平均高度・平均速度・最高高度を自動計算します。60 秒が経過するとウィンドウがリセットされ、常に「直近 60 秒の最新統計」が更新されます。


ダッシュボードの機能

ブラウザを開くと、世界地図上に飛行機が実際に動いている様子が見えます。

地図の見方

表示 意味
🟢 緑のアイコン 低高度(0〜3,000 m)
🔵 青のアイコン 中高度(3,000〜8,000 m)
🩵 水色のアイコン 高高度(8,000〜12,000 m)— 多くの旅客機が巡航する帯
🟣 紫のアイコン 超高高度(12,000 m 以上)
ティール色リング付きアイコン 日本登録機(Flink が flink_flight_japan から検出)
赤いパルスアニメーション 緊急スカワーク発信機(リアルタイム検知)
アイコンの向き 飛行機の進行方向(track フィールド)

右側サイドバーの 4 タブ

タブ データソース 内容
全機体リスト Kafka 追跡中の全機体をコールサイン・高度・速度・機種で一覧表示。検索バーで絞り込み、行クリックで地図フォーカス
日本登録機 Flink flink_flight_japan トピックで抽出した JA 登録機のみ。地図上ではティール色リングで強調
緊急アラート リアルタイム検知 スカワーク 7700(一般緊急)・7600(通信不能)・7500(ハイジャック)を区別してリアルタイム通知
航空会社別統計 Flink flink_airline_stats の 60 秒集計をフライト数降順で表示。フライト数・平均高度・平均速度を確認

緊急スカワークコード

コード 名称 意味
7700 一般緊急 エンジントラブル・急病人など機上での緊急事態
7600 通信不能(NORDO) 無線通信が使えず、管制と連絡が取れない状態
7500 ハイジャック 不法な乗っ取りを示す最も深刻なコード

このシステムは実際の ADS-B データを使用しているため、現実の緊急事態もリアルタイムで検知します。
実際には誤操作によるものが多く、大半は確認後にすぐクリアされます。


追加機能 — 異常検知と接近警報

メインの地図ダッシュボード以外にも、いくつかの拡張機能を実装しています。

⚠️ 異常降下・上昇の検知

垂直速度 |vertical_rate| が 20 m/s を超えた状態が 連続 2 回以上(10 秒間隔)続いた場合にアラート化。
30 m/s 超(約 6,000 ft/min)は「緊急降下疑い」として別表示されます。

📡 機体同士の異常接近検知

5 秒ごとに全機体間の距離を計算。水平 9.26 km(5 NM)・垂直 250 m 以内に近づくと警報。
3 NM・500 ft 以内はより高い重大度で通知します。連続 2 サイクル以上で確定。

重大度 水平距離 垂直差
注意(caution) < 9.26 km(5 NM) < 250 m
異常接近(emergency) < 5.56 km(3 NM) < 152 m(約 500 ft)

🤖 デモ用ダミーデータ注入

外部 API に接続できない環境向けに、リアルな飛行パスを持つダミー機体を Kafka に直接投入するスクリプト(1b_inject_demo_data.py)を用意しています。

python 1b_inject_demo_data.py                      # 40機体を10秒間隔で投入
python 1b_inject_demo_data.py --count 80 --anomaly # 80機体 + 異常検知データも投入
python 1b_inject_demo_data.py --proximity          # 機体同士の異常接近も発生させる

技術スタック

コンポーネント 役割
ADSB.lol API 無料・無認証の ADS-B 受信 API。日本周辺4エリアを 10 秒ごとにポーリング
Apache Kafka 秒速 1,500 件以上のメッセージを処理するストリーミングプラットフォーム
Confluent Platform Kafka のエンタープライズ版。Apache Flink の CMF(Confluent Manager for Apache Flink)管理も統合
Apache Flink SQL でストリームデータをリアルタイム分析。CMF REST API でジョブを管理
Python 3 データ取得・バックエンドサーバーの実装言語(confluent-kafka / websockets ライブラリ)
WebSocket ブラウザとサーバーを常時双方向接続。データ到着と同時に画面へ反映
Leaflet.js ブラウザで地図を表示する OSS JavaScript ライブラリ
nginx HTTPS リバースプロキシ。WebSocket のプロキシ設定も担当

実装で学んだこと・ハマりポイント

① callsign vs registration の罠

最初は「日本の飛行機 = callsign が JA で始まる」と思い込んでフィルタを実装しました。しかし実データを検証すると、JAL・ANA などの定期便のコールサインは「JAL762」「ANA302」のような形式で、先頭が航空会社コードです。

日本の飛行機かどうかを判定するには registration(機体登録番号)の先頭が JA かどうか を見る必要があります。この修正で日本機の捕捉率が約 71% 向上しました。

callsign : "JAL762"  ← 「JAL」= 航空会社コード。JA で始まらない
registration: "JA986A" ← 「JA」= 日本登録番号の先頭。これが正解

② ssl.ca.location はタスクマネージャ上のパス

Flink SQL ジョブから Kafka(SSL 有効)に接続する際、ssl.ca.location に指定するファイルパスは Flink タスクマネージャ上のパス です(例: /tmp/kafka-ca.crt)。

開発マシン上のローカルパスを書いても接続できないため、タスクマネージャに事前にファイルを配置しておく必要があります。

# ❌ 間違い — ローカルのパスを書いても Flink から見えない
'ssl.ca.location': './kafka-ca.crt'

# ✅ 正しい — タスクマネージャ上にあらかじめ配置したパス
'ssl.ca.location': '/tmp/kafka-ca.crt'

③ Flink の Catalog 名は UI 表示名と異なる

CMF の UI では flink-catalog と表示されていますが、SQL ステートメントの sql.current-catalog プロパティに指定する値は _env_flink-env(アンダースコア + env + ハイフン)です。
UI 表示名をそのまま使うとジョブが失敗します。

# ❌ UI 表示名をそのまま使うと失敗する
"sql.current-catalog": "flink-catalog"

# ✅ 実際に指定すべき値
"sql.current-catalog": "_env_flink-env"

④ フォールバック設計で壊れないデモを実現

Flink 派生トピックが存在しない場合や、ジョブが FAILED になった場合でも、バックエンドサーバーは自動的に flight_positions から直接計算するフォールバックモードに切り替わります。地図表示は常に正常動作するため、デモ中に Flink が止まっても「真っ白画面」にはなりません。

Kafka データが 30 秒来ない場合はさらにフォールバックして、東京周辺を飛ぶモック機体を自動生成します。
デモ環境のネットワーク不安定でも画面が止まらないよう設計しています。


5 分で体験できるデモウォークスルー

ステップ 時間 内容
Step 1 1 分 ダッシュボードを開き、日本上空の飛行機が 10 秒ごとに動いていることを確認。アイコンをクリックするとコールサイン・高度・速度・機種がポップアップで表示
Step 2 1 分 サイドバーの「日本登録機」タブを開き、地図上のティール色リングと連動していることを確認。Flink が registration LIKE 'JA%' でリアルタイムに選別している結果がここに出る
Step 3 2 分 「航空会社別統計」タブを開いて 60 秒待つ。JAL・ANA・ADA などのフライト数・平均高度・速度がタンブリングウィンドウで自動更新される様子を確認
Step 4 1 分 画面上部 KPI バーの「Kafka Rate」を見る。1,500 msgs/s 前後のリアルタイム受信速度が表示され、パイプラインが高スループットで動いていることを体感

このデモが伝えるもの

「外部 API → Kafka → Apache Flink → WebSocket → ブラウザ」という リアルタイムデータパイプラインの全レイヤー を 1 つのデモで体験できます。
IoT センサーデータ、金融取引、物流追跡など、あらゆるリアルタイムデータ活用シナリオにそのまま応用できるアーキテクチャです。


おわりに

Confluent Platform 上の Apache Flink を使うと、ストリーミングデータへの SQL 分析が驚くほど手軽に実装できます。今回のデモでは 4 本の INSERT ジョブを Python スクリプト 1 本(約 300 行)で管理・デプロイしており、CMF REST API の扱いやすさを実感しました。

飛行機データという「誰でも直感的に理解できる題材」を選んだことで、Kafka の高スループット、Flink の SQL ストリーム処理、WebSocket によるリアルタイム配信という 3 つの技術を一度に体験できるデモになりました。

コメントお待ちしております。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?