ここでの目的は 、MCU のPico を使用して、人間のジェスチャを「上下」、「左右」、「円」に分類することです。
元ネタ TinyML – Motion Recognition Using Raspberry Pi Pico
機械学習とモデルの作成にはクラウド環境のEdge Impulse Studio を使い、学習用のデータセットの収集と推論にPico を使います。
ここでは、Pico にファームウェアをインストールする母艦にラズパイ4Model B を使用します。
構成
母艦のラズパイ4Model B にはOSイメージにRaspberry Pi OS (64-bit) Desktop を使います。
元ネタは 2 つの部分から構成されています。
パート1は、Raspberry Pi Pico、その主要コンポーネント、
および Micropython とその C/C++ SDK (ソフトウェア開発キット) を使用したプログラミング方法を調査することです。
パート2では、Pico を使用し、Edge Impulse Studio を使用して、TinyML モデルのトレーニングで使用する「ジェスチャ データ」をキャプチャします。
開発してテストしたモデルは、同じデバイス上で実際の推論にデプロイされて使用されます。
パート1の主な目的は、母艦にPico のSDK をインストールすることです。
こんな感じ。
1 2 3 4 5 6 7 8 9 10 11 |
sudo apt update sudo apt install git cmake gcc-arm-none-eabi libnewlib-arm-none-eabi build-essential -y cd ~/ mkdir pico cd pico git clone -b master https://github.com/raspberrypi/pico-sdk.git cd pico-sdk git submodule update --init cd .. |
それ以外にblink(Lチカ)用のuf2ファイルを作っています。これはパート2でデータ収集用のaccel_ml.uf2を作る方法を事前に取得するためのものですが、記述どおりにやってもエラーになります。
まあ、それだけのことなので、べつにやらなくても問題ないです。
ここ(4-1)では、パート2のデータ収集をやってみます。
Pico に3軸加速度センサーモジュール(MMA7361L)を接続して、3種類のデータを収集し、それらをクラウド上のEdge Impulse Studio に転送するまでをやってみます。
このプロジェクトのアイデアは、Pico を使用して、人間が作成したジェスチャを「上下」、「左右」、「円」に分類することです。
この分類は、MPU レベルで 100% オフラインで行われます。
言い換えれば、私たちがやろうとしていることは、「TinyML としても知られる組み込み機械学習」です。
Edge Impulse のドキュメント (そもそも組み込み ML とは何か?) で説明されているように、マイクロプロセッサ アーキテクチャとアルゴリズム設計の最近の進歩により、
最小のマイクロコントローラでも高度な機械学習ワークロードを実行できるようになりました (RP2040 の場合)。
以下の図に示すように、基本的に HW 容量とメモリ サイズに応じて、TinyML アリーナではさまざまなタイプの MCU/アプリケーションを使用できます。
ARM Cortex-M0+ をベースにした Pico は、このプロジェクトで行うように、センサー分類を実行するのに十分以上に適しています。
The Machine Learning Workflow(機械学習のワークフロー)
ここまでで、プロジェクトの第 1 フェーズである目標 (ジェスチャ分類) をすでに定義しました。
以下のワークフローは、Pico でのデータ収集から、小さなデバイスに戻される最終的な推論と評価まで、クラウドの Edge Impulse Studio で行われる実際のモデル開発を経て、実行される残りのすべてのフェーズを示しています。
このページでは2つ目のEdge Impulse で行う最初のFeature Engineering の入り口あたりまでをやってみます。
Collect a dataset(データセットの収集)
TinyML は、物理世界、つまりセンサーの周囲でマシン インテリジェンスを実現します。したがって、最初に行うことは、これらのジェスチャを理解するためにデータをキャプチャすることです。そのために、単純な 3 軸加速度計を使用します。
The Sensor(センサー)
使用されるセンサーである MMA7361L は、わずかな電力しか必要としない 3 軸アナログ加速度計で、加速度計を ±1.5g と ±6g の測定範囲で切り替える g-select 入力を備えています。その他の機能には、スリープ モード、信号調整、1 極ローパス フィルター、温度補償、セルフテスト、線形自由落下を検出する 0g 検出などがあります。ゼロ G オフセットと感度は工場出荷時に設定されており、外部デバイスは必要ありません。
各センサーのアナログ出力 (XOUT、YOUT、および ZOUT) は、Pico の ADC 入力 (ADC0、1、および 2) に接続されます(Pico ではこの3pinのみがユーザが使用できるADCに対応しています)。VDD は 3.3V で、Pico からも供給されます。 ピン GS は g レベルを選択し、オープンのままになります (+/-1.5G)。
Wiring(配線)
3 軸アナログ加速度計にはいくつかの異なるパッケージがあります。
原則として、Freescale の MMA7361L 用のブレークアウト ボードはどれも動作します。
タクトスイッチも接続されていますが、何のためなのかは後でわかります。
Sensor Measurements(センサー測定)
GS ピンをオープン(+/-1.5G)にした場合、仕様に従ったセンサー感度は 800mV/g であり、1G(センサー静止)の場合、出力は約 1.65V(’G0′ )になります。
Pico ADC の分解能は 12 ビット(3.3V==> 4096)であるため、ADC の計測値を g 単位で知りたい場合は、収集した生データ(read_axis_raw)に以下の変換係数を適用する必要があることを覚えておくことが重要です:
1 2 |
conversion_factor = 3.3V / 4096 read_axis_in_g = (read_axis_raw * conversion_factor) - G0 |
加速度 (m/s) の場合
1 2 |
CONVERT_G_TO_MS2 = 9.80665 read_axis_in_ms = read_axis_in_g * CONVERT_G_TO_MS2 |
Preparing the project environment(プロジェクト環境の準備)
データ収集プロジェクト ファイルのツリー構造は次のようになります。
pico/
├── accelerometer_data_capture/
│ ├── accel_mma7361l.c
│ ├── CMakeLists.txt
│ ├── pico_sdk_import.cmake
│ └── build/
│
├── pico-sdk/
│ ├── cmake
まずプロジェクトフォルダーを作成します。
1 2 3 |
cd ~/pico mkdir accelerometer_data_capture cd accelerometer_data_capture |
データ収集のソース コードは次のとおりです。
ソース作成
1 |
sudo nano accel_mma7361l.c |
以下のコードをコピペします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
#include <stdio.h> #include "pico/stdlib.h" #include "hardware/gpio.h" #include "hardware/adc.h" #include "pico/binary_info.h" #define NSAMP 10 #define G0 1.65f #define CONVERT_G_TO_MS2 9.80665f #define FREQUENCY_HZ 50 #define INTERVAL_MS (1000 / (FREQUENCY_HZ + 1)) const float conversion_factor = 3.3f / (1 << 12); float get_axis (int adc_n) { adc_select_input(adc_n); unsigned int axis_raw = 0; for (int i=0;i<NSAMP;i++){ axis_raw = axis_raw + adc_read(); sleep_ms(1); } axis_raw = axis_raw/NSAMP; float axis_g = (axis_raw*conversion_factor)-G0; return axis_g; } int main() { stdio_init_all(); adc_init(); adc_gpio_init(26); adc_gpio_init(27); adc_gpio_init(28); while (1) { printf("%f \t", (get_axis (0) * CONVERT_G_TO_MS2)); printf("%f \t", (get_axis (1) * CONVERT_G_TO_MS2)); printf("%f \n", (get_axis (2) * CONVERT_G_TO_MS2)); sleep_ms(INTERVAL_MS); } } |
上記のコードでは、3 つの ADC を 10 回ずつ読み取り、平均値(平滑値)を返します。
各軸ごとにタブで区切られ、m/sに変換されたデータ値は、printf()命令を使用してPico USB出力に送信されます。読み取り周波数は50Hzとしましたが、データ取り込みと平滑処理の時間を考慮し、これより低くする必要があります。
CMakeLists.txtは以下のとおり。pico_enable_stdio_usb(accel_ml 1)という行がありますが、これはUSB(シリアル0)が有効になっていることを意味します。
CMakeLists.txt作成
1 |
sudo nano CMakeLists.txt |
以下をコピペします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
cmake_minimum_required(VERSION 3.13) include(pico_sdk_import.cmake) project(accelerometer_mma7361l_project C CXX ASM) set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 17) pico_sdk_init() add_executable(accel_ml accel_mma7361l.c ) pico_enable_stdio_usb(accel_ml 1) pico_add_extra_outputs(accel_ml) target_link_libraries(accel_ml pico_stdlib hardware_adc) |
pico_sdk_import.cmake をプロジェクトフォルダー にコピーします。
1 |
cp ../pico-sdk/external/pico_sdk_import.cmake . |
サブフォルダーを構築して移動し、ビルド実行。
1 2 3 4 5 |
mkdir build cd build export PICO_SDK_PATH=../../pico-sdk cmake .. make -j4 |
最終的にコンパイルされたコード (accel_ml.uf2) がビルド フォルダーに表示されます。
PicoのBOOTSELボタンを押しながら母艦とPicoをUSBケーブルで接続します。
これでPico は母艦からはストレージデバイスとして認識されます。
ウィンドウ RPI-RP2 が開くので、コンパイルされたプロジェクト ファイル accel_ml.uf2 をそのフォルダーにドラッグ・ドロップします。ドロップ終了と同時にPicoは再起動します。
Pico は加速度センサーからデータのキャプチャを開始し、USB (シリアル 0) に送信します。
これで、シリアル モニターで読み取ることができます。
Linux を使用している場合は、minicom が良い選択肢となります。 まず、インストールします。
1 |
sudo apt install minicom -y |
シリアル モニターを開きます。
1 |
minicom -b 115200 -o -D /dev/ttyACM0 |
ここで初めて何のためにタクトスイッチが使われていたのかが分かります。
一度押すと….
シリアルポートが遮断されます。もう一度押せば再開します。
ラズパイならArduino IDE でもモニターが使えますが、IDE ではプロッターを使いたいのでモニターはminicomでやります(IDEではモニターとプロッターの両立はできないです)。
ちなみにArduino IDE は以下でインストールできます。
1 |
sudo apt-get install arduino -y |
メニュのTools ー> Port で/dev/ttyACM0を選んで、Tools ->Serial Plotter を選択。
あるいは、15_pico_accel_multicore_capture_data のコードは、コア 0 で実行されているコードによってデータがシリアルに送信されるときに、コア 1 でデータを継続的にキャプチャする方法を示しています。
* ここまでは、Pico で MMA7361L を使うためのuf2ファイルを作成することが眼目でした。
もし他のセンサーを使うなら、それ用のuf2を用意すれば以降の段取りだけが必要になります。
ここで行う必要があるのは、分類する人間が作成したジェスチャごとにデータ サンプル (3 軸の値のセット) を収集することです。
「Up-down」(ピコ・センサーを高い位置から低い位置へ移動)
「Left – right」(ピコ/センサーを左から右へ、またはその逆に移動)
「circle」 (ピコ/センサーを時計回りと反時計回りに動かします)。
「rest」(ピコ/センサーをテーブルの上に放置し、動かさない)
ところで、Edge Impulse Studio のアカウントをお持ちでない場合は、今すぐアカウントを作成してください。
Edge Impulse は、エッジ デバイスでの機械学習のための主要な開発プラットフォームであり、開発者は無料で利用でき、企業からも信頼されています。
アカウントを開き、新しいプロジェクトを作成します(このプロジェクトが収集されたデータの保存場所になります)。
適当なプロジェクト名を入力して作成
元ネタの作者のプロジェクトも公開されておりクローンできます。
プロジェクトを作成したら、Edge Impulse CLI をコンピュータにインストールします。
これはローカルのラズパイ-Pico とクラウドの Edge Impulse Studio を接続するために使います。
つまり、コマンドラインでEdge Impulse Studio を使います。これを使えば、シアルポートにデータを流せるなら、実はセンサーはなんでもかまいません。
そのためには、次の手順に従ってください 。
1. Edge Impulse でアカウント作成
2. Python 3 インストール <ー ラズパイではインストール済みなので不要
3. Node.js v14 以上をインストール.
以下はバージョン14で実行しています、最初に非推奨のワーニングがでますが、問題ないのでそのまま実行します。
1 2 3 |
curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash - sudo apt-get install -y nodejs node -v |
nodeのバージョンはv14.21.3
1 |
npm config get prefix |
/usr
もし/usr/local/ が返された場合は、次のコマンドを実行して npm のデフォルト ディレクトリを変更します。
1 2 3 |
mkdir ~/.npm-global npm config set prefix '~/.npm-global' echo 'export PATH=~/.npm-global/bin:$PATH' >> ~/.profile |
4. CLI toolsをインストール
1 |
npm install -g edge-impulse-cli |
これで、PATH でツールが使用できるようになりました。
プロジェクトが作成され、CLI がインストールされたら、Pico からデータを取得する最も簡単な方法は、Edge Impulse Data forwarderを使用することです。
これにより、シリアル インターフェイス経由で収集されたデータをEdge Impulse Studio に転送できます。
この方法は、今回の場合 (人間のジェスチャー) のように、サンプリング周波数が低いセンサーでのみ完全に機能します。
Data forwarderは、シリアル経由で任意のデバイスから Edge Impulse にデータを簡単に中継するために使用されます (まさにこの場合)。
デバイスはシリアル接続経由でセンサー値を書き込み、Data forwarderがデータを収集し、データに署名し、取り込みサービスにデータを送信します。
Edge Impulse Studio にデータを転送して保存
ターミナルで次を実行します。
minicom が実行中なら停止しておきます。
1 2 3 4 5 |
edge-impulse-data-forwarder //コマンドが見つからない場合はフルパス /home/pi/.npm-global/bin/edge-impulse-data-forwarder |
以降、CLI は、資格情報(ユーザー名かメールアドレスやパスワード)、作業しているプロジェクトの名前、キャプチャするデータ値の名前を要求します (CLI はすでにシリアルを分析しており、3 軸センサー データが利用可能であることを認識していることに注意してください)。
最後にデバイス名を尋ねられます (オプション)。
まず、アカウント作成時のユーザー名(or メールアドレス)とパスワードを聞いてきます。パスワードは入力時何も反映されません。
認証に失敗することもあります。何度かチャレンジしましょう。
3つの軸を何と呼ぶのか聞いてきます。適当にカンマで区切って名前を入力(例:x,y,z)。またデバイス名を何とするのか聞いてきます。ここも適当に(例:rpi-pico)。
うまくいくと、あなたが作ったプロジェクトに紐づけられます。
表示されたURLを控えておきます。
上記のURLにアクセスして、Edge Impulse Studio にログインします。
デバイス名と、利用可能なセンサーおよびキャプチャの頻度が自動的に表示されます。
データ ラベルと必要なサンプルの量 (デフォルトは 10 秒) を定義し、[Start sampling] を押します。
Label のところには収集する4種類の動作名を入れます(例;up-down、left-right、circle、resting)。
以下は、10秒(10000ms) 間の ジェスチャをします。
Start sampling をクリックして、動作を開始してデータの収集をします。
具体的な動かし方は以下の動画を参照
経過時間が表示されます。
終了。収集後のデータセットの名前が表示されます。
Data forwarder にも終了が表示されます。
1つのデータ収集完了。
同様にして他の残りの収集も実行。
「機械学習は、生のデータを処理し、それをアプリケーション レベルで意味のある情報に変えるプログラムを作成する方法です。」
つまり、データの量が多ければ多いほど、より多くの情報を取得できるようになります。
各ラベルについて少なくとも 60 秒のデータをキャプチャしましょう。
各ラベル (クラス) にほぼ同じ量のデータを含めて、データセットのバランスを取るようにしてください。
ここではデフォルトの10秒でサンプリングしましたが、少なくとも60秒くらいは必要なようです。
Next
Feature Engineering(特徴量エンジニアリング)
収集した4種類のデータを使います。
Edge Impulse Studio のDesign を使ってパイプラインを設計し、データを前処理し、テストしてデプロイ用のC++パッケージを作るところまでやってみます。
Pi Pico でエッジAI を試してみる(4-2/3)TinyML – Raspberry Pi Pico を使ったモーション分類(抜粋)- モデル作成編
Appendix
新しいプロジェクトを使う場合
Edge Impulse でCreate new project を実行してプロジェクトを新規追加しておきます。
Pico ではデータのみ取得しておきたいので、accel_ml.uf2をインストールしておきます。
母艦で以下を実行。
1 |
edge-impulse-data-forwarder --clean |
再度認証を聞いてきます。
次に、以下のようにどのプロジェクトを使うか聞いてきます。
? To which project do you want to connect this device?
登録したプロジェクト名が表示されるので、矢印キーで選びます。
以降は上記と同様にセンサーの3つの軸の名前やデバイス名を聞いてきます。
以上
Leave a Reply