はじめに
MicropythonでFFTを使いたいと思い色々調べつつ備忘録的にこの記事を書いています
Mycropythonについて
今更なので割愛. 公式WEBページは下記
https://micropython.org/
今回使用したボード
https://www.adafruit.com/product/4900
特徴
・Raspberry Pi Picoと同じRP2040 SOCを搭載
・Flashは8MB
・Seeedstudio Xiaoシリーズと同じ大きさ
・USB Type C connector
・オンボードでNeoPixelとSTEMMA QTコネクタ
残念な点はちょっと高価
https://www.switch-science.com/products/7211
NumPyについて
Pythonライブラリで科学技術計算の基礎パッケージ
https://numpy.org/ja/
公式をはじめ解説はそこかしこにあるのでそれらを参照
ulabについて
基本は配列計算を行う為のライブラリにnumpyやscipyがサポートするファンクションの一部を組み込んだものでfftやrandomがサポートされる
Githubページに詳細やFFTベンチマーク結果が載っている
https://github.com/v923z/micropython-ulab
CircuitPythonではリリースにあらかじめ含まれているポートもある
(ちなみにQT Py RP2040は含まれている)
(ならCircuit Pythonを使えよ!)
Micropythonのリリースには含まれていないので自分でビルドする必要がある
ビルド
ということでmicropython-ulab Girhubページの手順にしたがってビルドをしていく
https://github.com/v923z/micropython-ulab?tab=readme-ov-file#rp2-based-boards
今回使用した環境
・Windows11 + VMWare Workstation 17 Player (注意:非営利目的の使用のみ)
・Ubuntu20.04.2
0. 前準備:gcc armコンパイラのインストール
その他makeやcmake等は予め入っていたので必要なものが何かは把握出来ていない
・sudo apt updateとsudo apt upgradeを実行
$ sudo apt install gcc-arm-none-eabi
・コンパイル用ディレクトリ(便宜的に$MP_DIRと表記)を作成してそこへ移動
1.micropython-ulabリポジトリのクローン
$ git clone https://github.com/v923z/micropython-ulab.git
2.ビルドスクリプト"build/rp2.sh"の修正
$ cd micropython-ulab
$ cp build/rp2.sh build/qtpy_rp2.sh
$ vi build/qtpy_rp2.sh
$ chemod +x build/qtpy_rp2.sh
適当なエディタを使って24行目のmakeコマンドにBOARD指定を追加
make USER_C_MODULES=$ULAB_DIR/micropython.cmake BOARD=ADAFRUIT_QTPY_RP2040
$ diff build/rp2.sh build/qtpy_rp2.sh
24c24
< make USER_C_MODULES=$ULAB_DIR/micropython.cmake
---
> make USER_C_MODULES=$ULAB_DIR/micropython.cmake BOARD=ADAFRUIT_QTPY_RP2040
3.ビルド
$ ./build/qtpy_rp2.sh
Cloning ulab
Cloning into 'ulab'...
... 途中省略 ...
[ 98%] Linking CXX executable firmware.elf
text data bss dec hex filename
453352 0 5520 458872 70078 $MP_DIR/micropython-ulab/micropython/ports/rp2/build-ADAFRUIT_QTPY_RP2040/firmware.elf
[100%] Built target firmware
$ ls -l micropython/ports/rp2/build-ADAFRUIT_QTPY_RP2040/
合計 20020
-rw-rw-r-- 1 k1 k1 21027 6月 12 11:43 CMakeCache.txt
drwxrwxr-x 9 k1 k1 4096 6月 12 11:44 CMakeFiles
-rw-rw-r-- 1 k1 k1 895679 6月 12 11:43 Makefile
-rw-rw-r-- 1 k1 k1 1840 6月 12 11:43 cmake_install.cmake
drwxrwxr-x 6 k1 k1 4096 6月 12 11:43 elf2uf2
-rwxrwxr-x 1 k1 k1 453352 6月 12 11:44 firmware.bin
-rw-rw-r-- 1 k1 k1 6994300 6月 12 11:44 firmware.dis
-rwxrwxr-x 1 k1 k1 7253384 6月 12 11:44 firmware.elf
-rw-rw-r-- 1 k1 k1 2208587 6月 12 11:44 firmware.elf.map
-rw-rw-r-- 1 k1 k1 1275225 6月 12 11:44 firmware.hex
-rw-rw-r-- 1 k1 k1 906752 6月 12 11:44 firmware.uf2
-rw-rw-r-- 1 k1 k1 423122 6月 12 11:43 frozen_content.c
drwxrwxr-x 3 k1 k1 4096 6月 12 11:43 frozen_mpy
drwxrwxr-x 3 k1 k1 4096 6月 12 11:43 generated
drwxrwxr-x 5 k1 k1 4096 6月 12 11:43 genhdr
drwxrwxr-x 6 k1 k1 4096 6月 12 11:43 pico-sdk
-rw-rw-r-- 1 k1 k1 16844 6月 12 11:43 pins_ADAFRUIT_QTPY_RP2040.c
手っ取り早く試したいならビルド済のイメージがここにあります
https://github.com/v923z/micropython-builder
ファームウエアの書き込み
・USBケーブルでボードをPCに接続しBOOTスイッチを押した状態でRSTスイッチを押してすぐ離すとRPI-RP2というドライブとしてPCに認識される
・このドライブへDrag & Dropで"micropython/ports/rp2/build-ADAFRUIT_QTPY_RP2040/firmware.uf2"を書き込むと自動的にブートが行われる
>>> MicroPython v1.24.0-preview.36.gd7d77d91b on 2024-06-12; Adafruit QT Py RP2040 with RP2040
Type "help()" for more information.
>>> help("modules")
__main__ asyncio/lock gc random
_asyncio asyncio/stream hashlib re
_boot binascii heapq rp2
_boot_fat builtins io select
_onewire cmath json struct
_rp2 collections machine sys
_thread cryptolib math time
array deflate micropython uasyncio
asyncio/__init__ dht neopixel uctypes
asyncio/core ds18x20 onewire ulab
asyncio/event errno os vfs
asyncio/funcs framebuf platform
Plus any modules on the filesystem
ulabがmoduleとして組み込まれていることが確認できた
(OPTION I) ulab.numpy.fft.fft()のcomplex対応
下記のリンクにあるようにデフォルトではfft結果はrealとimageそれぞれがarrayとして得られる
https://micropython-ulab.readthedocs.io/en/latest/numpy-fft.html
これをCPythonのnumpyとコンパチブルにするには下記リンクのマニュアルコンパイル手順に以下の1.~3.を追加する
https://github.com/v923z/micropython-ulab?tab=readme-ov-file#rp2-based-boards
1. submoduleの追加
(不要かもしれないがWarninbgがでたので)btstckとlwipを追加する
$ cd micropython
$ git submodule update --init lib/tinyusb
$ git submodule update --init lib/pico-sdk
$ git submodule update --init lib/btstack
$ git submodule update --init lib/lwip
$ cd lib/pico-sdk
$ git submodule update --init lib/tinyusb
2. code/ulab.hの変更
クローンしたulabリポジトリのcode/ulab.hのULAB_FFT_IS_NUMPY_COMPATIBLE定義を変更する
$ diff -c ../ulab/code/ulab.h.org ../ulab/code/ulab.h
*** ulab/code/ulab.h.org 2024-06-13 10:21:59.740449646 +0900
--- ulab/code/ulab.h 2024-06-13 10:19:43.422227473 +0900
***************
*** 440,446 ****
// Note that in this case, the input also must be numpythonic,
// i.e., the real an imaginary parts cannot be passed as two arguments
#ifndef ULAB_FFT_IS_NUMPY_COMPATIBLE
! #define ULAB_FFT_IS_NUMPY_COMPATIBLE (0)
#endif
#ifndef ULAB_FFT_HAS_FFT
--- 440,446 ----
// Note that in this case, the input also must be numpythonic,
// i.e., the real an imaginary parts cannot be passed as two arguments
#ifndef ULAB_FFT_IS_NUMPY_COMPATIBLE
! #define ULAB_FFT_IS_NUMPY_COMPATIBLE (1)
#endif
#ifndef ULAB_FFT_HAS_FFT
3. コンパイル
Qt Py RP2040ボード用のsubmodulesを設定したmake実行後、USER_C_MODULESとBOARD設定を追加してmakeする
$ cd port/rp2
$ make BOARD=ADAFRUIT_QTPY_RP2040 submodules
$ make USER_C_MODULES=/YOUR_PATH_TO/ulab/code/micropython.cmake BOARD=ADAFRUIT_QTPY_RP2040
(省略)
text data bss dec hex filename
453112 0 5520 458632 6ff88 /home/k1/MP/micropython/ports/rp2/build-ADAFRUIT_QTPY_RP2040/firmware.elf
[100%] Built target firmware
uf2ファイルがmicropython/ports/rp2/build-ADAFRUIT_QTPY_RP2040/firmware.uf2としてできている
このuf2ファイルをQt Py RP2040ボードに焼いてfftを実行してみると
>>> from ulab import numpy as np
x = np.linspace(0, 10, num=1024)
y = np.sin(x)
z = np.zeros(len(x))
x_fft = np.fft.fft(x)
print(x_fft)
v = abs(x_fft)
print(v)
array([5119.996+0.0j, -5.004667+1631.333j, -5.004776+815.6588j, ..., -5.005463-543.764j, -5.005635-815.6588j, -5.006575-1631.333j], dtype=complex)
array([5119.996, 1631.34, 815.6742, ..., 543.787, 815.6742, 1631.341], dtype=float32)
fft結果が複素数で返っておりabs()で絶対値が求められていることが確認できた
(OPTION II) 複数のUSER_C_MODULESを定義してコンパイルする方法
各モジュールのcmakefileをincludeしたcmakeファイルを作成し、そのパスをコンパイル時のUSER_C_MODULESとして指定する
以下はulabとst7789_mpyの2モジュールを組み込む例
$ cd $MP_DIR
$ cat micropython.cmake
include($MP_DIR/ulab/code/micropython.cmake)
include($MP_DIR/st7789_mpy/st7789/micropython.cmake)
$ cd micropytho/ports/rp2
$ make USER_C_MODULES=$MP_DIR/micropython.cmake BOARD=ADAFRUIT_QTPY_RP2040