Skip to content
Shop

STM32で人物検出する推論ネットワークの実装

Explore with your favorite AI

Gemini & other AI

Copy this page’s context into your AI to explore how it works and what you can build.

Open Gemini

Copy the prompt, then paste it into Gemini’s message box.

View prompt

This content is not available in your language yet.

カメラで画像データを取得し,学習済みネットワークを利用して人物検出を行います。
ここではSTM32リーフ上にネットワークを実装する方法を説明します。
実装の際にはカメラやディスプレイが必要ですが,最小限の説明に留めていますので予めご了承ください。

以下のリーフを使用します。

TypeNameQ’ty
AZ62Connector Cover2
AZ01USB1
AP05STM32N61
AI02SP&PIR1
AX0829pin header2
AZ63Nut Plate2
M2*10mm screw4

Leafonyにはカメラ専用コネクタがありません。
今回はSPI通信でデータ通信が可能なArduCAM Mini (2メガピクセル)を用意しました。

カメラから正しくデータを受け取れているかを確認するためにディスプレイを用意します。
今回はSPI通信でデータを表示することが可能なPmod MTDSを利用しました。

今回の人物検出はSTマイクロ社が用意されている学習済みネットワークを利用します。
まずは,FP-AI-VISION1をダウンロードします。

githubにArduCAMがArduino環境用に用意されたライブラリがアップロードされています。
今回はHALライブラリを利用するのでSPIを通信する部分を少し書き換える必要があります
STM32用のライブラリもアップロードされていますが,Standard Peripheral Libraryを利用しており,今回は使えません。

digilent社の公式のgithubにディスプレイをコントロールする関数が公開されています。
こちらもHALライブラリを利用するのでSPIの通信に関する関数を書き換える必要があります。

ターゲットマイコンにSTM31L452RETxを選択

推論ネットワークの初期化コードの設定

Section titled “推論ネットワークの初期化コードの設定”

STMマイコンに深層学習を実装するにはSTマイクロ社が用意しているツール(X-CUBE-AI)を利用するのが一番手軽に実装できます。
さらに同社からAIアプリケーション用の便利な関数パック(FP-AI-VISION1)が配布されています。
この中にExampleとして人物検出用の学習済みネットワークが含まれていますので,これを今回の推論ネットワークとして利用しました。

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: TFLite
    • Saved Model
    • Model: 先ほどのFP_AI_VISION1内のネットワークファイルを指定
  • ネットワークの圧縮と検証用の設定を入力します
    • Compression: None
    • Validation inputs: Random numbers
    • Validation outputs: None

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

  • 最後に,設定した内容でコードが生成されるようにModeで以下の箇所にチェックボックスにチェックを入れます
    • Artificial Intelligence X-CUBE-AI
    • Artificial Intelligence Application

設定が完了したのでコードを生成します。
Project -> Generate Codeをクリックして生成してください。
(Alt+Kでも生成できます。)

はじめにドライバや画像処理用のファイルを追加します。
STM32CubeIDEのProject Explorerを見ると次のフォルダが自動生成されていることが分かります。

  • Core
  • Drivers
  • X-CUBE-AI

まずArduCAMPmod MTDS制御用のコードをDriverに配置します。
次にFP_AI_VISION1内の画像処理用の便利な関数を利用したいので,以下のフォルダを作成します。

  • Middlewares

ダウンロードしたFP_AI_VISION1内の{FP_AI_VISION1}/Middlewares/ST/STM32_Imageを作成したMiddlewaresにインポートしてください。
グレイスケールからRGB画像データに変換する関数や画サイズを変換する関数が用意されています。

また,私は人物検出のネットワークのファイルを以下のように作成して入れました。
これは読み込ませたニューラルネットワークのファイルを分かりやすい場所においておきたいだけで必須ではありません。

  • Utilities

自動生成されたコードである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に追加し,以上のようにデータの取得時の動作と推論実行後の動作を記述してあげるだけで,実装は終了です。