import numpy as np
import serial
import time
import csv
from matplotlib import pyplot
###################################### 変数設定 ######################################
ser=serial.Serial('COM3',9600) ## Serial通信先の設定
a = 100 ## センサ値の移動平均をとる個数
b = 10 ## 差の移動平均をとる個数
############ ↓Arduinoの計測プログラムのサンプリング時間を変更したら変える↓ ###########
######## 注意1:t1 / t3が上の移動平均の個数a,bより小さくならないようにすること #######
######## 注意2:t1,t2,t4,60 > t3 になるようにすること #######
t1 = 2 ## 閾値設定のサンプリング時間 [秒]
t2 = 60 ## 心拍測定のサンプリング時間 [秒]
t3 = 0.01 ## arduinoの心拍センサのサンプリング時間 [秒]
t4 = 30 ## センサの値が0の時に停止するまでの時間設定 [秒]
###################################### 関数一覧 ######################################
def Measurement(ser,t3,t4): ## Arduinoから心拍の計測情報を受け取る関数
t = int(t4 / t3) ## forのrange範囲 = 操作なしで停止する時間 / センサのサンプリング時間 小数点以下いらないのでintで整数化
for i in range(0,10000): ## while文は無限ループが怖いのでfor文で対応 10000は適当 数値が大きければ何でもok
try: ## エラーが出た場合,大抵もう一度読み込めば読めるのでcontinueする
line = ser.readline() ## シリアル通信先の出力を読み込む
V = int(line.strip().decode('utf-8')) ## 出力をutf-8に変換.末端行削除.strなのでintにして計算できるようにした.
except:
continue
if 1 <= V <= 1000 or V >= 3000: ## 値が異常のためやり直し
pass
elif V == 0: ## V = 0は手が離れているため,for文で手が置かれるまでループ
for n in range(0,t):
try:
line = ser.readline() ## シリアル通信先の出力を読み込む
V = int(line.strip().decode('utf-8')) ## 出力をutf-8に変換.末端行削除.strなのでintにして計算できるようにした.
except:
continue
if V >= 3000: ## 手が置かれた瞬間はライトの光量がセンサに返ってくるまで完全に真っ暗になり,センサ値が3000以上になる(しばらくすると1000~2000くらいになる)
break ## 3000以下でbreakする設定すると部屋の光でbreakする可能性あり
V = 0 ## 一定時間計測できなかった場合はV = 0 でセンサ値を返す
if V ==0:
break
else:
pass
else:
break
return V ## センサの計測値を返す(センサ値は電圧)
def threshold_setting(ser,t1,t3,t4,a,b): ## 閾値設定関数
print("閾値設定開始")
V2 = Measurement(ser,t3,t4) ## センサ値読み取り
t = int(t1 / t3) ## for文の繰り返し回数 = 閾値設定時間 / センサのサンプリング時間 intで整数化
stack_V = [V2] ## センサ値の平均を求めるのに使用 (V:Voltage)
stack_D = [0] ## センサ値の差.センサ値の閾値を決める計算に使用 (D:Difference)
for i in range(0,t): ## 心拍のサンプリング時間×t回 = 設定時間
V1 = Measurement(ser,t3,t4) ## センサ値読み取り
D = V2 - V1 ## 一個前のセンサ値 - 現在のセンサ値
if D <= 0: ## 差がマイナスならプラスに直す
D = D * -1
stack_D = np.hstack([stack_D,D]) ## 差をリストに格納
stack_V = np.hstack([stack_V,V1]) ## 測定値をリストに格納
V2 = V1 ## 次のループ用に一個前のセンサ値として現在のセンサ値を登録
start_V,upper_limit,down_limit = threshold_calculating(stack_V,stack_D,a,b) ## 閾値計算関数
V_list = [V2] ## Voltage計測結果のリスト
upper_limit_list = [upper_limit] ## 上限閾値のリスト
down_limit_list = [down_limit] ## 下限閾値のリスト
max_V = max(stack_V) ## 閾値設定中の最大電圧
print("閾値設定終了")
print("______________________________")
print(" ")
print("現在の心拍センサ電圧値: " + str(V2))
print("閾値設定中の最大センサ電圧値" + str(max_V))
print("電圧平均: " + str(start_V))
print("閾値上限: " + str(upper_limit))
print("閾値下限: " + str(down_limit))
print("______________________________")
print(" ")
print("で測定開始します")
print(" ")
return V2,stack_V,stack_D,start_V,upper_limit,down_limit,V_list,upper_limit_list,down_limit_list,max_V
## 返し値:左から一個前のセンサ値,Vの平均,差の平均,上限閾値,下限閾値,センサ値のリスト,上限閾値のリスト,下限閾値のリスト
def threshold_calculating(stack_V,stack_D,a,b): ## 閾値計算関数
move_ave_V = np.convolve(stack_V,np.ones(a)/a,'valid') ## センサ値の移動平均を算出
start_V = move_ave_V[-1] ## センサ値の移動平均のリストの一番最新の値を出力
move_ave_D = np.convolve(stack_D,np.ones(b)/b,'valid') ## 差の移動平均を算出
ave_D = move_ave_D[-1] ## 差の移動平均のリストの一番最新の値を出力
upper_limit = start_V + (ave_D * (2/3)) ## 閾値の上限を設定
down_limit = start_V + (ave_D * (1/3)) ## 閾値の下限を設定
return start_V,upper_limit,down_limit
## ↓開始前に1分計測する関数(BPMを求めるのに,1分間の脈動回数が必要なため)
def start_setting(ser,t3,t4,V2,stack_V,stack_D,start_V,upper_limit,down_limit,V_list,upper_limit_list,down_limit_list):
n = int(60 / t3) ## for文の繰り返し回数 = 60秒 / センサ値のサンプリング時間 intで整数化
c = 0 ## 1秒の脈拍回数の保存用
e = 0 ## 1秒を図るのに使用
t = 0 ## 計測時間の保存用
g = 0 ## 閾値の切替に使用
stack_R = [0] ## 1秒の脈拍回数の保存用リスト
print("1分後に測定開始します。")
for i in range(0,n):
V1 = Measurement(ser,t3,t4) ## センサ値を読み取り
if V1 == 0: ## 一定時間計測できなかった場合,V = 0が出力されるので停止
print("一定時間心拍が読み取れなかったため,中止しました")
break
t = t + t3 ## サンプリング時間を足していき、総計測時間を求める (センサ読み取り関数のエラーしたときなどのcontinueした時間を足すとより正確な計測時間になる)
e = e + t3 ## サンプリング時間を足していき、1秒を求める (同上)
Extreme_Value1 = V1 - V2 ## センサ値の傾きを求める
if g == 0: ## 閾値が下限の時
stack_V = np.hstack([stack_V,float(V1)]) ## 脈動していない時のセンサ値を保存するリストを更新(閾値を求めるのに使用)
if V2 <= down_limit: ## 下限の閾値を下回ったら上限の閾値に移行
g = 1
elif g == 1: ## 閾値が上限の時
if V2 >= upper_limit and Extreme_Value2 >= 0 and Extreme_Value1 <= 0: ## 脈拍したとき
c = c + 1 ## 脈拍回数を1増やす
print(t)
D = V2 - start_V ## 脈拍してない時としてる時のセンサ値の差を求める(閾値の設定に使用)
if D <= 0: ## 差が負の場合,正に直す
D = D * -1
stack_D = np.hstack([stack_D,float(D)]) ## センサ値の差を保存するリストを更新(閾値を求めるのに使用)
g = 0 ## 閾値を下限に移行
elif V1 <= down_limit: ## センサ値が下限以下の時
stack_V = np.hstack([stack_V,float(V1)]) ## 脈動していない時のセンサ値を保存するリストを更新(閾値を求めるのに使用)
if e >= 1: ## 1秒経過したら
stack_R = np.hstack([stack_R,c]) ## 脈拍回数を保存
c = 0 ## 脈拍回数をリセット
e = 0 ## 1秒間の計測をリセット
start_V,upper_limit,down_limit = threshold_calculating(stack_V,stack_D,a,b) ## 閾値を計算
V2 = V1 ## 1回前のセンサ値を保存しておく(センサ値の差を求めるのに使用)
Extreme_Value2 = Extreme_Value1 ## 1回前のセンサ値の傾きを保存(脈拍を計測するのに使用)
if 15.01 >= t >= 15.00: ## 15秒経過
print("15秒経過")
elif 30.01 >= t >= 30.00: ## 30秒経過
print("30秒経過")
elif 45.01 >= t >= 45: ## 45秒経過
print("45秒経過")
print("計測開始")
print(" ")
return V2,stack_V,stack_D,start_V,upper_limit,down_limit,V_list,upper_limit_list,down_limit_list,stack_R
def BPM(ser,t1,t2,t3,t4,a,b): ## BPMの計測関数
t = 0 ## 計測時間の保存用
c = int(t2 / t3) ## for文の繰り返し回数 = 計測時間[秒] / センサ値のサンプリング時間 intで整数化
n = 0 ## 閾値の切替に使用
f = 0 ## 1秒を図るのに使用
g = 0 ## 1秒の脈拍回数の保存用
Extreme_Value2 = 0 ## 1回前のセンサ値の傾きを保存(脈拍を計測するのに使用)
BPMdata = ['time','BPM'] ## BPMの保存用リスト
original = ['time','Voltage','V_average','upper_limit','down_limit'] ## 原信号保存用リスト
## ↓閾値設定関数
V2,stack_V,stack_D,start_V,upper_limit,down_limit,V_list,upper_limit_list,down_limit_list,max_V = threshold_setting(ser,t1,t3,t4,a,b)
## ↓開始前に1分計測する関数(BPMを求めるのに,1分間の脈動回数が必要なため)
V2,stack_V,stack_D,start_V,upper_limit,down_limit,V_list,upper_limit_list,down_limit_list,stack_R = start_setting(ser,t3,t4,V2,stack_V,stack_D,start_V,upper_limit,down_limit,V_list,upper_limit_list,down_limit_list)
for i in range(0,c):
V1 = Measurement(ser,t3,t4) ## センサ値を読み取り
if V1 == 0: ## 一定時間計測できなかった場合,V = 0が出力されるので停止
print("一定時間心拍が読み取れなかったため,中止しました")
break
t = t + t3 ## サンプリング時間を足していき、総計測時間を求める (センサ読み取り関数のエラーしたときなどのcontinueした時間を足すとより正確な計測時間になる)
f = f + t3 ## サンプリング時間を足していき、1秒を求める (同上)
Extreme_Value1 = V1 - V2 ## センサ値の傾きを求める
if n == 0: ## 閾値が下限の時
stack_V = np.hstack([stack_V,float(V1)]) ## 脈動していない時のセンサ値を保存するリストを更新(閾値を求めるのに使用)
if V2 <= down_limit: ## 下限の閾値を下回ったら上限の閾値に移行
n = 1
elif n == 1: ## 閾値が上限の時
if V2 >= upper_limit and Extreme_Value2 >= 0 and Extreme_Value1 <= 0: ## 脈拍したとき
g = g + 1 ## 脈拍回数を1増やす
D = V2 - start_V ## 脈拍してない時としてる時のセンサ値の差を求める(閾値の設定に使用)
if D <= 0: ## 差が負の場合,正に直す
D = D * -1
stack_D = np.hstack([stack_D,float(D)]) ## センサ値の差を保存するリストを更新(閾値を求めるのに使用)
n = 0 ## 閾値を下限に移行
elif V1 <= down_limit: ## センサ値が下限以下の時
stack_V = np.hstack([stack_V,float(V1)]) ## 脈動していない時のセンサ値を保存するリストを更新(閾値を求めるのに使用)
if f >= 1: ## 1秒経過したら
stack_R = np.hstack([stack_R,g]) ## 脈拍回数を保存
BPM = BPM_calculating(stack_R) ## BPMを計算
print(BPM,t,V1)
data2 = [t,BPM] ## BPMをリスト化
BPMdata = np.vstack([BPMdata,data2]) ## リスト化したBPMを保存
f = 0 ## 脈拍回数をリセット
g = 0 ## 1秒間の計測をリセット
start_V,upper_limit,down_limit = threshold_calculating(stack_V,stack_D,a,b) ## 閾値を計算
data1 = [t,V1,start_V,upper_limit,down_limit] ## 原信号をリスト化
original = np.vstack([original,data1]) ## リスト化した原信号を保存
V2 = V1 ## 1回前のセンサ値を保存しておく(センサ値の差を求めるのに使用)
Extreme_Value2 = Extreme_Value1 ## 1回前のセンサ値の傾きを保存(脈拍を計測するのに使用)
ser.close() ## シリアルポートを閉じる
return original,BPMdata ## 原信号とBPMのリストを出力
def BPM_calculating(stack_R):
b = len(stack_R) ## 脈拍回数のリストの要素数を出力
c = 0 ## 脈拍回数を出力した際に使用
d = 0 ## 脈拍回数の総和を求めるのに使用
## 脈拍回数のリストに最初0が入っているため,for文の回数はリストの要素数-1しなくていい
if b >= 60: ## 要素が60以上なら
a = b - 60 ## 要素から60引いた数を求める(for文で最新60秒の脈拍回数の総和を求めたいため)
else:
a = 0 ## 60以下だと引くとマイナスになってしまうため,0 (基本ないはず)
for i in range(a,b): ## 最新60秒間の脈拍回数からBPMを求める
c = stack_R[i] ## 1秒間の脈拍回数を出力
d = d + c ## 60回1秒間の脈拍回数を足せば自動的にBPMになる
return d ## BPMを出力
def savedata(original,BPMdata): ## 結果保存用関数
with open('Original.csv','a') as f: ## csv製作 csvを開く
writer = csv.writer(f,lineterminator='\n') ## /nで改行
writer.writerows(original) ## 複数行の配列の出力 原信号を保存
f.close() ## csvを閉じる
with open('BPM.csv','a') as f: ## csv製作 csvを開く
writer = csv.writer(f,lineterminator='\n') ## /nで改行
writer.writerows(BPMdata) ## 複数行の配列の出力 BPMを保存
f.close() ## csvを閉じる
print("end")
def main(): ## メインループ関数
original,BPMdata = BPM(ser,t1,t2,t3,t4,a,b) ## 計測関数
savedata(original,BPMdata) ## 結果保存用関数
###################################### 実行処理 ######################################
if __name__ == "__main__": ## 他のプログラムにimportしたときに勝手に実行しないためのおまじない
main() ## メインループ関数