mbed + Pythonでマイコンボードからの加速度センサ情報をリアルタイムに可視化する


NXPの[FRDM-KL25Z](https://developer.mbed.org/platforms/KL25Z/)ボードを使いました。3軸の加速度センサー [MMA8451Q](https://strawberry-linux.com/pub/mma8451q-manual.pdf)が載っていたので、ここからシリアル通信で情報を吸い上げて、Pythonでリアルタイムにプロットしてみました。

(加速度センサ) FRDM-KL25Z --- (Serial) ---> PC (リアルタイムプロット)

FRDM-KL25Zは下図のような構成です。

![FRDM-KL25Z-1 2.jpg](https://qiita-image-store.s3.amazonaws.com/0/158680/762c77a5-2a65-40c5-bae4-b59ed2b0a4a4.jpeg)

## マイコンボード側のファーム開発

[mbed](https://www.mbed.com)を使いました。ここで、`MMA8451Q.h`のファイルをインクルードするために、[こちら](https://developer.mbed.org/components/FRDM-KL25Z-on-board-Accelerometer/)でmbedコンパイラにMMA8451Qのライブラリをインポートします。

```cpp:accel_serial.cpp
#include "mbed.h"
#include "MMA8451Q.h"
 
#define MMA8451_I2C_ADDRESS (0x1d<<1)
 
DigitalOut myled(LED_GREEN);
Serial pc(USBTX, USBRX);
MMA8451Q acc(PTE25, PTE24, MMA8451_I2C_ADDRESS);
PwmOut rled(LED_RED);
PwmOut gled(LED_GREEN);
PwmOut bled(LED_BLUE);
    
int main()
{
    pc.printf("Hello World!\n");

    while (true) {
        pc.printf("%f,", acc.getAccX());
        pc.printf("%f,", acc.getAccY());
        pc.printf("%f\n", acc.getAccZ());
        rled = 1.0 - abs(acc.getAccX());
        gled = 1.0 - abs(acc.getAccY());
        bled = 1.0 - abs(acc.getAccZ());
        wait(0.2);
    }
}
```

プログラムが書けたらコンパイルしてバイナリファイルをダウンロードし、FRDM-KL25ZをUSBで認識したフォルダに対して、ドラッグ&ドロップで書き込みました。

## PC側の可視化アプリケーション開発

Pythonを使いました。ライブラリをpipでインストールします

```bash
$ pip install numpy matplotlib seaborn pyserial
```

次にFRDM-KL25ZをUSBで接続して、割り当て先を確認します。

```bash
$ ls /dev/tty*
```

私の環境では `/dev/ttyACM1`に割り当てられていました。
異なっていれば、プログラム中の`/dev/ttyACM1`の部分を書き換えます。
以下のpythonファイルを作って、実行すれば完成です。（sudo権限をつけて実行してあげてください）
FRDM-KL25Zを傾けると連動して、加速度情報がグラフに反映されます。

```python:plot_accel.py
from __future__ import unicode_literals, print_function
import numpy as np
import matplotlib.pyplot as plt
import serial
import seaborn as sns
sns.set(font_scale=2)
s = serial.Serial('/dev/ttyACM1')

fig, ax = plt.subplots(3, 1)

t = np.arange(0,10,0.1)
list_x = np.zeros(100).tolist()
list_y = np.zeros(100).tolist()
list_z = np.zeros(100).tolist()

lines_x, = ax[0].plot(t, list_x)
lines_y, = ax[1].plot(t, list_y)
lines_z, = ax[2].plot(t, list_z)

ax[0].set_ylim((-90,90))
ax[1].set_ylim((-90,90))
ax[2].set_ylim((-1,1))
ax[0].set_ylabel("Rot_X", size=30)
ax[1].set_ylabel("Rot_Y", size=30)
ax[2].set_ylabel("Z", size=30)

acc = s.readline().split(",") # just for warming up

while True:
    t += 0.1
    acc = s.readline().split(",")
    acc_x = float(acc[0])*90
    acc_y = float(acc[1])*90
    acc_z = float(acc[2])

    list_x.pop(0)
    list_x.append(acc_x)
    list_y.pop(0)
    list_y.append(acc_y)
    list_z.pop(0)
    list_z.append(acc_z)

    plt.draw()

    lines_x.set_data(t, list_x)
    lines_y.set_data(t, list_y)
    lines_z.set_data(t, list_z)
    ax[0].set_xlim((t.min(), t.max()))
    ax[1].set_xlim((t.min(), t.max()))
    ax[2].set_xlim((t.min(), t.max()))

    plt.pause(.0001)
```

補足コメント

serial信号の取得は 以下の部分です。

```python
s = serial.Serial('/dev/ttyACM1')
acc = s.readline().split(",")
```

リアルタイムなプロットの更新は `plt.show()`ではなく以下を使えば実現できました。

```python
plt.draw()

# ... matplotlib.lines要素のアップデート ...

plt.pause(.0001)
```

>参考: http://qiita.com/hausen6/items/b1b54f7325745ae43e47