
こんにちは!
SSSの山根です。
AITRIOSのAITRIOS Edge Application SDKから公開されている物体検出用のサンプルアプリがYOLOv8nに対応しました。
AITRIOSデバイスのCSV26にはESP32が使われており手のひらサイズの大きさです。
今回はそんなデバイスで本当にYOLOv8nのようなAIモデルが正確に動くのか、大量のペットボトルをカウントさせて確かめてみました。
その結果、次のような推論結果が得られたので、このような結果を得る為の手順を紹介します。
用意したもの
-
CSV26(有線/PoEモデル)
エッジファームウェアをV2にする必要があります。V2にする手順はこちらをご参照ください。 - PoE スイッチ、インターネットと接続できる回線、LANケーブル
- 評価用の画像
- Ubuntu22.04のPC
PCに関してはPythonが動かせる環境であれば基本的に問題ありません。
ただし、計算リソースが潤沢な方がAIモデルのコンバートにかかる時間を節約できます。今回の手順はRaspberry Pi 5でも実行できました。 - AITRIOS Console
AIモデルの準備
AIモデルの作成
まず、Pythonスクリプトを実行する為の仮想環境を作成します。
python3 -m venv .venv
. ./.venv/bin/activate
その後、ultralyticsをインストールします。
そして、次のPythonスクリプトを実行します。
from ultralytics import YOLO
# 学習済みのYOLOv8nモデルをダウンロード
model = YOLO("yolov8n.pt")
# IMX500形式へエクスポート
model.export(format="imx", data="coco8.yaml", imgsz=544)
スクリプトの引用元:https://docs.ultralytics.com/integrations/sony-imx500/
上記のスクリプトの初回実行時に不足しているモジュール等のインストールが実行されるので実行完了には時間がかかります。
また、AIモデルのコンバートは実行環境によって速度が異なりますが、数分程度の時間がかかります。
実行が完了すると、yolov8n_imx_modelという名前のディレクトリがカレントディレクトリに生成されます。
中身は次のようになっています。
yolov8n_imx_model
├── dnnParams.xml
├── labels.txt
├── packerOut.zip
├── yolov8n_imx_MemoryReport.json
├── yolov8n_imx.onnx
└── yolov8n_imx.pbtxt
これで、AIモデルの準備は完了です。
この記事では、YOLOv8nのAIモデルをCSV26で使うにはどうすればよいかに着目している為、再学習のステップを省略しています。
上記のPythonスクリプトでmodel.exportする前に再学習のスクリプトを追加することで独自に学習したAIモデルを使用できます。
Consoleへインポート
今回は生成されたファイルの中からONNX形式のyolov8n_imx.onnxをConsoleへインポートします。
まず、Consoleを開いて、左のサイドバーのEdge Device SWをクリックして、Edge Device Softwareのページを開きます。AI Modelのタブをクリックし、+ Importをクリックします。
すると次のようなポップアップが表示されるので、先程生成したyolov8n_imx.onnxをAI Modelに選択し、Nameなどの項目を記載します。
2025年5月現在、Converter Versionは3.16でのみ動作を確認しています。3.16をご指定ください。その他の項目は任意で構いません。
Importをクリックすると、AI modelのStatusがConvertingになり、デプロイ可能になるとReady To Deployになります。
Edge Applicationの準備
サンプルのdetectionアプリの取得
https://github.com/SonySemiconductorSolutions/aitrios-sdk-edge-app/releases から最新バージョンのEdge Applicationをダウンロードします。
今回は現時点で最新のsample_edge_app_detection_wasm_v2_1.1.0.zipを使用します。
Consoleへインポート
ダウンロードしたsample_edge_app_detection_wasm_v2_1.1.0.zipをConsoleへインポートします。
AI Modelの時と同様に、Consoleを開いて、Edge Device Softwareのページを開きます。そして、今度はEdge Applicationのタブをクリックしてから+ Importをクリックします。
すると、次のようなポップアップが表示されるので、sample_edge_app_detection_wasm_v2_1.1.0.zipをEdge Applicationに選択し、
適当なNameとDescriptionを入力してImportをクリックします。
するとEdge ApplicationのStatusがNot Completedになり、デプロイ可能になるとReady To Deployになります。
AIモデルとEdge Applicationのデプロイ
AIモデルとEdge ApplicationがそれぞれReady To DeployになったらCSV26にデプロイします。
Consoleを開いて、左のサイドバーにあるSW Provisioningをクリックし、次の画像のように先程インポートしたAIモデルとEdge Applicationを選択し、Select Deviceで自分のデバイスを選択してDeployをクリックします。
Deploymentsからデプロイの進捗状況を確認します。
StatusがSuccessになればデプロイ成功です。
推論開始
ConsoleのDevicesから自分のデバイスの右端にある・・・をクリックすると次の画像のようにUpdate Module Configurationという項目が表示されるのでクリックします。
すると、次のような画面が表示されます。
この画面からEdge Applicationになって欲しい状態をJSON形式のデータ (Configuration) を入力して指定します。
今回はAITRIOS Edge Application SDKで公開されているサンプルアプリを使用しているので、リポジトリにあるConfigurationがそのまま使えます。
今回は次のようなConfigurationを送信します。
{
"edge_app": {
"req_info": {
"req_id": "od_sample"
},
"common_settings": {
"process_state": 2,
"log_level": 2,
"inference_settings": {
"number_of_iterations": 0
},
"pq_settings": {
"camera_image_size": {
"width": 2028,
"height": 1520,
"scaling_policy": 1
},
"frame_rate": {
"num": 2997,
"denom": 100
},
"digital_zoom": 1,
"camera_image_flip": {
"flip_horizontal": 0,
"flip_vertical": 0
},
"exposure_mode": 0,
"auto_exposure": {
"max_exposure_time": 10000,
"min_exposure_time": 100,
"max_gain": 51,
"convergence_speed": 2
},
"auto_exposure_metering": {
"metering_mode": 0,
"top": 0,
"left": 0,
"bottom": 0,
"right": 0
},
"ev_compensation": 0,
"ae_anti_flicker_mode": 0,
"manual_exposure": {
"exposure_time": 10000,
"gain": 0
},
"white_balance_mode": 0,
"auto_white_balance": {
"convergence_speed": 6
},
"manual_white_balance_preset": {
"color_temperature": 0
},
"image_cropping": {
"left": 0,
"top": 0,
"width": 2028,
"height": 1520
},
"image_rotation": 0
},
"port_settings": {
"metadata": {
"method": 0,
"storage_name": "",
"endpoint": "",
"path": "",
"enabled": true
},
"input_tensor": {
"method": 1,
"storage_name": "${project_id}",
"endpoint": "",
"path": "${device_id}/image/${current_timestamp}",
"enabled": true
}
},
"codec_settings": {
"format": 1
},
"number_of_inference_per_message": 1
},
"custom_settings": {
"ai_models": {
"detection": {
"ai_model_bundle_id": "${network_id_1}",
"parameters": {
"max_detections": 200,
"threshold": 0.1,
"input_width": 544,
"input_height": 544,
"bbox_order": "xyxy",
"bbox_normalization": false,
"class_score_order": "score_cls"
}
}
},
"metadata_settings": {
"format": 1
}
}
}
}
全ての項目を理解する必要はありませんので、今回の記事を理解する上で必要な項目だけ説明します。
-
common_settings
全てのEdge Applicationで共通してAITRIOSで使用可能な項目が定義されています。-
process_state
Edge Applicationの稼働状態を示します。1が停止、2が稼働している状態を表します。
上記のConfigurationを送信すると、Edge Applicationが推論を開始して、データを送信します。
今回のConfigurationでは2にした後自動では止まらないので、1に戻すのを忘れないようにしましょう。 -
port_settings
データの送信方法と送信先を指定します。-
metadata
推論結果の送信方法と送信先を指定します。
Consoleで確認する場合は上記のConfigurationのままで問題ありません。 -
input_tensor
画像の送信方法と送信先を指定します。
Consoleで画像を確認する場合は"storage_name"の"${project_id}"と"path": "${device_id}/image/${current_timestamp}"の${device_id}と${current_timestamp}は
Consoleから確認して設定する必要があります。-
"${project_id}"
PortalのURLの該当箇所から取得する必要があります。
https://portal.aitrios.sony-semicon.com/project/${project_id}/marketplace/product
URLには大文字と小文字が混ざっていますが、Configurationに指定する文字列は全て小文字で記載してください。 大文字が混ざっていると、正しく画像がストレージに送信されず、Dataでの画像の確認ができません。 -
${device_id}
ConsoleのDevicesのページから該当のデバイスをクリックした際にDeviceタブ内の右下ID>Device IDに表示される値を入力します。 -
${current_timestamp}YYYYMMDDHHMMの形式で任意の値を指定してください。
-
-
-
-
custom_settings
Edge Application特有の設定項目を入力します。-
ai_models.detection.ai_model_bundle_id${network_id_1}は ConsoleのDevicesのページから該当のデバイスをクリックした際にAI Modelタブ内の左上Model 1>Network IDに表示される値を入力します。 -
ai_models.detection.parameters
今回使用するAIモデルに合わせてai_models.detection.parametersの内容を設定します。-
max_detections
Edge Applicationが出力する物体の上限個数を指定します。
値を小さくすることで確度の高い物体のデータだけを送ることができます。 -
threshold
Edge Applicationが出力する物体のスコアの値がthresholdの値以上のものになるように、出力データを制限します。 -
input_width,input_height
AIモデルのサイズを指定します。
今回はPythonスクリプトでAIモデルをエクスポートした際にimgsz="544"とサイズを指定したので、それに合わせて544に設定しています。 -
metadata_settings.format
推論結果の出力形式を指定します。1の時はJSON形式で送信されます。 -
bbox_order,bbox_normalization,class_score_order
IMX500上のAIモデルの出力データの解釈の仕方を指定します。
それぞれBounding Boxの座標の順番、座標の値を正規化するかどうか、クラスとスコアの順番を指定します。
YOLOv8nの場合は、上記のConfigurationの内容で正しく出力されます。
-
-
検出結果の確認
在庫管理を自動化する場面を想定して次の画像を用意し、ディスプレイに表示して、CSV26に写しました。
(この画像はMicrosoft Copilotを使用して生成しました。)
74本のお茶のペットボトルと、1つだけワイングラスを紛れ込ませてみました。
この状態で上記のConfigurationを送信後、ConsoleのDataから出力データを確認すると次のように検出結果が描画されました。
今回はCOCOのデータセットで予め学習されており、各クラスの意味はPythonスクリプト実行時に出力された、yolov8n_imx_modelディレクトリ内のlabels.txtに記載されています。
これによると、class_id = 39はbottleで、class_id = 40はwine glassです。
labels.txtと推論結果に含まれるclass_idの値を照合する際に、オフセットを考慮して読み取る必要があります。
例えば、labels.txt内の40行目にあるラベルはclass_id = 39に相当します。 labels.txtをテキストエディタで参照した際にclass_id == (行数)と間違えないように注意してください。
ということで、ペットボトルとワイングラスが正しく検出されていることが分かりました。
これで、誰かが元の位置に商品を戻さなかった場合も自動で検知できますね。
特にAIモデルの再学習はしていませんが、十分在庫管理に使えそうです。
ただ、この画像からだと本当に正しく74本検出できたのか判定が難しいです。
勿論、JSONデータを解析すれば本数を確認できます。
「でも、JSONデータの解析 (クラスごとの個数のカウント) もエッジ側でやりたいし、追加で実装するのがめんどくさい。。。」
そんな場合、今回使用しているサンプルのEdge ApplicationのArea Count機能を使えます。(詳細はこちらのReadmeをご参照ください。Area Countに関しての詳細は次回の記事に書きます。)
上記のConfigurationのcustom_settingsにarea.class_idの項目を次のように追加して送信すると、各クラスのカウント結果をEdge Applicationが計算して送信してくれます。
"custom_settings": {
"ai_models": {
"detection": {
"ai_model_bundle_id": "061400",
"parameters": {
"max_detections": 200,
"threshold": 0.1,
"input_width": 544,
"input_height": 544,
"bbox_order": "xyxy",
"bbox_normalization": false,
"class_score_order": "score_cls"
}
}
},
"metadata_settings": {
"format": 1
},
"area": {
"class_id": []
}
}
class_idの配列の中にはカウントしたいクラスを入力することで、物体の検出とカウントを特定のクラスに限定することもできます。
今回のようにclass_idを空にした場合は、全ての検出されたクラスをカウントします。
上記のようにareaの設定を追加する場合、ConsoleのUpdate Module Configurationの画面内にあるVisual Editorをクリックすることで、追加することもできます。Visual EditorはEdge Applicationのインポート時にzipファイルの中に含まれているDTDLファイルを元にConfigurationで設定可能な項目を元にJSON形式ではなくUIで設定することが可能になります。
逆に、今回の序盤で試したような物体検出の結果を出力して可視化する場合には、areaの設定が入ってしまうので、今回はVisual Editorを使用していません。
areaの内容を含んだConfigurationを送信してデータを確認すると次のように表示されました。
"area_count"の中にカウント結果が格納されていることが分かります。
正しく、bottleが74本、wine glassが1本であることが読み取れます。
Consoleではarea_countを含んだデータの描画には対応していないのでErrorが表示されています。
何本検出できるのか試してみた
ペットボトルの本数を増やして、何本くらいまで検出できるのか試してみました。
その結果、今回の画像とAIモデルでは130本くらいが安定的に正確にカウントできる限界のようでした。
次の画像には140本のペットボトルが写っていますが、 カウント結果は131本で、例えば中央下の1本は検出できていません。
一般的な500mlペットボトルの高さが21 cm、直径7 cm程度で、今回の画像ではペットボトルを縦に7本、横に20本並べています。
正確にカウントできるように縦2列 (14本分) 減らした場合、
エッジデバイス1台で高さ147 cm、幅126 cm程度の範囲の商品棚のスペースの在庫管理を自動化できそうです。
また、今回は画質調整やAIモデルの再学習はしていないので、チューニングすればより広い範囲に対して多くのペットボトルを検出できる可能性があります。
パフォーマンスの比較
検出精度の高いYOLOv8nのAIモデルですが、精度が高い分、推論速度の低下が懸念されます。
今回はConsoleで確認できるタイムスタンプを元に、どれくらいの頻度でデータが送られてくるのかを確認してみました。
その結果、YOLOv8nではおおよそ9〜11秒ごとにデータが送られていました。
一方で、SSD MobileNetを用いた場合、6〜7秒ごとにデータが送られていました。
SSD MobileNetでの検出結果は次のような結果で、検出数に大きな違いは無さそうでしたが、検出クラスは間違っており、精度は低そうです。
当然、学習のさせ方などに依存するので一概に言うことは難しいですが、SSD MobileNetに比べるとYOLOv8nの方が検出精度は高く、パフォーマンスは劣ると言えそうです。
また、今回は画像も送信する設定ですが、推論結果のみを送る (common_settings.port_settings.input_tensor.enabled = falseにする) 場合はさらにパフォーマンスが改善します。
さらに、今回はJSON形式を出力形式に選びましたが、FlatBuffersシリアライズを使用する (custom_settings.metadata_settings.format = 0にする) ことでパフォーマンスの改善が期待できます。
ただ、ワーストケースのパフォーマンスとして9~11秒ごとに推論結果が送られてくることは、在庫管理のユースケースでは許容できるのではないかと個人的には思います。
まとめと所感
今回はYOLOv8nの物体検出AIモデルをAITRIOSのESP32搭載エッジデバイスのCSV26で使用して、ペットボトルのカウントの限界を調べてみました。
CSV26はESP32-WROVER-Eが使われており、高精度なAIモデルを動かすには小さな計算リソースだと思いますが、IMX500を使って沢山の検出結果を処理して送信できているのは、すごいと思いました。
また、SSD MobileNetのモデルを用意する場合と比較すると、簡単に数行のコードでモデルを準備できてAITRIOSで使用できるので手軽だと感じました。
今回はAIモデルとEdge Applicationをそのまま使用しましたが、ユースケースに応じて改変することが可能なので、様々な用途でご活用頂ければと思います。
困った時は
もし、記事の途中でうまくいかなかった場合は、気軽にこの記事にコメントをください。以下のサポートサイトもご覧ください。
コメントのお返事にはお時間を頂く可能性がありますが、ご了承ください。
また、記事の内容以外で AITRIOS についてお困りごとなどあれば以下よりお問い合わせください。
Views: 0








