まなびサイエンス

RaspberryPi と gnuplot でセンサの測定値を「リアルタイムに」グラフ描画する

  • 公開 2017/02/14
  • 更新 2017/02/16
カバーイメージ
  • graph
  • Photo by Esther Simpson
    • CreativeCommons
    • Attribution
    • NonCommercial
    • ShareAlike

 gnuplot を利用して、センサの測定値をグラフ描画できるようになりました。しかし、測定値をデータ ファイルに一旦書き出してからグラフを描くため、まさに今、センサがどのような測定値を出力して、どのように測定値が変化しているのか、その変化をリアルタイムに見ることができませんでした。この記事では、センサなどの測定値を、リアルタイムに gnuplot でグラフ描画する方法を紹介します。

データ ファイルを使う方法

 先の例と同じように、測定プログラムを実行して、センサの測定値をデータ ファイルに書き出します。そして、この測定プログラムを走らせたまま、平行して、データ ファイルからデータを読出し、グラフを描画します。

 先ず、下準備として、gnuplot の一連のコマンドを書いた gnuplot.cmd を作成しておきます。このファイルには、sensor.txt からデータを読み出し、一定周期で繰り返しグラフを描画するようコマンドが書かれています。

set xrange [-1.5:1.5]
set yrange [-1.5:1.5]
plot "< tail -10 sensor.txt" using 2:3 w l
pause 0.1
reread

コマンドの解説

  • X 軸と Y 軸の表示範囲を設定します(set xrange [下限値:上限値])
  • 1 つ目のポイント。シェル コマンド tail を利用して、データ ファイルの末尾からデータを取り出し、その結果を gnuplot に取り込みます(<)。測定プログラムは、データ ファイルの末尾に新しい測定値データを追記していきますので、最新のデータから 10 行だけを取り出してグラフを描画します。この数値を増やすと、グラフの軌跡が長くなります。
  • データ ファイルの 2 ケタ目(X 軸)と 3 ケタ目(Y 軸)のデータを使って X-Y グラフを描画します。
  • 0.1 秒間だけ待って(pause 0.1)、
  • 2 つ目のポイント。現在のスクリプト ファイルの始めに戻ります(reread)。

 測定プログラムを実行中のターミナル ウィンドゥとは別に、新しいターミナル ウィンドゥを開き、gnuplot の引数に gnuplot.cmd を与えて実行します。すると、gnuplot のグラフ ウィンドゥが表示され、データ ファイルの内容が(ほぼ)リアルタイムにグラフ描画されます(Fig.1 )。スクリーン ショットでは判りませんが、センサを傾けると、その動きに応じて即座にグラフが変化します。測定を止めたり、グラフ描画を止めるには、ターミナル ウィンドゥで CtrlC を押します*1

$ gnuplot gnuplot.cmd


Fig.1

 この方法の大きなメリットは、測定値などのデータをテキスト形式で書き出せるプログラムであれば、改造を必要とせずにそのまま使えるという点です。実際、この例でも、前回使った測定プログラムをそのまま使っています。

プロセス間通信(パイプ)を使う方法

 先のデータ ファイルを介してグラフ描画を行う方法とは別に、プロセス間通信(パイプ)を利用して gnuplot でグラフ描画を行う方法を紹介します。gnuplot は標準入力からコマンドやデータを受け取ることができるので、測定プログラム内において gnuplot を子プロセスとして起動し、gnuplot へパイプを介して測定値を書き出すことで、gnuplot にグラフを描画させることができます。

  • メリット
    • データ ファイルや gnuplot のためのコマンド ファイル(gnuplot.cmd)を必要とせず、計測プログラムを一つを実行するだけで、測定とグラフ描画を行えます。
    • gnuplot と直接やり取りするので、高速に描画できます。
  • デメリット
    • 測定プログラムごとに、プログラムにグラフ描画のための改造を施す必要があります。
    • 先の方法では、グラフを描画しながらも、測定結果を記録したデータ ファイルが手元に残りますが、グラフ描画と併せてデータ ファイルも必要な場合、測定値をパイプに書き出すのとは別に、画面、またはファイルに測定値を出力するようプログラムを書く必要があります。

 測定プログラムを実行すれば、測定とグラフ描画を開始します。測定を止めるには CtrlC でプログラムを止めます。

$ python i2c_mpu6050_pipe.py


Fig.2

:
# gnuplot を子プロセスとして起動
g = os.popen( 'gnuplot -noraise', 'w' )
:
# gnuplot にコマンドを書き出す
g.write("""
set xrange [-1.5:1.5]
set yrange [-1.5:1.5]
set zrange [-1.5:1.5]
splot '-' with lines linewidth 3
""")
while i < BUFFER_SIZE:
    g.write( "%f %f %f\n" % ( accel_x[i], accel_y[i], accel_z[i] ))
g.write( "e\n" )
g.flush() # 即時反映
:
g.close()

解説

 プログラム全体が長く、センサからの測定値の取得処理などは全く同じですので、要所のみ説明します。他に少し違う点としては、軌跡を描く際に必要な過去のデータを自前で保持する必要があり、その目的で ring buffer を使用するようになっています。

  • gnuplot を書き込みモード(w)で、子プロセスとして起動し、そのパイプ(g)を取得します。
  • gnuplot の引数に -persist オプションを付けると、プログラムが終了してパイプが閉じられても、gnuplot のウィンドゥは閉じられずに残ったままになります。測定プログラムを何度も実行すると、ウィンドゥが次々と開いて鬱陶しいので付けていません。
  • gnuplot の引数に -noraise オプションを付けると、ウィンドゥが前に固定されません。ターミナル ウィンドゥで CtrlC しないと終了できないので、このオプションがあった方が良いと思います。
  • パイプに対して、gnuplot のコマンドを書きこみます(g.write)。
  • おそらく、ここがキモ。gnuplot の標準入力('-')からデータを読込み、3 次元グラフを描画(splot)します。パイプは gnuplot の標準入力に繋がっているので、パイプに対して直接データを流し込むことが可能です。
  • 空白区切りで 1 行に 1 件づつデータ列を書き込み、最後にデータ列の終端を表す e を書き込みます。これは gnuplot のデータ列の仕様です。
  • パイプのバッファを flush します。このタイミングでグラフが描画・更新されます。
  • プログラムが終了する前に、ハンドルを閉じて(g.close) gnuplot を終了します。

参考リンク

  1. *1 グラフ ウィンドゥは直接閉じれません

カテゴリー

過去ログ