STM32で人物検出する推論ネットワークの実装
This content is not available in your language yet.
カメラで画像データを取得し,学習済みネットワークを利用して人物検出を行います。
ここではSTM32リーフ上にネットワークを実装する方法を説明します。
実装の際にはカメラやディスプレイが必要ですが,最小限の説明に留めていますので予めご了承ください。
使用するリーフ
Section titled “使用するリーフ”以下のリーフを使用します。
| Type | Name | Q’ty |
|---|---|---|
| AZ62 | Connector Cover | 2 |
| AZ01 | USB | 1 |
| AP04 | STM32 MCU 2Bus | 1 |
| AI02 | SP&PIR | 1 |
| AX08 | 29pin header | 2 |
| AZ63 | Nut Plate | 2 |
| M2*10mm screw | 4 |
カメラの準備
Section titled “カメラの準備”Leafonyにはカメラ専用コネクタがありません。
今回はSPI通信でデータ通信が可能なArduCAM Mini (2メガピクセル)を用意しました。
ディスプレイの準備
Section titled “ディスプレイの準備”カメラから正しくデータを受け取れているかを確認するためにディスプレイを用意します。
今回はSPI通信でデータを表示することが可能なPmod MTDSを利用しました。
深層学習ネットワークの準備
Section titled “深層学習ネットワークの準備”今回の人物検出はSTマイクロ社が用意されている学習済みネットワークを利用します。
まずは,FP-AI-VISION1をダウンロードします。
各種ドライバの準備
Section titled “各種ドライバの準備”カメラのドライバ
Section titled “カメラのドライバ”githubにArduCAMがArduino環境用に用意されたライブラリがアップロードされています。
今回はHALライブラリを利用するのでSPIを通信する部分を少し書き換える必要があります
STM32用のライブラリもアップロードされていますが,Standard Peripheral Libraryを利用しており,今回は使えません。
ディスプレイのドライバ
Section titled “ディスプレイのドライバ”digilent社の公式のgithubにディスプレイをコントロールする関数が公開されています。
こちらもHALライブラリを利用するのでSPIの通信に関する関数を書き換える必要があります。
STMマイコンの基本的な設定
Section titled “STMマイコンの基本的な設定”ターゲットマイコンにSTM31L452RETxを選択

推論ネットワークの初期化コードの設定
Section titled “推論ネットワークの初期化コードの設定”STMマイコンに深層学習を実装するにはSTマイクロ社が用意しているツール(X-CUBE-AI)を利用するのが一番手軽に実装できます。
さらに同社からAIアプリケーション用の便利な関数パック(FP-AI-VISION1)が配布されています。
この中にExampleとして人物検出用の学習済みネットワークが含まれていますので,これを今回の推論ネットワークとして利用しました。
X-CUBE-AIのインストール
Section titled “X-CUBE-AIのインストール”X-CUBE-AIはオプションのツールなので,IDEへ導入する必要があります。
- はじめに
Manage Software Packsをクリックしてください。

Emmbedded Software Packages Managerというウィンドウが開かれます。STMicroelectronicsというタブを選択します。X-CUBE-AIをクリックすると様々なバージョンのArtificial Intelligenceが表示されますので,導入したいバージョンのチェックボックをクリックし,下のInstall Nowボタンをクリックしてインストールしてください。

ニューラルネットワーク用の設定
Section titled “ニューラルネットワーク用の設定”ダウンロードしたFP_AI_VISION1の中に人物検出ネットワークが入っています。
FP_AI_VISION1/Utilities/AI_resources/PersonDetection/Google_Model/person_detect.tflite
これを次の手順でX-CUBE-AIに読みこませます。
Pinout & ConfigurationのタブをクリックSoftware Packsを選択Configurationが開かれるので,networkのタブをクリックModel inputsにネットワーク情報を入力しますchoose model:TFLiteSaved ModelModel: 先ほどのFP_AI_VISION1内のネットワークファイルを指定
- ネットワークの圧縮と検証用の設定を入力します
Compression:NoneValidation inputs:Random numbersValidation outputs:None

Analyzeボタンをクリックすると解析が始まります- 以下の画面が出れば完了です

- 最後に,設定した内容でコードが生成されるように
Modeで以下の箇所にチェックボックスにチェックを入れますArtificial Intelligence X-CUBE-AIArtificial Intelligence Application
初期化コードの生成
Section titled “初期化コードの生成”設定が完了したのでコードを生成します。
Project -> Generate Codeをクリックして生成してください。
(Alt+Kでも生成できます。)
プロジェクトの構成
Section titled “プロジェクトの構成”はじめにドライバや画像処理用のファイルを追加します。
STM32CubeIDEのProject Explorerを見ると次のフォルダが自動生成されていることが分かります。
CoreDriversX-CUBE-AI
まずArduCAMとPmod MTDS制御用のコードをDriverに配置します。
次にFP_AI_VISION1内の画像処理用の便利な関数を利用したいので,以下のフォルダを作成します。
Middlewares
ダウンロードしたFP_AI_VISION1内の{FP_AI_VISION1}/Middlewares/ST/STM32_Imageを作成したMiddlewaresにインポートしてください。
グレイスケールからRGB画像データに変換する関数や画サイズを変換する関数が用意されています。
また,私は人物検出のネットワークのファイルを以下のように作成して入れました。
これは読み込ませたニューラルネットワークのファイルを分かりやすい場所においておきたいだけで必須ではありません。
Utilities
推論実行部のコード
Section titled “推論実行部のコード”自動生成されたコードであるX-CUBE-AI/App内のapp_x-cube-ai.cのコード内にMX_X_CUBE_AI_Processという関数があります。
この関数内部で以下の関数が重要です。
acquire_and_process_data()- ネットワークに入力するデータを取得する
ai_run()- 推論を実行する
post_process()- 推論実行後のデータに対する対応を記述
それぞれ以下のように実装しましょう
ai_runはメモリ確保された入力データと出力データのポインタを渡して上げるだけで,自動生成されたコードだけで実行されます。
ですからほぼ修正する必要がありません。とても便利ですね。
int acquire_and_process_data(uint8_t *data){ uint32_t sTime; uint32_t eTime; uint8_t eMsg[256] = {0}; extern Image_t cameraImg; extern Image_t resizedImg; extern Image_t inputImg;
sTime = HAL_GetTick();
captureBMP565();
eTime = HAL_GetTick();
sprintf(eMsg, " Done : %lums\r\n", eTime-sTime); HAL_UART_Transmit(&huart2, (uint8_t *)eMsg, sizeof(eMsg), 0xFFFF);
if ((cameraImg.width != inputImg.width) || (cameraImg.height != inputImg.height)) { ImgResize(&cameraImg, &resizedImg, NEAREST); ImgToGrayscale(&resizedImg, &inputImg); } else { ImgToGrayscale(&cameraImg, &inputImg); } memcpy(data, inputImg.pData, INPUT_IMG_HEIGHT*INPUT_IMG_WIDTH);
return 0;}static int ai_run(void *data_in, void *data_out){ ai_i32 batch;
ai_buffer *ai_input = network_info.inputs; ai_buffer *ai_output = network_info.outputs;
ai_input[0].data = AI_HANDLE_PTR(data_in); ai_output[0].data = AI_HANDLE_PTR(data_out);
batch = ai_network_run(network, ai_input, ai_output); if (batch != 1) { ai_log_err(ai_network_get_error(network), "ai_network_run"); return -1; }
return 0;}int post_process(uint8_t *data){ uint32_t sTime; uint32_t eTime; uint8_t eMsg[256] = {0}; extern Image_t inputImg; extern Image_t cameraImg;
#if defined(USE_PMODMTDS) sTime = HAL_GetTick();
displayReInit(); displayImage(&cameraImg, 120-cameraImg.height/2, 160-cameraImg.width/2);
eTime = HAL_GetTick(); sprintf(eMsg, "Done : %lums\r\n", eTime-sTime); HAL_UART_Transmit(&huart2, (uint8_t *)eMsg, sizeof(eMsg), 0xFFFF);#endif
if ((data[1] > data[2])) { char msg[] = "\r\n______ Person Detected!!\r\n\r\n"; HAL_UART_Transmit(&huart2, (uint8_t *)msg, sizeof(msg), 0xFFFF); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_SET);#if defined(USE_BUZZER) myPWMStart(10, 1000, 500); HAL_Delay(100); myPWMStop(); HAL_Delay(100); myPWMStart(10, 1000, 500); HAL_Delay(100); myPWMStop();#endif#if defined(USE_PMODMTDS) displayPersonDetected();#endif } else { char msg[] = "\r\n______ No Person\r\n\r\n"; HAL_UART_Transmit(&huart2, (uint8_t *)msg, sizeof(msg), 0xFFFF); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_RESET);#if defined(USE_BUZZER) myPWMStop();#endif#if defined(USE_PMODMTDS) displayNoPersonDetected();#endif }
return 0;}この3つの関数は自動生成されたコードに修正を加えなくてもmain.cppから勝手に呼び出され,ずっとループ実行されます。
各種インタフェースのドライバの初期化コードをmain.cppに追加し,以上のようにデータの取得時の動作と推論実行後の動作を記述してあげるだけで,実装は終了です。