C語言的也有,但是是繪圖不方便,就先拿Python寫了,我直接接了一個(gè)串口來解析的數(shù)據(jù),第一部分是電路焊接。
板子的接口定義
這個(gè)就是串口的全套
在這里可以更改波特率
焊盤
這個(gè)板子上面焊接了一塊藍(lán)牙的穿透模塊:
左上的位置和下面的位置都是聯(lián)通的
TTL電平是傳輸級(jí)邏輯(Transistor-Transistor Logic)電路的信號(hào)電平。TTL電路使用雙極性晶體管實(shí)現(xiàn)邏輯功能,所以TTL電平也稱為雙極性邏輯電平。TTL電平有兩種,高電平(HIGH)和低電平(LOW):-
高電平:大于2.4V,通常是5V。這代表邏輯"1"。
低電平:小于0.8V,通常是0V。這代表邏輯"0"。
在TTL電路中,電平在2.4V到0.8V之間是不確定的,屬于無效區(qū)域。所以為了穩(wěn)定和可靠地區(qū)分高低電平,都會(huì)選用遠(yuǎn)離無效區(qū)域的電壓,通常是0V和5V。除了高低電平的幅值之外,TTL電平還有以下一些要點(diǎn):
TTL電平的變化速率不能太快,通常小于1V/ns,否則會(huì)產(chǎn)生噪聲干擾TTL電
TTL輸入電平的閾值約為1.4V,超過該閾值會(huì)被識(shí)別為高電平,低于該閾值會(huì)被識(shí)別為低電平。
TTL輸出電平會(huì)受溫度的影響。通常會(huì)設(shè)計(jì)足夠大的邊緣區(qū)域(2.4V-0.8V)以適應(yīng)溫度變化的影響。
TTL電平不夠穩(wěn)定,輸出電平可能會(huì)發(fā)生assaults,導(dǎo)致接收端誤判電平。常用抑制assaults的方法是上拉或下拉電阻。
由于TTL電路功耗較大,輸出會(huì)有一定的驅(qū)動(dòng)能力,一般可以驅(qū)動(dòng)10個(gè)TTL輸入。
如果需要驅(qū)動(dòng)更多輸入,可以用OpenCollector輸出來提高驅(qū)動(dòng)能力。所以TTL電平是典型的雙極性邏輯電平,使用0V和5V代表低電平和高電平。
腦電采集+TGAM腦電模塊
我不記得上面的文章里面有沒有寫具體的解析協(xié)議,Python也是一樣的就是來解析串口的協(xié)議。
一開始的程序可以寫成這樣
1. EEGThread:這是讀取腦電波設(shè)備數(shù)據(jù)的線程。會(huì)通過串口讀取原始數(shù)據(jù),解析并存儲(chǔ)在data, data2和data3列表中。這些數(shù)據(jù)分別代表腦電波值,放松值和專注值。
這個(gè)就是一個(gè)對(duì)現(xiàn)在來處理程序的前置情況的處理
2.ShowThread:這是顯示數(shù)據(jù)的線程。它會(huì)創(chuàng)建一個(gè)PyQtGrah窗口,包含兩個(gè)plot。第一個(gè)plot顯示腦電波值,第二個(gè)plot同時(shí)顯示放松值(綠色)和專注值(藍(lán)色)。該線程會(huì)不斷從data, data2和data3列表中讀取最新數(shù)據(jù),更新plot的顯示。
3.程序入口:這里會(huì)啟動(dòng)上述兩個(gè)線程,并調(diào)用QtGui.QApplication.instance().exec_()進(jìn)入Qt事件循環(huán)。
4.checkEeg():這是一個(gè)幫助方法,用于檢查腦電波是否異常。通過檢測old_data和delta_data列表中超出閾值的數(shù)據(jù)數(shù)量,判斷是否異常。
5. serial和threading模塊用于串口通信和多線程。
6. pyqtgraph模塊用于完成數(shù)據(jù)的可視化顯示。
這個(gè)程序的工作流程如下:
1. EEGThread線程啟動(dòng),開始讀取串口數(shù)據(jù)。
2. 將解析后的腦電波,放松值和專注值數(shù)據(jù) append 到 data, data2 和 data3 列表。
3. ShowThread線程啟動(dòng),創(chuàng)建PyQtGraph窗口和兩個(gè)plot。
4. ShowThread線程定期從上述3個(gè)列表讀取最新數(shù)據(jù),更新plot的顯示。
5. 主程序進(jìn)入Qt事件循環(huán),ShowThread線程能定期更新顯示。
6. EEGThread線程持續(xù)讀取串口數(shù)據(jù),不斷更新列表內(nèi)容。這樣,通過兩個(gè)線程協(xié)同工作,實(shí)現(xiàn)了從腦電波設(shè)備獲取數(shù)據(jù)并實(shí)時(shí)顯示的功能。PyQtGraph提供了比較簡潔的API來完成數(shù)據(jù)可視化,相比matplotlib更適合這個(gè)實(shí)時(shí)顯示的場景。
這個(gè)方法checkList的參數(shù)是:
- list: 要檢查的列表
- num: 閾值它的功能是:通過遍歷list中的所有值,統(tǒng)計(jì)大于num的元素的數(shù)量,并返回這個(gè)數(shù)量。
舉例:
python a = [1, 3, 2, 5, 4] checkList(a, 3) # 返回2,因?yàn)閍中有兩個(gè)值大于3 b = [1, 2, 1, 2] checkList(b, 3) # 返回0
基本的實(shí)現(xiàn)代碼如下:
python def checkList(self, list, num): count = 0 for i in list: if i > num: count += 1 return count
它定義了一個(gè)count變量,遍歷list中的每個(gè)元素i,如果i大于num,則count加1。遍歷完成后返回count的值,這個(gè)值就是大于num的元素?cái)?shù)量。
這個(gè)方法的作用是提供一個(gè)列表值異常判斷的手段。通過設(shè)置一個(gè)閾值num,可以輕松統(tǒng)計(jì)列表中異常大的元素?cái)?shù)量,從而判斷該列表的值是否異常。
在腦電波數(shù)據(jù)分析中,可以設(shè)置某個(gè)通道數(shù)據(jù)或差分?jǐn)?shù)據(jù)的閾值,通過這個(gè)方法判斷腦電波數(shù)據(jù)是否出現(xiàn)異常的突增,這在腦電波監(jiān)測中比較重要。
這個(gè)方法checkEeg用于判斷腦電波數(shù)據(jù)是否異常。
它的參數(shù)是:
- old_data: 原始腦電波數(shù)據(jù)的歷史記錄,包含多個(gè)數(shù)據(jù)列表
- delta_data: 差分腦電波數(shù)據(jù)的歷史記錄,
包含多個(gè)數(shù)據(jù)它的功能是:
通過checkList方法,統(tǒng)計(jì)old_data中的每個(gè)數(shù)據(jù)列表中大于200的值的數(shù)量。如果有3個(gè)數(shù)據(jù)列表的大于200的值的數(shù)量大于5,則old_num加1。
通過checkList方法,統(tǒng)計(jì)delta_data中的值大于50000的值的數(shù)量,賦值給delta_num。
如果old_num大于3,并且delta_num大于4,則返回True,說明腦電波數(shù)據(jù)異常。否則返回False,數(shù)據(jù)正常。
這實(shí)現(xiàn)了通過檢測原始數(shù)據(jù)和差分?jǐn)?shù)據(jù)的異常值來判斷腦電波數(shù)據(jù)是否異常的目的。異常數(shù)據(jù)可能意味著受試者的腦電波出現(xiàn)突變,需要注意。示例:
python old_data = [[1, 2, 3, 210, 5], [3, 4, 5, 220, 6], [100, 2, 3, 4, 5]] delta_data = [1, 2, 3, 60000, 4, 5] checkEeg(old_data, delta_data) # 返回True,old_num=2,delta_num=1,所以數(shù)據(jù)異常
這個(gè)方法為腦電波數(shù)據(jù)的實(shí)時(shí)監(jiān)測提供了重要的支持。通過定期調(diào)用這個(gè)方法,并檢查其返回值,可以實(shí)時(shí)判斷受試者的腦電波數(shù)據(jù)是否出現(xiàn)異常,有助于及時(shí)發(fā)現(xiàn)問題。
代碼中還可以繼續(xù)優(yōu)化:
可以這樣的來設(shè)計(jì)腦電的數(shù)據(jù)
read這個(gè)函數(shù)是最重要的
那么就是可以變成重要的使用模式
這樣通過將數(shù)據(jù)讀取和解析的過程封裝在EEGData類中,外部程序只需要關(guān)注從該類獲取數(shù)據(jù)并進(jìn)行顯示或其它處理,代碼會(huì)更清晰簡潔。這個(gè)封裝也使得EEGData類具有更高的復(fù)用性,如果有其它需要讀取和解析同樣串口數(shù)據(jù)的場景,可以直接復(fù)用這個(gè)類,而不是重新編寫讀取和解析的過程。
可視化的代碼再第一個(gè)版本的時(shí)候?qū)懙木湍茱@示一個(gè)通道的
我想要更多的通道,那么可以這樣設(shè)計(jì):
這樣通過參數(shù)就可以讓我彈性的獲得腦電可視化的數(shù)據(jù)
這個(gè)類EEGPlot的功能是:
1. 根據(jù)傳入的通道名稱ch_names,初始化一個(gè)圖形窗口和多個(gè)繪圖區(qū)域,每個(gè)區(qū)域?qū)?yīng)顯示一路腦電波數(shù)據(jù)。
2. show_data方法用于刷新所有繪圖區(qū)的顯示,它接收所有的通道數(shù)據(jù),并設(shè)置給相應(yīng)的曲線來更新顯示。
3. start方法啟動(dòng)一個(gè)定時(shí)器timer來周期調(diào)用show_data方法,實(shí)現(xiàn)數(shù)據(jù)的動(dòng)態(tài)顯示。
4. 外部通過采集好的多通道數(shù)據(jù)調(diào)用show_data方法,EEGPlot內(nèi)部會(huì)自動(dòng)將數(shù)據(jù)映射到對(duì)應(yīng)的繪圖區(qū)顯示。
這樣,通過這個(gè)封裝,可以很方便的支持任意數(shù)量的通道,并且內(nèi)部自動(dòng)處理數(shù)據(jù)如何映射到對(duì)應(yīng)通道的顯示,外部只需要將整理好的最新數(shù)據(jù)傳入即可,簡化了多通道數(shù)據(jù)處理與顯示的難度。
在這段代碼中,t = serial.Serial(self.com, self.bps) 這一行打開了串口并初始化了Serial對(duì)象t。之后,代碼會(huì)進(jìn)入一個(gè)死循環(huán),持續(xù)的從串口讀取數(shù)據(jù)。主要的讀取流程如下:
1. 先讀取3個(gè)字節(jié)的數(shù)據(jù)b,用于檢測設(shè)備是否連上。如果b符合要求(b[0] == b[1] == 170 and b[2] == 4),則認(rèn)為已經(jīng)連接上設(shè)備。
成功
2. 然后再讀取5個(gè)字節(jié)的數(shù)據(jù)a。如果a符合要求(a[0] == 170 and a[1] == 170 and a[2] == 4 and a[3] == 128 and a[4] == 2),則繼續(xù)讀取。
3. 讀取8個(gè)字節(jié)的數(shù)據(jù)a,用于獲取實(shí)際的腦電波數(shù)據(jù)。
4. 從a中解析出高8位high和低8位low,構(gòu)成16位的原始數(shù)據(jù)rawdata。rawdata會(huì)存儲(chǔ)在self.vaul列表中。
三和四的代碼太早了,還有一段串口重試。
1. 首先讀取8個(gè)字節(jié)的數(shù)據(jù)a。
2. 計(jì)算a中的第6和第7字節(jié)(數(shù)據(jù)部分)的校驗(yàn)和sum。
3. 判斷a的前3個(gè)字節(jié)是否是170,170,32。如果是,設(shè)置y=1,否則y=0。
4. 判斷a的前5個(gè)字節(jié)是否是170,170,4,128,2。如果是,設(shè)置p=1,否則p=0。
5. 如果sum校驗(yàn)失敗,并且y!=1和p!=1,則繼續(xù)讀取3個(gè)字節(jié)的數(shù)據(jù)b。
6. 從b中解析出c,d,e三個(gè)字節(jié),并循環(huán)讀取直到c=170,d=170和e=4。
7.如果循環(huán)出的c,d,e符合要求,再讀取5個(gè)字節(jié)的數(shù)據(jù)g。如果g符合要求(g[0]=128和g[1]=2),則再次讀取8個(gè)字節(jié)的數(shù)據(jù)a。
8. 這樣重復(fù)3-7步,直到讀取一組校驗(yàn)和sum正確的數(shù)據(jù)a為止。這個(gè)過程是數(shù)據(jù)讀取的重試機(jī)制。由于串口通信可能存在噪聲或幀錯(cuò)誤,導(dǎo)致讀取的數(shù)據(jù)校驗(yàn)失敗。這段代碼實(shí)現(xiàn)了這樣的重試機(jī)制:如果讀取的數(shù)據(jù)a校驗(yàn)失敗,它不會(huì)直接丟棄這組數(shù)據(jù)。
而是繼續(xù)讀取,判斷下一組數(shù)據(jù)b是否為起始幀(170,170,4),如果是則繼續(xù)判斷g是否為頭兩字節(jié)(128,2),如果仍然符合則重新讀取一組完整的數(shù)據(jù)a。這樣通過在校驗(yàn)失敗后繼續(xù)“撿漏”,增加了數(shù)據(jù)正確讀取的幾率。有效避免由于偶爾的通信錯(cuò)誤導(dǎo)致丟失有效數(shù)據(jù)的問題。這在構(gòu)建穩(wěn)定性高的數(shù)據(jù)讀取機(jī)制時(shí)是很有用的方式。尤其是在通信環(huán)境較差的情況下,這段“重試”邏輯可以顯著提高數(shù)據(jù)的正確采集率。
上面的代碼不好看,讓我來重構(gòu)一下:
這樣寫
這樣用
這個(gè)DataRetry類實(shí)現(xiàn)了數(shù)據(jù)讀取的重試邏輯,其功能是:
1. read方法讀取8字節(jié)數(shù)據(jù)a,并校驗(yàn)數(shù)據(jù)校驗(yàn)和。如果失敗,繼續(xù)讀取以判斷是否為起始幀和包頭。如果通過,則重新讀取8字節(jié)數(shù)據(jù)a。
2. 通過這種方式,內(nèi)部實(shí)現(xiàn)了在校驗(yàn)失敗后繼續(xù)讀取從而重新獲取完整數(shù)據(jù)的重試機(jī)制。
3. 使用簡單的try/except,外部程序可以輕松調(diào)用retry.read(),不需要關(guān)心內(nèi)部的重試細(xì)節(jié)。讀取失敗由retry內(nèi)部捕獲并重試,成功則返回?cái)?shù)據(jù)。
4. 通過這個(gè)類,外部可以像讀取正常數(shù)據(jù)一樣簡單調(diào)用,但相比直接讀取,會(huì)顯著提高數(shù)據(jù)正確讀取的幾率,增強(qiáng)程序的健壯性。這個(gè)封裝使復(fù)雜的重試讀取邏輯和外部數(shù)據(jù)讀取接口解耦, outwardly具有簡單讀取的表象, inwardly卻具備重試的能力,體現(xiàn)了很好的封裝思想。
這里才是接上面的代碼繼續(xù)
5. 如果校驗(yàn)和sum校驗(yàn)失敗,會(huì)重新讀取數(shù)據(jù),直到獲取一組正確的數(shù)據(jù)。
6. 如果讀取到的a數(shù)據(jù)第1,2字節(jié)為170,第3字節(jié)為32,則認(rèn)為這是28字節(jié)的數(shù)據(jù)幀c。從c中可以解析出更多的信息,比如放松值和專注值,存儲(chǔ)在data2和data3列表。
7. 每10組數(shù)據(jù),會(huì)對(duì)old_data和delta_data列表中的數(shù)據(jù)進(jìn)行檢查,看是否異常。
8. 最后會(huì)清空self.vaul列表,準(zhǔn)備讀取下一組數(shù)據(jù)。這樣,通過持續(xù)循環(huán)讀取串口數(shù)據(jù),并解析存儲(chǔ)在不同列表中,實(shí)現(xiàn)了對(duì)腦電波原始數(shù)據(jù),放松值和專注值的采集。這些數(shù)據(jù)會(huì)在其他線程ShowThread中讀取和顯示。
這里已經(jīng)重構(gòu)過了
這段代碼的功能是:
1. 讀取8字節(jié)的數(shù)據(jù)a,判斷a的前5字節(jié)是否是170, 170, 4, 128, 2。如果是,繼續(xù)解析a。
2. 從a中取出第5和第6字節(jié)(high和low)。
3. 計(jì)算high<<8 | low得到原始數(shù)據(jù)rawdata。如果rawdata大于32768,減去65536。
4.計(jì)算a的校驗(yàn)和sum,如果等于a的第8字節(jié),則將rawdata添加到列表self.vaul。
5. 如果校驗(yàn)失敗,進(jìn)入重試機(jī)制(省略)。如果讀取失敗,跳出。
6. 判斷a的前3字節(jié)是否是170, 170, 32。如果是,繼續(xù)讀取28字節(jié)的數(shù)據(jù)c。
7. 從c中解析出delta = c[7]<<16 | c[8]<<8 | c[9]。8. 顯示delta。
這個(gè)過程實(shí)現(xiàn)了:
1. 對(duì)讀取的8字節(jié)數(shù)據(jù)a進(jìn)行校驗(yàn),如果通過則解析出原始腦電波采樣數(shù)據(jù)rawdata并添加到列表。
2. 在校驗(yàn)失敗的情況下,進(jìn)行重試讀取以盡量不丟失有效數(shù)據(jù)。
3. 如果a的數(shù)據(jù)標(biāo)識(shí)為170,170,32,則繼續(xù)讀取28字節(jié)數(shù)據(jù)c,并從中解析出放松值或?qū)W⒅礵elta的信息。
4. 顯示解析出的delta值。這個(gè)過程對(duì)應(yīng)了從串口讀取一幀完整的腦電波數(shù)據(jù),解析出原始采樣值rawdata,放松/專注值delta,并進(jìn)行必要的校驗(yàn)和重試機(jī)制來提高數(shù)據(jù)正確率。
external調(diào)用此過程,即可從串口解析和獲取腦電波的采樣數(shù)據(jù)、放松值與專注值,并判斷采集是否正常進(jìn)行。通過定期調(diào)用,可實(shí)現(xiàn)對(duì)整個(gè)采集過程的監(jiān)控。
這個(gè)類EEGData的功能是:
1. read方法讀取8字節(jié)和28字節(jié)的數(shù)據(jù),并進(jìn)行校驗(yàn)與解析。
2. 如果通過校驗(yàn),將解析出的原始數(shù)據(jù)添加到data列表,放松/專注值添加到relax/focus列表。
3. 在校驗(yàn)失敗時(shí),進(jìn)行數(shù)據(jù)重試讀取。讀取錯(cuò)誤時(shí),打印錯(cuò)誤信息。
4. 提供data,relax和focus三個(gè)列表,分別存儲(chǔ)原始數(shù)據(jù),放松值和專注值信息。5. get_sum方法用于計(jì)算校驗(yàn)和。
ser = serial.Serial('COM11', 57600) # 打開串口 eeg = EEGData(ser) # 創(chuàng)建EEGData對(duì)象 while True: eeg.read() # 讀取并解析一組數(shù)據(jù) # 使用eeg.data, eeg.relax和eeg.focus訪問解析出的數(shù)據(jù) ...
使用代碼
1. EEGData類實(shí)現(xiàn)數(shù)據(jù)解析和采集,提供eeg.data, eeg.relax和eeg.focus三個(gè)列表存取解析后的數(shù)據(jù)。
2.ShowThread類實(shí)現(xiàn)一個(gè)顯示線程,從eeg對(duì)象中獲取最新數(shù)據(jù)并實(shí)時(shí)顯示。3. 主程序從串口read()讀取數(shù)據(jù),并通過eeg對(duì)象解析。
4.show_thread啟動(dòng)一個(gè)定時(shí)器周期調(diào)用show_data方法來顯示最新數(shù)據(jù)。
5. 這樣通過 ShowThread 的實(shí)時(shí)顯示和 EEGData的數(shù)據(jù)解析,實(shí)現(xiàn)了對(duì)整個(gè)腦電波采集過程的監(jiān)視與反饋。
這個(gè)程序通過將數(shù)據(jù)采集、解析和顯示過程解耦到不同線程中的不同對(duì)象,使得整體邏輯清晰且專注。EEGData專注于數(shù)據(jù)解析,ShowThread專注于數(shù)據(jù)顯示,主程序只關(guān)注數(shù)據(jù)讀取本身。這體現(xiàn)了較好的邏輯劃分和職責(zé)分配。
審核編輯:劉清
-
晶體管
+關(guān)注
關(guān)注
77文章
9979瀏覽量
140696 -
C語言
+關(guān)注
關(guān)注
180文章
7630瀏覽量
140450 -
藍(lán)牙模塊
+關(guān)注
關(guān)注
30文章
603瀏覽量
56491 -
python
+關(guān)注
關(guān)注
56文章
4825瀏覽量
86212 -
TTL電平
+關(guān)注
關(guān)注
1文章
116瀏覽量
12289
原文標(biāo)題:TGAM腦電模塊-實(shí)戰(zhàn)應(yīng)用(良好封裝版)
文章出處:【微信號(hào):TT1827652464,微信公眾號(hào):云深之無跡】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評(píng)論請先 登錄
請問ADS1299適用于腦電系統(tǒng)嗎?
【TL6748 DSP申請】腦電放大器
【藍(lán)牙4.1申請】基于藍(lán)牙的頭戴式腦電采集模塊
關(guān)于TGAM(關(guān)于腦電檢測芯片)
【HC-02V1.1藍(lán)牙串口模塊申請】便攜式腦電設(shè)備
腦電溯源定位即腦電逆向問題
高精度低功耗腦電EEG信號(hào)傳感模塊EEGM102簡介
腦電采集+TGAM腦電模塊介紹
TGAM數(shù)據(jù)流格式說明

腦電偽跡系列之腦電偽跡處理與技術(shù)剖析

腦電基礎(chǔ)系列之腦電電極的分類與技術(shù)對(duì)比

評(píng)論