- 2022.09.26
- 電源関連の記載を追記
- 2022.09.18
- 新規作成
Raspberry pi picoとアンプでポータブル心電計を作ってみました
生体センシングといえば、心拍/脈拍、血圧、心電、筋電、血中酸素……。いろいろありますが、 中でも、「心電図」は健康診断では測定するものの、なかなかご家庭で測定するのは難しい技術でした。
しかし、2022現在、「心電計」がスマートウォッチ等に採用み、「心電図」も身近なものになってきました。
その普及の舞台裏には、全部入りコンボチップの登場があるようです。 たとえば、アナログデバイセズ製心電モニタIC(AD8232) ですと、アンプから信号処理までワンチップです。 これに、汎用マイコンと組み合わせることで、簡単に心電計が作れるのだとか。
こういったICを使った工作も、それはそれで面白いのですが、もう少し心電測定の技術に深く触れてみたいですよね。 というわけで、今回は簡単な構成のアンプと、Raspberry pi picoの組み合わせで、 ポータブル心電計を作ってみることにしました。
- 測定電極:皮膚に取り付ける電極です。心電パルスを取り込みます
- 信号増幅機:測定電極からの微小パルスを増幅し、LPFを通してマイコンのAD変換器に接続します
- 送信機:AD変換器で信号増幅器からの波形を取り込み、OLEDに表示します。データはUSBシリアルでも出力しています。
- 受信機:USBシリアルで信号受信し、グラフに表示したり、CSVに保存したりします。データはCSV形式なので、Arduinoのシリアルプロッタの他、各種ツールで表示、保存できます。
信号増幅器の回路を紹介します。
fig.2:信号増幅器・回路図(クリックで全体表示)- U1B:筋電電極のDCオフセット電圧をフィードバック制御する回路
- U2:計装アンプ。測定電極からのパルスを差動増幅する
- U1A:51倍AC増幅回路
- U2A:フィルタ回路
- U2B:レールスプリッタ
- U4:3.3V低損失定電圧レギュレータ(LDO)
回路設計の詳細は後述します。
内部配線はこんな感じです
外付け測定電極は衣類用のスナップボタンで接続しており、脱着可能です。
外付け測定電極を使用しない場合、下の図のように本体のチタン電極を胸と指にあてて測定できます。
使用したパーツ:
- Raspberry pi pico
- 計装アンプ:LT1167
- OPアンプ:LT1112
- OPアンプ:NJM4580DD
- 低損失定電圧レギュレータ(LDO):48M033F
- OLED:0.96インチ128x64白色(秋月のP-12031)
- ユニバーサル基板:秋月のCタイプ
- 測定電極:Amazonで買った謎のEMS用電極
- 測定電極:チタン薄板 t=0.1
- スナップボタン φ8
- 18650リチウムイオン電池および電池ケース
- その他、抵抗、コンデンサ、スイッチングダイオードなど
回路は、測定電極と電池以外はすべて秋月電子通商で揃えました。筐体は3Dプリンタで自作しています。
心電計用プログラム(GitHub)
電源ONで動作開始し、測定電極からの心電波形および心拍数を、OLEDに表示します。
操作部はボタン1か所で、ボタン押下ごとに表示速度を4段階に変更できます。 例えば、0だと約1心拍、3だと約4心拍になります。 表示速度の基本周期は心拍数の4回平均から算出しています。 このため、心拍表示が安定してからボタンを押すと、表示と心拍が同期しやすいです。
心電波形および心拍数は、USBシリアルで送信されます。 115200bpsでCSV形式です。
心電図の表示例です
起動時の表示周期は固定で、約2心拍の波形が表示されます。
右上は心拍数で4心拍の移動平均です。
上の小さい線は、R波を検知した位置に表示され、心拍検知の目安になっています。
PCへの出力は「心電波形,心拍数\n」形式のCSVです。適当なシリアルプロッタで波形表示できます。
1.センサー電極
AmazonでEMS用電極のコピー品を入手して使用しました。 端子が汎用的なスナップボタンだったので、スナップボタンにワイヤーを取り付けて着脱可能にしています。
本体側もスナップボタンで取り付けました。回転の自由度も高く、思いのほか使いやすいです。 感度も十分で、2段目増幅が100倍だとアンプが飽和してしまうので、若干感度を下げているぐらいです。
また、作ってから気が付いたのですが、本体側のボタンを凸ボタンにするべきだったかもしれません。 凸ボタンは掃除しやすいですが、凹ボタンは掃除しにくいので、ワイヤー側なら交換してしまうという手がありました。
本体についている電極はチタンの薄板を使用しており、本体だけで心電図が表示できるようになっています。
下の写真のように、胸に押し当てて測定するのですが、心臓以外の筋肉の動きにも敏感に反応するので、指先含め、なるべく脱力するのがコツです。出来れば、寝た状態で測定すると安定します。
両手の指先でも心電図は出るのですが、心拍検出が不安定です。指先で測定するためにはアンプゲインが不足のようです。2段目のアンプを可変できるようにすればよかったかもしれません。
2.信号増幅器
初段は秋月で一番人気の計装アンプとして、LT1167を使用しました。お高いけど筋電計の実績もありますのでこれにしました。
回路は計装アンプのデータシートに書いてある以下の回路を参考にしました。しかし、これがなかなかの曲者でした。
LT1167で差動増幅し、同相信号のGND形成にLT1112を使用している回路(図の左下)および、HPFと101倍AC増幅+LPFという構成です。
前者の同相GND形成は非常に良好に動作します。 心電計の測定にはどうしても同相GNDの問題がついて回り、適当に体にGNDを接続しておいてもうまく動作しません。 いろいろ試した結果、データシートにあるこの回路を利用しました。
また、同相入力電流経路の問題も、他の回路を試した時には発生したのですが、 上記データシートの同相GND生成回路では異常は出ませんでした。フィードバック回路のほうに電流が流れるからですかね?
そして、2段目はLT1112を使用した51倍アンプ+LPFです。
心電計は筋電計と違い、DC変動を測定するので、DCオフセットは非常に重要です。LT1112はDCオフセット電圧が極めて低いので、外付け部品が少なくて済みます。
裏を返せば、DCオフセット調整回路をつければLT1112である必要はないのですが、筋電計のようにたくさん作るわけでもないので、LT1112を使ってしまいました。
フィルタは、2段目のアンプで1次、3段目アンプで2次、合計3次のローパスフィルタです。 ターゲットはADCのエイリアシング防止です。 ADCのサンプリング周波数は1600Hzにするので、ナイキスト周波数は800Hzです。 カットオフは約80Hzで3次ですから、800Hzでは80Hzに対して-60dBです。これより低い周波数はデジタルフィルタでカットします。
ちょっとわからないのは、どうもこのフィルタ回路の下側が飽和しやすいようでした。
そのうち解析したいところです。(下図は2次LPF部の解析結果)
(2022.09.26追記) 後述の電源回路部分でも触れますが、アンプの電源電圧不足が原因です。 4580DDの定格が±2.0V(0-4V)必要なのに、0-3.3Vしか入れていないので当然ですね。
2.電源回路
電源回路は18650リチウムイオン電池と3.3VのLDOで構成されています。 ポイントは、Raspberry pi pico の電源回路を停止して外部電源を使うことで、低ノイズ化を図っています。
改造としては、raspberry pi picoの電源は3V3_EN端子をLOにし、3V3端子に3.3Vレギュレーター出力を直接つなぎまます。 レギュレーターの出力回路に別電源の電圧を入れるので、非常に危ない気がしますが、 どうも流行のテクニックみたいなので真似しました。
こんな感じでLDOのGNDを隣の3V3_ENにショートする形で差し込むだけで簡単にテストできます。 raspberry pi picoの電源ノイズは単体でも非常に大きく、ADCに誘導で乗ってしまうようです。 ADCを使うときに内蔵電源停止は必須と思います。
バッテリーは、18650リチウムイオン電池を使用しています。 充電時で4V以上、通常でも3.7Vありますから、3.3VをLDOで直接作ることができます。 18650リチウムイオン電池を使うと、3.3V系の装置は低ノイズかつコンパクトにできて、非常によろしいと思います。 本当は正直乾電池のほうが安心安全なのですが、18650リチウムイオン電池を使ってみたかったので使いました。
OPアンプ用に負電源は用意せず、OPアンプで作るレールスプリッタ(中間電位を作る)を使用しました。 この方法はレールスプリッタの出力にコンデンサをつけられない(発振するらしい)のと、 両電源と比較してダイナミックレンジが稼げないのが難点です。 ノイズも心配しましたが、支配的なのは人体のハムノイズなので、それに比べると無視してもよさそうでした。
(2022.09.26追記) この3.3Vの電源をレールスプリッタを使ってアンプの電源にする方法は定格外動作です。 LT1167は±2.7V、4580DDは±2V必要なので、どちらも満たしていません。 後で考えると、若干起動が不安定だったのはこれが原因だったのだと思います。(よく動いてたな……。)
3.デジタルフィルタAD変換機で取り込んだ信号をデジタルフィルタでノイズ処理しています。
アナログ系でないキスと周波数以上はおおむねカットしていますから、 残りで支配的なのはハムノイズ(AC電源の50Hzノイズ)です。 人体は、どこにもアースされていませんし、大きさもインピーダンスも大きいですから、ハムノイズの塊なのです。
調べてみたところ、商用電源周波数に合わせたタップ長で平均化する方法があるようで、これを試してみました。 平均するサンプル時間(サンプル数)を商用電源のTの整数倍になるようにする方法です。 しかし、この方法ではハムノイズは消えますが、周波数が高めのR波がつぶれてしまうようでした。
そこで、FIRでLPFを構成することにました。 タップ長は商用電源のTの整数倍になるように設定し(64サンプル=商用電源の2T)、 ハムノイズが消えるように係数を設定しました。 カットオフ周波数は合わせこみで35Hzにしました。R波を大きく損なうことなく、ハムノイズが最小化するような値です。
下図の緑が平均、赤がFIRのLPFです。平均(緑)は最も尖っているR波がつぶれていますが、FIRのLPF(赤)ではR波への影響が最小限になっているのがわかります。
デジタルフィルタの効果は絶大で、下図のような波形であっても心拍が抽出できます。 外部回路でこの性能を出すのは大変です。
4.OLED
今回使用したのは秋月で買った128*64ドットのもので、制御ICはSSD1306のものです。
この手のOLEDはパルスオキシメーターなどに多く使われています。 量産効果が高いのか、非常にお安くなっており、たったの580円です。 OLEDですからバックライトもいらず、視認性も抜群。使わない手はないでしょう。
仕様としては、I2C制御で最高周波数が400KHzなのがネックで、arduinoで引っ張ってくるライブラリを使うとちょっと遅いです。 そこで、心電計の描画速度に追いつくために、ひと工夫しています。
まず、横128列なので、リアルタイムに1心拍を1画面に表示するとすると、200bpmでは427fps相当(2.34ms周期)になります。 これを全画面書き換えていては間に合いませんから、ロール表示することにします。
具体的には、過去の最古の列を消去して、最新のデータを書き込みます。 その際、1個前の列のデータとの間に補完線を書きます。 つまり、1列進むごとに2列書き換えていくことになります。
この書き込み時間と、後述のFIR等の演算時間と、UARTの出力時間等を含めて、OLED描画周期に間に合えばOKです。 実際、2列描画のOLED処理時間はOLEDの描画時間は4.5msですから、目標の2.34msには及びません。
そして最後に手を付けたのはOLEDのクロックです。 実はこのOLEDはどうやら3MHz設定ぐらいまでは実力動作するみたいなのです。 その約半分の1.6MHz としても、OLEDの描画は1ms強で済むことになります。
2列折れ線描画をあきらめればクロックアップの必要もないのですが(2 segment * 8 page書くところ、1 segment書くだけでいいので十分間に合う) 折れ線表示はどうしてもしたかったのでクロックアップを選択しました。工業製品じゃないですしね!
また、低速表示時にも間引き表示するのではなく、常にベースの周期で描画用のバッファを上書き塗りすることで、 急峻な波形も抜けなく表示するようにしています。
5.心拍検出
おまけ機能として付けたのが心拍の検出です。
心拍の検出方法は、フィルタしたあとの波形の2階微分値を使用する方法です。心拍はR波やT波など、ピークがいくつもあるので、単純なレベル検出ではうまくいきません。
- 波形の2階微分値の符号反転したものの最高値を2秒間探索、記録する。
- 1.で取得した最高値の1/4値を閾値にする。閾値の更新は2秒ごとに続ける。
- 2で決めた閾値を超えたら1心拍検出とする。
- 3.で検出した後一定のマスク期間を設け、マスク期間は心拍検出しない(これをしないと、R波のところで何何度も検出してしまう)
この方法ですと、保持バッファは最小で済みますし、微小時間で積分(演算としては3回引き算)するだけであり、とても単純です。 結果としては、R波が最も尖っている場合、これを適切にとらえることができました。(赤が心電図、青が2階微分値)
下の図のように、T波が高い場合でも、T波は尖っていないので、2階微分値は低くなるのがわかると思います。この方法を提案していただいたリスナーのみんなに感謝!
心拍数は4回の移動平均を保持しており、表示速度切り替えボタンを押したときに、 ベース速度を1心拍で1周となるようにタイミング補正をするようにしました。
この方法では、例えば表示切り替え直後は波形がおおむね同じ位置に表示し続けられますから、 波形をじっくり見る場合に見やすいです。 また、心拍数がより大になれば波形が右にずれていく、心拍数が性になればその逆というように、 心拍数の変化がより視覚的になるのを楽しむのもいいと思いました。
また、OLEDの項目で述べたように、心電波形を出すだけでも時間が厳しいので、 心拍数表示はUARTのみと考えていたのですが、 極小フォントを使うことで、何とか表示することができました。
また、さらにおまけとして、心拍検出したタイミングでOLED上部に心拍検出マークをつけるようにしました。
この方法で、心拍の間隔がより視覚的になるので、不整脈が見分けやすいように感じました。
6.筐体
各筐体は3Dプリンタで作成しました。 使用したユニバーサル基板が「秋月のC基板」でして、同基板と18650電池がピッタリ収まるサイズに設計しています。
チタン電極を通す隙間も3Dプリンタなら簡単に作れます。 チタン電極を筐体内部に通した後は、隠しねじでハーネスと共締めすることで、すっきりした筐体になりました。
ちなみに、チタン電極はお肌に優しくて生体センシングにはもってこいですが、 電子工作としては、チタンに半田が全くつきませんので、非常に扱いにくいです。
7.次に向けて生体センシングは自分の体で体感できるので、作りがいがあります。 今回は心電計がなんとか実現できましたので、次は脳波計にステップアップかな?<終>
[ DIYに戻る ]