ハイブリッドワーク渦でのメンタルヘルス！ラズパイとANT+無線通信を利用した心拍数センシング

#はじめに
本記事はCiscoの有志による Cisco Systems Japan Advent Calendar 2021 (一枚目) の 15日目として投稿しています。
非常に興味深い内容で様々なトピックが投稿されていますのでぜひご覧下さい！

- 2021年版(一枚目): https://qiita.com/advent-calendar/2021/cisco <<-こちら
- 2021年版(二枚目): https://qiita.com/advent-calendar/2021/cisco2
- 2020年版(一枚目): https://qiita.com/advent-calendar/2020/cisco
- 2020年版(二枚目): https://qiita.com/advent-calendar/2020/cisco2
- 2019年版: https://qiita.com/advent-calendar/2019/cisco
- 2018年版: https://qiita.com/advent-calendar/2018/cisco

#目次
- はじめに
- 構成図
- 準備するもの
- 構築(Step1-3)
- 今後の展望

#はじめに
こんにちは。今回は、「Ciscoの社内ハッカソン」で実際に私たちの作成したプロトタイプについてご説明させていただきます。Ciscoの社内ハッカソン流れとプロトタイプの概要については、[前回の@dnozakiさんの記事](https://qiita.com/dnozaki)をご覧ください。😊
念のため、私たちの取り組んだユースケースと、プロトタイプのデモ動画をご覧いただけますと幸いです。

###ユースケース
**「ハイブリットワーク」** をテーマに掲げ、下記のユースケースに取り組みました。
**ユースケース名：**
**「Remote Work Solution for Employee’s Mental Health.」**
**(リモートワーク環境での精神的ストレスの可視化と緩和)**
具体的には、
**「リモートワーク環境下におけるメンタルストレスを自己認識し、必要に応じて共有することで相互理解と緩和を図る」**
ということをプロトタイプの目標としています。

###サービス実現の流れ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/865731/b4f72bf1-d3d7-532a-7f6a-a56bb020ee39.png)
上記の図に示すように、

1. **データの取得**<<-こちらの部分を説明していきます！😃
1. **データ蓄積と分析**
1. **可視化**
1. **フィードバック（自己理解・共有）**

といったフェーズに分解することができます。
データの取得に関しては、**心拍データ**と**Webexサービスによる自己評価**の2つのデータをCSV形式で蓄積していきます。
そして、集まったデータをサーバに収集し、人が見やすいグラフなどの形式に整形します。
最終的に、アウトプットされた結果を見て、自己理解や他者との共有に役立てるという内容です。

###プロトタイプのデモビデオ
プロトタイプの説明のデモビデオがありますのでそちらを紹介させていただきます。
約3分の短い動画ですが、**プロトタイプのコンポーネントや流れが視覚的にご理解いただけると思います**。

<iframe width="560" height="315" src="https://www.youtube.com/embed/RHwo2KXBlxg" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>


#構成図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2319558/f25f7d4c-2259-af7c-81c5-44a0324304b0.png)

2種類のデータ取得の内、上図の左側の**心拍センサによる心拍データの取得**について説明させていただきます。
**Node-REDとWebex APIを利用したセルフアセスメント**についてはまたの機会に触れさせていただきます。

心拍センサーにて心拍データを取得する際の無線技術としては、**Bluetooth**もしくは、**ANT+**があります。
今回は、**ANT+**技術を利用しプロトタイプを作成しました。
**ANT+**については、こちらの[ANT/ANT+とは](https://k-tai.watch.impress.co.jp/docs/column/keyword/434325.html)という記事をご参照ください。簡単に説明させていただきますと**ANT**は、サイクルコンピュータやナビなどで有名なGARMIN社傘下であるカナダのDynastream Innovations社が開発した技術となります。**ANT**は、2.4GHz 帯を使用し，超低消費電力という点がBluetooth Low Energyと似ていますが、より通信距離が短く、フレキシブルなネットワーク構成（**PAN:Personal Area Network**）が可能という特徴を持っています。**ANT+**では、心拍計や血圧計、歩数計、体重計、自転車に搭載するセンサー向けなど、複数のプロファイルが制定されていて、採用されているプロファイルが同じかつ、ユーザーが指定した機器同士であれば，データのやり取りが可能となっています。スポーツ、ヘルスケア、医療系デバイスのほか、一部スマートフォンでも採用されている技術となります。

#準備するもの
以下のものを準備してください。心拍センサとUSBドングルはAmazonで購入しました。
- **Raspberry Pi 4（Model:B）**※SSDカードや電源などが別途必要です。
- **心拍センサ（ANT+規格対応）**：CYCPLUS心拍計アームバンド 光学式心拍センサー ANT+&Bluetooth4.0対応
- **受信機USBドングル（GARMIN互換）**：AZ4U ANT+ USBドングル GARMIN互換 ZWIFT ミニタイプ

#Step_1
ラズベリーパイの初期セットアップは、こちらの記事が大変わかりやすかったので、[Raspberry Pi 初期設定](https://qiita.com/HeRo/items/c1c30d7267faeb304538)をご参照ください。また、ラズベリーパイでPython（2.X系）の動作する環境を準備してください。また、ラズベリーパイは便利なことにデフォルトでVNC接続に対応しています。**VNC**にて、PC (パソコン) からRaspberry Pi(ラズパイ)にリモート接続し利用されたいということであれば[VNC接続方法](https://www.indoorcorgielec.com/resources/raspberry-pi/raspberry-pi-vnc/)をご参照ください。
まずは、ANT+ドングルをラズベリーパイのUSBポートに接続します。
ラズベリーパイが受信機を認識しているかなどは、
[こちらのQiita記事：Raspberry Piで心拍数を監視して心臓が止まりそうになったらアラート音を鳴らす](https://qiita.com/kswc/items/6e2e7214738e8d30d91a)に具体的な方法が記載されています。
また、Python環境でANT+を利用するためのbaderjさんがフォークしたpython-antをインストールします。
`pip` コマンドでインストールができます。
[GitHubリポジトリ：mvillalba/python-ant](https://github.com/mvillalba/python-ant)


#Step_2
心拍を取得するためのPythonのコードは、以下のコードと記事を参考にしました。
こちらのライブラリとコードが**Python(2.X系)**のものです。
[GitHubリポジトリ：mvillalba/python-ant](https://github.com/mvillalba/python-ant)
[Qiita記事：Raspberry Piで心拍数を監視して心臓が止まりそうになったらアラート音を鳴らす](https://qiita.com/kswc/items/6e2e7214738e8d30d91a)

ANT+規格によって取得されたデータを、CSVファイルを生成・書き込むコードを記述しました。
以下が、コードとなります。

```heartrate_csv.py
import sys
import time
from ant.core import driver, node, event, message, log
from ant.core.constants import CHANNEL_TYPE_TWOWAY_RECEIVE, TIMEOUT_NEVER
import csv
import datetime

startTime = datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
filename = "data"+startTime+".csv"

class HeartRateMonitor(event.EventCallback):

    DEFAULT_SERIAL = "/dev/ttyUSB0"
    DEFAULT_NETKEY = 'B9A521FBBD72C345'.decode('hex')

    def __init__(self, call_func, serial = None, netkey = None):
        self.call_func = call_func
        self.serial = serial or HeartRateMonitor.DEFAULT_SERIAL
        self.netkey = netkey or HeartRateMonitor.DEFAULT_NETKEY
        self.antnode = None
        self.channel = None

    def start(self):
        #print("starting node")
        self._start_antnode()
        self._setup_channel()
        self.channel.registerCallback(self)
        #print("start listening for hr events")

    def stop(self):
        if self.channel:
            self.channel.close()
            self.channel.unassign()
        if self.antnode:
            self.antnode.stop()

    def __enter__(self):
        return self

    def __exit__(self, type_, value, traceback):
        self.stop()

    def _start_antnode(self):
        stick = driver.USB2Driver(self.serial)
        self.antnode = node.Node(stick)
        self.antnode.start()

    def _setup_channel(self):
        key = node.NetworkKey('N:ANT+', self.netkey)
        self.antnode.setNetworkKey(0, key)
        self.channel = self.antnode.getFreeChannel()
        self.channel.name = 'C:HRM'
        self.channel.assign('N:ANT+', CHANNEL_TYPE_TWOWAY_RECEIVE)
        self.channel.setID(120, 0, 0)
        self.channel.setSearchTimeout(TIMEOUT_NEVER)
        self.channel.setPeriod(8070)
        self.channel.setFrequency(57)
        self.channel.open()

    def process(self, msg):
        if isinstance(msg, message.ChannelBroadcastDataMessage):
            #print("heart rate is {}".format(ord(msg.payload[-1])))
            heart_rate = int(ord(msg.payload[-1]))
            self.call_func(heart_rate)

def onDataRecieved(data):
    datatime = datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S.%f")
    print datatime,
    print " " ,
    print data
    f = open(filename,mode="a")
    writer = csv.writer(f)
    writer.writerow([datatime,data])
    f.close()

if __name__ == '__main__':
    hrm = HeartRateMonitor(onDataRecieved)
    print("monitor start")
    hrm.start()
    print("monitor start listening")
    print("save to " + filename)
    #Ctrl + c：データ取得を終了する
    print("press Ctrl-C to stop this script")
    while True:
        try:
            time.sleep(1)
        except KeyboardInterrupt:
            print("monitor stop")
            hrm.stop()
            sys.exit(0)

```

実際に、心拍データの取得は **1秒間に3-4回のインターバル（0.2-0.3秒/回）** で行われました。
ほぼリアルタイムにきちんとデータ取得されました。

#Step_3
Step_2で生成されたCSV形式のファイルをPythonの **Matplotlib** で簡単に描画したものとなります。
**Webex API** と **Node-RED** を連携させたアセスメント評価と相関分析などをしていくことを検討しています。

```data_graph.py
import pandas as pd
import matplotlib.pyplot as plt
#CSVファイルをUTF-8形式で読み込む 'ファイル名を記述'
data = pd.read_csv('ファイル名',encoding = 'UTF8')
#dataを出力
data
data.plot()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2319558/0f190d9c-2447-7a17-0170-a5c76aec2a6f.png)

#今後の展望
今回のプロトタイプでは、データの取得部分を実装しました。今後、取得されたデータをPythonのディープラーニング向けライブラリにはいくつかありますが、Google社が開発した**TensorFlow**というライブラリや、**tableau**といったツールでよりグラフィカルに可視化させることを検討しています。

今回は、生体データの取得をしましたが、[Merakiから湿度・温度など環境情報もセンシングするIoT機器](https://meraki.cisco.com/ja-jp/products/sensors/)から湿度・温度など環境情報もセンシングするIoT機器製品があるので、環境情報もリモートワーク化のメンタルヘルスの指標となるか検討しています。

センシングされたデータをもとに分析を実施し、その後の実際の対処、フィードバックの部分も検討中です。（照明や部屋温度の調整、特定BPMの音楽再生、触覚フィードバックなど）


最後までお読みいただき、ありがとうございました。
それでは、またどこかの記事でお会いするのを楽しみにしています！


#免責事項
本サイトおよび対応するコメントにおいて表明される意見は、投稿者本人の個人的意見であり、シスコの意見ではありません。本サイトの内容は、情報の提供のみを目的として掲載されており、シスコや他の関係者による推奨や表明を目的としたものではありません。各利用者は、本Webサイトへの掲載により、投稿、リンクその他の方法でアップロードした全ての情報の内容に対して全責任を負い、本Web サイトの利用に関するあらゆる責任からシスコを免責することに同意したものとします。