com.xrlab.labframe_brainbit 1.1.0 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,237 +1,602 @@
1
- # LabFrame 2023 - BrainBit Plugin
2
-
3
- 此套件為 LabFrame 2023 專用的 BrainBit 設備插件,用於連接與管理 BrainBit 腦波儀。
4
-
5
- > [!NOTE]
6
- > 請再記得多安裝此套件 https://github.com/BrainbitLLC/unity_em_st_artifacts.git#a04238a934b3da0494dd9120a489005277063a1f
7
- > 開發當下此套件最新版(1.0.3)在android平台會有問題
8
-
9
- ## 支援功能
10
- 1. **設備連線管理:** 自動搜尋並手動觸發連接藍牙 BrainBit 設備。
11
- 2. **EEG 腦波數據收集:** 自動收集四個通道 (T3, T4, O1, O2) 的腦波數據。
12
- 3. **即時阻抗檢查:** 確認電極與頭皮的接觸阻抗值是否過高 (> 200,000Ω)。
13
- 4. **多階段資料分流儲存:** 收集期間可動態切換儲存 Tag(依照遊戲階段無縫寫入不同檔案)。
14
- 5. **情緒與光譜分析:** 透過 NeuroSDK `EegEmotionalMath` 即時運算專注 / 放鬆(MindData)與 δ/θ/α/β/γ 五頻段光譜百分比。
15
-
16
- ---
17
-
18
- ## 基本使用方式
19
-
20
- ### 1. 手動觸發設備連線
21
- 預設啟動遊戲時**不會**自動連線,需在適當時間點(例如點擊按鈕或進入準備階段時)透過程式碼手動掃描並連線。
22
- ```csharp
23
- BrainBitManager.Instance.ManualConnect();
24
- ```
25
-
26
- ---
27
-
28
- ### 2. 關於 EEG (持續腦波) 的收集方式
29
-
30
- #### 👉 開始 / 停止收集
31
- 開始收集時,可傳入對應的遊戲階段 Tag:
32
- ```csharp
33
- // "Intro_Phase" 將作為存檔檔名的後綴
34
- BrainBitManager.Instance.StartEEGStream(true, "Intro_Phase");
35
-
36
- // 停止收集
37
- BrainBitManager.Instance.StopEEGStream();
38
- ```
39
-
40
- #### 👉 動態無縫切換儲存階段 (Tag)
41
- 當遊戲進入下一個階段時,你**不需**停止收集,只需修改 Tag
42
- ```csharp
43
- if (BrainBitManager.Instance.IsStreamingEEG)
44
- {
45
- // 下一毫秒收集的封包,就會被寫進 "Tutorial_Phase" 的檔案中
46
- BrainBitManager.Instance.SetEEGTag("Tutorial_Phase");
47
- }
48
- ```
49
-
50
- #### 👉 取得最新的 EEG 值
51
- 若需要在遊戲邏輯中使用即時腦波:
52
- ```csharp
53
- var eegData = BrainBitManager.Instance.GetLatestEEGData();
54
- if (eegData != null)
55
- {
56
- Debug.Log($"T3:{eegData.T3} | T4:{eegData.T4} | O1:{eegData.O1} | O2:{eegData.O2}");
57
- }
58
- ```
59
-
60
- ---
61
-
62
- ### 3. 關於 Impedance (阻抗) 的檢測方式
63
-
64
- > 阻抗 (Impedance) 資料流與 EEG (腦波) 資料流是**獨立**的。通常在實驗或遊戲開始前,先開啟阻抗檢測確認配戴良好,然後停止阻抗檢測,再開始 EEG 腦波收集。
65
-
66
- #### 👉 開啟 / 停止阻抗檢測與修改 Tag (與 EEG 邏輯完全相同)
67
- ```csharp
68
- // 啟動阻抗數據流並儲存至 "Preparation_Impedance"
69
- BrainBitManager.Instance.StartImpedanceStream(true, "Preparation_Impedance");
70
-
71
- // 也可呼叫這個無縫切換 Tag
72
- BrainBitManager.Instance.SetImpedanceTag("Another_Phase_Impedance");
73
-
74
- // 停止阻抗流
75
- BrainBitManager.Instance.StopImpedanceStream();
76
- ```
77
-
78
- #### 👉 判斷各通道阻抗是否過高與取得數值
79
- 可以在 `Update()` 或檢查迴圈中呼叫此段程式碼,一次取得四個通道的數值與警示結果:
80
- ```csharp
81
- void CheckImpedanceStatus()
82
- {
83
- // 確保有資料進來
84
- var data = BrainBitManager.Instance.GetLatestImpedanceData();
85
- if (data == null) return;
86
-
87
- // 直接一次性取得所有數值與 Boolean (各通道大於 200,000 即為 true)
88
- var (t3_val, t3_high, t4_val, t4_high, o1_val, o1_high, o2_val, o2_high) = data.GetImpedanceValues();
89
-
90
- // 判斷是否「整體」阻抗都正常 (< 200,000)
91
- if (data.IsImpedanceGood)
92
- {
93
- Debug.Log("✅ 所有通道阻抗良好!可以開始遊戲/實驗了!");
94
- // 進入下一階段、開啟 EEG 等等...
95
- }
96
- else
97
- {
98
- Debug.LogWarning("❌ 有通道阻抗太高!");
99
-
100
- // 具體顯示是哪個通道有問題
101
- if (t3_high) Debug.LogWarning($"- 左側前額 (T3) 接觸不良,目前數值高達: {t3_val}");
102
- if (t4_high) Debug.LogWarning($"- 右側前額 (T4) 接觸不良,目前數值高達: {t4_val}");
103
- if (o1_high) Debug.LogWarning($"- 左後腦 (O1) 接觸不良,目前數值高達: {o1_val}");
104
- if (o2_high) Debug.LogWarning($"- 右後腦 (O2) 接觸不良,目前數值高達: {o2_val}");
105
- }
106
- }
107
- ```
108
-
109
- ---
110
-
111
- ### 4. 情緒與光譜分析 (MindData / SpectralData)
112
-
113
- 整合 NeuroSDK `EegEmotionalMath`,即時取得受測者的**專注度 / 放鬆度**以及**五頻段光譜百分比**。
114
-
115
- > 情緒處理需要先完成約 6 秒的**校正** (請受測者安靜配戴),校正完成後才會開始輸出有效的 MindData / SpectralData。
116
-
117
- #### 👉 開始 / 停止情緒處理
118
-
119
- ```csharp
120
- // 啟動情緒處理(若 EEG 尚未啟動,會自動開啟;停止時會一併停止該次自動啟動的 EEG)
121
- BrainBitManager.Instance.StartEmotionsProcessing(
122
- autoWriteToLabData: true,
123
- mindTag: "Gameplay_Mind",
124
- spectralTag: "Gameplay_Spectral");
125
-
126
- // 停止情緒處理
127
- BrainBitManager.Instance.StopEmotionsProcessing();
128
- ```
129
-
130
- #### 👉 等待校正完成
131
-
132
- ```csharp
133
- BrainBitManager.Instance.OnCalibrationProgress += pct => Debug.Log($"校正中 {pct}%");
134
- BrainBitManager.Instance.OnCalibrationFinished += () => Debug.Log("校正完成!");
135
-
136
- // 或在輪詢中判斷:
137
- if (BrainBitManager.Instance.IsEmotionsCalibrated)
138
- {
139
- // 此時才能讀到有效的 MindData / SpectralData
140
- }
141
- ```
142
-
143
- 若遊戲中需要重新校正(換受測者、中途摘下又戴回):
144
- ```csharp
145
- BrainBitManager.Instance.RestartCalibration();
146
- ```
147
-
148
- #### 👉 取得最新的專注 / 放鬆值
149
-
150
- ```csharp
151
- void Update()
152
- {
153
- if (!BrainBitManager.Instance.IsEmotionsCalibrated) return;
154
-
155
- var mind = BrainBitManager.Instance.GetLatestMindData();
156
- if (mind == null) return;
157
-
158
- Debug.Log($"專注: {mind.Attention:F1} / 放鬆: {mind.Relaxation:F1}");
159
- // mind.InstAttention / mind.InstRelaxation 為瞬時值(抖動大,僅進階使用)
160
- }
161
- ```
162
-
163
- #### 👉 取得最新的五頻段光譜百分比
164
-
165
- ```csharp
166
- var spec = BrainBitManager.Instance.GetLatestSpectralData();
167
- if (spec != null)
168
- {
169
- Debug.Log($"δ:{spec.Delta:F1} θ:{spec.Theta:F1} α:{spec.Alpha:F1} β:{spec.Beta:F1} γ:{spec.Gamma:F1}");
170
- }
171
- ```
172
-
173
- #### 👉 事件訂閱(event-driven 寫法)
174
-
175
- ```csharp
176
- BrainBitManager.Instance.OnMindDataReceived += mind => { /* 更新 UI */ };
177
- BrainBitManager.Instance.OnSpectralDataReceived += spec => { /* 更新 UI */ };
178
- BrainBitManager.Instance.OnEmotionsArtifact += hasArtifact => { /* 顯示雜訊警告 */ };
179
- ```
180
-
181
- #### 👉 動態無縫切換儲存階段 (Tag)
182
-
183
- ```csharp
184
- // 不用停情緒處理,下一筆資料就會被寫進新 tag
185
- BrainBitManager.Instance.SetMindTag("Boss_Phase_Mind");
186
- BrainBitManager.Instance.SetSpectralTag("Boss_Phase_Spectral");
187
- ```
188
-
189
- #### 👉 可調設定(`BrainBitConfig`)
190
-
191
- | 欄位 | 預設 | 說明 |
192
- |---|---|---|
193
- | `EmotionsCalibrationLength` | `6` | 校正時間(秒)。越長越穩,但受測者需要等更久 |
194
- | `EmotionsMentalEstimation` | `false` | 啟用 Mental Estimation(依實驗類型決定) |
195
- | `EmotionsPrioritySide` | `SideType.NONE` | 優先分析腦側:`NONE` / `LEFT` / `RIGHT` |
196
-
197
- ---
198
-
199
- ### 5. 設備搜尋與多設備選擇機制
200
-
201
- 預設情況下,`BrainBitManager` 會自動過濾周遭的藍牙設備,只尋找型號為 `SensorLEBrainBit` 的腦波儀。
202
-
203
- 如果現場有多台 BrainBit 同時開啟,系統如何決定連哪台?
204
- 你可以在 Unity Inspector 或是程式碼中修改 `BrainBitConfig.AutoSelectBestSignal` 的設定:
205
-
206
- - **`AutoSelectBestSignal = false`(預設):** 先搶先贏。系統會直接連線到藍牙掃描名單上的第一台設備。
207
- - **`AutoSelectBestSignal = true`:** 訊號最強優先。系統會比較所有掃描到的 BrainBit 訊號強度 (RSSI),並自動連線到訊號最強(距離接收器最近)的那台設備。建議在展位或多人環境中開啟此設定。
208
-
209
- ---
210
-
211
- ### 6. 實用除錯工具:獲取設備詳細參數
212
-
213
- 如果需要查看設備底層的詳細資訊(例如:電量、硬體版本、韌體版本、取樣頻率等),可使用內建的解析器 `SensorInfoProvider.cs` 將複雜的底層參數結構化。
214
-
215
- ```csharp
216
- using NeuroSDK;
217
-
218
- void ShowDeviceInfo(BrainBitSensor sensor)
219
- {
220
- // 將龐雜的系統屬性轉換為易讀的 Dictionary
221
- Dictionary<string, string> parameters = SensorInfoProvider.GetBrainBitSensorParameters(sensor);
222
-
223
- foreach (var param in parameters)
224
- {
225
- Debug.Log($"[{param.Key}]: {param.Value}");
226
- // 範例輸出: [BattPower]: 85, [State]: StateInRange, [SamplingFrequency]: 250
227
- }
228
- }
229
- ```
230
-
231
- ---
232
-
233
- ## 平台需求與建置 (Android / iOS)
234
-
235
- 本套件已內附相關處理機制:
236
- - **Android:** 必須包含定位 (`ACCESS_FINE_LOCATION`) 與藍牙及掃描 (`BLUETOOTH_CONNECT`, `BLUETOOTH_SCAN` 等) 權限。相關權限已配置在 `Runtime/Plugins/Android/AndroidManifest.xml` 中,Unity 建置時將自動打包。
237
- - **iOS:** 打包後製腳本 `BrainBitPostProcess.cs` 會自動為 `Info.plist` 加入必要藍牙權限 (`NSBluetoothAlwaysUsageDescription`)。
1
+ # LabFrame BrainBit Package
2
+
3
+ LabFrame BrainBit Package 是給 LabFrame 2023 專案使用的 BrainBit 腦波儀套件。它負責搜尋與連接 BrainBit、讀取 EEG 腦波與阻抗資料,並可把資料自動寫入 LabFrame 的資料系統。套件也整合 NeuroSDK 的情緒與頻譜分析,讓實驗或互動專案可以直接取得專注、放鬆與五頻段腦波比例。
4
+
5
+ 這份文件面向使用者與實驗操作人員,重點是怎麼安裝、怎麼戴、怎麼開始錄資料,以及錄出來的資料代表什麼。
6
+
7
+ ## 支援功能
8
+
9
+ - 搜尋並連接 BrainBit 藍牙低功耗裝置。
10
+ - 讀取 4 通道 EEG:`T3`、`T4`、`O1`、`O2`。
11
+ - 檢查 4 通道阻抗,協助確認電極與頭皮接觸狀態。
12
+ - 依實驗階段用不同 tag 自動寫入 LabFrame 資料。
13
+ - 即時取得專注、放鬆與 `Delta`、`Theta`、`Alpha`、`Beta`、`Gamma` 五頻段百分比。
14
+ - 提供阻抗檢查 UI 控制器與範例場景。
15
+
16
+ ## 設備說明
17
+
18
+ 本套件主要支援實驗室使用的 BrainBit Headband。BrainBit 是 4 通道乾式 EEG 頭帶,透過 Bluetooth LE 連線。官方文件標示 BrainBit 有 `O1`、`O2`、`T3`、`T4` 四個資料通道,每個通道取樣頻率為 250 Hz。
19
+
20
+ | 通道 | 位置 | 常見用途 |
21
+ |---|---|---|
22
+ | `T3` | 左側顳葉區 | 常用於注意、專注相關節律觀察 |
23
+ | `T4` | 右側顳葉區 | 常用於注意、專注相關節律觀察 |
24
+ | `O1` | 左側枕葉區 | 常用於 alpha、放鬆相關節律觀察 |
25
+ | `O2` | 右側枕葉區 | 常用於 alpha、放鬆相關節律觀察 |
26
+
27
+ 配戴時,前額的參考/共用電極要貼在額頭,`T3`、`T4` 靠近左右顳側,`O1`、`O2` 靠近後腦枕側。頭帶要貼合但不需要勒緊;如果阻抗過高,通常代表接觸不穩、頭髮擋住電極、頭帶位置偏移,或裝置電極沒有確實貼到皮膚。
28
+
29
+ 本套件不是醫療診斷工具。EEG、專注、放鬆與頻譜資料適合用於研究、教育、互動體驗與實驗紀錄,不應直接解讀為醫療結論。
30
+
31
+ ## 安裝
32
+
33
+ ### 1. Unity 與 LabFrame
34
+
35
+ - 建議 Unity 版本:`2022.3.62f2`,或同系列 `2022.3` LTS。
36
+ - 需要 LabFrame package:`com.xrlab.labframe`。
37
+ - 本 package 的 `package.json` 已依賴 BrainBit `NeuroSDK2`。
38
+
39
+ ### 2. 安裝情緒/頻譜分析套件
40
+
41
+ package 的情緒與頻譜功能會使用 `SignalMath`,請在 Unity Package Manager 加入下列 Git URL
42
+
43
+ ```text
44
+ https://github.com/BrainbitLLC/unity_em_st_artifacts.git#a04238a934b3da0494dd9120a489005277063a1f
45
+ ```
46
+
47
+ 目前建議使用上面的固定 commit。開發時曾遇過較新的 Android 版本套件相容性問題,因此不要任意升級到最新版,除非你已經重新測試 Android build。
48
+
49
+ ### 3. 平台權限
50
+
51
+ Android 權限已放在:
52
+
53
+ ```text
54
+ Packages/LabFrame_Brainbit/Runtime/Plugins/Android/AndroidManifest.xml
55
+ ```
56
+
57
+ 它包含定位、Bluetooth scan、Bluetooth connect 等權限。實機第一次啟動時仍可能需要使用者允許權限。
58
+
59
+ iOS build 後處理會自動加入藍牙用途文字:
60
+
61
+ ```text
62
+ NSBluetoothAlwaysUsageDescription
63
+ NSBluetoothPeripheralUsageDescription
64
+ ```
65
+
66
+ ## 建議使用流程
67
+
68
+ 1. 開啟 BrainBit,確認電量足夠。
69
+ 2. 在手機、平板或電腦開啟藍牙,並允許 App 使用藍牙與定位權限。
70
+ 3. 戴上 BrainBit,調整前額與左右/後方電極位置。
71
+ 4. 呼叫 `ManualConnect()` 或使用範例 UI 連線。
72
+ 5. 先開啟阻抗檢查,確認 4 個通道接觸穩定。
73
+ 6. 停止阻抗檢查。
74
+ 7. 開始 EEG 錄製,並用 tag 標示實驗階段。
75
+ 8. 若需要專注、放鬆或頻譜資料,啟動情緒處理並等待校正完成。
76
+ 9. 實驗階段切換時,用 `SetEEGTag()`、`SetMindTag()`、`SetSpectralTag()` 切換資料 tag。
77
+ 10. 結束時停止情緒處理、EEG 與阻抗串流,必要時斷線。
78
+
79
+ ## 快速開始
80
+
81
+ ### 連接設備
82
+
83
+ 預設不會在遊戲啟動時自動連線。請在開始畫面、準備階段或使用者按下按鈕時呼叫:
84
+
85
+ ```csharp
86
+ BrainBitManager.Instance.ManualConnect();
87
+ ```
88
+
89
+ 斷線:
90
+
91
+ ```csharp
92
+ BrainBitManager.Instance.ManualDisconnect();
93
+ ```
94
+
95
+ 常用狀態:
96
+
97
+ ```csharp
98
+ bool connected = BrainBitManager.Instance.IsConnected;
99
+ bool scanning = BrainBitManager.Instance.IsScanning;
100
+ string deviceName = BrainBitManager.Instance.ConnectedDeviceName;
101
+ string deviceAddress = BrainBitManager.Instance.ConnectedDeviceAddress;
102
+ ```
103
+
104
+ ### 阻抗檢查
105
+
106
+ 阻抗資料用來確認電極接觸品質。建議在正式錄 EEG 前先檢查阻抗,確認通道穩定後再開始實驗。
107
+
108
+ ```csharp
109
+ BrainBitManager.Instance.StartImpedanceStream(
110
+ autoWriteToLabData: false,
111
+ tag: "precheck_impedance");
112
+ ```
113
+
114
+ 讀取目前阻抗:
115
+
116
+ ```csharp
117
+ var impedance = BrainBitManager.Instance.GetLatestImpedanceData();
118
+ if (impedance != null)
119
+ {
120
+ Debug.Log(impedance.ToString());
121
+
122
+ if (impedance.IsImpedanceGood)
123
+ {
124
+ Debug.Log("All channels are ready.");
125
+ }
126
+ else
127
+ {
128
+ Debug.LogWarning(impedance.GetImpedanceStatus());
129
+ }
130
+ }
131
+ ```
132
+
133
+ 停止阻抗檢查:
134
+
135
+ ```csharp
136
+ BrainBitManager.Instance.StopImpedanceStream();
137
+ ```
138
+
139
+ 本套件目前用 `200000` ohm 作為預設警示門檻。若任何通道高於此值,`IsImpedanceGood` 會是 `false`。
140
+
141
+ ### EEG 錄製
142
+
143
+ 開始 EEG:
144
+
145
+ ```csharp
146
+ BrainBitManager.Instance.StartEEGStream(
147
+ autoWriteToLabData: true,
148
+ tag: "baseline_eeg");
149
+ ```
150
+
151
+ 取得最新 EEG:
152
+
153
+ ```csharp
154
+ var eeg = BrainBitManager.Instance.GetLatestEEGData();
155
+ if (eeg != null)
156
+ {
157
+ Debug.Log($"T3:{eeg.T3} T4:{eeg.T4} O1:{eeg.O1} O2:{eeg.O2}");
158
+ }
159
+ ```
160
+
161
+ 切換實驗階段時,不需要停止 EEG,可以直接改 tag:
162
+
163
+ ```csharp
164
+ BrainBitManager.Instance.SetEEGTag("task1_eeg");
165
+ ```
166
+
167
+ 停止 EEG:
168
+
169
+ ```csharp
170
+ BrainBitManager.Instance.StopEEGStream();
171
+ ```
172
+
173
+ ### 專注、放鬆與五頻段資料
174
+
175
+ 情緒處理會使用 EEG 訊號計算 MindData 與 SpectralData。啟動後需要先校正,預設校正時間為 6 秒。校正完成前 `GetLatestMindData()` 與 `GetLatestSpectralData()` 可能回傳 `null`。
176
+
177
+ 啟動:
178
+
179
+ ```csharp
180
+ BrainBitManager.Instance.StartEmotionsProcessing(
181
+ autoWriteToLabData: true,
182
+ mindTag: "baseline_mind",
183
+ spectralTag: "baseline_spectral");
184
+ ```
185
+
186
+ 監聽校正:
187
+
188
+ ```csharp
189
+ BrainBitManager.Instance.OnCalibrationProgress += progress =>
190
+ {
191
+ Debug.Log($"Calibration: {progress}%");
192
+ };
193
+
194
+ BrainBitManager.Instance.OnCalibrationFinished += () =>
195
+ {
196
+ Debug.Log("Calibration finished.");
197
+ };
198
+ ```
199
+
200
+ 讀取專注與放鬆:
201
+
202
+ ```csharp
203
+ if (BrainBitManager.Instance.IsEmotionsCalibrated)
204
+ {
205
+ var mind = BrainBitManager.Instance.GetLatestMindData();
206
+ if (mind != null)
207
+ {
208
+ Debug.Log($"Attention:{mind.Attention:F1} Relaxation:{mind.Relaxation:F1}");
209
+ }
210
+ }
211
+ ```
212
+
213
+ 讀取五頻段百分比:
214
+
215
+ ```csharp
216
+ var spectral = BrainBitManager.Instance.GetLatestSpectralData();
217
+ if (spectral != null)
218
+ {
219
+ Debug.Log(
220
+ $"Delta:{spectral.Delta:F1} " +
221
+ $"Theta:{spectral.Theta:F1} " +
222
+ $"Alpha:{spectral.Alpha:F1} " +
223
+ $"Beta:{spectral.Beta:F1} " +
224
+ $"Gamma:{spectral.Gamma:F1}");
225
+ }
226
+ ```
227
+
228
+ 切換 tag:
229
+
230
+ ```csharp
231
+ BrainBitManager.Instance.SetMindTag("task1_mind");
232
+ BrainBitManager.Instance.SetSpectralTag("task1_spectral");
233
+ ```
234
+
235
+ 停止:
236
+
237
+ ```csharp
238
+ BrainBitManager.Instance.StopEmotionsProcessing();
239
+ ```
240
+
241
+ 若中途換受測者、頭帶取下後重新戴上,或資料明顯不穩,可以重新校正:
242
+
243
+ ```csharp
244
+ BrainBitManager.Instance.RestartCalibration();
245
+ ```
246
+
247
+ ## 完整範例程式
248
+
249
+ 以下範例示範一個常見實驗流程:連線、檢查阻抗、開始錄 EEG 與情緒資料、切換階段、結束錄製。
250
+
251
+ ```csharp
252
+ using UnityEngine;
253
+
254
+ public class BrainBitSessionExample : MonoBehaviour
255
+ {
256
+ private void OnEnable()
257
+ {
258
+ BrainBitManager.Instance.OnConnectionStatusChanged += HandleConnectionChanged;
259
+ BrainBitManager.Instance.OnCalibrationProgress += HandleCalibrationProgress;
260
+ BrainBitManager.Instance.OnCalibrationFinished += HandleCalibrationFinished;
261
+ BrainBitManager.Instance.OnError += HandleError;
262
+ }
263
+
264
+ private void OnDisable()
265
+ {
266
+ if (BrainBitManager.Instance == null) return;
267
+
268
+ BrainBitManager.Instance.OnConnectionStatusChanged -= HandleConnectionChanged;
269
+ BrainBitManager.Instance.OnCalibrationProgress -= HandleCalibrationProgress;
270
+ BrainBitManager.Instance.OnCalibrationFinished -= HandleCalibrationFinished;
271
+ BrainBitManager.Instance.OnError -= HandleError;
272
+ }
273
+
274
+ public void ConnectBrainBit()
275
+ {
276
+ BrainBitManager.Instance.ManualConnect();
277
+ }
278
+
279
+ private void HandleConnectionChanged(bool connected)
280
+ {
281
+ if (!connected) return;
282
+
283
+ Debug.Log($"Connected: {BrainBitManager.Instance.ConnectedDeviceName}");
284
+
285
+ BrainBitManager.Instance.StartImpedanceStream(
286
+ autoWriteToLabData: false,
287
+ tag: "precheck_impedance");
288
+ }
289
+
290
+ public void StartRecordingIfReady()
291
+ {
292
+ var impedance = BrainBitManager.Instance.GetLatestImpedanceData();
293
+ if (impedance == null || !impedance.IsImpedanceGood)
294
+ {
295
+ Debug.LogWarning("BrainBit impedance is not ready. Adjust the headband first.");
296
+ return;
297
+ }
298
+
299
+ BrainBitManager.Instance.StopImpedanceStream();
300
+
301
+ BrainBitManager.Instance.StartEEGStream(
302
+ autoWriteToLabData: true,
303
+ tag: "baseline_eeg");
304
+
305
+ BrainBitManager.Instance.StartEmotionsProcessing(
306
+ autoWriteToLabData: true,
307
+ mindTag: "baseline_mind",
308
+ spectralTag: "baseline_spectral");
309
+ }
310
+
311
+ public void SetExperimentPhase(string phaseName)
312
+ {
313
+ BrainBitManager.Instance.SetEEGTag($"{phaseName}_eeg");
314
+ BrainBitManager.Instance.SetMindTag($"{phaseName}_mind");
315
+ BrainBitManager.Instance.SetSpectralTag($"{phaseName}_spectral");
316
+ }
317
+
318
+ private void Update()
319
+ {
320
+ if (!BrainBitManager.Instance.IsEmotionsCalibrated) return;
321
+
322
+ var mind = BrainBitManager.Instance.GetLatestMindData();
323
+ var spectral = BrainBitManager.Instance.GetLatestSpectralData();
324
+
325
+ if (mind != null)
326
+ {
327
+ Debug.Log($"Attention:{mind.Attention:F1} Relaxation:{mind.Relaxation:F1}");
328
+ }
329
+
330
+ if (spectral != null)
331
+ {
332
+ Debug.Log($"Alpha:{spectral.Alpha:F1} Beta:{spectral.Beta:F1}");
333
+ }
334
+ }
335
+
336
+ public void StopRecording()
337
+ {
338
+ BrainBitManager.Instance.StopEmotionsProcessing();
339
+ BrainBitManager.Instance.StopEEGStream();
340
+ BrainBitManager.Instance.StopImpedanceStream();
341
+ }
342
+
343
+ private void HandleCalibrationProgress(int progress)
344
+ {
345
+ Debug.Log($"Calibration progress: {progress}%");
346
+ }
347
+
348
+ private void HandleCalibrationFinished()
349
+ {
350
+ Debug.Log("BrainBit emotion calibration finished.");
351
+ }
352
+
353
+ private void HandleError(string message)
354
+ {
355
+ Debug.LogError($"BrainBit error: {message}");
356
+ }
357
+ }
358
+ ```
359
+
360
+ ## 範例場景
361
+
362
+ 本 repo 內有一個範例場景:
363
+
364
+ ```text
365
+ Packages/LabFrame_Brainbit/Sample/SampleScene.unity
366
+ ```
367
+
368
+ 範例場景使用 `BrainBitCheckController` 顯示連線狀態、阻抗數值與阻抗警示。它適合拿來做實驗前的配戴檢查畫面,也可以直接參考按鈕如何呼叫 `BrainBitManager`。
369
+
370
+ ## 資料格式
371
+
372
+ 當 `autoWriteToLabData` 設為 `true` 且 `LabDataManager` 已初始化時,套件會把資料物件寫入 LabFrame。實際檔案位置與檔案格式由 LabFrame 專案設定決定;下面列的是本套件交給 LabFrame 的資料欄位。
373
+
374
+ ### EEG:`BrainBit_EEGData`
375
+
376
+ | 欄位 | 型別 | 單位 | 說明 |
377
+ |---|---|---|---|
378
+ | `T3` | `double` | V | 左側顳葉通道 EEG |
379
+ | `T4` | `double` | V | 右側顳葉通道 EEG |
380
+ | `O1` | `double` | V | 左側枕葉通道 EEG |
381
+ | `O2` | `double` | V | 右側枕葉通道 EEG |
382
+ | `EEGValues` | `List<double>` | V | 順序為 `T3`、`T4`、`O1`、`O2` |
383
+
384
+ BrainBit 官方資料流每通道為 250 samples/sec。本套件保留最新一筆 EEG,可用 `GetLatestEEGData()` 取得。
385
+
386
+ ### 阻抗:`BrainBit_ImpedanceData`
387
+
388
+ | 欄位 | 型別 | 單位 | 說明 |
389
+ |---|---|---|---|
390
+ | `T3` | `double` | ohm | 左側顳葉通道阻抗 |
391
+ | `T4` | `double` | ohm | 右側顳葉通道阻抗 |
392
+ | `O1` | `double` | ohm | 左側枕葉通道阻抗 |
393
+ | `O2` | `double` | ohm | 右側枕葉通道阻抗 |
394
+ | `ImpedanceValues` | `List<double>` | ohm | 順序為 `T3`、`T4`、`O1`、`O2` |
395
+ | `IsImpedanceGood` | `bool` | 無 | 4 通道都低於 `200000` ohm 時為 `true` |
396
+
397
+ 輔助方法:
398
+
399
+ ```csharp
400
+ string status = impedance.GetImpedanceStatus();
401
+ var values = impedance.GetImpedanceValues();
402
+ ```
403
+
404
+ ### 專注與放鬆:`BrainBit_MindData`
405
+
406
+ | 欄位 | 型別 | 範圍 | 說明 |
407
+ |---|---|---|---|
408
+ | `Attention` | `double` | 約 0-100 | 相對專注度,較平滑,建議顯示給使用者 |
409
+ | `Relaxation` | `double` | 約 0-100 | 相對放鬆度,較平滑,建議顯示給使用者 |
410
+ | `InstAttention` | `double` | 約 0-100 | 瞬時專注度,波動較大 |
411
+ | `InstRelaxation` | `double` | 約 0-100 | 瞬時放鬆度,波動較大 |
412
+
413
+ MindData 必須等 `IsEmotionsCalibrated == true` 後才有穩定意義。
414
+
415
+ ### 五頻段百分比:`BrainBit_SpectralData`
416
+
417
+ | 欄位 | 型別 | 單位 | 說明 |
418
+ |---|---|---|---|
419
+ | `Delta` | `double` | percent | Delta 波百分比 |
420
+ | `Theta` | `double` | percent | Theta 波百分比 |
421
+ | `Alpha` | `double` | percent | Alpha 波百分比 |
422
+ | `Beta` | `double` | percent | Beta 波百分比 |
423
+ | `Gamma` | `double` | percent | Gamma 波百分比 |
424
+
425
+ ### 連線事件:`BrainBit_ConnectionData`
426
+
427
+ | 欄位 | 型別 | 說明 |
428
+ |---|---|---|
429
+ | `IsConnected` | `bool` | 連線狀態 |
430
+ | `DeviceName` | `string` | BrainBit 裝置名稱 |
431
+ | `DeviceAddress` | `string` | BrainBit 裝置位址 |
432
+ | `EventType` | `string` | `Connected`、`Disconnected`、`EEG_Stream_Started` 等事件 |
433
+
434
+ ## Tag 與資料分段
435
+
436
+ `tag` 是寫入 LabFrame 時用來區分資料階段的名稱。建議使用清楚且穩定的命名,例如:
437
+
438
+ ```text
439
+ baseline_eeg
440
+ baseline_mind
441
+ baseline_spectral
442
+ task1_eeg
443
+ task1_mind
444
+ task1_spectral
445
+ rest_impedance
446
+ ```
447
+
448
+ EEG、MindData、SpectralData 都可以在不中斷串流的情況下切換 tag:
449
+
450
+ ```csharp
451
+ BrainBitManager.Instance.SetEEGTag("task2_eeg");
452
+ BrainBitManager.Instance.SetMindTag("task2_mind");
453
+ BrainBitManager.Instance.SetSpectralTag("task2_spectral");
454
+ ```
455
+
456
+ 下一筆寫入的資料會使用新的 tag。
457
+
458
+ ## 事件 API
459
+
460
+ 若你不想在 `Update()` 裡輪詢,可以訂閱事件:
461
+
462
+ ```csharp
463
+ BrainBitManager.Instance.OnEEGDataReceived += data =>
464
+ {
465
+ Debug.Log(data.ToString());
466
+ };
467
+
468
+ BrainBitManager.Instance.OnImpedanceDataReceived += data =>
469
+ {
470
+ Debug.Log(data.GetImpedanceStatus());
471
+ };
472
+
473
+ BrainBitManager.Instance.OnMindDataReceived += data =>
474
+ {
475
+ Debug.Log($"Attention:{data.Attention:F1}");
476
+ };
477
+
478
+ BrainBitManager.Instance.OnSpectralDataReceived += data =>
479
+ {
480
+ Debug.Log($"Alpha:{data.Alpha:F1}");
481
+ };
482
+
483
+ BrainBitManager.Instance.OnEmotionsArtifact += hasArtifact =>
484
+ {
485
+ if (hasArtifact)
486
+ {
487
+ Debug.LogWarning("BrainBit signal artifact detected.");
488
+ }
489
+ };
490
+ ```
491
+
492
+ ## 設定項目
493
+
494
+ `BrainBitConfig` 可控制掃描、重連與情緒校正行為。
495
+
496
+ | 欄位 | 預設 | 說明 |
497
+ |---|---:|---|
498
+ | `AutoConnectOnInit` | `false` | Manager 初始化後是否自動掃描連線 |
499
+ | `DisconnectNotification` | `true` | 斷線時是否顯示提示 |
500
+ | `ScanTimeoutSeconds` | `10.0` | 掃描逾時秒數 |
501
+ | `ImpedanceWarningThreshold` | `200000.0` | 阻抗警示參考門檻,單位 ohm |
502
+ | `AutoReconnectAttempts` | `3` | 斷線後自動重連嘗試次數 |
503
+ | `ReconnectIntervalSeconds` | `2.0` | 重連間隔秒數 |
504
+ | `AutoSelectBestSignal` | `false` | 多台 BrainBit 時是否選 RSSI 最強者 |
505
+ | `TargetDeviceAddress` | `""` | 優先連線的 BrainBit MAC;留空時維持原本選台行為 |
506
+ | `ConnectDelaySeconds` | `1.0` | 停止掃描後等待多久才建立連線 |
507
+ | `EmotionsCalibrationLength` | `6` | 情緒處理校正秒數 |
508
+ | `EmotionsMentalEstimation` | `false` | 是否啟用 Mental Estimation |
509
+ | `EmotionsPrioritySide` | `SideType.NONE` | 情緒分析優先腦側:`NONE`、`LEFT`、`RIGHT` |
510
+
511
+ 如果現場同時有多台 BrainBit,建議用 `TargetDeviceAddress` 優先指定 MAC。設定後,掃描會先尋找該 MAC;如果連續 3 次、每次間隔 2 秒的掃描結果都沒有看到目標,系統會改用原本的選台策略尋找其他 BrainBit。`AutoSelectBestSignal` 只建議在沒有指定 MAC、或目標 MAC 找不到後可接受用 RSSI 選台時使用。
512
+
513
+ Android 實機若要每台頭顯綁定不同 BrainBit,請在各裝置的 `Application.persistentDataPath/Config/BrainBitConfig.json` 放完整設定檔,例如:
514
+
515
+ ```json
516
+ {
517
+ "AutoConnectOnInit": false,
518
+ "DisconnectNotification": true,
519
+ "ScanTimeoutSeconds": 20.0,
520
+ "ImpedanceWarningThreshold": 200000.0,
521
+ "AutoReconnectAttempts": 3,
522
+ "ReconnectIntervalSeconds": 2.0,
523
+ "AutoSelectBestSignal": false,
524
+ "ConnectDelaySeconds": 1.0,
525
+ "EmotionsCalibrationLength": 6,
526
+ "EmotionsMentalEstimation": false,
527
+ "EmotionsPrioritySide": 0,
528
+ "TargetDeviceAddress": "E0:39:7A:68:75:58"
529
+ }
530
+ ```
531
+
532
+ 若裝置上已有舊版 `BrainBitConfig.json`,請整檔覆蓋成最新欄位;磁碟上的設定會優先於內建範本。
533
+
534
+ 目前 `BrainBit_ImpedanceData.IsImpedanceGood` 使用 `200000` ohm 作為判斷門檻;若你的實驗需要不同標準,請在 UI 或檢查流程中使用自訂門檻判斷。
535
+
536
+ ## 設備資訊查詢
537
+
538
+ 需要查看電量、韌體版本、硬體版本、取樣頻率等資訊時,可使用 `SensorInfoProvider`:
539
+
540
+ ```csharp
541
+ using NeuroSDK;
542
+ using System.Collections.Generic;
543
+
544
+ void ShowDeviceInfo(BrainBitSensor sensor)
545
+ {
546
+ Dictionary<string, string> parameters =
547
+ SensorInfoProvider.GetBrainBitSensorParameters(sensor);
548
+
549
+ foreach (var item in parameters)
550
+ {
551
+ Debug.Log($"{item.Key}: {item.Value}");
552
+ }
553
+ }
554
+ ```
555
+
556
+ 一般使用者不一定需要呼叫這段。它比較適合現場除錯、確認電量與確認裝置版本。
557
+
558
+ ## 常見問題
559
+
560
+ ### 掃不到設備
561
+
562
+ - 確認 BrainBit 已開機且有電。
563
+ - 確認 App 已允許藍牙、定位與附近裝置權限。
564
+ - 確認 BrainBit 沒有被其他 App 或其他手機連線。
565
+ - 把 BrainBit 靠近執行 App 的裝置後再掃描。
566
+ - 如果現場有多台 BrainBit,優先在 `TargetDeviceAddress` 填入指定 MAC;若目標連續 3 次、每次間隔 2 秒的掃描都找不到,Manager 會改找其他設備。
567
+
568
+ ### 阻抗一直太高
569
+
570
+ - 調整頭帶位置,讓 `T3`、`T4`、`O1`、`O2` 更貼近頭皮。
571
+ - 撥開頭髮,避免頭髮卡在電極與頭皮之間。
572
+ - 確認前額參考電極有貼住皮膚。
573
+ - 讓受測者先保持不動,再觀察數值是否下降。
574
+ - 若只有單一通道高,優先調整該通道所在的位置。
575
+
576
+ ### MindData 或 SpectralData 一直是空的
577
+
578
+ - 確認已呼叫 `StartEmotionsProcessing()`。
579
+ - 確認 EEG 正在輸入。
580
+ - 等待 `OnCalibrationFinished` 或 `IsEmotionsCalibrated == true`。
581
+ - 校正期間請受測者保持安靜、少眨眼、少咬牙、少說話。
582
+ - 若中途重新配戴,呼叫 `RestartCalibration()`。
583
+
584
+ ### 資料沒有寫入 LabFrame
585
+
586
+ - 確認呼叫串流時 `autoWriteToLabData` 是 `true`。
587
+ - 確認 `LabDataManager.Instance.IsInited` 為 `true`。
588
+ - 確認 tag 不為空字串。
589
+ - 確認有呼叫 `StartEEGStream()` 或 `StartEmotionsProcessing()`。
590
+
591
+ ### Android build 或執行出錯
592
+
593
+ - 確認已安裝 `unity_em_st_artifacts` 固定 commit。
594
+ - 確認 Android 權限已在實機上允許。
595
+ - 若使用較新的 Android 版本,請特別檢查 `BLUETOOTH_SCAN` 與 `BLUETOOTH_CONNECT` 權限。
596
+
597
+ ## 官方參考
598
+
599
+ - BrainBit SDK - Receiving data: https://sdk.brainbit.com/sdk2_bb/
600
+ - BrainBit SDK - Device recommendation: https://sdk.brainbit.com/device-recommendation/
601
+ - BrainBit SDK - Emotions library: https://sdk.brainbit.com/lib-emotions/
602
+ - BrainBit SDK - Overview and supported platforms: https://sdk.brainbit.com/sdk2_overview/