前言
接触TGAM脑电波模块是在年初的时候。刚好临近毕业,指导老师找到了我,想让我加入他负责的毕业课题。在此之前有用过Tensorflow做过一个菜品识别的小程序,当时第一步的构思就是收集大量的脑电波数据,用TF训练机器学习模型,但是后来因为考HCIE跟公司项目的事情耽搁了几个月,又临近了毕业答辩,就只是简单的利用了TGAM模块收集到的专注值跟放松值对灯泡进行了一个简单的控制。
前期准备
网上收集TGAM模块的资料
网上介绍TGAM的模块的资料实在是太少了,找了挺久的,找到了一个“基于神念TGAM的脑波小车”的项目,链接如下:https://www.cnblogs.com/fangyuanjili/archive/2018/04/01/8687844.html 。这个项目对TGAM模块发送过来的包的讲解非常的详细。接下来就是进行解包操作,还是由于时间问题,解包的步骤是参考这个老哥的,链接如下:https://blog.csdn.net/y786256773/article/details/80167900
了解蓝牙
刚好公司的上一个项目是一个蓝牙控制器的小程序,手里有一些蓝牙模块,了解了一下蓝牙的工作原理。主要是手贱,把模块给弄坏了,用某宝的物品识别竟然找到了相同型号的SPP模块,艰难的手动焊了一个上去,完美修复。(滑稽)
ps:接收端是用的千月的蓝牙适配器(有点想吐槽)
项目架构
理想级架构
现实级架构
不放了,放了丢人
代码讲解
整个项目分:脑电波接收端、树莓派控制端.
脑电波接收端
脑电波接收端用了pyqtgraph库,用于显示脑波数据跟专注值/放松值
//数据显示
class ShowThread(threading.Thread):
def __init__(self, parent=None):
super(ShowThread, self).__init__(parent)
self.is_started = threading.Event()
self.win = pg.GraphicsWindow(title="脑电波")
self.win.resize(1000, 600)
self.win.setWindowTitle('脑电波检测值')
pg.setConfigOptions(antialias=True)
self.p2 = self.win.addPlot(title="专注值(蓝色)/放松值(绿色)")
self.p6 = self.win.addPlot(title="脑电波值")
self.curve = self.p6.plot(pen='y')
self.curve2 = self.p2.plot(pen=(0, 255, 0), name="放松值")
self.curve3 = self.p2.plot(pen=(0, 0, 255), name="专注值")
self.ptr = 0
self.ptr2 = 0
def run(self):
while True:
self.curve.setData(data)
self.curve2.setData(data2)
self.curve3.setData(data3)
self.is_started.wait(timeout=0.2)
//接收eeg数据
class EEGThread(threading.Thread):
def __init__(self, parent=None):
super(EEGThread, self).__init__(parent)
self.ip = "http://192.168.43.10"
self.com = "COM9"
self.bps = 57600
self.vaul = []
self.is_open = False
self.is_close = True
def checkList(self,list,num):
list_num = 0
for i in list:
if i > num:
list_num += 1
return list_num
//检测接收的eeg信号包中的delta值是否持续大于200,此方法是我写的一个粗糙的检测眨眼等生物电流的算法,当监测到之后执行关灯操作
def checkEeg(self):
old_num = 0
delta_num = 0
for old in old_data:
if self.checkList(old,200)>5:
old_num += 1
delta_num =self.checkList(delta_data, 50000)
if old_num > 3 and delta_num > 4:
return True
else:
return False
def run(self):
global data,data2,data3,old_data,delta_data
try:
t = serial.Serial(self.com, self.bps)
b = t.read(3)
requests.get(self.ip + "/gpio", params={"oper": "startr"})
print(str(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())))+"脑电波设备配对中")
while b[0] != 170 or b[1] != 170 \
or b[2] != 4:
b = t.read(3)
if b[0] == b[1] == 170 and b[2] == 4:
print(str(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())))+"配对成功。")
requests.get(self.ip+"/gpio", params={"oper": "startb"})
a = b + t.read(5)
if a[0] == 170 and a[1] == 170 and a[2] == 4 and a[3] == 128 and a[4] == 2:
while 1:
try:
a = t.read(8)
sum = ((0x80 + 0x02 + a[5] + a[6]) ^ 0xffffffff) & 0xff
if a[0] == a[1] == 170 and a[2] == 32:
y = 1
else:
y = 0
if a[0] == 170 and a[1] == 170 and a[2] == 4 and a[3] == 128 and a[4] == 2:
p = 1
else:
p = 0
if sum != a[7] and y != 1 and p != 1:
b = t.read(3)
c = b[0]
d = b[1]
e = b[2]
while c != 170 or d != 170 or e != 4:
c = d
d = e
e = t.read()
if c == (b'\xaa' or 170) and d == (b'\xaa' or 170) and e == b'\x04':
g = t.read(5)
if c == b'\xaa' and d == b'\xaa' and e == b'\x04' and g[0] == 128 and g[1] == 2:
a = t.read(8)
break
if a[0] == 170 and a[1] == 170 and a[2] == 4 and a[3] == 128 and a[4] == 2:
high = a[5]
low = a[6]
rawdata = (high << 8) | low
if rawdata > 32768:
rawdata = rawdata - 65536
sum = ((0x80 + 0x02 + high + low) ^ 0xffffffff) & 0xff
if sum == a[7]:
self.vaul.append(rawdata)
if sum != a[7]:
b = t.read(3)
c = b[0]
d = b[1]
e = b[2]
while c != 170 or d != 170 or e != 4:
c = d
d = e
e = t.read()
if c == b'\xaa' and d == b'\xaa' and e == b'\x04':
g = t.read(5)
if c == b'\xaa' and d == b'\xaa' and e == b'\x04' and g[0] == 128 and g[
1] == 2:
a = t.read(8)
break
if a[0] == a[1] == 170 and a[2] == 32:
c = a + t.read(28)
delta = (c[7] << 16) | (c[8] << 8) | (c[9])
# print(delta)
data = self.vaul
old_data.append(data)
if len(old_data) > 10:
old_data = old_data[-10:]
delta_data.append(delta)
if len(delta_data) > 10:
delta_data = delta_data[-10:]
flag = self.checkEeg()
data2.append(c[32])
if len(data2) > 20:
data2 = data2[-20:]
data3.append(c[34])
if len(data3) > 20:
data3 = data3[-20:]
if self.is_open and flag and not self.is_close:
print(str(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())))+"关闭灯成功")
requests.get(self.ip+"/gpio", params={"oper": "stopall"})
# requests.get(self.ip + "/gpio", params={"oper": "startr"})
self.is_close = True
self.is_open = False
if c[32] > 70 and not self.is_open and self.is_close and not flag:
print(str(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())))+"开启灯成功")
requests.get(self.ip+"/gpio", params={"oper": "startg"})
requests.get(self.ip+"/gpio", params={"oper": "start"})
self.is_open = True
self.is_close = False
self.vaul = []
except Exception as e:
# print(e)
sse =1
except Exception as e:
# print(e)
sse = 1
树莓派控制端
树莓派利用GPIO串口对led灯进行开关操作,led灯泡一头接gnd,一头接入程序设定的串口上,这里我做了指示灯跟照明灯,使用flask框架向外提供url接口
//flask设置的路由
api.add_resource(GPIORoute, '/gpio')
class GPIORoute(Resource):
def get(self):
try:
self.parser = reqparse.RequestParser()
self.parser.add_argument('oper', type=str, help='status: type is str')
self.args = self.parser.parse_args()
self.oper = self.args['oper']
if self.oper == "start":
gpio.jox_start("LED")
elif self.oper == "stop":
gpio.jox_stop("LED")
elif self.oper == "startr":
oper=""
gpio.jox_start("RED")
elif self.oper == "startg":
gpio.jox_start("GREEN")
elif self.oper == "startb":
gpio.jox_start("BULE")
elif self.oper == "stoprgb":
gpio.jox_stop("RGB")
elif self.oper == "stopall":
gpio.jox_stop("LED")
gpio.jox_start("RED")
elif self.oper == "exit":
gpio.jox_exit()
except Exception as e:
print(e)
return "erro"
//控制GPIO串口的线程类,这里使用的是13、19、26串口
class GPIO(Thread):
def __init__(self):
Thread.__init__(self)
self.LED = 15
self.SLEDR,self.SLEDG,self.SLEDB =13,19,26
RPi.GPIO.setmode(RPi.GPIO.BCM)
RPi.GPIO.setup(self.LED, RPi.GPIO.OUT)
self.pwm = RPi.GPIO.PWM(self.LED, 70)
self.pwm.start(0)
RPi.GPIO.setup(self.SLEDR, RPi.GPIO.OUT)
RPi.GPIO.setup(self.SLEDG, RPi.GPIO.OUT)
RPi.GPIO.setup(self.SLEDB, RPi.GPIO.OUT)
self.pwmR = RPi.GPIO.PWM(self.SLEDR, 70)
self.pwmG = RPi.GPIO.PWM(self.SLEDG, 70)
self.pwmB = RPi.GPIO.PWM(self.SLEDB, 70)
self.pwmR.start(0)
self.pwmG.start(0)
self.pwmB.start(0)
def jox_start(self,jox_type):
print(jox_type+":start")
try:
if jox_type == "LED":
self.led_exit = Event()
self.pwm.ChangeDutyCycle(100)
elif jox_type == "RED":
self.pwmR.ChangeDutyCycle(100)
self.pwmG.ChangeDutyCycle(0)
self.pwmB.ChangeDutyCycle(0)
elif jox_type == "GREEN":
self.pwmR.ChangeDutyCycle(0)
self.pwmG.ChangeDutyCycle(100)
self.pwmB.ChangeDutyCycle(0)
elif jox_type == "BULE":
self.pwmR.ChangeDutyCycle(0)
self.pwmG.ChangeDutyCycle(0)
self.pwmB.ChangeDutyCycle(100)
except Exception as e:
print(e)
def jox_stop(self,jox_type):
print(jox_type+":stop")
try:
if jox_type == "LED":
self.pwm.ChangeDutyCycle(0)
elif jox_type =="RGB":
self.pwmR.ChangeDutyCycle(0)
self.pwmG.ChangeDutyCycle(0)
self.pwmB.ChangeDutyCycle(0)
except Exception as e:
print(e)
def jox_exit(self):
try:
RPi.GPIO.cleanup()
except Exception as e:
print(e)
结尾
写得有点菜,欢迎各位大佬指正,github地址:https://github.com/jon-son/jox-egg