前回まで心拍センサを Arduino 互換の Seeduino で使ってきましたが、今回は Raspberry Pi で心拍センサを使ってみたいと思います。スクリプトは Python で実装し、 GPIO からの入力を検知したら心拍数の計算等を実行します。
回路構成
Raspberry Pi には Grove コネクタがついていないので、前々回のケースと同様に、 Grove コネクタに直接ジャンパコードを挿し、 Raspberry Pi の GPIO ピンに接続しました。 Raspberry Pi 側は 3.3V、GND、GPIO17 のピンに接続します。
Raspberry Pi での割り込み処理
今回スクリプトは Python で実装しますが、内容としては前々回の c++ のコードと同様です。心拍モニタが心拍を検知すると GPIO の17番ピンが High になるので、この入力を待ち受けるようにします。
電気信号が Low -> High もしくは High -> Low に切り替わる瞬間のことをエッジ(edge)と言い、今回はエッジ検出のために GPIO.wait_for_edge()
メソッドを使いました。引数には対象のピンの番号と、検出したいエッジの種類を指定します。エッジの種類には、 Low -> High(立ち上がりエッジ)、 High -> Low(立ち下がりエッジ)、そしてその両方の3種類があり、今回は立ち上がりエッジを検出したいので、 GPIO.RISING
を指定しています。このメソッドを実行すると、エッジが検出されるまで待ち受け状態になりますので、無限ループの中でこのメソッドを実行し、エッジが検出されたら処理を行うようにしています。
while True: GPIO.wait_for_edge(self.INTERRUPT_PIN, GPIO.RISING) self._interrupt()
Queue の使用
前回までの c++ のスクリプト内では心拍数の計算のために配列を使用していましたが、今回は単純なリストではなく、 Queue として使用したかったので、 Python の collections.deque を使ってみました。普通のリストでも append() と pop() を使うことで同様の処理を行うことができますが、リストの先頭の要素を pop すると、全ての要素の移動処理が行われるため、処理効率は悪いようです。
まずは下記のように deque をインポートします。
from collections import deque
queue の初期化は deque()
に初期配列を渡します。
self.detected_times = deque([])
要素の追加は普通のリストと同様に append()
で行います。
self.detected_times.append(time.time())
先頭要素の削除は popleft()
で行うことができます。
self.detected_times.popleft()
スクリプト実装
今回のスクリプト全体は下記のように実装しました。心拍が検知される(エッジが検出される)とその時刻を Queue に格納し、 20回を越えるとその差分から心拍数を計算して出力します。エラーハンドリングは考慮していないので、実際に使用する場合はエラーハンドリングのコードを追加することになると思います。
#!/usr/bin/env python import time from collections import deque import RPi.GPIO as GPIO class HeartRateMonitor: INTERRUPT_PIN = 17 MAX_DETECTED_TIMES_COUNT = 20 MAX_PULSE_INTERVAL = 2.0 def __init__(self): GPIO.setmode(GPIO.BCM) GPIO.setup(self.INTERRUPT_PIN, GPIO.IN) self._init_array() def _init_array(self): self.detected_times = deque([]) def _calc_heart_rate(self): return 1200.0 / (self.detected_times[-1] - self.detected_times[0]) def _interrupt(self): self.detected_times.append(time.time()) if(len(self.detected_times) == 1): return interval = self.detected_times[-1] - self.detected_times[-2] heart_rate = -1 if interval > self.MAX_PULSE_INTERVAL: print('Heart rate measure error. Monitoring will restart!') self._init_array() return if(len(self.detected_times) >= self.MAX_DETECTED_TIMES_COUNT): heart_rate = self._calc_heart_rate() self.detected_times.popleft() print("HeartRate: {heart_rate}, Interval: {interval}".format(heart_rate = heart_rate, interval = interval)) def execute(self): print('Please ready your heart rate monitor.') time.sleep(3) while True: GPIO.wait_for_edge(self.INTERRUPT_PIN, GPIO.RISING) self._interrupt() if __name__ == '__main__': monitor = HeartRateMonitor() monitor.execute()
動作確認
上記のコードを実行すると、ターミナルに下記のように心拍数の計算結果が出力されます。
$ ./heart_rate_monitor.py Please ready your heart rate monitor. HeartRate: -1, Interval: 0.8302757740020752 HeartRate: -1, Interval: 0.808060884475708 HeartRate: -1, Interval: 0.7631323337554932 HeartRate: -1, Interval: 0.7801287174224854 HeartRate: -1, Interval: 0.798215389251709 HeartRate: -1, Interval: 0.8021731376647949 HeartRate: -1, Interval: 0.7605118751525879 HeartRate: -1, Interval: 0.7294180393218994 HeartRate: -1, Interval: 0.7548770904541016 HeartRate: -1, Interval: 0.8093967437744141 HeartRate: -1, Interval: 0.8305354118347168 HeartRate: -1, Interval: 0.8199899196624756 HeartRate: -1, Interval: 0.8051409721374512 HeartRate: -1, Interval: 0.8412587642669678 HeartRate: -1, Interval: 0.8870439529418945 HeartRate: -1, Interval: 0.9384407997131348 HeartRate: -1, Interval: 0.933556079864502 HeartRate: -1, Interval: 0.9079298973083496 HeartRate: 76.52472304674038, Interval: 0.8811209201812744 HeartRate: 76.12490143070653, Interval: 0.9126362800598145 HeartRate: 75.64256806120363, Interval: 0.9085769653320312 HeartRate: 75.02947229026323, Interval: 0.8927640914916992 HeartRate: 74.78054308152043, Interval: 0.8333685398101807 HeartRate: 74.81021338673118, Interval: 0.7918510437011719 HeartRate: 74.72784839482473, Interval: 0.8198530673980713 HeartRate: 74.24507270335705, Interval: 0.8649301528930664 HeartRate: 73.53067103751506, Interval: 0.8864498138427734 HeartRate: 73.00755517148106, Interval: 0.8718116283416748 HeartRate: 72.93730864636744, Interval: 0.8252270221710205 HeartRate: 72.89795075840209, Interval: 0.8394181728363037 HeartRate: 72.71868699404068, Interval: 0.860569953918457 HeartRate: 72.42184236861083, Interval: 0.8727796077728271 HeartRate: 72.46996284628561, Interval: 0.830256462097168 HeartRate: 73.06855132933228, Interval: 0.7513935565948486 HeartRate: 74.17623306838857, Interval: 0.6931953430175781
まとめ
Raspberry Pi は Arduino 系のマイコン等を使用した場合と比べて省電力が大きいので、ボタン電池等で小型化してウェアラブルデバイスを作るというようなことには向いていませんが、個人的には c++ よりも Python の方が馴染みがあるということもあり色々柔軟に扱えて取っつきやすいので、電源を確保できるような環境での使用であればとてもプロトタイピングしやすいですね。 GPIO のエッジ検出も簡単にできたので、今後色々な用途を検討してみたいと思います。
今回はこちらのサイトを参考にさせていただきました。