工作と競馬2

電子工作、プログラミング、木工といった工作の記録記事、競馬に関する考察記事を掲載するブログ

M5UnitV2用の物体検出モデルを作成する

概要

M5UnitV2用の物体検出モデルをEdge Impulseにて作成し動作させた。




背景と目的



詳細

0. 実施環境

  • Windows PC
  • Ubuntu 20.04.6(WSL)
  • 社内プロキシ設定は適宜やっておくこと。やっておかないと後述のapt、cmakeなどでダウンロード失敗する。


1. クロスビルド環境の用意

ターゲットデバイスであるM5UnitV2は、ハードウェアリソースが限られるためセルフビルトが難しい。そこで、PC(Ubuntu)上にM5UnitV2すなわちARM/Linux用クロスビルド環境を準備する。

参考1に従い、クロスコンパイラ一式をダウンロード。解凍して、/opt/externalに配置。

apt update
apt install cmake
wget -O gcc-arm-10.2-2020.11-x86_64-arm-none-linux-gnueabihf.tar.xz "https://developer.arm.com/-/media/Files/downloads/gnu-a/10.2-2020.11/binrel/gcc-arm-10.2-2020.11-x86_64-arm-none-linux-gnueabihf.tar.xz?revision=d0b90559-3960-4e4b-9297-7ddbc3e52783&la=en&hash=985078B758BC782BC338DB947347107FBCF8EF6B"
tar -xf gcc-arm-10.2-2020.11-x86_64-arm-none-linux-gnueabihf.tar.xz
sudo mkdir /opt/external
sudo mv gcc-arm-10.2-2020.11-x86_64-arm-none-linux-gnueabihf /opt/external

.bashrcを編集して、クロスコンパイラへのパスを通す。

sudo nano ~/.bashrc

最下行に以下を追記。

export PATH="$PATH:/opt/external/gcc-arm-10.2-2020.11-x86_64-arm-none-linux-gnueabihf/bin/"

現在のコマンドラインに適用。

source ~/.bashrc


2. アプリケーションソースコードの準備

2.1 アプリケーションのひな型ソースコード

参考1サイトにある以下のリポジトリからダウンロードする。

git clone https://github.com/metanav/example-standalone-inferencing-linux.git

2.2 OpenCV関連のビルド

アプリケーションで利用するため、OpenCVのライブラリ群をビルドする。example-standalone-inferencing-linuxに入っているbuild-opencv-linux.shを使用するが、その前にクロスコンパイラを使うように修正が必要。まず、build-opencv-linux.shの以下部分をコメントアウト

# mkdir -p build_opencv
# cd build_opencv
# cmake -DCMAKE_TOOLCHAIN_FILE=../opencv/platforms/linux/arm-gnueabi.toolchain.cmake -DOPENCV_EXTRA_MODULES_PATH=../opencv_contrib/modules  -DBUILD_LIST=photo,stitching,objdetect,tracking,imgcodecs,videoio,highgui,features2d,ml,xfeatures2d -DCMAKE_BUILD_TYPE=Release -DBUILD_EXAMPLES=ON ../opencv
# make -j
# sudo make install

そのうえで、実行。

./build-opencv-linux.sh

example-standalone-inferencing-linuxディレクトリにopencvディレクトリができる。ここで、opencv/opencv/platforms/linux/arm-gnueabi.toolchain.cmakeを修正する。

set(GCC_COMPILER_VERSION "" CACHE STRING "GCC Compiler version")
set(GNU_MACHINE "arm-linux-gnueabi" CACHE STRING "GNU compiler triple")

# この2行を追記
set(CMAKE_C_COMPILER "arm-none-linux-gnueabihf-gcc")
set(CMAKE_CXX_COMPILER "arm-none-linux-gnueabihf-g++")

include("${CMAKE_CURRENT_LIST_DIR}/arm.toolchain.cmake")

ジョブ数を指定。指定しないと、メモリを使い果たして途中でエラーが起きるのでbuild-opencv-linux.shを修正。

make -j
↓
make -j 2

再度、build-opencv-linux.shのコメントアウトした部分を戻す。

mkdir -p build_opencv
cd build_opencv
cmake -DCMAKE_TOOLCHAIN_FILE=../opencv/platforms/linux/arm-gnueabi.toolchain.cmake -DOPENCV_EXTRA_MODULES_PATH=../opencv_contrib/modules  -DBUILD_LIST=photo,stitching,objdetect,tracking,imgcodecs,videoio,highgui,features2d,ml,xfeatures2d -DCMAKE_BUILD_TYPE=Release -DBUILD_EXAMPLES=ON ../opencv
make -j
sudo make install

そのうえで、実行。10分くらいかかった。

./build-opencv-linux.sh


3. Edge Impulseでモデルを作成

諸設定があるが、出力がTensorFlow Lite形式とする。


4. アプリケーションにモデルを埋め込む

4.1 cpp-mjpeg-streamerのソースコードを取得

git clone https://github.com/nadjieb/cpp-mjpeg-streamer.git 

なお、後述のいくつかのファイル修正は、このソースコードに関連したもののようだが、このソースコードが作成された時と関連するライブラリのバージョン等が整合しないために起きていると思われる。なので、現物に合わせて修正をしていくしかない。

4.2 モデルデータをコピー

Edge Impulseからダウンロードしたファイル一式を解凍し、以下のフォルダ、ファイルを、example-standalone-inferencing-linuxディレクトリ直下にコピーする。

  • edge-impulse-sdk
  • model-parameters
  • tflite-model
  • CMakeList.txt


4.3 Makefile修正

example-standalone-inferencing-linuxMakefileをそのまま使ってビルドすると、cpp-mjpeg-streamer関連で以下のいくつかエラーが出るのであらかじめ修正をしておく。

エラー内容: shared_mutexが存在しないなどと一連のエラーが出る

cpp-mjpeg-streamer/include/nadjieb/net/topic.hpp:73:10: error: ‘shared_mutex’ in namespace ‘std’ does not name a type
   73 |     std::shared_mutex buffer_mtx_;
      |          ^~~~~~~~~~~~
cpp-mjpeg-streamer/include/nadjieb/net/topic.hpp:73:5: note: ‘std::shared_mutex’ is only available from C++17 onwards
   73 |     std::shared_mutex buffer_mtx_;

C++17でしか使えないため、Makefileで以下を修正。

CXXFLAGS += -std=c++14
↓
CXXFLAGS += -std=c++17

エラー内容: opencvの各種ライブラリが参照できないエラー

opencv/build_opencv/libを参照する必要があるのでMakefileの以下の部分を修正。

LDFLAGS += -Lopencv/build_opencv/install/lib -Wl,-Ropencv/build_opencv/install/lib
↓
LDFLAGS += -Lopencv/build_opencv/lib -Wl,-Ropencv/build_opencv/lib

ロスコンパイラーのパス指定

CC := ../gcc-arm-10.2-2020.11-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf-gcc
CXX := ../external/gcc-arm-10.2-2020.11-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf-g++
↓
CC := /opt/external/gcc-arm-10.2-2020.11-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf-gcc
CXX := /opt/external/gcc-arm-10.2-2020.11-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf-g++

4.4 camera.cpp修正

isAliveが存在しない旨のエラーが出るので、camera.cppを修正。

source/camera.cpp:183:15: error: ‘using MJPEGStreamer = class nadjieb::MJPEGStreamer’ {aka ‘class nadjieb::MJPEGStreame’} has no member named ‘isAlive’
  183 |  if (streamer.isAlive()) {
      |               ^~~~~~~
make: *** [Makefile:104: source/camera.o] Error 1

MJPEGStreamerの定義を見る限り、isAliveというメソッドはなく代わりにisRunningというものがある。なので、source/camera.cppの185行目を以下のように修正。

isAlive
↓
isRunning

4.5 ビルド

以下を実行。

APP_CAMERA=1 TARGET_LINUX_ARMV7=1 USE_FULL_TFLITE=1 make -j 4

buildディレクトリにcameraというアプリができる。


5. デバイスで実行

作成したアプリケーションをデバイスに転送。

scp build/camera root@10.254.239.1:/home/m5stack

UnitV2にSSHログインする。 SSHログイン情報は、こちら

ssh m5stack@10.254.239.1

以下、UnitV2側のコンソールで作業。

アプリケーションを実行する前にプリインストールされたアプリケーションが動作しているため、それを止めないと競合してしまいちゃんと動かない。なので、

/etc/init.d/S85runpayload stop

を実行。それから、

cd /home/m5stack
./camera 0

として実行開始する。


6. シリアルに結果を出力する

プリインストールアプリのserver_core.pyを見ると、/dev/ttyS1を使用している。つまり、Grove端子に出ているのは/dev/ttyS1だ。

ser = serial.Serial('/dev/ttyS1', 115200, timeout=0.5)

なので、/home/m5stackの./cameraの標準出力を/dev/ttyS1に流すPythonスクリプトを作成。

import serial
import time
import traceback
import subprocess

ser = serial.Serial("/dev/ttyS1", 115200)

try:
    with subprocess.Popen(["./camera", "0"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) as proc:
        while True:
            line = proc.stdout.readline()
            # print(line.decode("utf-8"))
            ser.write(line + b"\n")
            ser.flush()
            time.sleep(0.01)
except:
    traceback.print_exc()

ser.close()

これで、Grove端子に接続した他のデバイスで、検出結果がもらえるようになった。


7. その他

7.1 CPU温度をチェック

cat /sys/devices/system/cpu/cpufreq/temp_out

7.2 作成したアプリケーションの自動起動

プリインストールアプリのinit.dを参考にする。

/etc/init.d/S85runpayload

このファイルをコピーして作成したアプリケーションを起動するように編集し、/etc/init.dに配置する。既存のS85runpayloadは別ディレクトリに退避する。


参考



まとめと今後の課題