2026-10-11 更新
RON-Vへ画像AIを追加し、車載カメラ画像から 倒木・路上の落下物 / 瓦礫・路面の穴・陥没・道路冠水・煙 を候補として検出し、さらに画像全体を clear / low_visibility に分類できるようにしました。
この記事では検出対象、AI構築の考え方、学習・評価・実機配置の進め方を説明します。コマンドと再学習手順は #22補足 にまとめました。
RON-V 連載リンク
← 前の記事:#21 — AtomS3RのCAN・IMU・Shutdown制御
→ 次の記事:#22補足 — Kaggle学習・再学習の備忘録
はじめに — 今回できるようになったこと
RON-VではUSBカメラ(Logicool C270)で保存した道路画像に対し、次の状態を画像AIで判定します。
| 状況 | AIの出力 | 使用モデル |
|---|---|---|
| 倒木 |
fallen_tree のBBox |
road-v02 |
| 路上の落下物・瓦礫・枝など |
road_debris のBBox |
road-v02 |
| 路面の穴・陥没 |
pothole のBBox |
road-v02 |
| 道路冠水 |
flood のBBox |
flood-v05 |
| 煙 |
smoke のBBox |
smoke-v01 |
| 視界不良 |
clear / low_visibility の画像分類 |
visibility-v02 |
路面の「凹凸」は、ここでは pothole として学習した穴・陥没を指します。すべての段差や起伏を検出できるという意味ではありません。
倒木が通行を妨げるか、冠水が何cmあるか、といった判断までは行いません。画像と位置・時刻を残し、人が後から異常候補を確認することが目的です。
物体検出モデルでは、単に「ある / ない」だけでなく、画像のどこに対象があるかをBBoxで返します。
例えばroad-v02では、
fallen_tree 0.65
road_debris 0.31
pothole 0.88
のように、クラス名・confidence・位置を取得できます。
一方、visibility-v02は物体の位置ではなく、画像全体を、
clear
low_visibility
のどちらかへ分類します。
これらは自動運転の安全判断そのものではなく、RON-Vが記録した画像へ「道路異常候補」を付加するための補助判定です。
1. どうやって検出できるようにしたか
先に「道路で何を見つけたいか」と「必要な出力」を決めました。倒木や落下物には位置が必要なので物体検出、視界には画像全体の状態が必要なので画像分類を選びます。
道路障害物・冠水・煙は教師データと誤検出の傾向が異なり、視界はタスク自体が異なります。4モデルに分けることで、冠水だけ再学習する、モデルごとに閾値を調整する、といった更新ができます。道路障害物の3クラスは同じ場面の位置検出なので1モデルで扱います。
担当を次のように分けました。
| 場所・処理 | 役割 |
|---|---|
| Roboflow | 既存のラベル付きデータセットを取得 |
GitHub vision/
|
クラス変換・データ構築・学習呼出しを管理 |
| Kaggle GPU | YOLO11n / YOLO11n-clsを学習・評価 |
| ONNX変換 | Pi側の入力・出力仕様へ合わせる |
| Raspberry Pi 5 | イベント画像をONNX Runtimeで推論 |
進め方は、対象とラベルを決める → データを整える → 学習 → 実画像で評価 → 別の教師画像で改善 → 再評価 → ONNX変換 → 実機確認です。
最初から数千枚へ自分でBBoxを付けるのではなく、既存データで最初のモデルを作り、C270の実画像で弱点を探します。
2. 道路障害物 — Roboflowから3クラスを抽出
道路障害物モデルの出発点として、Roboflow Universeの road-obstacle-detection を使用しました。
取得元: road-obstacle-detection Version 3
取得元はLaasyaのObject Detectionデータセットです。利用条件はデータセットのversionとともに記録し、モデルの配布条件とは分けて確認します。
RON-Vで取り込んだVersion 3の data.yaml は5クラスでした。
0: Pothole - v1 raw
1: barrier
2: fallen_tree
3: road_debris
4: traffic_cone
このうちRON-Vで直接使いたい3クラスだけを抽出しました。
fallen_tree
road_debris
pothole
再マッピングは次です。
new 0 = fallen_tree <- old 2
new 1 = road_debris <- old 3
new 2 = pothole <- old 0
この3クラスを1つのYOLOモデルで同時に検出します。
3. Kaggleで学習用データを再構築
Roboflow Version 3をYOLO形式でKaggleへ取り込み、RON-V用データセットへ変換しました。
| split | images | fallen_tree | road_debris | pothole |
|---|---|---|---|---|
| train | 883 | 310 | 507 | 1256 |
| valid | 183 | 34 | 66 | 330 |
| test | 115 | 28 | 49 | 153 |
追加前のVersion 3抽出結果は合計1,181画像です。
クラスの列はBBox数です。元データにはDetection形式とSegmentation形式が混在していたため、polygonをBBoxへ変換し、YOLO Detectionの5列形式へ正規化しました。
invalid labels : 0
corrupt images : 0
4. road-v02 — 倒木の教師データを補って学習する
最終的な道路データには fallen-trees-detection Version 1 も加え、倒木の教師データを補いました。
構築時の記録は合計1,500画像(train 1,143 / valid 213 / test 144)。前節の1,181画像は追加前のVersion 3抽出結果です。画像数とBBox数を区別し、元画像のsplitを保ち、重複を除外します。
学習は yolo11n.pt から50 epoch、入力640、batch 16で行います。データを増やして再学習することと、前の best.pt を初期重みにして追加学習することは別です。この道路用スクリプトは前者です。
Kaggleの指標だけで採用せず、C270画像で倒木の見逃し、道路脇の小物体への反応、穴の検出を確認しました。最終モデルは fallen_tree / road_debris / pothole の3クラスです。
5. Flood — 濡れた道路と冠水を分ける
冠水検出では、単に「水の画像」を集めればよいわけではありませんでした。
誤検出しやすかったのは、
- 濡れたアスファルト
- 雨天道路
- 水面反射
- 夜間ライト反射
です。
そこで、これらをnegative / hard negativeとして追加し、通常の濡れた道路と、冠水候補を分ける方向で調整しました。
最終モデルは flood-v05 です。
validation:
Precision : 1.000
Recall : 0.866
mAP50 : 0.875
mAP50-95 : 0.673
独立test 65枚を conf=0.50 で評価した結果:
TP = 7
FN = 0
FP = 0
TN = 58
C270評価9枚でもfalse positiveは0でした。これは画像単位の候補有無の結果で、BBoxの位置精度を含むmAPとは分けて読みます。正例7枚の小さいテストであり、一般の道路画像でも誤検出がゼロになる保証ではありません。
6. Smoke — 煙を1クラスで検出
煙は Roboflow RF100 smoke Version 1 を教師データに、YOLO11nを1クラスのObject Detectionへ学習しました。煙という出力だけで火災原因を判断するものではありません。
class : smoke
validation:
Precision : 0.978
Recall : 0.912
mAP50 : 0.970
mAP50-95 : 0.721
最終モデルは smoke-v01、初期confidenceは 0.20 です。
7. Visibility — 視界不良は画像全体を分類
視界不良は画像全体の分類にしました。PHD Project Version 5 の Fog を low_visibility、Normal / Cloudy / Sunny を clear へまとめ、YOLO11n-clsを学習します。主に霧に似た見え方が出発点です。雨・雪・逆光・夜間をすべて同じ精度で扱えるとは確認していません。
clear
low_visibility
通常道路の誤判定を減らすため、別の道路画像を clear として追加しました。道路障害物の有無と、視界の良し悪しは別の意味です。
最終モデル visibility-v02 の独立test 160枚:
accuracy : 0.9812
low_visibility:
precision : 0.9487
recall : 0.9737
F1 : 0.9610
8. hard negativeで精度を上げる
今回の学習で効果が大きかったのは、単純に正例を増やすことより、何を誤検出しているかを確認することでした。
改善は次の順序で行います。
- 実画像で見逃しと誤検出を確認する。
- 道路という運用場面に合う別の画像を集め、人が対象の有無とラベルを確認する。
- trainへ追加して再学習する。
- 固定した評価条件で、誤検出が減り、見逃しが増えていないかを確認する。
物体検出のnegativeは、対象がないことを確認した画像と空のラベルです。対象が写っている画像のラベルを消すと、誤った教師データになります。
flood-v05は38枚のhard negative候補からtrain側28枚だけを使い、追加98ファイルを生成しました。元のvalid / test分割を保ち、C270評価9枚と自宅内ベンチ画像は学習へ入れていません。
| 区分 | 役割 |
|---|---|
| train | 重みを学習する |
| valid / val | 学習中にモデルを選ぶ |
| test | 固定条件で性能を確認する |
| C270評価 | 車載カメラとの見え方の違いを確認する |
同一画像や加工画像を別splitへ移さないようにします。C270評価を繰り返して開発上の判断に使ったため、採用後の性能評価には新しい走行画像も必要です。
confidenceはモデル間で同じ意味の確率として比較せず、モデルごとに閾値を決めます。
9. ONNXへ変換してRaspberry Pi 5へ
学習済みモデルはRaspberry Pi 5で実行するためONNXへ変換しました。
road-v02.onnx
flood-v05.onnx
smoke-v01.onnx
visibility-v02.onnx
採用した best.pt をONNXへ変換し、入力サイズ・出力shape・クラス順・前処理をPi側と合わせます。モデル本体は現状runtime ZIPとして別管理しています。
FloodについてはPyTorch版とONNX版をtest + C270の合計74枚で比較し、候補有無の判定不一致0を確認しました。この結果は全4モデルの同等性を確認した意味ではありません。再学習・再変換したモデルでも比較し直します。
10. 同じAIを動かし、再学習できるために残す情報
同じAIを動かすにはソースコードに加えてモデル本体が必要です。再学習には元データ、分割、学習条件、学習用の .pt も必要です。
- GitHubのcommit、実行コマンド、Python / package / GPU。
- 元モデル、データのworkspace / project / version、出典と利用条件。
- クラス順、split、manifest、hard negativeの選び方。
-
args.yaml、results.csv、best.pt、ONNX、SHA256。 - Pi側の入力サイズ・前処理・閾値・配置先・動作確認方法。
Notebookを実行の入口とし、処理本体はGitHubに残します。Kaggleの一時領域が消える前に学習結果を保存します。学習結果ZIPに画像が含まれていなければ、それだけではデータを復元できません。
道路・煙・視界の再構築スクリプトは vision/ にあります。冠水の最終データ作成セルは、確認できた記録には全文がなく、GitHubにも未収録です。#22補足 に実行できる範囲と必要な保存物を明記しました。
ONNXは、学習済みAIモデルを保存するファイル形式です。ONNXに変換しても、それだけで自由に配布できるようになるわけではありません。
GitHubでモデル本体を公開する前に、学習の出発点に使ったYOLO11などの元モデルと、学習に使った画像データについて、それぞれの利用条件を確認します。出典の表示、ライセンス文の添付、関連するソースコードの公開など、求められる条件があれば、それを満たしてから配布します。
例えば、設定ファイルに学習画像のライセンスとして CC BY 4.0 と書いてあっても、それだけではYOLO11側の利用条件まで確認したことにはなりません。設定ファイルの記述が正しいかも、画像の取得元の説明と照合します。つまり、「学習に使えるか」と「作ったモデルをどの条件で配布できるか」の両方を確認する、という意味です。
確認先: Ultralyticsのライセンス案内 / Roboflowの利用規約。
11. 最終的に採用した4モデル
| profile | 何を見るか | task | Raspberry Pi側の初期閾値 |
|---|---|---|---|
| road-v02 | 倒木 / 路上落下物 / 路面の穴・陥没 | detection | 0.20 |
| flood-v05 | 道路冠水 | detection | 0.50 |
| smoke-v01 | 煙 | detection | 0.20 |
| visibility-v02 | clear / low_visibility | classification | top-1 |
road-v02の最終validation:
Precision : 0.726
Recall : 0.616
mAP50 : 0.683
mAP50-95 : 0.370
fallen_tree mAP50 : 0.657
road_debris mAP50 : 0.620
pothole mAP50 : 0.772
これでRON-Vは、電波・位置・車両情報だけでなく、イベント発生時の道路画像へ、
倒木
落下物 / 瓦礫
路面損傷
冠水
煙
視界不良
という追加情報を付けられるようになりました。
RON-VではBUTTON / SHOCKで保存した event.jpg に4モデルを適用し、結果を vision_result.json へ残します。
次の RON-V #23 では、この4モデルをRaspberry Pi 5上で動かし、AtomS3RのBUTTON / SHOCKから自動推論する実装を扱います。