見出し

マイコンで過去60秒間の波形から震度を計算する

   2023年09月24日     2分で読めます

最近、震度計のファームウェア(ingen084/seismometer)を開発しています。

要は加速度センサーの値をフィルタにかけて過去60秒間のデータをソートするだけ(過去記事)なのですが、60秒間(100Hz * 60sec = 6000sample)のソートはなかなかに厳しいです。
僕が震度計を作るときは ArduinoSort というライブラリを使用していますが、100サンプル程度では非常に高速に動作する一方で60秒間のサンプル数をソートするとなるとかなり時間がかかります。

今回はこの課題に対する僕が実施した解決方法の記事です。
ソースコードを読めば一発なんですが、あえて記事にしてみます。

ソートをする数を減らす

ソートは様々な最適化手段が提案されていますが、手っ取り早く高速化するにはソートする数を減らすのが一番です。
そこで、一定時間ごとにグループに分けそのグループに溜まった時点でソートを行い、グループを60秒分ローテーションしていくことで60秒間の波形データを保持することにしました。

グループごとに充填したデータを転がしておくようなイメージ

さすがに 100Mhz 越えのマイコンなのもあってメモリのコピーは ns 単位でできるのでとてもいいです。

グループ同士のソート

グループごとにまとめることはできましたが、それでも最終的には順番に並べた結果を出す必要があります。
今回必要なのは下位30サンプル目の値のみでそれ以外は必要ないので、各グループから大きい順番に値を引っ張っていき、30サンプル目になればそれが震度算出に使用する値となります。

図に起こしてみるとこんな感じです。

こういう配列があったとして、最初は各グループ一番末尾の数値を舐めて、最大値の数値を持っているグループの参照インデックスをインクリメントします。
次のループからはそのグループの次に小さい要素と他のグループの末尾を参照し…というのを30回繰り返すことで30番目に大きい数値を出すことができます。

この方式では新しい震度が算出できるタイミングが1グループが貯まり終わったタイミングになるため、震度の算出頻度は 毎秒(グループサイズ / 100)回 となります。
この数字を大きくすればソートに時間がかかる代わりにグループごとの探索にかかる時間が減り、逆に小さくすればソートに時間がかからない代わりにグループごとの探索にかかる時間が増えます。
あまり大きくするメリットはなさそうですが、RP2040 や ESP32S3/ESP32C3 では20に設定しているため、毎秒5回計測震度相当の算出ができました。

FreeRTOS のタスクの活用

本題とはあまり関係ありませんが、開発している震度計では FreeRTOS のタスクを活用しているため、震度計算を行っていないタイミングでシリアルコマンドの処理やディスプレイの更新やネットワークの処理を行うことができます。
これらの工夫により、各マイコンで1コアでセンサーの計測値の取得と震度の計算ができています。

さいごに

めっちゃ簡単な話だったので一瞬で終わりましたね!
ちなみに本当に僕の知識がないだけなので、いい感じのソートアルゴリズムとか、手法を持っている方がいらっしゃればぜひ教えてください。

今後はこの地震計のファームウェアにWiFiとMQTTを使用したデータ送信機能を追加する予定です。
これを発展させていずれはみんなで作る強震観測網に…!

という感じで頑張っておりますので引き続きよろしくお願いいたします。