はじめに
今回は心電センサICを使って、ストレスを数値化してリアルタイムにグラフ表示するIoT作りに挑戦してみたいと思います。

センサーICとして、アナログ・デバイセズ社のAD8232という心電モニタICが搭載されたモジュールを使います。AD8232は、微弱な生体信号から必要な信号を抽出し、増幅とフィルタ処理を行った結果をアナログ信号で出力します。ArduinoやBLE NanoなどADC入力のあるマイコンボードを使えば、モジュールから簡単に心拍波形データを得ることが出来ます。
心拍波形の揺らぎからストレスの指標を算出することが知られているため、その情報を活用してストレスを可視化します。

心拍
心拍と言えば、「心拍数」が頭に浮かぶと思います。
心拍数とは、一定の時間内に心臓が拍動する回数で、通常は1分間あたりの拍動数のことを指します。健康な成人の場合、安静時の心拍数は1分間に60~100回程度です。
一方で「脈拍数」という言葉もあります。心臓から送り出される血液によって生じた拍動が動脈に現れたものが「脈拍」です。
つまり、不整脈のない健康状態であれば「心拍数」=「脈拍数」となります。
心拍数(脈拍数)の計測には主に「心電図法」と「光電脈波法」の2つがありますので、簡単に説明します。
心電図法(ECG)
心臓には洞房結節という部分があり、電気パルスを発生して心筋を収縮させることで周期的に拍動させています。心電図法は体の数箇所に電極パッドを付け、心臓から発生する微弱な電気パルスを捉えて測る方法です。
今回のAD8232搭載モジュールはこの方式になります。

光電脈波法(PPG)
光電脈波法は、近赤外光を皮膚表面に当てて、反射した光をフォトダイオードなどで受光する方式です。動脈血管内のヘモグロビンが光を吸収する特性を利用し脈拍をセンシングしています。多くのウォッチ型のウェアラブルデバイスに採用されています。
さらにもっと簡易的には、スマホ本体に搭載されているCMOSカメラを受光器、LEDフラッシュライトを発光源とした方法もあり、スマホさえあれば脈拍数を測定することが可能になっています。既にiPhone/Androidのアプリが複数あります。

心拍波形
心拍数の計測については、手軽に測定できる光電脈波法で十分だと思います。
一方で、波形を観測したときには2つの方法で精度に差が現れます。
以下に「心電図による波形」と「光電脈波による波形」を示します。


脈波波形のピークは心電波形のピークのように尖っておらず、心拍間隔の計測精度が低いことが分かると思います。心拍波形の揺らぎに関しては、心電波形を使った方が高い精度で測定出来るということです。
(ただし、脈波波形が心拍波形の揺らぎに全く使えないわけではありません。脈波波形を2階微分した「加速度脈波」を用いた解析を用いるなどの手法もあります。)
心電波形をもう少し長い時間で観測したときの図を示します。

心電波形には P, Q, R, S, T と名前が付いています。正常な心電図であれば、図のように周期的に同じ波形が現れます。
RからRまでの間隔はRRI(R-R Interval)と呼ばれており、ストレス指標を算出するための必要な要素となります。
次のセクションではストレス指標とRRIの関係を説明します。
ストレス指標
「自律神経系」は、血液循環・呼吸・体温調節など、意識の介在なしに制御するシステムで、「交感神経系」と「副交感神経系」があります。
「交感神経系」は身体の活動レベルや運動能力を高める方向に働き、「副交感神経系」は心身の鎮静化・エネルギーの消費抑制と蓄えの方向に働きます。
心拍変動(HRV)は交感神経と副交感神経の両方に影響を与えます。
先程の心電図のRRIを周波数解析してパワースペクトルにしたときに、LF(Low Frequency)とHF(High Frequency)の2つの領域に注目します。LF成分は0.05Hz~0.15Hzまで、HF値は0.15Hz~0.40Hzまでの成分です。

LF成分とHF成分は以下の特性を持っています。
- LF成分:交感神経または副交感神経が活性化しているときに増加
- HF成分:副交感神経が活性化している場合のみ増加
つまり、2つの比であるLF/HFを指標としてみれば、数値が高いときはストレスがある、低いときはリラックスしていると判定できる仕組みです。
全体システム
今回はこちらのwebサイトを参考にして、ストレスを可視化するアルゴリズムを組み立てました。
アルゴリズムは以下の通りです。
- 心拍波形のピークを検出
- 心拍のピークからピークまでの時間(RRI)を計測
- 線形補間で再サンプリングを行う
- フーリエ変換を行い、パワースペクトラムにする
- ストレス指標LF/HFを算出
マイコンとしてBLE Nano2 を使ってBLEを経由してPCまたはスマホで結果をグラフ表示します。
ただし、BLE Nano2の送信頻度は実力的に最小500msくらいが限界です。つまり、心拍波形データそのものをPCやスマホ側にリアルタイム送信するのは無理ということです。
そこで、BLE Nano2側で心拍のピーク検出と心拍間隔RRIを取得し、それをBLE送信することとします。それであれば60~100BPM つまり 600~1000ms くらいの間隔になります。
線形補間、フーリエ変換、ストレス指標の算出はPC/スマホ側のブラウザを使ってJavaScriptでプログラミングします。

なお、AD8232搭載モジュールは電源とGNDさえ接続すれば、アナログ信号が出力されます。特に何かマイコンから通信・制御しなければならないことは一切なく、非常に簡単に扱えます。
電子部品
今回使用する電子部品も、すべて電子部品専門の通販サイトで購入が可能です。
- BLE Nanoキット V2
BLE通信とセンサーデータを取得するCPUが搭載されたモジュール
※BLE Nanoキット V1 でも動作可能です(現在は生産終了のため購入はできません)
- AD8232搭載単極誘導心電モニター
単極誘導心電モニタIC(AD8232)を用いた心電計基板モジュール
- センサケーブル
3つのスナップパッド付きのケーブル
- センサパッド
直径 24 mmの使い捨て電極パッド
- ブレッドボード
はんだ付けなしで電子回路を組める簡易的なボード
※第1回記事で使用した部品と同一です
- ジャンパー線(オス-オス)
ブレッドボードを使用する際に、配線を簡易的に行う導線
※第1回記事で使用した部品と同一です
電子回路
今回、心拍波形のピークを検出するにあたり、心拍波形がどんなものかを知る必要があります。そこでPCとシリアル通信でき、書き込みもでき、BLE通信にも対応できる回路にしました。ハンダ付けは一切不要です。
一見複雑そうに見えますが、AD8232搭載モジュールからのジャンパー線3本以外は、BLE Nano2と書き込み器の接続をジャンパー線で延長したと考えれば分かりやすいと思います。
※BLE Nano V1 の場合も同じ結線です

実際の結線はこのような感じです。

センサパッドの貼り付け
貼り付け位置は、腕と足に付ける方法と、胸の周囲に貼り付ける方法の2通りあります。付けやすい方を選択して下さい。
センサケーブルにセンサパッドを付けてから貼り付けた方が良いと思います。赤、青、黒のそれぞれのパッドをできるだけ図に近い位置通りに貼ります。
ハートマークは心臓を表しています。

画像出典元:https://learn.sparkfun.com/tutorials/ad8232-heart-rate-monitor-hookup-guide
心拍波形ピーク検出
ピークを検出する方法として、閾値(threshold_v)を設けて判断するという最もシンプルな方法を取ります。さらに連続誤検知防止で一旦検出したらしばらくは検出しない時間(threshold_t)も決め打ちで設定します。

閾値を決めるために、まず実際の心拍波形を知る必要があります。
AD8232からのアナログ信号をA/D変換して得られた値(0.0~1.0)に1024を掛けてUSBシリアルで送信するプログラムを作り、BLE Nano2へ書き込みます。

プログラム全体をこちらからimportできます。
※BLE Nano V1 の場合はこちらからimportしてください。
次にPC側でシリアルデータを受信して波形を出力します。
自作して波形出力するアプリを作ることも可能ですが、既に世の中にあるアプリを活用します。Arduino IDEにはシリアルプロッタというツールがあり、シリアル受信したデータを波形として描画してくれます。このとき、Arduino側のプログラムは一切不要です。
なお、シリアルプロッタはver1.6.6以降に追加された機能ですので、古いArduino IDEをお持ちの方は最新のIDEをダウンロードしてください。
Arduino IDEはこちらのサイトからダウンロードできます。
お使いのOSにあったソフトを選んで "JUST DOWNLOAD" を選択すればダウンロード可能です。
インストール出来たら、Arduino IDEを起動します。
ツール > シリアルポート からBLE Nano2のシリアルポートを選択します。

ツール > シリアルプロッタ を選択するとグラフが表示されます。
右下の数値はボーレート(転送速度)を示しています。BLE Nano2側で設定した値と同じ "9600 bps" に設定して下さい。

このグラフから、閾値threshold_vを650と決定しました。この閾値は各個人毎に異なりますので、グラフを見て適宜調整してください。
次に時間ですが、シリアルプロッタの横軸の単位からは求められなさそうです。健康な成人の安静時脈拍数は60~100BPMで、周期に変換すると600ms~1000msです。
このことから300msくらい取っておけば、一番尖っているR波形の横幅は十分に超えそうです。threshold_tを300と決定しました。
シリアルプロッタのグラフ波形をよく観察しておいてください。
腕を動かしたり、足を動かしたり、線をさわったり、大きく息を吸ったりしたときなど、波形にどのような影響がでるかチェックして下さい。
ストレスを計測するためには数分のデータ蓄積が必要です。その際に異常波形にならないようにするためには、どのくらいまでなら体を動かしても影響が出ないのか、ここでしっかり感覚をつかんでおきましょう。
心拍間隔RRI取得
先程得たthreshold_v:650とthreshold_t:300を使ってRRIを取得します。
第6回までのMbedプログラムからの主な変更点を中心に説明します。
今回新たに3つのグローバル変数を用意します。
時間をカウントするためのtimer_num、ピーク検出の閾値threshold_v、ピーク検出後の無検出時間threshold_t です。

これまでのプログラムでは、200ms毎にBLEデータを送信していましたが、今回のプログラムではRRIが取得でき次第の送信に変更します。
これまで使っていたタイマー200msは廃止し、代わりに時間をカウントするためのタイマーとして役割を変更します。
TICKER_TIMEは200msから1msに変更します。

タイマーのイベントハンドラは変数timer_numのインクリメントを行うのみに変更します。

これまでのプログラムではmain関数のwhileループにおいてwaitForEventでBLEイベント待ちのみでしたが、変更後はAD8232からの信号値をアナログ入力して条件が揃った場合のみRRI値をBLE送信しています。条件というのは、入力信号値が閾値threshold_v以上、かつ、前回のピーク検出からの時間経過がthreshold_t以上であることです。

プログラム全体はこちらからimportできます。
※BLE Nano V1 の場合はこちらからimportしてください
Webブラウザ側でRRIをグラフ表示
実際にPC側でBLE受信できるか確認してみましょう。
第6回で使用したHTML/JavaScriptプログラムから3点変更しました。
- グラフのmin/maxを-128/128から0/1024へ変更
- onRead時のデータフォーマットを1Byteから2Byteへ変更
- 表示サブタイトルを変更
プログラム全体をこちらからダウンロードできます。右上にある”Download ZIP”ボタンを押してください。
※HTMLファイル以外は前回と全く同じファイルです。
実行してみます。
ダウンロードしたファイルの中のhtmlファイルを開き、”Start Notify”ボタンをクリックしてBLE接続を行います。

これで心拍間隔RRI(単位ms)を取得できました。
ちなみに心拍数を出力したい場合は 60000/RRI (単位:BPM) で出力可能です。
線形補間で再サンプリング
LFとHFを求めるためにフーリエ変換を行う必要がありますが、デジタルで計算を行うため、サンプリング間隔を一定にする必要があります。その際に使うのが線形補間です。

この2つのグラフはX軸に経過時間[ms]、Y軸にRRIをプロットしたものです。
上側グラフは、実際に取得したRRIを元にプロットしています。RRIの揺らぎがあるため、サンプリング間隔は一定ではありません。このままだとフーリエ変換できないため、一定間隔になるように再サンプリングを行います。再サンプリングの方法は複数ありますが、シンプルな方法である線形補間を採用しました。線形補間は実際のデータの隣接した2点間を結んだ直線から求める方法です。
下側グラフが線形補間を行った後のグラフで、データが500[ms]毎に一定間隔でプロットされているのが分かると思います。
HTML/JavaScriptプログラムでは、プロットすべき点(x, y)の隣接2点(x0, y0), (x1, y1)を見つけて、以下の計算を行っています。

フーリエ変換でパワースペクトル
再サンプリング出来たら、波形からパワースペクトルに変換します。
パワースペクトルとは、横軸が周波数で縦軸が振幅です。各周波数における成分がどれくらいあるかを示しています。

波形からパワースペクトルに変換するにはフーリエ変換を行います。
離散フーリエ変換は以下の式です。

このままではプログラムで計算できないため、以下のオイラーの公式を使って、実部(Re)と虚部(Im)に分けます。

以下のような式に展開できます。

高速に離散フーリエ変換を行うFFTと呼ばれるアルゴリズムがありますが、今回はサンプリング周波数とサンプル数を少なく設定しているため、FFTを使わない離散フーリエ変換を行っています。
ReとImをそれぞれ算出した後、周波数と振幅を求めることにより、パワースペクトルを得ることができます。
周波数は以下の式で求まります。
(fsはサンプリング周波数)

各周波数に対する振幅は以下の式で求まります。

ストレス指標を算出
以下に示す周波数範囲にある振幅値の合計を出して、LFとHFを算出します。
LF:0.05~0.15Hz
HF:0.15~0.40Hz

ストレス指標は以下の式で求まります。

これでストレスの可視化ができます。
RRIデータをBLE受信してから、再サンプリング・フーリエ変換・ストレス指標算出・グラフ表示を行うプログラムを作成しました。プログラムの詳細説明は割愛します。
プログラム全体をこちらからダウンロードできます。右上にある”Download ZIP”ボタンを押してください。
※HTMLファイル以外は前回と全く同じファイルです。
実際に実行して、数分経過するとLF/HFの値がグラフに表示されます。
中央の大きな数値はRRIのリアルタイム値です。LF/HF値はその下の小さな数値とグラフの両方で表現しています。

サンプリング周波数は2Hzとし、サンプル数Nは256としました。
256個のデータが溜まるまではempty状態でLF/HFは表示されません。
RRIが400~800の範囲を超えている場合は異常データとしました。(※この値は各個人で調整してください)
異常なデータが来た場合はerrorとし、全てのデータをクリアして0から溜め直します。
256個以上のデータが溜まったら、LF/HFが算出され出力されます。
新しい心拍間隔データを受け取ったら、一番古いデータを捨て、再サンプリング・フーリエ変換・ストレス指標算出までの計算を行い、エラーが無ければ、ずっとストレス指標を出し続けます。
注意点
PCからUSB給電の場合、ノイズが混じってしまうためACアダプタは抜いて下さい。
もしくは、このようにモバイルバッテリーで給電させることが望ましいです。

実験と考察
実際にリラックスした状態とストレスを与えた状態で、ストレス指標を実測してみました。
条件は同じ姿勢で5分間、以下の状態を保ち続けることです。
- リラックス状態:リラックスするBGMを聴きながら目をつぶった状態
- ストレス状態:緊張するBGMを聴きながら、スマホで暗算アプリをひたすら解く
これを交互に3回繰り返したとき、それぞれのストレス指標の平均値結果がこちらです。
※先のプログラムから約5分間のログを出してExcelで集計しました。

図34, ストレス指標の実測結果
リラックス状態とストレス状態ではっきりと数値に差が出ました。リラックス状態時のストレス指標は0.9以下、ストレス状態時のストレス指標は1.2以上とキレイに分けることが出来ます。
この結果は1人だけの結果なので、研究結果としては不足していますが、それなりにストレス指標と実際のストレスに相関がありそうだということが実感できました。
今回は、ストレス指標算出及び実証実験を簡易的に行いましたが、興味のある方はもう少し深く研究してみると面白いと思います。
参考サイト
- AD8232 Heart Rate Monitor Hookup Guide - learn.sparkfun.com
- 心臓の電気の流れ | フクダ電子
- 豊橋ハートセンター
- 心拍数と運動強度 | 健康長寿ネット
- 医薬関係者向け製品情報 | タケダ健康サイト
- 特集③人間工学のための計測手法-心拍の計測-
- 特集③人間工学のための計測手法-自律神経系指標の計測と解析-
- ストレスと自律神経の科学
- Arduino – Software
- Javascriptで離散フーリエ変換(DFT)
- FFTの扱い方 - ロジカルアーツ研究所
- 2点間の線形補間を計算する
- エクセルを用いたフーリエ変換(FFT)
- クラゲのIoTテクノロジー BLE Nano2
この記事に関して
産業向けソリューション
製品カテゴリ
資料
{{modalTitle}}
{{modalDescription}}
{{dropdownTitle}}
- {{defaultSelectedText}} {{#each projectNames}}
- {{name}} {{/each}} {{#if newProjectText}}
-
{{newProjectText}}
{{/if}}
{{newProjectTitle}}
{{projectNameErrorText}}