工作と競馬2

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

ESP32用micropythonで、numpy, scipyライクなパッケージmicropython-ulabを組み込み、FFTしてみる【ulab v0.36.0版】

概要

ESP32用micropythonで、micropython-ulabを組み込んだファームウェアを作成し、無事FFTを実行することができた。また、動作速度も、前回の自前実装FFTより数倍高速化できた。


背景と目的

前回FFTを実行するためC実装の自作モジュールを組み込んだファームウェアを作成できた。これは、全然使えないわけではないものの、動作速度とメモリ利用効率が悪かったので改善を目指し、知人から存在を聞いたnumpy, scipyライクなパッケージmicropython-ulabを組み込んでみる。


詳細

0.実施条件

2020/05/16時点のmicropython最新版をセットアップして実施。


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.ビルド

以下、ulab v0.24.0版の記事とエラーの出方が異なるので対処方法を書く。

前回と同じ要領で、やるのだが、一応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

ビルドしてみたところ、ndarray.c、linalg.c、fft.cに非常に大量のエラーが出たが、エラーは型変換に関する2種類。

'mp_float_t {aka float}' to 'double' to match other operand of binary expression [-Werror=double-promotion]
conversion to 'float' from 'double' may alter its value [-Werror=float-conversion]

これらに対して2種類の修正方法が考えられたので、それぞれ試し、どちらもビルドが成功した。どちらかをやればよい。


3.1 mpconfigport.hを修正する方法

mp_float_t型は、MICROPY_FLOAT_IMPLというマクロが何を指しているかによって、doubleかfloatかが変わる。そして、今回のエラーは、doubleかfloatかが食い違っているせいで起きているようだ。 micropythonでは、マイコンの種類ごとにmpconfigport.hの中でMICROPY_FLOAT_IMPLを定義しているようなので、esp32の場合はports/esp32/mpconfigport.hの47行目を

//#define MICROPY_FLOAT_IMPL                  (MICROPY_FLOAT_IMPL_FLOAT)
↓
#define MICROPY_FLOAT_IMPL                  (MICROPY_FLOAT_IMPL_DOUBLE)

として、make cleanして再度make。


3.2 方法2: 地道にulabのコードを書き換える方法

方法1は、マイコンごとの定義を変えてしまっていて気持ち悪い感じがするので、逆にulab側を変更する方法も考えられる。 エラーか所は、mp_float_t型をdouble型へ暗黙の型変換と、floatからdoubleの変換で怒られているので、

// int32_t x = (int32_t)MICROPY_FLOAT_C_FUN(floor)(mp_obj_get_float(tmp)+0.5);int32_t x = (int32_t)MICROPY_FLOAT_C_FUN(floor)(mp_obj_get_float(tmp)+(float)0.5);

という感じで修正していく。このような小数との足し算や掛け算で怒られている部分は、linalg.c、fft.cなどで10数か所以上はある。

また、エラーの中にはlinalg.c:91:53

if(MICROPY_FLOAT_C_FUN(fabs)(data[m*(N+1)]) < epsilon) {

のように、epsilonという変数が含まれるものがある。これについては、epsilonというマクロがlinalg.hで定義されているので、以下のように修正することで、epsilon関連のエラーは消える。

//#define epsilon        1.2e-7
↓
#define epsilon        (float)1.2e-7

以上がすべてできたら、make cleanして再度make。


ファイル書き換え上の注意

ファイル変更の際もしも、Windows側からエクスプローラー上でファイルの移動を行ったりすると、Ubuntu側のパーミッションが000になってしまい、makeしたときにパーミッションエラーが発生する。その際は、

sudo chmod 664 ファイル名

で付け直し、make cleanしてからmakeし直すこと。


というわけで、以上のようにエラーを修正し、再度makeしたところ、見事に成功!ulabが組み込まれたファームウェアができたようだ!


4.動作確認

4.1 3.1の方法でビルドした場合

v0.24.0版と同様に実施したところ、

(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:', 19.277, 'msec')

という感じで、正しく動いたようだ。若干遅くなった気がするが、doubleにしたから?(この時間は正しくなさそう。4.2を参照)


4.2 3.2の方法でビルドした場合

こちらも、

(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)

となり、正しく動作している模様。なお、実行速度は、

t0 = utime.ticks_us()
y = fft.fft(x)
t1 = utime.ticks_us()

t0 = utime.ticks_us(); y = fft.fft(x); t1 = utime.ticks_us()

とで、ずいぶん変わる。前者は複数行をまとめてコマンドラインに張り付けても微妙に行ごとのオーバーヘッドがあるらしい。なので20msec弱かかるが、後者は1.3msec程度だった。多分後者が正しいと思われるので、N=512では1msecちょっとでできるということだろう。


まとめと今後の課題

esp32用micropythonにulab v0.36.0を無事組み込むことができた。