概要
ESP32用micropythonで、micropython-ulabを組み込んだファームウェアを作成し、無事FFTを実行することができた。また、動作速度も、前回の自前実装FFTより数倍高速化できた。
2020/04/12時点でのmicropython、ulabを使用して実施している。情報が古いことに注意。新しいものは、こちら
背景と目的
前回、FFTを実行するためC実装の自作モジュールを組み込んだファームウェアを作成できた。これは、全然使えないわけではないものの、動作速度とメモリ利用効率が悪かったので改善を目指し、知人から存在を聞いたnumpy, scipyライクなパッケージmicropython-ulabを組み込んでみる。
詳細
0.実施条件
前回と同じ。
1.micropython-ulabのダウンロード
cd ~/MicroPythonProjects git clone https://github.com/v923z/micropython-ulab.git
2.モジュール用ソースコードを配置
micropython-ulabのファイル一式のうち、必要そうなのはcodeディレクトリのものだけのようだ。なので、
~/MicroPythonProjects/ modules/ ulab/ codeディレクトリのソースコード :
となるように、
cp -r ~/MicroPythonProjects/micropython-ulab/code ~/MicroPythonProjects/modules/ulab
3.ビルド
前回と同じ要領で、やるのだが、一応make cleanでいったんリセットして実行。makeの引数CFLAGS_EXTRAに、 ulabを参照するように、-DMODULE_ULAB_ENABLED=1をつけるのを忘れない。
cd ~/micropython/ports/esp32 make clean make USER_C_MODULES=~/MicroPythonProjects/modules CFLAGS_EXTRA = -DMODULE_ULAB_ENABLED=1 all
ビルドしてみたところ、以下のエラーが出た。以下に乗せているのは1か所だが、ndarray.c内で何か所も同じものが出た。
CC /root/MicroPythonProjects/modules/ulab/ndarray.c In file included from /root/MicroPythonProjects/modules/ulab/ndarray.c:21:0: /root/MicroPythonProjects/modules/ulab/ndarray.c: In function 'ndarray_make_new_core': /root/MicroPythonProjects/modules/ulab/ndarray.c:196:39: error: passing argument 1 of 'mp_raise_ValueError' from incompatible pointer type [-Werror=incompatible-pointer-types] mp_raise_ValueError(translate("first argument must be an iterable")); ^ /root/MicroPythonProjects/modules/ulab/ndarray.h:31:22: note: in definition of macro 'translate' #define translate(x) x ^ In file included from /root/MicroPythonProjects/modules/ulab/ndarray.c:17:0: ../../py/runtime.h:167:15: note: expected 'mp_rom_error_text_t {aka struct <anonymous> *}' but argument is of type 'char *' NORETURN void mp_raise_ValueError(mp_rom_error_text_t msg);
内容を見ると、mp_raise_ValueError関数の引数は、mp_rom_error_text_tだが、char * が与えられているとのこと。 その個所を見てみると、
mp_raise_ValueError(translate("first argument must be an iterable"));
で、translate関数に文字列が渡されて、その戻り値がmp_raise_ValueErrorに渡されている。このtranslateというのは、上記のエラーを見るとわかるように、ndarray.hで、
#define translate(x) x
となっていて、何かを変換しているようで何もしていない。なので、char*が渡されてしまうということだ。
じゃあ、これをど卯修正したらよいか、検討をつけるために、micropythonで、mp_raise_ValueErrorを検索すると、いろいろなソースコードで、
mp_raise_ValueError(MP_ERROR_TEXT(文字列))
となっていた。MP_ERROR_TEXTという関数だかマクロだかわからないが、恐らくこれを使えばいいのではないだろうか?ということで、ndarray.hを以下のように修正。
//#define translate(x) x #define translate(x) MP_ERROR_TEXT(x)
再度makeしたところ、見事に成功!ulabが組み込まれたファームウェアができたようだ!
4.動作確認
ファームウェアをESP32に書き込み以下を実行。
import utime import math # ulab import ulab from ulab import fft # 入力信号 N = 512 x = ulab.array([[0.0] * N]) for i in range(N): x[i] = math.cos(2 * math.pi * i / N) # FFT実行 t0 = utime.ticks_us() y = fft.fft(x) t1 = utime.ticks_us() # 結果表示 for i in range(N): i , y[0][i], y[1][i] "elapsed_time:", (t1-t0) / 1000, "msec"
無事、FFTが実行された。問題の速度だが、前回自前で作成したモノより5倍程度速いことが分かった。ulabの威力は十分だ。
(505, -6.804023e-06, 2.942783e-06) (506, -7.816223e-08, -7.783505e-07) (507, -1.806117e-05, 1.898014e-06) (508, -3.251397e-07, -1.51736e-06) (509, -1.919823e-05, 2.977413e-06) (510, -4.541771e-06, -2.580135e-06) (511, 256.0, -0.0001847311) >>> >>> "elapsed_time:", (t1-t0) / 1000, "msec" ('elapsed_time:', 11.56, 'msec')
さらに、Nを増やしてどこまでいくか確認したところ、N=4096までが実行可能だった。時間は20msec弱。それ以上だとメモリエラー。 とはいえ、前回のモノより8倍までいける。ただし、限界ギリギリまでFFTでメモリを使うとなると、他の余裕がなくなるので、実際はもう少し少ない長さまでとなるだろうが。
('elapsed_time:', 19.866, 'msec')
まとめと今後の課題
micropython-ulabを、ESP32用micropythonに組み込み、FFTを実行することができた。