TensorFlow搭載IoT気象観測装置で降雨強度を高精度予測
必要な部品と材料
| 部品 | 数量 | 備考 |
|---|---|---|
| DFRobot気象観測キット(風速計/風向計/雨量計付き) | 1 | |
| NodeMCU ESP8266 (ESP-12E) | 1 | Wi-Fi接続用 |
| DFRobot SIM808 GPS/GPRS/GSMシールド | 1 | Arduino用、GPRS接続用 |
| Arduino UNO | 1 | SIM808用ホスト |
| Raspberry Pi 3 Model B+ または 4 | 1 | Webサーバー/基地局用 |
| ハンダ付け可能ミニブレッドボード | 2 |
必要な工具
- ホットグルーガン
使用アプリ・サービス
- Arduino IDE
- TensorFlow / Keras
- Microsoft Visual Studio 2017(PHP開発用)
プロジェクト概要
背景と動機
2020年、局地的な激しい豪雨が温室栽培や農作物に深刻な被害をもたらしました。気候変動の影響で予期せぬ豪雨が頻発し、一方で干ばつに見舞われる地域もあります。私の地域だけでも2020年に35件以上の農業被害が記録されています。農業への豪雨被害を防ぐ出発点としてこのプロジェクトを構想しました。
豪雨がもたらす危険性:
- 洪水(人的被害のリスク)
- 建物・インフラの損壊
- 農作物・家畜の損失
- 土砂崩れ(交通・通信の寸断、人的脅威)
最新研究では、過剰降雨が作物生産性に及ぼす影響は干ばつや極端な高温と同等以上とされています(イリノイ大学の研究:トウモロコシ収量が最大34%減少、干ばつ・高温では最大37%減)。
早期警戒システムだけでは不十分で、各温室・農場ごとのローカル気象データに基づく予測が必要です。そこで、低コストなリモート気象観測装置を自作し、収集したローカルデータで降雨強度を予測するシステムを構築しました。
アプローチ
深層学習・ニューラルネットワークを調査し、分類モデルベースの人工ニューラルネットワーク(ANN)で蓄積気象データを解釈、降雨強度を5クラスに分類予測することにしました。先行研究でもニューラルネットで降雨量予測や竜巻予測、霧予測等に成功例があります。
3ヶ月以上、自宅バルコニーと裏庭(半径3km圏内)で気象データを収集。DFRobot気象観測キットを2種類の開発ボードで運用:
- NodeMCU ESP-12E(Wi-Fi接続・バルコニー用)
- Arduino Uno + SIM808シールド(GPRS接続・裏庭用、Wi-Fi届かない場所対応)
データ蓄積にはGoogle Sheets APIを活用。PHP製Webアプリで開発ボードからのHTTP GETリクエストを受け、スプレッドシートに追記します。
5分ごとのデータを3ヶ月以上収集(32,219行)後、TensorFlowでANNモデルを構築・訓練。降雨強度を以下の5クラスに分類:
- 降雨なし (0)
- 小雨 < 2.5 mm/h (1)
- 中雨 2.5–7.6 mm/h (2)
- 大雨 7.6–50 mm/h (3)
- 猛烈な雨 > 50 mm/h (4)
ステップ1:気象観測キットの組み立て
キットには風速計、風向計、雨量計、センサーボード(シリアル出力)が含まれます。出力形式(37バイト/秒、CR/LF終端):
c000s000g000t086r000p000h53b10020
データパーサー:
c000:風向(度)s000:平均風速(0.1 mph)g000:最大風速(0.1 mph)t086:温度(華氏)r000:1時間雨量(0.01 inch)p000:24時間雨量(0.01 inch)h53:湿度(%)(00=100%)b10020:気圧(0.1 hPa)
組み立て手順:風速計・風向計をブリッジにネジ止め→RJ11コード接続→ステンレス棒取付→雨量計ジョイント取付→雨量計を棒に固定。センサーボードは防水でないため、プラスチックケースで保護しています。

ステップ2:Google Sheetsでデータ収集
2.1 Google Sheets API認証情報の取得
- Google Developer Consoleで「Remote Weather Station」プロジェクト作成
- APIライブラリでGoogle Sheets APIを有効化
- 認証情報でサービスアカウント・キー作成
- OAuth同意画面設定→OAuthクライアントID作成(デスクトップアプリ)
- 認証情報(ID/Secret)を
credentials.jsonとして保存
2.2 スプレッドシート作成
- Google Sheetsで「Remote Weather Station」シート作成
- 1行目に列名定義:
WD, Av_WS, Mx_WS, 1h_RF, 24h_RF, Tem, Hum, b_PR - 必要に応じてグラフ設定
ステップ3:PHP Webアプリでデータ追記
開発ボードからのHTTP GETを受け、Google Sheetsに追記するPHPアプリ。Google Client Library for PHPが必要。
remote_weather_station/フォルダに以下4ファイルを配置:
index.php(メイン処理)account_verification_token.php(認証コード保持)credentials.jsontoken.json(初回認証後自動生成)
index.php の要点
// GETパラメータから気象データ取得
$variables_from_module = [
"wd" => (int)$_GET['wd'], "a_ws" => (float)$_GET['a_ws'],
"m_ws" => (float)$_GET['m_ws'], "1_rf" => (float)$_GET['1_rf'],
"24_rf" => (float)$_GET['24_rf'], "tem" => (float)$_GET['tem'],
"hum" => (int)$_GET['hum'], "b_pr" => (float)$_GET['b_pr']
];
// Google APIクライアント設定
$client = new Google_Client();
$client->setApplicationName('Remote Weather Station');
$client->setScopes('https://www.googleapis.com/auth/spreadsheets');
$client->setAuthConfig('credentials.json');
$client->setAccessType('offline');
// トークン読み込み・期限切れなら再認証
$tokenPath = 'token.json';
if (file_exists($tokenPath)) { ... }
if ($client->isAccessTokenExpired()) { ... }
// スプレッドシートに追記
$spreadsheetId = '';
$range = 'A1:H1';
$values = [[...]]; // 8列分の値
$body = new Google_Service_Sheets_ValueRange(['values' => $values]);
$params = ['valueInputOption' => 'RAW'];
$result = $service->spreadsheets_values->append($spreadsheetId, $range, $body, $params);
account_verification_token.php
$account_verification_token = ""; // 認証後のコードを入力
ステップ3.1:Raspberry PiでWebアプリ展開
- アプリフォルダをApache公開ディレクトリへ移動:
sudo mv ~/Downloads/remote_weather_station /var/www/html/ - 初回アクセスで認証URL表示→ブラウザで許可→認証コード取得→
account_verification_token.phpに記入 - ページリフレッシュで
token.json生成、以降は自動動作 - テスト:
https://localhost/remote_weather_station/?wd=12&a_ws=8&m_ws=11&1_rf=14&24_rf=84&tem=24&hum=32&b_pr=57
ステップ4:ESP8266(Wi-Fi)でデータ送信
NodeMCU ESP-12Eで5分ごとにPHPアプリへHTTP GET送信。主要コード:
#include
#include
#include
#include
const char *ssid = "";
const char *password = "";
SoftwareSerial Serial_1(D6, D5); // RX, TX
void loop() {
getBuffer(); // 気象キットから35バイト読取
// パースして各種気象値算出
String weather_data = "wd=" + WindDirection() + "&a_ws=" + WindSpeedAverage() + ...;
String server = "https://192.168.1.24/remote_weather_station/?";
if (++transferring == 300) { // 300秒 = 5分
HTTPClient http;
http.begin(server + weather_data);
int httpCode = http.GET();
Serial.println("HTTP Code => " + httpCode);
http.end();
transferring = 0;
}
delay(1000);
}
シリアルモニタで毎秒データ表示、5分ごとに送信・サーバー応答(8 cells appended)を確認。
ステップ4.1:SIM808(GPRS)でデータ送信
Arduino Uno + SIM808シールドで同様の処理。違いは通信部分のみ:
DFRobot_SIM808ライブラリ使用sim808.join("cmnet")でDHCP接続sim808.connect(TCP, "192.168.1.24", 80)でTCP接続- HTTP GETリクエストを文字列組み立て→
sim808.send()で送信
※ SIM808には外部電源(7-23V)、GPS/GSMアンテナ、SIMカードが必要。ファンクションスイッチをNone(1)→コード書き込み→Arduino(3)→Bootボタン長押しでネットワーク登録待ち。
ステップ4.2:3ヶ月分のデータセット構築
収集項目:風向(deg)、平均風速(m/s)、最大風速(m/s)、1時間雨量(mm)、24時間雨量(mm)、気温(℃)、湿度(%)、気圧(hPa)。CSVエクスポートで32,219行のデータセット完成。継続収集中。
ステップ5:TensorFlowでANNモデル構築
5.1 データ可視化・スケーリング(正規化)
PandasでCSV読み込み、Matplotlibで各変数と1時間雨量の相関を2Dヒストグラムで可視化。
風向・風速はベクトル表現(X/Y成分)に変換してニューラルネットに入力しやすくする:
wv = df.pop('Av_WS'); max_wv = df.pop('Mx_WS')
wd_rad = df.pop('WD') * np.pi / 180
df['scaled_WX'] = wv * np.cos(wd_rad)
df['scaled_WY'] = wv * np.sin(wd_rad)
df['scaled_max_WX'] = max_wv * np.cos(wd_rad)
df['scaled_max_WY'] = max_wv * np.sin(wd_rad)
df['scaled_Tem'] = df.pop('Tem') / 25
df['scaled_Hum'] = df.pop('Hum') / 70
df['scaled_bPR'] = df.pop('b_PR') / 1013
df['scaled_24h_RF'] = df.pop('24h_RF') / 24
入力特徴量(7次元):scaled_WX, scaled_WY, scaled_max_WX, scaled_max_WY, scaled_Tem, scaled_Hum, scaled_bPR
5.2 ラベル作成・データ分割
近似降雨率 approx_RF_rate = (1h_RF + scaled_24h_RF) * 100 からクラス割当:
if approx_RF_rate == 0: _class = 0
elif approx_RF_rate < 2.5: _class = 1
elif 2.5 < approx_RF_rate < 7.6: _class = 2
elif 7.6 < approx_RF_rate < 50: _class = 3
else: _class = 4
訓練60% / テスト40%で分割。
5.3 モデル構築・訓練
model = keras.Sequential([
keras.Input(shape=(7,)),
keras.layers.Dense(16, activation='relu'),
keras.layers.Dense(32, activation='relu'),
keras.layers.Dense(64, activation='relu'),
keras.layers.Dense(128, activation='relu'),
keras.layers.Dense(5, activation='softmax') # 5クラス分類
])
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
model.fit(train_input, train_label, epochs=19)
訓練データでの精度:0.83–0.85。テストデータでの精度:0.72–0.73(過学習気味、データ増加で改善見込み)。
5.4 評価・予測
test_loss, test_acc = model.evaluate(test_input, test_label)
prediction = model.predict(pre_array)
for i in range(len(pre_array)):
print("Prediction =>", class_names[np.argmax(prediction[i])])
予測例:
- 入力
[0, 0, 0.318, 0.318, 0.699, 0.815, 0.993]→ 降雨なし (0) - 入力
[0, -0, 0, -0, 0.844, 1, 0.968]→ 大雨 (3) - 入力
[0, 0, 0.45, 0, 0.876, 0.959, 1.001]→ 猛烈な雨 (4) - 入力
[-0, -0, -0, -0, 0.822, 1.057, 0.993]→ 猛烈な雨 (4)
接続図
ESP8266接続
NodeMCU ESP-12E : Weather Station
VV (5V) -------- 5V
D5 -------------- RX
D6 -------------- TX
G --------------- GND

SIM808接続
Arduino Uno : SIM808 Shield
D0 ----------- RX
D1 ----------- TX
D12 ---------- POWER
Weather Station
5V ----------- 5V
D5 ----------- RX
D6 ----------- TX
GND ---------- GND
※ D0/D1/D12はSIM808占有。外部電源・アンテナ・SIMカード必須。

動画と結論
3ヶ月以上のデータ収集とモデル構築を完了。現在もバルコニーと裏庭でデータ収集継続中、モデル精度向上を目指しています。

さらなる議論・展望
- 農場・温室への安価なNN搭載気象ステーション配置で、豪雨被害を局地的に回避
- 複数ステーションのスウォーム化で相互通信・データ共有し、局地的降雨予測精度向上
- 水資源管理、農業生産性、生態系、水文学への応用
参考文献
- NIWA, "Extreme weather - heavy rainfall"
- Univ. Illinois, "Excessive rainfall as damaging to corn yield as extreme heat, drought", ScienceDaily, 2019
- A. Culclasure, "Using Neural Networks to Provide Local Weather Forecasts", Georgia Southern Univ., 2013
ソースコード
回路図

カスタムパーツ
収集データCSV:remote_weather_station_OobOCQNPSW.csv
製造プロセス
- Raspberry Pi搭載DIYソーラー駆動気象ステーション
- Raspberry Pi 2とADS-WS1で屋外気象観測ステーションを構築
- Raspberry Piで気象観測ステーションを構築し、日次天気データをメール送信
- 初心者向けIoTチュートリアル:Raspberry Piから温度データをストリーミング
- Raspberry Pi 3とPubNubでリモート気象監視
- 気象観測ステーションをCWOPと統合:実践ガイド
- Java ME 8でRaspberry Pi上のIoTを構築: パート1 - GPIOセンサー統合
- Raspberry Pi 3とDHT11センサーを接続し、ThingsIo.aiクラウドへデータ送信
- Raspberry Pi 2からAzure Event HubsへAdafruit 10DOF IMUデータを送信(Windows 10 IoT Core)
- 高度気象ステーション v2.0:温度・気圧・湿度・高度のリアルタイム監視
- Raspberry Pi 3 Model B+へのWindows 10 IoT Coreインストール:ステップバイステップガイド