概要
M5UnitV2用の物体検出モデルをEdge Impulseにて作成し動作させた。
背景と目的
詳細
0. 実施環境
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-linuxのMakefileをそのまま使ってビルドすると、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は別ディレクトリに退避する。