├── Bean ├── __init__.py ├── packet.py ├── myo_config.py ├── myo_utils.py ├── bt.py └── myo.py ├── voice ├── __init__.py └── speech.py ├── getData ├── __init__.py └── getData.py ├── macheLearn ├── __init__.py └── macheLearn.py ├── featureEngineer ├── __init__.py └── featureEngineer.py ├── 1.mat ├── KNN30 ├── test ├── 1.jpg ├── 1.mat ├── 3.PNG ├── 1.xlsx ├── emg.npy ├── imu.npy ├── engery.npy ├── labels.npy ├── result.npy ├── dataSheet.xlsx ├── example.txt └── test1.py ├── venv └── pip-selfcheck.json ├── dataSheet.xlsx ├── __pycache__ ├── bt.cpython-35.pyc ├── myo.cpython-35.pyc ├── packet.cpython-35.pyc ├── myo_config.cpython-35.pyc ├── myo_utils.cpython-35.pyc └── myoAnalysis.cpython-35.pyc ├── readme.md ├── .gitignore ├── main.py └── myoAnalysis.py /Bean/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /voice/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /getData/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /macheLearn/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /featureEngineer/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /1.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shicaiwei123/myoPython/HEAD/1.mat -------------------------------------------------------------------------------- /KNN30: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shicaiwei123/myoPython/HEAD/KNN30 -------------------------------------------------------------------------------- /test/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shicaiwei123/myoPython/HEAD/test/1.jpg -------------------------------------------------------------------------------- /test/1.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shicaiwei123/myoPython/HEAD/test/1.mat -------------------------------------------------------------------------------- /test/3.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shicaiwei123/myoPython/HEAD/test/3.PNG -------------------------------------------------------------------------------- /test/1.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shicaiwei123/myoPython/HEAD/test/1.xlsx -------------------------------------------------------------------------------- /test/emg.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shicaiwei123/myoPython/HEAD/test/emg.npy -------------------------------------------------------------------------------- /test/imu.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shicaiwei123/myoPython/HEAD/test/imu.npy -------------------------------------------------------------------------------- /venv/pip-selfcheck.json: -------------------------------------------------------------------------------- 1 | {"last_check":"2018-02-14T09:39:22Z","pypi_version":"9.0.1"} -------------------------------------------------------------------------------- /dataSheet.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shicaiwei123/myoPython/HEAD/dataSheet.xlsx -------------------------------------------------------------------------------- /test/engery.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shicaiwei123/myoPython/HEAD/test/engery.npy -------------------------------------------------------------------------------- /test/labels.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shicaiwei123/myoPython/HEAD/test/labels.npy -------------------------------------------------------------------------------- /test/result.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shicaiwei123/myoPython/HEAD/test/result.npy -------------------------------------------------------------------------------- /test/dataSheet.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shicaiwei123/myoPython/HEAD/test/dataSheet.xlsx -------------------------------------------------------------------------------- /test/example.txt: -------------------------------------------------------------------------------- 1 | 1,5 2 | 2,3 3 | 3,4 4 | 4,7 5 | 5,4 6 | 6,3 7 | 7,5 8 | 8,7 9 | 9,4 10 | 10,4 -------------------------------------------------------------------------------- /__pycache__/bt.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shicaiwei123/myoPython/HEAD/__pycache__/bt.cpython-35.pyc -------------------------------------------------------------------------------- /__pycache__/myo.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shicaiwei123/myoPython/HEAD/__pycache__/myo.cpython-35.pyc -------------------------------------------------------------------------------- /__pycache__/packet.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shicaiwei123/myoPython/HEAD/__pycache__/packet.cpython-35.pyc -------------------------------------------------------------------------------- /__pycache__/myo_config.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shicaiwei123/myoPython/HEAD/__pycache__/myo_config.cpython-35.pyc -------------------------------------------------------------------------------- /__pycache__/myo_utils.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shicaiwei123/myoPython/HEAD/__pycache__/myo_utils.cpython-35.pyc -------------------------------------------------------------------------------- /__pycache__/myoAnalysis.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shicaiwei123/myoPython/HEAD/__pycache__/myoAnalysis.cpython-35.pyc -------------------------------------------------------------------------------- /Bean/packet.py: -------------------------------------------------------------------------------- 1 | from .myo_utils import multichr, multiord 2 | 3 | 4 | class Packet(object): 5 | def __init__(self, ords): 6 | self.typ = ords[0] 7 | self.cls = ords[2] 8 | self.cmd = ords[3] 9 | self.payload = multichr(ords[4:]) 10 | 11 | def __repr__(self): 12 | return 'Packet(%02X, %02X, %02X, [%s])' % \ 13 | (self.typ, self.cls, self.cmd, 14 | ' '.join('%02X' % b for b in multiord(self.payload))) 15 | -------------------------------------------------------------------------------- /Bean/myo_config.py: -------------------------------------------------------------------------------- 1 | class MyoConfig: 2 | 3 | def __init__(self): 4 | self.emg_enable = False 5 | self.imu_enable = False 6 | self.arm_enable = False 7 | self.emg_raw_enable = False 8 | 9 | def open_all_except_emg_raw(self): 10 | self.emg_enable = True 11 | self.imu_enable = True 12 | self.arm_enable = True 13 | self.emg_raw_enable = False 14 | 15 | def open_all(self): 16 | self.emg_enable = True 17 | self.imu_enable = True 18 | self.arm_enable = True 19 | self.emg_raw_enable = True -------------------------------------------------------------------------------- /Bean/myo_utils.py: -------------------------------------------------------------------------------- 1 | """ 2 | 封装myo的一些常用函数 3 | """ 4 | import struct 5 | import sys 6 | 7 | 8 | def multichr(ords): 9 | """ 10 | 返回多个字符的bytes值 11 | :param ords: 12 | :return: 13 | """ 14 | if sys.version_info[0] >= 3: 15 | return bytes(ords) 16 | else: 17 | return ''.join(map(chr, ords)) 18 | 19 | 20 | def multiord(b): 21 | if sys.version_info[0] >= 3: 22 | return list(b) 23 | else: 24 | return map(ord, b) 25 | 26 | 27 | def pack(fmt, *args): 28 | return struct.pack('<' + fmt, *args) 29 | 30 | 31 | def unpack(fmt, *args): 32 | return struct.unpack('<' + fmt, *args) 33 | 34 | 35 | def text(scr, font, txt, pos, clr=(255, 255, 255)): 36 | scr.blit(font.render(txt, True, clr), pos) 37 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | #目标 2 | 3 | - 基于Myo手环,获取手运动时的imu和emg数据,进行手语识别,将手语翻译成文字, 4 | 并且调用语音合成模块将文字转换成语音。 5 | 6 | #工程介绍 7 | 8 | ##代码架构 9 | 10 | - Myo手环底层驱动的模块 11 | - 数据获取模块 12 | - 机器学习模型训练和测试模块 13 | - 主模块 14 | - 用于代码调试和算法测试的模块 15 | - 算法仿真模块 16 | 17 | ## 驱动模块 18 | 19 | - 存放于Bean包中,包含功能5个文件和一个init文件 20 | - 实现Myo手环的底层驱动 21 | - 提供接口用于选择获取数据 22 | 23 | ## 数据获取模块 24 | 25 | - 存放于getData包中,包含一个功能文件和一个init文件 26 | - 依赖于底层驱动模块 27 | - 实现单次数据的获取和手势分割 28 | - 提供单次数据和一次手势数据的获取接口 29 | 30 | ## 机器学习模块 31 | 32 | - 存放于macheLearn中,包含一个功能文件和一个init文件 33 | - 依赖主模块获取的数据 34 | - 实现机器学习模型搭建和测试 35 | - 提供模型和精度 36 | 37 | ## 主模块 38 | - 直接存放在工程目录下 39 | - 依赖于机器学习模型和和数据获取模型 40 | - 实现数据的存储或识别 41 | - 输出为数据文件或者手势 42 | 43 | ## 测试模块 44 | 45 | - 存放于test文件中,一个功能文件和诸多测试数据 46 | - 根据测试的内容不同,需要不同的输入数据 47 | - 实现细节和算法的功能测试 48 | - 无输出 49 | 50 | ## 算法仿真模块 51 | 52 | - 存放于matla文件夹,包含算法的m文件 53 | - 对数据获取模块有一定依赖 54 | - 实现整个工程的算法仿真 55 | - 无输出 56 | 57 | ## 语音模块 58 | 59 | - 存放于voice包中,一个功能文件和一个init文件 60 | - 输入是文字符号 61 | - 实现语音合成输出 62 | - 输出是语音输出 63 | 64 | ## 特征工程模块 65 | 66 | - 存放与featureEngineer中,包含一个功能文件和init文件 67 | - 与其他模块无交互 68 | - 实现特征遴选,选择恰当的特征组和进行识别 69 | - 输出理论上是最值特征集 70 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### JetBrains template 3 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 4 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 5 | 6 | # User-specific stuff: 7 | .idea/**/workspace.xml 8 | .idea/**/tasks.xml 9 | .idea/dictionaries 10 | 11 | # Sensitive or high-churn files: 12 | .idea/**/dataSources/ 13 | .idea/**/dataSources.ids 14 | .idea/**/dataSources.xml 15 | .idea/**/dataSources.local.xml 16 | .idea/**/sqlDataSources.xml 17 | .idea/**/dynamic.xml 18 | .idea/**/uiDesigner.xml 19 | 20 | # Gradle: 21 | .idea/**/gradle.xml 22 | .idea/**/libraries 23 | 24 | # CMake 25 | cmake-build-debug/ 26 | cmake-build-release/ 27 | 28 | # Mongo Explorer plugin: 29 | .idea/**/mongoSettings.xml 30 | 31 | ## File-based project format: 32 | *.iws 33 | 34 | ## Plugin-specific files: 35 | 36 | # IntelliJ 37 | out/ 38 | 39 | # mpeltonen/sbt-idea plugin 40 | .idea_modules/ 41 | 42 | # JIRA plugin 43 | atlassian-ide-plugin.xml 44 | 45 | # Cursive Clojure plugin 46 | .idea/replstate.xml 47 | 48 | # Crashlytics plugin (for Android Studio and IntelliJ) 49 | com_crashlytics_export_strings.xml 50 | crashlytics.properties 51 | crashlytics-build.properties 52 | fabric.properties 53 | 54 | .idea/ 55 | *.iml 56 | 57 | 58 | -------------------------------------------------------------------------------- /featureEngineer/featureEngineer.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import scipy.io as scio 3 | import myoAnalysis as mAna 4 | 5 | 6 | def dataRead(file): 7 | data=scio.loadmat(file) 8 | w = data['data'] 9 | emgData = w['emgData'] 10 | imuData = w['imuData'] 11 | labels = w['lables'] 12 | len=w['len'] 13 | emgData = emgData[0, 0] 14 | imuData = imuData[0, 0] 15 | labels = labels[0, 0] 16 | len=len[0,0] 17 | row=(len-1)*5-1 18 | row=row[0,0] 19 | emgData = emgData[0:row, :] 20 | imuData = imuData[0:row, :] 21 | #归一化 22 | emgMax=np.max(np.max(emgData)) 23 | imuMax=np.max(np.max(imuData)) 24 | imuMin=np.min(np.min(imuData)) 25 | emgData=(emgData)/emgMax 26 | imuData=(imuData-imuMin)/(imuMax-imuMin) 27 | return emgData,imuData,labels 28 | 29 | 30 | 31 | 32 | features = [] 33 | labels = [] 34 | len = 1296 # 数据总数 35 | #获取特征和标签 36 | 37 | for i in range(1, len): 38 | file = '/home/intel/dataOneFiginer/' + str(i) + '.mat' 39 | emg, imu, label = dataRead(file) 40 | feature = mAna.fetureGet(emg, imu) 41 | features.append(feature) 42 | labels.append([label]) 43 | 44 | 45 | 46 | #特征工程 47 | 48 | # from sklearn.feature_selection import SelectKBest 49 | # from sklearn.feature_selection import chi2 50 | # 51 | # features=np.array(features) 52 | # labels=np.array(labels) 53 | # 54 | # SelectKBest(chi2, k=20).fit_transform(features, labels) 55 | 56 | from sklearn.feature_selection import RFE 57 | from sklearn.linear_model import LogisticRegression 58 | from sklearn.neighbors import KNeighborsClassifier as knn 59 | #递归特征消除法,返回特征选择后的数据 60 | #参数estimator为基模型 61 | #参数n_features_to_select为选择的特征个数 62 | RFE(estimator=knn(), n_features_to_select=20).fit_transform(features, labels) 63 | 64 | -------------------------------------------------------------------------------- /voice/speech.py: -------------------------------------------------------------------------------- 1 | #coding:utf-8 2 | import serial 3 | import binascii 4 | import sys 5 | 6 | class xf_speech(object): 7 | 8 | def __init__(self, port='/dev/ttyS4'): 9 | 10 | #打开端口 11 | #self.ser = serial.Serial(port, bps, byte_size, parity, stop_bit, time_out) 12 | self.ser = serial.Serial(port, 9600, 8, 'N', 1, 3) 13 | #self.ser.open() 14 | 15 | #设置音量 16 | self.ser.write(binascii.a2b_hex('5b7631305d')) 17 | #语音合成函数 18 | def speech_sy(self, data='欢迎使用', code_way='gb2312'): 19 | 20 | 21 | data_str = str(binascii.b2a_hex(data.encode(code_way)))[2:-1] 22 | 23 | length_str = hex(int(len(data_str)/2)+2) 24 | if len(length_str)==3: 25 | n_str = '000'+length_str[2:] 26 | elif len(length_str)==4: 27 | n_str = '00'+length_str[2:] 28 | elif len(length_str)==5: 29 | n_str = '0'+length_str[2:] 30 | else: 31 | n_str = '0000' 32 | 33 | frame_str = 'fd'+n_str+'01'+'00'+data_str 34 | #print(data_str) 35 | self.ser.write(binascii.a2b_hex(frame_str)) 36 | #print(binascii.a2b_hex(frame_str)) 37 | 38 | #停止语音合成 39 | def speech_stop(self): 40 | frame_str = 'fd000102' 41 | self.ser.write(binascii.a2b_hex(frame_str)) 42 | 43 | #暂停语音合成 44 | def speech_pause(self): 45 | frame_str = 'fd000103' 46 | self.ser.write(binascii.a2b_hex(frame_str)) 47 | 48 | #恢复语音合成 49 | def speech_con(self): 50 | frame_str = 'fd000104' 51 | self.ser.write(binascii.a2b_hex(frame_str)) 52 | 53 | #获取当前状态 54 | def get_state(self): 55 | frame_str = 'fd000121' 56 | self.ser.write(binascii.a2b_hex(frame_str)) 57 | info = self.ser.read(2) 58 | print (info) 59 | 60 | #进入省电模式 61 | def power_save(self): 62 | frame_str = 'fd000188' 63 | self.ser.write(binascii.a2b_hex(frame_str)) 64 | 65 | #唤醒 66 | def wake_up(self): 67 | frame_str = 'fd0001ff' 68 | self.ser.write(binascii.a2b_hex(frame_str)) 69 | 70 | #退出 71 | def speech_quit(self): 72 | self.ser.close() 73 | 74 | def speech_test(): 75 | s = xf_speech() 76 | s.speech_sy() 77 | #s.ser.close() 78 | 79 | if __name__=='__main__': 80 | speech_test() 81 | -------------------------------------------------------------------------------- /macheLearn/macheLearn.py: -------------------------------------------------------------------------------- 1 | import scipy.io as scio 2 | import numpy as np 3 | import sklearn 4 | import myoAnalysis as mAna 5 | #mat数据结构 6 | #包含结构体w 7 | #w包含四个数据,emgData imuData len以及 lables 8 | #len是包含了,但是当时统计错误 9 | #nonZeoLabel是非0数组下标,row是非0数据行数 10 | #读取数据 11 | def dataRead(file): 12 | from sklearn import preprocessing as pre 13 | data=scio.loadmat(file) 14 | w = data['data'] 15 | emgData = w['emgData'] 16 | imuData = w['imuData'] 17 | labels = w['lables'] 18 | len=w['len'] 19 | emgData = emgData[0, 0] 20 | imuData = imuData[0, 0] 21 | labels = labels[0, 0] 22 | len=len[0,0] 23 | row=(len-1)*5-1 24 | row=row[0,0] 25 | emgData = emgData[0:row, :] 26 | imuData = imuData[0:row, :] 27 | #归一化 28 | emgMax=np.max(np.max(emgData)) 29 | imuMax=np.max(np.max(imuData)) 30 | imuMin=np.min(np.min(imuData)) 31 | emgData=(emgData)/emgMax 32 | imuData=(imuData-imuMin)/(imuMax-imuMin) 33 | return emgData,imuData,labels 34 | 35 | 36 | def getKNN(trainX,trainY): 37 | from sklearn.neighbors import KNeighborsClassifier as knn 38 | trainX=np.array(trainX) 39 | trainY=np.array(trainY) 40 | model=knn(n_neighbors=30,weights='distance') 41 | model.fit(trainX,trainY.ravel()) 42 | return model 43 | 44 | 45 | def getSVM(trainX,trainY): 46 | from sklearn.svm import SVC 47 | trainX=np.array(trainX) 48 | trainY=np.array(trainY) 49 | model=SVC(kernel='rbf',degree=3) 50 | model.fit(trainX,trainY.ravel()) 51 | return model 52 | 53 | 54 | if __name__ == '__main__': 55 | from sklearn.externals import joblib 56 | #xunlieheceshi 57 | isLearn =False 58 | modelName = 'KNN30' 59 | len = 1295 # 数据总数 60 | if isLearn: 61 | #读并且处理换粗特征值和标签,等待一起训练 62 | features=[] 63 | labels=[] 64 | counter=1 65 | a=[] 66 | for i in range(1,len): 67 | if i%10 ==0: #jimanshici 68 | counter=counter+1 69 | if counter==8: 70 | counter=1 71 | if counter!=1: 72 | a.append(i) 73 | file ='/home/intel/dataOneFiginer/'+str(i)+'.mat' 74 | emg,imu,label=dataRead(file) 75 | feature=mAna.fetureGet(emg,imu) 76 | features.append(feature) 77 | labels.append([label]) 78 | 79 | #训练模型 80 | model=getKNN(features,labels) 81 | joblib.dump(model,modelName) 82 | else: 83 | feature = [] 84 | labels = [] 85 | result=[] 86 | counter = 1 87 | right=1 88 | wrong=1 89 | model=joblib.load(modelName) 90 | a = [] 91 | for i in range(1, len): 92 | if i % 10 == 0: # 集满十次 93 | counter = counter + 1 94 | if counter == 8: 95 | counter = 1 96 | if counter == 1: 97 | # a.append(i) 98 | file ='/home/intel/dataOneFiginer/'+str(i)+'.mat' 99 | emg,imu,label=dataRead(file) 100 | labels.append(label) 101 | feature=mAna.fetureGet(emg,imu) 102 | r=model.predict([feature]) 103 | result.append(r) 104 | #jielunjieguo 105 | if r==label: 106 | right=right+1 107 | else: 108 | wrong=wrong+1 109 | score=right/(right+wrong) 110 | #chucunjieguo 111 | labels=np.array(labels) 112 | result=np.array(result) 113 | # np.save('labels',labels) 114 | # np.save('result',result) 115 | print(score) 116 | # print(a) 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /Bean/bt.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | from .packet import Packet 4 | import serial 5 | # from serial import serial 6 | import threading 7 | from .myo_utils import * 8 | 9 | 10 | class BT(object): 11 | """Implements the non-Myo-specific details of the Bluetooth protocol.""" 12 | 13 | def __init__(self, tty): 14 | self.ser = serial.Serial(port=tty, baudrate=9600, dsrdtr=1) 15 | self.buf = [] 16 | self.lock = threading.Lock() 17 | self.handlers = [] 18 | 19 | ## internal data-handling methods 20 | def recv_packet(self, timeout=None): 21 | t0 = time.time() 22 | self.ser.timeout = None 23 | while timeout is None or time.time() < t0 + timeout: 24 | if timeout is not None: self.ser.timeout = t0 + timeout - time.time() 25 | c = self.ser.read() 26 | if not c: return None 27 | 28 | ret = self.proc_byte(ord(c)) 29 | if ret: 30 | if ret.typ == 0x80: 31 | self.handle_event(ret) 32 | return ret 33 | 34 | def recv_packets(self, timeout=.5): 35 | res = [] 36 | t0 = time.time() 37 | while time.time() < t0 + timeout: 38 | p = self.recv_packet(t0 + timeout - time.time()) 39 | if not p: return res 40 | res.append(p) 41 | return res 42 | 43 | def proc_byte(self, c): 44 | if not self.buf: 45 | if c in [0x00, 0x80, 0x08, 0x88]: 46 | self.buf.append(c) 47 | return None 48 | elif len(self.buf) == 1: 49 | self.buf.append(c) 50 | self.packet_len = 4 + (self.buf[0] & 0x07) + self.buf[1] 51 | return None 52 | else: 53 | self.buf.append(c) 54 | 55 | if self.packet_len and len(self.buf) == self.packet_len: 56 | p = Packet(self.buf) 57 | self.buf = [] 58 | return p 59 | return None 60 | 61 | def handle_event(self, p): 62 | for h in self.handlers: 63 | h(p) 64 | 65 | def add_handler(self, h): 66 | self.handlers.append(h) 67 | 68 | def remove_handler(self, h): 69 | try: 70 | self.handlers.remove(h) 71 | except ValueError: 72 | pass 73 | 74 | def wait_event(self, cls, cmd): 75 | """ 76 | 等待命令的Event响应 77 | :param cls: 命令类别ID 78 | :param cmd: 命令ID 79 | :return: 80 | """ 81 | res = [None] 82 | 83 | def h(p): 84 | if p.cls == cls and p.cmd == cmd: 85 | res[0] = p 86 | 87 | self.add_handler(h) 88 | while res[0] is None: 89 | self.recv_packet() 90 | 91 | self.remove_handler(h) 92 | return res[0] 93 | 94 | ## specific BLE commands 95 | def connect(self, addr): 96 | return self.send_command(6, 3, pack('6sBHHHH', multichr(addr), 0, 6, 6, 64, 0)) 97 | 98 | def get_connections(self): 99 | return self.send_command(0, 6) 100 | 101 | def discover(self): 102 | return self.send_command(6, 2, b'\x01') 103 | 104 | def end_scan(self): 105 | return self.send_command(6, 4) 106 | 107 | def disconnect(self, h): 108 | return self.send_command(3, 0, pack('B', h)) 109 | 110 | def read_attr(self, con, attr): 111 | self.send_command(4, 4, pack('BH', con, attr)) 112 | return self.wait_event(4, 5) 113 | 114 | def write_attr(self, con, attr, val): 115 | # B->unsigned char 116 | # H->unsigned short 117 | self.send_command(4, 5, pack('BHB', con, attr, len(val)) + val) 118 | return self.wait_event(4, 1) 119 | 120 | def send_command(self, cls, cmd, payload=b'', wait_resp=True): 121 | s = pack('4B', 0, len(payload), cls, cmd) + payload 122 | self.ser.write(s) 123 | 124 | while True: 125 | p = self.recv_packet() 126 | 127 | ## no timeout, so p won't be None 128 | if p.typ == 0: return p 129 | 130 | ## not a response: must be an event 131 | self.handle_event(p) -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from getData.getData import * 2 | from myoAnalysis import * 3 | from voice.speech import xf_speech 4 | 5 | #speaker = xf_speech() # 在minnowboard板子上无需设置端口号,默认'/dev/ttyS4' 6 | # speaker = xf_speech('/dev/ttyUSB0') 7 | 8 | 9 | #译码,将识别到的标签翻译成手势含义 10 | # def decode(label): 11 | # 12 | # label=int(label) 13 | # dict=\ 14 | # {#称呼 15 | # 1:'大家',2:'你',3:'我',4:'他',5:'和',6:'同',7:'学',8:'男',9:'女',10:'爸爸', 16 | # 11:'妈妈',12:'爷爷',13:'奶奶',14:'人', 17 | # #时间 18 | # 31:'早上',32:'中午',33:'晚上',34:'年', 19 | # #礼貌用语 20 | # 51:'请',52:'好',53:'谢谢',54:'谢谢',55:'不',56:'用',57:'早上', 21 | # #地点 22 | # 71:'去',72:'在',73:'到',74:'家',75:'火车站',76:'机场',77:'汽车站', 23 | # #交通 24 | # 101:'坐',102:'火车',103:'飞机',104:'公交车',105:'大巴',106:'地铁', 25 | # #证件 26 | # 121:'证',122:'身份',123:'学生', 27 | # #疑问 28 | # 131:'问',132:'什么',133:'多少',134:'哪里', 29 | # #数字 30 | # 141:0,142:1,143:2,144:3,145:4,146:5,147:6,148:7,149:8,150:9, 31 | # 151:10,152:20,153:30,154:40,155:50,156:60,157:70,158:80,159:90, 32 | # 160:100,161:200,162:300,163:400,164:500,165:600,166:700,167:800,168:900, 33 | # #就餐 34 | # 191:'吃',192:'饭',193:'饮料',194:'啤酒',195:'果汁',196:'钱', 35 | # #生活 36 | # 211:'手机',212:'钱包',213:'没有',214:'看见', 37 | # #情绪 38 | # 231:'爱',232:'对不起',233:'高兴',234:'危险',235:'误会',236:'想',237:'不要' 39 | # } 40 | # return dict[label] 41 | 42 | 43 | #isSave取True时时存储数据,取False时时分析数据 44 | if __name__ == '__main__': 45 | 46 | 47 | m = init() 48 | #shifoubaocunshuju 49 | isSave = False 50 | #导入模型 51 | 52 | #如果是存储数据 53 | if isSave: 54 | emgData=[] 55 | imuData=[] 56 | threshold=[] 57 | try: 58 | while True: 59 | emg, imu, emg_raw = getOnceData(m) 60 | emgData.append(emg) 61 | imuData.append(imu) 62 | E=engery(emg) 63 | threshold.append([E]) 64 | if HAVE_PYGAME: 65 | for ev in pygame.event.get(): 66 | if ev.type == QUIT or (ev.type == KEYDOWN and ev.unicode == 'q'): 67 | name='中午' 68 | testXlwt('dataWSC/'+name+'/emgData.xls', emgData) 69 | testXlwt('dataWSC/'+name+'/imuData.xls', imuData) 70 | testXlwt('dataWSC/'+name+'/emgRawData.xls', emg_raw) 71 | testXlwt('dataWSC/'+name+'/thresholdData.xls', threshold) 72 | raise KeyboardInterrupt() 73 | elif ev.type == KEYDOWN: 74 | if K_1 <= ev.key <= K_3: 75 | m.vibrate(ev.key - K_0) 76 | if K_KP1 <= ev.key <= K_KP3: 77 | m.vibrate(ev.key - K_KP0) 78 | except KeyboardInterrupt: 79 | pass 80 | finally: 81 | m.disconnect() 82 | #否则是分析数据 83 | else: 84 | from sklearn.externals import joblib 85 | import threading 86 | import queue 87 | import time 88 | 89 | #导入字典数据,后期译码使用 90 | dataDict=excelToDict('dataSheet.xlsx') 91 | # 预测函数,用于多线程的回调 92 | #isFinsh 是线程锁 93 | isFinish=False 94 | def predict(model, data): 95 | t1=time.time() 96 | global isFinish 97 | global dataDict 98 | result = model.predict(data) 99 | result = int(result) 100 | t2=time.time() 101 | isFinish=True 102 | out = dataDict[result] 103 | # speaker.speech_sy(out) 104 | print(t2-t1) #测试识别时间 105 | print(out) #输出结果 106 | #导入模型 107 | threads = [] 108 | model=joblib.load('KNN30') 109 | emg=[] 110 | imu=[] 111 | fetureCache=queue.Queue(10) 112 | while True: 113 | emg,imu = getGestureData(m) 114 | if emg==10000: 115 | break 116 | #归一化 117 | emgMax = np.max(np.max(emg)) 118 | imuMax = np.max(np.max(imu)) 119 | imuMin = np.min(np.min(imu)) 120 | emg = (emg) / emgMax 121 | imu = (imu - imuMin) / (imuMax - imuMin) 122 | #特征提取 123 | feture=fetureGet(emg,imu) 124 | #数据缓存 125 | fetureCache.put([feture]) 126 | #识别 127 | t1 = threading.Thread(target=predict, args=(model,fetureCache.get(),)) 128 | t1.start() 129 | 130 | -------------------------------------------------------------------------------- /myoAnalysis.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | #一些常用的数据处理的函数 3 | #数据储存 4 | #数据特征提取 5 | 6 | import numpy as np 7 | import math 8 | 9 | def ZCR(data): 10 | # 输入是numpy的一维数组 11 | # 输出是过零率 12 | zcrSum=0 13 | len=np.size(data) 14 | for i in range(len): 15 | if i>=1: 16 | result=np.abs(np.sign(data[i])-np.sign(data[i-1])) 17 | zcrSum=zcrSum+result 18 | return zcrSum 19 | 20 | def fetureGet(emgData,imuData): 21 | #初始参数 22 | frq=50 #频率50Hz 23 | #数据预处理,归一化,无量纲化 24 | #转成数组 25 | emgData=np.array(emgData) 26 | imuData=np.array(imuData) 27 | # emgRow=emgData.size/8 28 | # imuRow=imuData.size/6 29 | accX=imuData[:,0] 30 | accY=imuData[:,1] 31 | accZ=imuData[:,2] 32 | gcoX=imuData[:,3] 33 | gcoY=imuData[:,4] 34 | gcoZ=imuData[:,5] 35 | emg1=emgData[:,0] 36 | emg2 = emgData[:, 1] 37 | emg3 = emgData[:, 2] 38 | emg4 = emgData[:, 3] 39 | emg5 = emgData[:, 4] 40 | emg6 = emgData[:, 5] 41 | emg7 = emgData[:, 6] 42 | emg8 = emgData[:, 7] 43 | acc=np.sqrt(accX**2+accY**2+accZ**2) 44 | 45 | 46 | 47 | #特征提取 48 | # 了解一下各个参数的物理意义呢?这样就可以转换 49 | #是不是某一类的特征多,他就会占据主要地位,就算其他变量很有用,影响也会被消除 50 | #差分 51 | diffAccX=np.diff(accX) 52 | diffAccY=np.diff(accY) 53 | diffAccZ=np.diff(accZ) 54 | gco=np.sqrt(gcoX**2+gcoY**2+gcoZ**2) 55 | diffGcoX=np.diff(gcoX) 56 | diffGcoY=np.diff(gcoY) 57 | diffGcoZ=np.diff(gcoZ) 58 | #均值 59 | meanAccX=np.mean(accX) 60 | meanAccY=np.mean(accY) 61 | meanAccZ=np.mean(accZ) 62 | meanGcoX=np.mean(np.abs(gcoX)) 63 | meanGcoY=np.mean(np.abs(gcoY)) 64 | meanGcoZ=np.mean(np.abs(gcoZ)) 65 | meanDiffAccX=np.mean(np.abs(diffAccX)) 66 | meanDiffAccY = np.mean(np.abs(diffAccY)) 67 | meanDiffAccZ = np.mean(np.abs(diffAccZ)) 68 | meanDiffGcoX=np.mean(np.abs(diffGcoX)) 69 | meanDiffGcoY = np.mean(np.abs(diffGcoY)) 70 | meanDiffGcoZ = np.mean(np.abs(diffGcoZ)) 71 | #均方值 72 | rmsAccX=np.sqrt(np.mean(accX**2)) 73 | rmsAccY=np.sqrt(np.mean(accY**2)) 74 | rmsAccZ=np.sqrt(np.mean(accZ**2)) 75 | rmsAcc=np.sqrt(np.mean(acc**2)) 76 | rmsGcoX=np.sqrt(np.mean(gcoX**2)) 77 | rmsGcoY=np.sqrt(np.mean(gcoY**2)) 78 | rmsGcoZ=np.sqrt(np.mean(gcoZ**2)) 79 | #积分 80 | integralAccX=np.sum(accX)*1/frq 81 | integralAccY=np.sum(accY)*1/frq 82 | integralAccZ=np.sum(accZ)*1/frq 83 | #范围 84 | rangeAccX=np.max(accX)-np.min(accX) 85 | rangeAccY=np.max(accY)-np.min(accY) 86 | rangeGcoX=np.max(gcoX)-np.min(gcoX) 87 | rangeGcoY=np.max(gcoX)-np.min(gcoY) 88 | rangeGcoZ=np.max(gcoX)-np.min(gcoZ) 89 | #过零率 90 | gcoXZCR=ZCR(gcoX) 91 | gcoYZCR=ZCR(gcoY) 92 | gcoZZCR=ZCR(gcoZ) 93 | #均值 94 | meanEmg1=np.mean(emg1) 95 | meanEmg2 = np.mean(emg2) 96 | meanEmg3 = np.mean(emg3) 97 | meanEmg4 = np.mean(emg4) 98 | meanEmg5 = np.mean(emg5) 99 | meanEmg6 = np.mean(emg6) 100 | meanEmg7 = np.mean(emg7) 101 | meanEmg8 = np.mean(emg8) 102 | # 103 | rmsEmg1=np.mean(emg1) 104 | rmsEmg2 = np.sqrt(np.mean(emg2**2)) 105 | rmsEmg3 = np.sqrt(np.mean(emg3**2)) 106 | rmsEmg4 = np.sqrt(np.mean(emg4**2)) 107 | rmsEmg5 = np.sqrt(np.mean(emg5**2)) 108 | rmsEmg6 = np.sqrt(np.mean(emg6**2)) 109 | rmsEmg7 = np.sqrt(np.mean(emg7**2)) 110 | rmsEmg8 = np.sqrt(np.mean(emg8**2)) 111 | feature=[] 112 | feature.append(meanAccX);feature.append(meanAccY);feature.append(meanAccZ) 113 | # feature.append(meanGcoX);feature.append(meanGcoY);feature.append(meanGcoZ) 114 | feature.append(rmsAccX);feature.append(rmsAccY);feature.append(rmsAccZ) 115 | feature.append(rmsGcoX);feature.append(rmsGcoY);feature.append(rmsGcoZ) 116 | feature.append(integralAccX);feature.append(integralAccY);feature.append(integralAccZ) 117 | feature.append(rangeAccX);feature.append(rangeAccY) 118 | feature.append(rangeGcoX);feature.append(rangeGcoY);feature.append(rangeGcoZ) 119 | # feature.append(meanDiffAccX);feature.append(meanDiffAccY);feature.append(meanDiffAccZ) 120 | # feature.append(meanDiffGcoX);feature.append(meanDiffGcoY);feature.append(meanDiffGcoZ) 121 | feature.append(gcoXZCR);feature.append(gcoYZCR);feature.append(gcoZZCR) 122 | feature.append(meanEmg1) 123 | feature.append(meanEmg2) 124 | feature.append(meanEmg3) 125 | feature.append(meanEmg4) 126 | feature.append(meanEmg5) 127 | feature.append(meanEmg6) 128 | feature.append(meanEmg7) 129 | feature.append(meanEmg8) 130 | # feature.append(rmsEmg1) 131 | # feature.append(rmsEmg2) 132 | # feature.append(rmsEmg3) 133 | # feature.append(rmsEmg4) 134 | # feature.append(rmsEmg5) 135 | # feature.append(rmsEmg6) 136 | # feature.append(rmsEmg7) 137 | # feature.append(rmsEmg8) 138 | 139 | 140 | # 141 | # 142 | # feature.append(meanAccX);feature.append(meanAccY);feature.append(meanAccZ) 143 | # # feature.append(meanGcoX);feature.append(meanGcoY);feature.append(meanGcoZ) 144 | # feature.append(rmsAccX);feature.append(rmsAccY);feature.append(rmsAccZ) 145 | # feature.append(rmsGcoX);feature.append(rmsGcoY);feature.append(rmsGcoZ) 146 | # feature.append(integralAccX);feature.append(integralAccY);feature.append(integralAccZ) 147 | # feature.append(rangeAccX);feature.append(rangeAccY) 148 | # feature.append(rangeGcoX);feature.append(rangeGcoY);feature.append(rangeGcoZ) 149 | # # feature.append(meanDiffAccX);feature.append(meanDiffAccY);feature.append(meanDiffAccZ) 150 | # # feature.append(meanDiffGcoX);feature.append(meanDiffGcoY);feature.append(meanDiffGcoZ) 151 | # feature.append(gcoXZCR);feature.append(gcoYZCR);feature.append(gcoZZCR) 152 | # feature.append(meanEmg1) 153 | # feature.append(meanEmg2) 154 | # feature.append(meanEmg3) 155 | # feature.append(meanEmg4) 156 | # feature.append(meanEmg5) 157 | # feature.append(meanEmg6) 158 | # feature.append(meanEmg7) 159 | # feature.append(meanEmg8) 160 | # feature.append(rmsEmg1) 161 | # feature.append(rmsEmg2) 162 | # feature.append(rmsEmg3) 163 | # feature.append(rmsEmg4) 164 | # feature.append(rmsEmg5) 165 | # feature.append(rmsEmg6) 166 | # feature.append(rmsEmg7) 167 | # feature.append(rmsEmg8) 168 | return feature 169 | 170 | 171 | import xlwt 172 | #xlwt只能储存float数据 173 | def testXlwt(file='new.xls', dataArray=[]): 174 | book = xlwt.Workbook() # 创建一个Excel 175 | sheet1 = book.add_sheet('hello') # 在其中创建一个名为hello的sheet 176 | for i in range(len(dataArray)): # 行数 177 | for j in range(len(dataArray[i])): # 列数 178 | sheet1.write(i, j, float(dataArray[i][j])) 179 | book.save(file) # 创建保存文件 180 | 181 | 182 | 183 | 184 | #测试excle文件生成dict且储存 185 | import xlrd 186 | import pickle 187 | #根据名称获取Excel表格中的数据 参数:file:Excel文件路径 colnameindex:表头列名所在行的所以 ,by_name:Sheet1名称 188 | def excelToDict(file,colnameindex=0,by_name=u'Sheet1'): 189 | data = xlrd.open_workbook(file) 190 | table = data.sheet_by_name(by_name) 191 | colnames = table.row_values(colnameindex) 192 | nrows = table.nrows 193 | dict = {} 194 | for rownum in range(0,nrows): 195 | row = table.row_values(rownum) 196 | if row: 197 | keyName = int(row[0]) 198 | value = row[1] 199 | if isinstance(value ,float): 200 | value=int(value) 201 | dict[keyName]=value 202 | return dict 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | -------------------------------------------------------------------------------- /getData/getData.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import xlwt 3 | 4 | import sys 5 | import time 6 | import pygame 7 | from pygame.locals import * 8 | import numpy as np 9 | from myoAnalysis import * 10 | from Bean.myo import MyoRaw 11 | from Bean.myo_config import MyoConfig 12 | 13 | HAVE_PYGAME = True 14 | 15 | global timeBegin 16 | 17 | global arr1, arr2 ,arr1Temp ,arr2Temp #缓存初始数据 18 | dataCache=list(range(1,105)) #缓存5个 19 | #存储一个手势的数据 20 | dataCounter=0 21 | dataFresh =False 22 | isFull = False 23 | 24 | #初始化 25 | arr1=[] 26 | arr2=[] 27 | emg_raw_list = [] 28 | 29 | 30 | # 尝试导入pygame包,如果导入成功则显示emg数据轨迹,如果没有pygame包则不显示 31 | w, h = 1200, 500 32 | scr = pygame.display.set_mode((w, h)) 33 | # scr1 = pygame.display.set_mode((w, h)) 34 | last_vals = None 35 | # 绘图函数,使用pygame绘制emg数据 36 | def plot(scr, vals): 37 | global w,h 38 | DRAW_LINES = True 39 | 40 | global last_vals 41 | if last_vals is None: 42 | last_vals = vals 43 | return 44 | 45 | D = 5 46 | scr.scroll(-D) 47 | scr.fill((0, 0, 0), (w - D, 0, w, h)) 48 | for i, (u, v) in enumerate(zip(last_vals, vals)): 49 | if DRAW_LINES: 50 | pygame.draw.line(scr, (0, 255, 0), 51 | (w - D, int(h / 8 * (i + 1 - u))), 52 | (w, int(h / 8 * (i + 1 - v)))) 53 | pygame.draw.line(scr, (255, 255, 255), 54 | (w - D, int(h / 8 * (i + 1))), 55 | (w, int(h / 8 * (i + 1)))) 56 | else: 57 | c = int(255 * max(0, min(1, v))) 58 | scr.fill((c, c, c), (w - D, i * h / 8, D, (i + 1) * h / 8 - i * h / 8)); 59 | 60 | pygame.display.flip() 61 | last_vals = vals 62 | 63 | 64 | 65 | def proc_emg_raw(emg_raw, times=[]): 66 | global dataFresh 67 | global emg_raw_list 68 | dataFresh = True 69 | t = [1.1] 70 | global emgCount 71 | # if HAVE_PYGAME: 72 | # # update pygame display 73 | # plot(scr, [e / 2000. for e in emg]) 74 | 75 | # print(emg) 76 | 77 | # print frame rate of received data 78 | times.append(time.time()) 79 | if len(times) > 20: 80 | # print((len(times) - 1) / (times[-1] - times[0])) 81 | times.pop(0) 82 | if emg_raw_list[0] > 0: 83 | t1 = (time.time() - timeBegin) 84 | emg_raw_list = list(emg_raw_list) 85 | t[0] = t1 86 | data = t + emg_raw 87 | emg_raw_list = data 88 | 89 | 90 | def proc_emg(emg, times=[]): 91 | global scr2 92 | global dataFresh 93 | global arr1 94 | dataFresh=True 95 | t=[1.1] 96 | global emgCount 97 | # if HAVE_PYGAME: 98 | # # update pygame display 99 | # plot(scr, [e / 2000. for e in emg]) 100 | 101 | # print(emg) 102 | 103 | # print frame rate of received data 104 | times.append(time.time()) 105 | if len(times) > 20: 106 | # print((len(times) - 1) / (times[-1] - times[0])) 107 | times.pop(0) 108 | if emg[0]>0: 109 | t1 = (time.time() - timeBegin) 110 | emg=list(emg) 111 | t[0]=t1 112 | data=t+emg 113 | arr1 = data 114 | 115 | 116 | def imu_proc(a,b,c): 117 | global scr1 118 | global imuCount 119 | global arr2 120 | # imuCount = imuCount + 1 121 | t = [1.1] 122 | # print(a,b,c) 123 | global timeBegin 124 | t1=(time.time()-timeBegin) 125 | # print(t1) 126 | t[0] = t1 127 | # t[0] = int(t1*10000) 128 | a=list(a) 129 | b=list(b) 130 | c=list(c) 131 | data=c 132 | if HAVE_PYGAME: 133 | # # update pygame display 134 | plot(scr, [e / 2000. for e in data]) 135 | c=t+a+b+c 136 | arr2 = c 137 | 138 | def init(): 139 | # 初始化配置,并打开emg数据开关 140 | global timeBegin 141 | config = MyoConfig() 142 | config.emg_enable = True 143 | config.imu_enable = True 144 | config.arm_enable = False 145 | config.emg_raw_enable = True 146 | 147 | # 初始化myo实体 148 | m = MyoRaw(sys.argv[1] if len(sys.argv) >= 2 else None, 149 | config=config) 150 | 151 | # 连接 152 | m.connect() 153 | 154 | # 添加各种数据的回调 155 | m.add_emg_handler(proc_emg) 156 | m.add_imu_handler(imu_proc) 157 | # m.add_emg_handler(lambda emg: print(emg)) 158 | # m.add_imu_handler(lambda a, b, c: print(a, b, c)) 159 | # m.add_arm_handler(lambda arm, xdir: print('arm', arm, 'xdir', xdir)) 160 | # m.add_pose_handler(lambda p: print('pose', p)) 161 | m.add_emg_raw_handler(proc_emg_raw) 162 | timeBegin = time.time() 163 | return m 164 | 165 | #yicishuju 166 | def getOnceData(m): 167 | global arr1 168 | global arr2 169 | global dataCounter 170 | global dataFresh 171 | global emg_raw_list 172 | while True: 173 | m.run(1) 174 | if dataFresh: 175 | emgCache=arr1[1:9] 176 | imuCache=arr2[5:11] 177 | data=arr1[1:9]+arr2[5:11] 178 | emg_raw = emg_raw_list[1:9] 179 | dataFresh =False 180 | emgCache=list(np.array(emgCache)/100) 181 | emgRawCache = list(np.array(emg_raw)/100) 182 | imuCache=list(np.array(imuCache)/20) 183 | 184 | # if HAVE_PYGAME: 185 | # # update pygame display 186 | # plot(scr, [e / 2000. for e in data]) 187 | return emgCache, imuCache, emgRawCache 188 | 189 | 190 | #求emg数据能力用来判断阈值 191 | def engery(emgData): 192 | emgArray = np.array(emgData) 193 | emgArray=emgArray 194 | emgSquare = np.square(emgArray) 195 | emgSum = np.sum(emgSquare) 196 | emgMean=emgSum/5 #在过去的0.1s内 197 | return emgMean 198 | 199 | def gyoEngery(gyoData): 200 | gyoData=np.array(gyoData) 201 | gyoData=gyoData*10 202 | gyoData=gyoData/100 203 | gyoSquare=np.square(gyoData) 204 | gyoSum=np.sum(gyoSquare) 205 | return gyoSum 206 | 207 | threshold=300 208 | engeryData = [] 209 | #在原始数据基础上获取一次手势的数据 210 | #实现分段 211 | # 212 | def getGestureData(m): 213 | global threshold #能量阈值,当能量高于阈值是active状态,低于阈值是quiet状态 214 | #阈值在变化,如果是离散分割,那么第一次阈值大,第二次阈值小,连续分割阈值一样。 215 | #根据实际分割的方式要修改代码中修改阈值的代码 216 | 217 | beginSave=5 #当能量大于这个值,开始记录数据,防止记录平衡时的无效数据 218 | isSave=False #是否记录数据 219 | gyo=[] #缓存gyo数据方便求能量,缓存5次 220 | dataTimes=1 #记录gyo存储的次数 221 | gyoRigthQuiet=0 #记录gyo能量低于阈值的次数 222 | gyoRigthActive=0 #记录gyo能量高于阈值的次数 223 | activeTimes=0 #记录能量峰的次数 224 | ActiveTimes=2 #几次能量峰表示一次手势,2是记录到两次能量峰的时候就表明记录了一次手势数据,修改这个参数可以进行连续和离散的手势分割 225 | GyoRigthQuietTimes=1 #几次低于阈值表示一次能量峰的结束 226 | emgRigthData=[] #缓存数据 227 | imuRightData=[] 228 | global engeryData 229 | while True: 230 | if HAVE_PYGAME: 231 | for ev in pygame.event.get(): 232 | if ev.type == QUIT or (ev.type == KEYDOWN and ev.unicode == 'q'): 233 | np.save('test/engery',np.array(engeryData)) 234 | return 10000,10000 235 | m.disconnect() 236 | break 237 | emgRigthCache ,imuRigthCache,emgRigthRaw= getOnceData(m) 238 | gyo=gyo+imuRigthCache[4:6] 239 | 240 | #分割 241 | if dataTimes<5: 242 | dataTimes=dataTimes+1 243 | 244 | else: 245 | gyoE=gyoEngery(gyo) 246 | # print(gyoE) 247 | gyo=[] 248 | engeryData.append(gyoE) 249 | dataTimes=1 250 | if gyoE>beginSave: 251 | isSave=True 252 | 253 | if isSave: 254 | emgRigthData.append(emgRigthCache) 255 | imuRightData.append(imuRigthCache) 256 | 257 | if gyoE>threshold: 258 | gyoRigthActive=gyoRigthActive+1 259 | gyoRigthQuiet=0 260 | 261 | else: 262 | gyoRigthQuiet=gyoRigthQuiet+1 263 | 264 | if gyoRigthQuiet>GyoRigthQuietTimes-1: 265 | 266 | if gyoRigthActive<2: 267 | 268 | gyoRigthQuiet=0 269 | else: 270 | 271 | gyoRigthQuiet=0 272 | gyoRigthActive=0 273 | activeTimes=activeTimes+1 274 | threshold=50 275 | GyoRigthQuietTimes=2 276 | if activeTimes==ActiveTimes: 277 | isSave=False 278 | print(len(emgRigthData)) 279 | if len(emgRigthData)!=len(imuRightData): #接收到的鞥和imu数据长度不等 280 | print('wrong Data') 281 | #ping一下?? 282 | else: 283 | emgRight=emgRigthData 284 | imuRight=imuRightData 285 | emgRigthData=[] 286 | imuRightData=[] 287 | activeTimes=0 288 | threshold=300 289 | GyoRigthQuietTimes=1 290 | return emgRight,imuRight 291 | # print('ok') 292 | 293 | 294 | #isSave取True时时存储数据,取False时时分析数据 295 | # if __name__ == '__main__': 296 | # 297 | # 298 | # m = init() 299 | # #shifoubaocunshuju 300 | # isSave = False 301 | # #导入模型 302 | # 303 | # #如果是存储数据 304 | # if isSave: 305 | # emgData=[] 306 | # imuData=[] 307 | # threshold=[] 308 | # try: 309 | # while True: 310 | # emg, imu = getOnceData(m) 311 | # emgData.append(emg) 312 | # imuData.append(imu) 313 | # E=engery(emg) 314 | # threshold.append([E]) 315 | # if HAVE_PYGAME: 316 | # for ev in pygame.event.get(): 317 | # if ev.type == QUIT or (ev.type == KEYDOWN and ev.unicode == 'q'): 318 | # testXlwt('emgData.xls', emgData) 319 | # testXlwt('imuData.xls', imuData) 320 | # testXlwt('threshold.xls', threshold) 321 | # raise KeyboardInterrupt() 322 | # elif ev.type == KEYDOWN: 323 | # if K_1 <= ev.key <= K_3: 324 | # m.vibrate(ev.key - K_0) 325 | # if K_KP1 <= ev.key <= K_KP3: 326 | # m.vibrate(ev.key - K_KP0) 327 | # except KeyboardInterrupt: 328 | # pass 329 | # finally: 330 | # m.disconnect() 331 | # #否则是分析数据 332 | # else: 333 | # from sklearn.externals import joblib 334 | # model=joblib.load('KNN') 335 | # emg=[] 336 | # imu=[] 337 | # while True: 338 | # emg,imu = getGestureData(m) 339 | # if emg==10000: 340 | # break 341 | # np.save('emg',emg) 342 | # np.save('imu',imu) 343 | # feture=fetureGet(emg,imu) 344 | # r=model.predict([feture]) 345 | # print(r) -------------------------------------------------------------------------------- /test/test1.py: -------------------------------------------------------------------------------- 1 | #veriso = "" 2 | #description ="" 3 | #author = "shicaiwei" 4 | # -*- coding: -*- 5 | 6 | # import matlab.engine 7 | # 8 | # eng=matlab.engine.start_matlab() 9 | # a=eng.sqrt(4.0) 10 | # print(a) 11 | # eng.quit() 12 | import numpy as np 13 | #测试图像----完成 14 | # import matplotlib.pyplot as plt 15 | # import matplotlib.image as mimg 16 | # a=[1,2,3,4,5] 17 | # img=mimg.imread('1.jpg') 18 | # plt.imshow(img) 19 | # plt.plot(a) 20 | # plt.show() 21 | 22 | # import matplotlib.pyplot as plt 23 | # 24 | # x = [1,2,3,4,5,6,7,8] 25 | # y = [5,2,4,2,1,4,5,2] 26 | # 27 | # plt.scatter(x,y, label='skitscat', color='k', s=25, marker="o") 28 | # 29 | # plt.xlabel('x') 30 | # plt.ylabel('y') 31 | # plt.title('Interesting Graph\nCheck it out') 32 | # plt.legend() 33 | # plt.show() 34 | 35 | 36 | 37 | # 读取mat数据测试 38 | import scipy.io as scio 39 | # data=scio.loadmat('/home/intel/data/1.mat') 40 | # w=data['data'] 41 | # emgData=w['emgData'] 42 | # imuData = w['imuData'] 43 | # labels = w['lables'] 44 | # emgData=emgData[0,0] 45 | # imuData=imuData[0,0] 46 | # labels=labels[0,0] 47 | # nonZeroLable=np.nonzero(emgData[:,0]) 48 | # row=np.size(nonZeroLable) 49 | # emgData=emgData[0:row,:] #此时数据就可以直接使用了。0也去掉了 50 | # imuData=imuData[0:row,:] 51 | # print(labels) 52 | # print(nonZeroLable) 53 | # print(emgData) 54 | # # print(a) 55 | # print(data.keys()) 56 | 57 | 58 | # #测试新数据结构 59 | # emg=np.load('emg.npy') 60 | # imu=np.load('imu.npy') 61 | # emg1=emg[:,1] 62 | # a=np.shape(emg1) 63 | # b=np.shape(imu) 64 | # print(emg1) 65 | # print(b) 66 | 67 | 68 | #数字字符串转测试 69 | # a=1 70 | # b=str(a) 71 | # print(b) 72 | # c=b+'.mat' 73 | # print(c) 74 | 75 | 76 | #测试双线程 77 | # import threading 78 | # from time import ctime,sleep 79 | # 80 | # 81 | # def music(a): 82 | # for i in range(2): 83 | # b=a*2 84 | # print ("I was listening to %d" %(b)) 85 | # sleep(1) 86 | # 87 | # def move(a): 88 | # for i in range(2): 89 | # b=a**2 90 | # print ("I was listening to %d" %(b)) 91 | # sleep(5) 92 | # 93 | # 94 | # 95 | # if __name__ == '__main__': 96 | # a=4 97 | # threads = [] 98 | # t1 = threading.Thread(target=music, args=(a,)) 99 | # threads.append(t1) 100 | # t2 = threading.Thread(target=move, args=(a,)) 101 | # threads.append(t2) 102 | # while True: 103 | # for t in threads: 104 | # t.setDaemon(True) 105 | # t.start() 106 | # 107 | # print("all over %s" %ctime()) 108 | 109 | 110 | # #python队列测试 111 | # import queue 112 | # q=queue.Queue(10) 113 | # for i in range(20): 114 | # q.put(i) 115 | # if i>8: 116 | # print(q.get()) 117 | # while not q.empty(): 118 | # print(q.get()) 119 | 120 | 121 | #字典测试 122 | # dict=\ 123 | # {1:'大家'\ 124 | # ,2:'你'} 125 | # print(dict[1]) 126 | # 127 | # 分割测试 128 | # from getData.getData import * 129 | # m=init() 130 | # active = 1 131 | # quiet = 1 132 | # dataTimes = 1 133 | # emgData = [] 134 | # imuData = [] 135 | # emg = [] # 缓存5次 136 | # gyo=[] 137 | # isactive=False 138 | # isDown=False 139 | # gyoLatter=0 140 | # gyoFormer=0 141 | # segTimes = 0; 142 | # while True: 143 | # if HAVE_PYGAME: 144 | # for ev in pygame.event.get(): 145 | # if ev.type == QUIT or (ev.type == KEYDOWN and ev.unicode == 'q'): 146 | # 147 | # m.disconnect() 148 | # break 149 | # emgCache, imuCache, emgRaw = getOnceData(m) 150 | # # print(emgCache ) 151 | # # print(imuCache) 152 | # emgData.append(emgCache) 153 | # imuData.append(imuCache) 154 | # emg = emg + emgCache 155 | # gyo = gyo+imuCache[4:6] 156 | # 157 | # # 分割 158 | # if dataTimes < 5: 159 | # dataTimes = dataTimes + 1 160 | # 161 | # else: 162 | # gyoE = gyoEngery(gyo) 163 | # gyoFormer=gyoLatter 164 | # gyoLatter=gyoE 165 | # # print(gyoE) 166 | # emgData=[] 167 | # imuData=[] 168 | # gyo=[] 169 | # dataTimes=1 170 | # if gyoE>50: 171 | # isactive=True 172 | # if isactive: 173 | # gyoSub=gyoFormer-gyoLatter 174 | # if gyoSub<0: 175 | # if gyoLatter<200: 176 | # isactive=False 177 | # print(segTimes) 178 | # segTimes+=1 179 | 180 | #matplotlib绘图 181 | 182 | # import numpy as np 183 | # from matplotlib import pyplot as plt 184 | # from matplotlib import animation 185 | # 186 | # # first set up the figure, the axis, and the plot element we want to animate 187 | # fig = plt.figure() 188 | # ax1 = fig.add_subplot(2, 1, 1, xlim=(0, 2), ylim=(-4, 4)) 189 | # ax2 = fig.add_subplot(2, 1, 2, xlim=(0, 2), ylim=(-4, 4)) 190 | # line, = ax1.plot([], [], lw=2) 191 | # line2, = ax2.plot([], [], lw=2) 192 | # 193 | # 194 | # def init(): 195 | # line.set_data([], []) 196 | # line2.set_data([], []) 197 | # return line, line2 198 | # 199 | # 200 | # # animation function. this is called sequentially 201 | # def animate(i): 202 | # x = np.linspace(0, 2, 100) 203 | # y = np.sin(2 * np.pi * (x - 0.01 * i)) 204 | # line.set_data(x, y) 205 | # 206 | # x2 = np.linspace(0, 2, 100) 207 | # y2 = np.cos(2 * np.pi * (x2 - 0.01 * i)) * np.sin(2 * np.pi * (x - 0.01 * i)) 208 | # line2.set_data(x2, y2) 209 | # return line, line2 210 | # 211 | # 212 | # anim1 = animation.FuncAnimation(fig, animate, init_func=init, frames=50, interval=10) 213 | # plt.show() 214 | 215 | 216 | 217 | # 218 | # import numpy as np 219 | # import matplotlib.pyplot as plt 220 | # from matplotlib import animation 221 | # 222 | # # New figure with white background 223 | # fig = plt.figure(figsize=(6,6), facecolor='white') 224 | # 225 | # # New axis over the whole figure, no frame and a 1:1 aspect ratio 226 | # ax = fig.add_axes([0, 0, 1, 1], frameon=False, aspect=1) 227 | # 228 | # # Number of ring 229 | # n = 50 230 | # size_min = 50 231 | # size_max = 50 ** 2 232 | # 233 | # # Ring position 234 | # pos = np.random.uniform(0, 1, (n,2)) 235 | # 236 | # # Ring colors 237 | # color = np.ones((n,4)) * (0,0,0,1) 238 | # # Alpha color channel geos from 0(transparent) to 1(opaque) 239 | # color[:,3] = np.linspace(0, 1, n) 240 | # 241 | # # Ring sizes 242 | # size = np.linspace(size_min, size_max, n) 243 | # 244 | # # Scatter plot 245 | # scat = ax.scatter(pos[:,0], pos[:,1], s=size, lw=0.5, edgecolors=color, facecolors='None') 246 | # 247 | # # Ensure limits are [0,1] and remove ticks 248 | # ax.set_xlim(0, 1), ax.set_xticks([]) 249 | # ax.set_ylim(0, 1), ax.set_yticks([]) 250 | # 251 | # def update(frame): 252 | # global pos, color, size 253 | # 254 | # # Every ring is made more transparnt 255 | # color[:, 3] = np.maximum(0, color[:,3]-1.0/n) 256 | # 257 | # # Each ring is made larger 258 | # size += (size_max - size_min) / n 259 | # 260 | # # Reset specific ring 261 | # i = frame % 50 262 | # pos[i] = np.random.uniform(0, 1, 2) 263 | # size[i] = size_min 264 | # color[i, 3] = 1 265 | # 266 | # # Update scatter object 267 | # scat.set_edgecolors(color) 268 | # scat.set_sizes(size) 269 | # scat.set_offsets(pos) 270 | # 271 | # # Return the modified object 272 | # return scat, 273 | # 274 | # anim = animation.FuncAnimation(fig, update, interval=10, blit=True, frames=200) 275 | # plt.show() 276 | # 277 | 278 | 279 | 280 | # from matplotlib import pyplot as plt 281 | # from matplotlib import animation 282 | # import numpy as np 283 | # import seaborn as sns 284 | # sns.set_style("whitegrid") 285 | # 286 | # 287 | # def randn_point(): 288 | # # 产生随机散点图的x和y数据 289 | # x=np.random.randint(1,100,3) 290 | # y=np.random.randint(1,2,3) 291 | # return x,y 292 | # 293 | # # 创建画布,包含2个子图 294 | # fig = plt.figure(figsize=(15, 10)) 295 | # ax1 = fig.add_subplot(2, 1, 1) 296 | # ax2 = fig.add_subplot(2, 1, 2) 297 | # 298 | # # 先绘制初始图形,每个子图包含1个正弦波和三个点的散点图 299 | # x = np.arange(0, 2*np.pi, 0.01) 300 | # 301 | # line1, = ax1.plot(x, np.sin(x)) # 正弦波 302 | # x1,y1=randn_point() 303 | # sca1 = ax1.scatter(x1,y1) # 散点图 304 | # 305 | # line2, = ax2.plot(x, np.cos(x)) # 余弦波 306 | # x2,y2=randn_point() 307 | # sca2 = ax2.scatter(x2,y2) # 散点图 308 | # 309 | # def init(): 310 | # # 构造开始帧函数init 311 | # # 改变y轴数据,x轴不需要改 312 | # line1.set_ydata(np.sin(x)) 313 | # line1.set_ydata(np.cos(x)) 314 | # # 改变散点图数据 315 | # x1, y1 = randn_point() 316 | # x2, y2 = randn_point() 317 | # data1 = [[x,y] for x,y in zip(x1,y1)] 318 | # data2 = [[x, y] for x, y in zip(x2, y2)] 319 | # sca1.set_offsets(data1) # 散点图 320 | # sca2.set_offsets(data2) # 散点图 321 | # label = 'timestep {0}'.format(0) 322 | # ax1.set_xlabel(label) 323 | # return line1,line2,sca1,sca2,ax1 # 注意返回值,我们要更新的就是这些数据 324 | # 325 | # def animate(i): 326 | # # 接着,构造自定义动画函数animate,用来更新每一帧上各个x对应的y坐标值,参数表示第i帧 327 | # # plt.cla() 这个函数很有用,先记着它 328 | # line1.set_ydata(np.sin(x + i/10.0)) 329 | # line2.set_ydata(np.cos(x + i / 10.0)) 330 | # x1, y1 = randn_point() 331 | # x2, y2 = randn_point() 332 | # data1 = [[x,y] for x,y in zip(x1,y1)] 333 | # data2 = [[x, y] for x, y in zip(x2, y2)] 334 | # sca1.set_offsets(data1) # 散点图 335 | # sca2.set_offsets(data2) # 散点图 336 | # label = 'timestep {0}'.format(i) 337 | # ax1.set_xlabel(label) 338 | # return line1,line2,sca1,sca2,ax1 339 | # 340 | # 341 | # # 接下来,我们调用FuncAnimation函数生成动画。参数说明: 342 | # # fig 进行动画绘制的figure 343 | # # func 自定义动画函数,即传入刚定义的函数animate 344 | # # frames 动画长度,一次循环包含的帧数 345 | # # init_func 自定义开始帧,即传入刚定义的函数init 346 | # # interval 更新频率,以ms计 347 | # # blit 选择更新所有点,还是仅更新产生变化的点。应选择True,但mac用户请选择False,否则无法显示动画 348 | # 349 | # ani = animation.FuncAnimation(fig=fig, 350 | # func=animate, 351 | # frames=100, 352 | # init_func=init, 353 | # interval=20, 354 | # blit=True) 355 | # plt.show() 356 | 357 | 358 | 359 | 360 | 361 | 362 | import matplotlib.pyplot as plt 363 | import matplotlib.animation as animation 364 | from matplotlib import style 365 | # 366 | # style.use('fivethirtyeight') 367 | # 368 | # fig = plt.figure() 369 | # ax1 = fig.add_subplot(1,1,1) 370 | # 371 | # def animate(i): 372 | # graph_data = open('example.txt','r').read() 373 | # lines = graph_data.split('\n') 374 | # xs = [] 375 | # ys = [] 376 | # for line in lines: 377 | # if len(line) > 1: 378 | # x, y = line.split(',') 379 | # xs.append(x) 380 | # ys.append(y) 381 | # ax1.clear() 382 | # ax1.plot(xs, ys) 383 | # 384 | # 385 | # ani = animation.FuncAnimation(fig, animate, interval=1000) 386 | # plt.show() 387 | 388 | 389 | 390 | 391 | 392 | 393 | #测试excle文件生成dict 394 | import xlrd 395 | import pickle 396 | #根据名称获取Excel表格中的数据 参数:file:Excel文件路径 colnameindex:表头列名所在行的所以 ,by_name:Sheet1名称 397 | def excleToDict(excleFile,colnameindex=0,by_name=u'Sheet1'): 398 | data = xlrd.open_workbook(excleFile) 399 | table = data.sheet_by_name(by_name) 400 | nrows = table.nrows 401 | colnames = table.row_values(colnameindex) 402 | dict = {} 403 | for rownum in range(0,nrows): 404 | row = table.row_values(rownum) 405 | if rownum==100: 406 | print(rownum) 407 | if row: 408 | keyName = int(row[0]) 409 | value = row[1] 410 | if isinstance(value ,float): 411 | value=int(value) 412 | dict[keyName]=value 413 | return dict 414 | 415 | 416 | 417 | # 测试dit数据储存 418 | # recodes = excleToDict('dataSheet.xlsx') 419 | # a=recodes[2] 420 | # print(a) 421 | # print(q) 422 | # output = open('dataDict.pkl', 'wb') 423 | # pickle.dump(recodes, output) 424 | # output.close() 425 | # pklFile=open('dataDict.pkl','rb') 426 | # dict=pickle.load(pklFile) 427 | # print(dict) 428 | 429 | 430 | 431 | # 能量分析 432 | engery=np.load('engery.npy') 433 | plt.plot(engery) 434 | plt.show() 435 | 436 | -------------------------------------------------------------------------------- /Bean/myo.py: -------------------------------------------------------------------------------- 1 | # coding=utf8 2 | """ 3 | Myo相关定义类 4 | """ 5 | import re 6 | import enum 7 | 8 | import time 9 | 10 | from .bt import BT 11 | from serial.tools.list_ports import comports 12 | from .myo_utils import * 13 | 14 | 15 | class MyoHandler(enum.Enum): 16 | """ 17 | Myo 不同Handle的值和对应的意义 18 | CCC 代表对应数据的控制位Handle 19 | """ 20 | EMG_DATA_HANDLE = 0x27 21 | EMG_CCC_HANDLE = 0x28 22 | IMU_DATA_HANDLE = 0x1C 23 | IMU_CCC_HANDLE = 0x1D 24 | ARM_DATA_HANDLE = 0x23 25 | ARM_CCC_HANDLE = 0x24 26 | COMMAND_INPUT_HANDLE = 0x19 27 | FIRMWARE_HANDLE = 0x17 28 | BATTERY_LEVEL_HANDLE = 0x11 29 | BATTERY_LEVEL_CCC_HANDLE = 0x12 30 | EMG_RAW_DATA_1_HANDLE = 0X2B 31 | EMG_RAW_DATA_1_CCC_HANDLE = 0x2C 32 | EMG_RAW_DATA_2_HANDLE = 0x2E 33 | EMG_RAW_DATA_2_CCC_HANDLE = 0x2F 34 | EMG_RAW_DATA_3_HANDLE = 0x31 35 | EMG_RAW_DATA_3_CCC_HANDLE = 0x32 36 | EMG_RAW_DATA_4_HANDLE = 0x34 37 | EMG_RAW_DATA_4_CCC_HANDLE = 0x35 38 | 39 | 40 | class Arm(enum.Enum): 41 | UNKNOWN = 0 42 | RIGHT = 1 43 | LEFT = 2 44 | 45 | 46 | class XDirection(enum.Enum): 47 | UNKNOWN = 0 48 | X_TOWARD_WRIST = 1 49 | X_TOWARD_ELBOW = 2 50 | 51 | 52 | class Pose(enum.Enum): 53 | REST = 0 54 | FIST = 1 55 | WAVE_IN = 2 56 | WAVE_OUT = 3 57 | FINGERS_SPREAD = 4 58 | THUMB_TO_PINKY = 5 59 | UNKNOWN = 255 60 | 61 | 62 | class MyoRaw(object): 63 | """Implements the Myo-specific communication protocol.""" 64 | 65 | def __init__(self, tty=None, config=None): 66 | """ 67 | :param tty: 串口实例 68 | :param config: Myo配置文件,应传入myo_config实例 69 | """ 70 | if tty is None: 71 | tty = self.detect_tty() 72 | if tty is None: 73 | raise ValueError('Myo dongle not found!') 74 | 75 | self.bt = BT(tty) 76 | self.conn = None 77 | self.config = config 78 | self.emg_handlers = [] 79 | self.imu_handlers = [] 80 | self.arm_handlers = [] 81 | self.pose_handlers = [] 82 | self.emg_raw_handlers = [] 83 | 84 | def detect_tty(self): 85 | """ 86 | 检测tty 87 | :return: 88 | """ 89 | for p in comports(): 90 | if re.search(r'PID=2458:0*1', p[2]): 91 | print('using device:', p[0]) 92 | return p[0] 93 | return None 94 | 95 | def run(self, timeout=None): 96 | self.bt.recv_packet(timeout) 97 | 98 | def write_attr(self, attr, val): 99 | if self.conn is not None: 100 | self.bt.write_attr(self.conn, attr, val) 101 | 102 | def read_attr(self, attr): 103 | if self.conn is None: 104 | return None 105 | return self.bt.read_attr(self.conn, attr) 106 | 107 | def connect(self): 108 | """ 109 | 连接myo 110 | :return: 111 | """ 112 | # 停止之前的扫描和连接 113 | self.bt.end_scan() 114 | self.bt.disconnect(0) 115 | self.bt.disconnect(1) 116 | self.bt.disconnect(2) 117 | 118 | # 开始扫描 119 | print('scanning...') 120 | self.bt.discover() 121 | 122 | while True: 123 | p = self.bt.recv_packet() 124 | print('scan response:', p) 125 | 126 | # Find Myo armband 127 | if p.payload.endswith(b'\x06\x42\x48\x12\x4A\x7F\x2C\x48\x47\xB9\xDE\x04\xA9\x01\x00\x06\xD5'): 128 | address = list(multiord(p.payload[2:8])) 129 | break 130 | self.bt.end_scan() 131 | 132 | # use bt manager to connect 133 | conn_pkt = self.bt.connect(address) 134 | self.conn = multiord(conn_pkt.payload)[-1] 135 | self.bt.wait_event(3, 0) 136 | 137 | v0, v1, v2, v3 = self.get_firmware_version() 138 | 139 | print('firmware version: %d.%d.%d.%d' % (v0, v1, v2, v3)) 140 | 141 | is_old = (v0 == 0) 142 | 143 | if is_old: 144 | # old version 145 | ## don't know what these do; Myo Connect sends them, though we get data 146 | ## fine without them 147 | self.write_attr(0x19, b'\x01\x02\x00\x00') 148 | self.write_attr(0x2f, b'\x01\x00') 149 | self.write_attr(0x2c, b'\x01\x00') 150 | self.write_attr(0x32, b'\x01\x00') 151 | self.write_attr(0x35, b'\x01\x00') 152 | 153 | ## enable EMG data 154 | self.write_attr(0x28, b'\x01\x00') 155 | ## enable IMU data 156 | self.write_attr(0x1d, b'\x01\x00') 157 | 158 | ## Sampling rate of the underlying EMG sensor, capped to 1000. If it's 159 | ## less than 1000, emg_hz is correct. If it is greater, the actual 160 | ## framerate starts dropping inversely. Also, if this is much less than 161 | ## 1000, EMG data becomes slower to respond to changes. In conclusion, 162 | ## 1000 is probably a good value. 163 | C = 1000 164 | emg_hz = 50 165 | ## strength of low-pass filtering of EMG data 166 | emg_smooth = 100 167 | 168 | imu_hz = 50 169 | 170 | ## send sensor parameters, or we don't get any data 171 | self.write_attr(0x19, pack('BBBBHBBBBB', 2, 9, 2, 1, C, emg_smooth, C // emg_hz, imu_hz, 0, 0)) 172 | else: 173 | print('device name: %s' % self.get_name()) 174 | self.config_myo(self.config) 175 | 176 | ## add data handlers 177 | def data_handler(p): 178 | # check whether is the command response packet 179 | if (p.cls, p.cmd) != (4, 5): return 180 | 181 | # attr is the handle value 182 | c, attr, typ = unpack('BHB', p.payload[:4]) 183 | pay = p.payload[5:] 184 | 185 | if attr in (0x2B, 0x2E, 0x31, 0x34): 186 | # raw data 0 1 187 | print(attr) 188 | emg_raw_data = unpack('16B', pay) 189 | self.on_emg_raw(emg_raw_data[:8]) 190 | self.on_emg_raw(emg_raw_data[8:]) 191 | 192 | elif attr == 0x27: 193 | # emg data 194 | vals = unpack('8HB', pay) 195 | emg = vals[:8] 196 | self.on_emg(emg) 197 | elif attr == 0x1c: 198 | # imu data 199 | vals = unpack('10h', pay) 200 | quat = vals[:4] 201 | acc = vals[4:7] 202 | gyro = vals[7:10] 203 | self.on_imu(quat, acc, gyro) 204 | # elif attr == 0x23: 205 | # # arm data 206 | # typ, val, xdir, _, _, _ = unpack('6B', pay) 207 | # 208 | # if typ == 1: # on arm 209 | # self.on_arm(Arm(val), XDirection(xdir)) 210 | # elif typ == 2: # removed from arm 211 | # self.on_arm(Arm.UNKNOWN, XDirection.UNKNOWN) 212 | # elif typ == 3: # pose 213 | # self.on_pose(Pose(val)) 214 | else: 215 | print('data with unknown attr: %02X %s' % (attr, p)) 216 | 217 | self.bt.add_handler(data_handler) 218 | # self.start_collection() 219 | 220 | def disconnect(self): 221 | if self.conn is not None: 222 | self.bt.disconnect(self.conn) 223 | 224 | def config_myo(self, myo_config): 225 | """ 226 | 如果沒有配置文件则默认开启emg数据通道 227 | :param myo_config: 228 | :return: 229 | """ 230 | if myo_config is None: 231 | self.is_broadcast_data(MyoHandler.EMG_CCC_HANDLE.value, True) 232 | self.is_enable_data(emg_enable=True) 233 | return 234 | 235 | if myo_config.emg_enable: 236 | self.is_broadcast_data(MyoHandler.EMG_CCC_HANDLE.value, True) 237 | elif myo_config.emg_raw_enable: 238 | self.is_broadcast_data(MyoHandler.EMG_RAW_DATA_1_CCC_HANDLE.value, True) 239 | self.is_broadcast_data(MyoHandler.EMG_RAW_DATA_2_CCC_HANDLE.value, True) 240 | self.is_broadcast_data(MyoHandler.EMG_RAW_DATA_3_CCC_HANDLE.value, True) 241 | self.is_broadcast_data(MyoHandler.EMG_RAW_DATA_4_CCC_HANDLE.value, True) 242 | 243 | if myo_config.imu_enable: 244 | self.is_broadcast_data(MyoHandler.IMU_CCC_HANDLE.value, True) 245 | 246 | if myo_config.arm_enable: 247 | # 使能arm数据通知 248 | self.is_broadcast_data(MyoHandler.ARM_CCC_HANDLE.value, True) 249 | 250 | self.is_enable_data(emg_enable=myo_config.emg_enable, 251 | imu_enable=myo_config.imu_enable, 252 | arm_enable=myo_config.arm_enable, 253 | emg_raw_enable=myo_config.emg_raw_enable) 254 | 255 | def start_collection(self): 256 | """Myo Connect sends this sequence (or a reordering) when starting data 257 | collection for v1.0 firmware; this enables raw data but disables arm and 258 | pose notifications. 259 | """ 260 | 261 | self.write_attr(0x19, b'\x09\x01\x01\x00\x00') 262 | 263 | def end_collection(self): 264 | """Myo Connect sends this sequence (or a reordering) when ending data collection 265 | for v1.0 firmware; this reenables arm and pose notifications, but 266 | doesn't disable raw data. 267 | """ 268 | 269 | self.write_attr(0x19, b'\x09\x01\x00\x00\x00') 270 | 271 | def vibrate(self, length): 272 | if length in range(1, 4): 273 | # first byte tells it to vibrate; purpose of second byte is unknown 274 | self.write_attr(0x19, pack('3B', 3, 1, length)) 275 | 276 | def get_firmware_version(self): 277 | fw = self.read_attr(0x17) 278 | _, _, _, _, v0, v1, v2, v3 = unpack('BHBBHHHH', fw.payload) 279 | return v0, v1, v2, v3 280 | 281 | def get_name(self): 282 | return self.read_attr(0x03).payload 283 | 284 | def is_broadcast_data(self, handle, enable): 285 | """ 286 | 使能或关闭数据广播 287 | :param handle: 数据对应的CCC的handle 288 | :param enable: True or False 289 | :return: 290 | """ 291 | if enable: 292 | # 写入命令让Myo广播对应的数据 293 | self.write_attr(handle, b'\x01\x00') 294 | else: 295 | self.write_attr(handle, b'\x00\x00') 296 | 297 | def is_enable_data(self, 298 | emg_enable=False, 299 | imu_enable=False, 300 | arm_enable=False, 301 | emg_raw_enable=False): 302 | """ 303 | 打开或关闭数据开关 304 | :param emg_enable: 使能emg数据 305 | :param imu_enable: 使能imu数据 306 | :param arm_enable: 使能arm数据 307 | :param emg_raw_enable: 使能raw数据 308 | :return: 309 | """ 310 | enable_code = b'\x01\x03' 311 | 312 | if emg_enable: 313 | enable_code += b'\x01' 314 | elif emg_raw_enable: 315 | enable_code += b'\x03' 316 | else: 317 | enable_code += b'\x00' 318 | 319 | if imu_enable: 320 | enable_code += b'\x01' 321 | else: 322 | enable_code += b'\x00' 323 | 324 | if arm_enable: 325 | enable_code += b'\x01' 326 | else: 327 | enable_code += b'\x00' 328 | 329 | self.write_attr(0x19, enable_code) 330 | 331 | def add_emg_handler(self, h): 332 | self.emg_handlers.append(h) 333 | 334 | def add_imu_handler(self, h): 335 | self.imu_handlers.append(h) 336 | 337 | def add_pose_handler(self, h): 338 | self.pose_handlers.append(h) 339 | 340 | def add_arm_handler(self, h): 341 | self.arm_handlers.append(h) 342 | 343 | def add_emg_raw_handler(self, h): 344 | self.emg_raw_handlers.append(h) 345 | 346 | def on_emg(self, emg): 347 | for h in self.emg_handlers: 348 | h(emg) 349 | 350 | def on_imu(self, quat, acc, gyro): 351 | for h in self.imu_handlers: 352 | h(quat, acc, gyro) 353 | 354 | def on_pose(self, p): 355 | for h in self.pose_handlers: 356 | h(p) 357 | 358 | def on_arm(self, arm, xdir): 359 | for h in self.arm_handlers: 360 | h(arm, xdir) 361 | 362 | def on_emg_raw(self, data): 363 | # index: emg sensor index 364 | for h in self.emg_raw_handlers: 365 | h(data) 366 | --------------------------------------------------------------------------------