概要
心拍センサとシグナルタワー、Raspberry Piを組み合わせてLT登壇中のドキドキを視える化、さらにドキドキの度合に応じて音声で自分のことを見守ってほしいアピールしてくれるBotを作りました!登壇中の心拍数データを随時SORACOM Harvestに貯めていき心拍数の推移のLagoon可視化も出来ます。
つい先日、実際に実機をSORACOM UG四国 勉強会 in 丸亀の会場に持ちこみ、本作品の作り方を紹介するLTをしながら、ドキドキ感の共有、IoF: Internet of Feelings 視える化に挑戦しました!
完成品の動画
心拍センサで随時心拍数を測定し、心拍数が閾値以下のときは緑色に光り、余裕感を伝える音声が流れます。閾値を超えると赤色に光り、パニック感を伝える音声が流れます。光と音でドキドキを共有、さらにLagoon可視化して、ドキドキの推移を視える化しました。
製作の動機
私は結構見た目の印象から落ち着いているように見られがちなのですが、実は、内心、密かにドキドキすることが多く、見た目と心のギャップが結構あったりします。人前で話すときとかも淡々としゃべってるように見えて、本当はめちゃめちゃ緊張してたりする。このドキドキのギャップも含めて自分のことをもっと知ってほしい、そしてLTの時は、温かく見守ってほしい欲求、密かにめっちゃありました。
目標
作りたい!
心拍数をモニタリングしながら、ドキドキに応じたメッセージをしゃべってくれたり、光で今のドキドキを伝える作品を作る!
挑戦したい!
実際のLTでDemoしながら使ってみてそのときのリアルなドキドキを共有する!
ということで、さっそく、頭の中にあるアイデアを形にすべく開発に取り組むことにしました。
全体構成
動作の概略は以下の通りです。
① 心拍数を測定し、閾値以下なら、緑色のシグナルタワーが点灯、閾値を超えたら赤色のシグナルタワーが点灯する。(24Vで駆動させる為、リレーで制御しました。*動画撮影のときはそこまで明るさ要らなかったので15Vで使用しました。)
② DACオーディオアンプに接続したスピーカから音声メッセージが再生されます。I2Sを利用しました。
③ SORACOM AirでSORACOMのプラットフォームと接続、心拍数のデータをJSON形式にてHarvestに送ります。
④ データが貯まっていきます。貯まったデータはLagoonで可視化します。
⑤ ユーザはPC、タブレットで登壇者のドキドキ状態(心拍数)を確認できます。
材料
・シグナルタワー: LR4-302WJNW-RYG DC24V Φ40
・4chリレーシールド:KS0212 keyestudio
・直流安定化電源
・Raspberry Pi 4B
・心拍センサ: SEEED-101020033
・i2S DAC: MAX98357 I2S Class-D Mono Amp
・超音波センサ:HC-SR04
・ダイナミックスピーカ φ50 8Ω 0.4W
・Seriaで購入した100均ケース (※シグナルタワー保持に使用します)
工作
最初シグナルタワーは左上の状態で送られてきました。台がないので立たない。。どうしよう。。とりあえず100均でケースを買ってきて、ドリルや、ホットナイフなどを使って加工し、結束バンドで留めるなどして、土台を作ってみました!シグナルタワーの土台は手作りなので実はとても安上がりに出来ています!
準備
a) Raspberry Piの音声設定
adafruitのページを参考に以下のコマンドを実行
$ curl -sS https://raw.githubusercontent.com/adafruit/Raspberry-Pi-Installer-Scripts/master/i2samp.sh | bash
b) 音声データの準備
音声データをOpen J Talkで事前に準備し、ダウンロードしてRaspberry Piの任意のフォルダに保存します。
c) SORACOM Airの設定
SORACOMの公式ページを参考に以下のコマンドを実行
curl -O https://soracom-files.s3.amazonaws.com/setup_air.sh
$ sudo bash setup_air.sh
実行結果
$ sudo bash setup_air.sh
.
.
.
Adding udev rules for modem detection.
ok.
--- 3. Connect
Found un-initilized modem. Trying to initialize it ...
Now you are all set.
Tips:
- When you plug your usb-modem, it will automatically connect.
- If you want to disconnect manually or connect again, you can use 'sudo ifdown wwan0' / 'sudo ifup wwan0' commands.
- Or you can just execute 'sudo wvdial'.
接続確認
ifconfig ppp0
ppp0 Link encap:Point-to-Point Protocol
inet addr:10.xxx.xxx.xxx P-t-P:10.64.64.64 Mask:255.255.255.255
UP POINTOPOINT RUNNING NOARP MULTICAST MTU:1500 Metric:1
RX packets:133 errors:0 dropped:0 overruns:0 frame:0
TX packets:134 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:3
RX bytes:2092 (2.0 KiB) TX bytes:4039 (3.9 KiB)
全体プログラム
コードはコチラ
import time
from collections import deque
import subprocess
import shlex
import glob
import requests
import json
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setup(4,GPIO.OUT) #green
GPIO.setup(22,GPIO.OUT) #yellow
GPIO.setup(6,GPIO.OUT) #red
class HeartRateMonitor:
INTERRUPT_PIN = 12
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))
payload = {"Heart Rate":heart_rate}
requests.post("http://harvest.soracom.io", data=json.dumps(payload))
print("data transfer harvest")
if heart_rate > 80:
GPIO.output(6, 1)
GPIO.output(4, 0)
GPIO.output(22, 0)
time.sleep(0.1)
print("You're Nervous. Calm down")
if heart_rate <= 80:
GPIO.output(6, 0)
GPIO.output(4, 1)
GPIO.output(22, 0)
time.sleep(0.1)
def execute(self):
print('Please ready your heart rate monitor.')
time.sleep(3)
count = 0
while True:
GPIO.wait_for_edge(self.INTERRUPT_PIN, GPIO.RISING)
self._interrupt()
if count ==300 and self._calc_heart_rate()>80:
command = "sudo aplay %s"%("/media/scbc1167/E466-2D1B/231215/めっちゃパニック.wav")
subprocess.Popen(shlex.split(command))
count=0
if count ==300 and self._calc_heart_rate()<=80:
command = "sudo aplay %s"%("/media/scbc1167/E466-2D1B/231215/余裕.wav")
subprocess.Popen(shlex.split(command))
count=0
count = count + 1
if __name__ == '__main__':
monitor = HeartRateMonitor()
monitor.execute()
履歴データ確認(Lagoon&SORACOM Harvest)
公式ページを参考にHarvestにデータを貯めていき、Lagoon可視化できるようにしました!
実証実験(リアルタイムデモ)
平常時の心拍数がだいたい60~85くらい、LT中の心拍数がだいたい75~110くらい!ざっくり15~25くらい高い結果が出ました。心臓バクバクとまではいってないとしても、ドキドキしていたようです!
実際には15分くらい登壇、話していたのですが、7分くらいしかデータとれていませんでした。心拍センサとRaspberry Piの接続不良の為、途中からデータ取得に失敗していたようです(泣)。その間にドキドキする場面もあったりしたのでおそらく、瞬間的に高くなっていたと思われます。このデータ取得できなかったの、めっちゃ惜しかった。でも7分位のデータをみるだけでもLT中はやっぱりドキドキしてたの再確認できたのと、あと、LT中、このBotのおかげでとても温かく見守られてたのを実感したので、すごく楽しいドキドキLTが出来ました!あの場にいたみなさんの温かさに本当、感謝の気持ちでいっぱいです。大変、思い出深いLTが出来ました!
まとめ
リアルタイムで心拍数を共有しながらLTしました!温かく見守られてるのめっちゃ感じた!
思い出に残るLTが出来ました!
今後の課題
わりと大がかりな作品になったので、登壇の入れ替えのときにデモの準備は出来そうになかった。そのため、事前セッティングが必要になるのですが、設置する位置が、登壇机から離れていると、HDMIとか届かなくて困ります。今回かろうじて届きましたが、やっぱりケーブル長の制約はなんとかクリアしたいなと思いました。たとえば、シグナルタワーを制御する用のRaspberry Piと心拍数計測用のRaspberry Piを別々に2台用意し、BLE通信するなどして、有線の制約を解消できるよう出来るといいなと思いました。